payplus-python 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. payplus_python-0.1.2/LICENSE +21 -0
  2. payplus_python-0.1.2/PKG-INFO +446 -0
  3. payplus_python-0.1.2/README.md +402 -0
  4. payplus_python-0.1.2/examples/basic_payment.py +29 -0
  5. payplus_python-0.1.2/examples/fastapi_webhooks.py +130 -0
  6. payplus_python-0.1.2/examples/subscription_saas.py +206 -0
  7. payplus_python-0.1.2/payplus/__init__.py +30 -0
  8. payplus_python-0.1.2/payplus/api/__init__.py +15 -0
  9. payplus_python-0.1.2/payplus/api/base.py +37 -0
  10. payplus_python-0.1.2/payplus/api/payment_pages.py +176 -0
  11. payplus_python-0.1.2/payplus/api/payments.py +117 -0
  12. payplus_python-0.1.2/payplus/api/recurring.py +216 -0
  13. payplus_python-0.1.2/payplus/api/transactions.py +203 -0
  14. payplus_python-0.1.2/payplus/client.py +211 -0
  15. payplus_python-0.1.2/payplus/exceptions.py +57 -0
  16. payplus_python-0.1.2/payplus/models/__init__.py +23 -0
  17. payplus_python-0.1.2/payplus/models/customer.py +136 -0
  18. payplus_python-0.1.2/payplus/models/invoice.py +242 -0
  19. payplus_python-0.1.2/payplus/models/payment.py +179 -0
  20. payplus_python-0.1.2/payplus/models/subscription.py +193 -0
  21. payplus_python-0.1.2/payplus/models/tier.py +226 -0
  22. payplus_python-0.1.2/payplus/subscriptions/__init__.py +11 -0
  23. payplus_python-0.1.2/payplus/subscriptions/billing.py +231 -0
  24. payplus_python-0.1.2/payplus/subscriptions/manager.py +600 -0
  25. payplus_python-0.1.2/payplus/subscriptions/storage.py +571 -0
  26. payplus_python-0.1.2/payplus/webhooks/__init__.py +10 -0
  27. payplus_python-0.1.2/payplus/webhooks/handler.py +370 -0
  28. payplus_python-0.1.2/payplus_python.egg-info/PKG-INFO +446 -0
  29. payplus_python-0.1.2/payplus_python.egg-info/SOURCES.txt +34 -0
  30. payplus_python-0.1.2/payplus_python.egg-info/dependency_links.txt +1 -0
  31. payplus_python-0.1.2/payplus_python.egg-info/requires.txt +26 -0
  32. payplus_python-0.1.2/payplus_python.egg-info/top_level.txt +4 -0
  33. payplus_python-0.1.2/pyproject.toml +79 -0
  34. payplus_python-0.1.2/setup.cfg +4 -0
  35. payplus_python-0.1.2/tests/__init__.py +1 -0
  36. payplus_python-0.1.2/tests/test_models.py +348 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Two Solutions
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,446 @@
1
+ Metadata-Version: 2.4
2
+ Name: payplus-python
3
+ Version: 0.1.2
4
+ Summary: Python SDK for PayPlus payment gateway with subscription management for SaaS apps
5
+ Author-email: Two Solutions <dev@two-solutions.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Two-Solutions/payplus-python
8
+ Project-URL: Documentation, https://github.com/Two-Solutions/payplus-python#readme
9
+ Project-URL: Repository, https://github.com/Two-Solutions/payplus-python
10
+ Project-URL: Issues, https://github.com/Two-Solutions/payplus-python/issues
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: httpx>=0.25.0
23
+ Requires-Dist: email-validator>=2.0.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: sqlalchemy>=2.0.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
30
+ Requires-Dist: black>=23.0.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
32
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
33
+ Provides-Extra: fastapi
34
+ Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
35
+ Provides-Extra: flask
36
+ Requires-Dist: flask>=2.0.0; extra == "flask"
37
+ Provides-Extra: postgres
38
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
39
+ Requires-Dist: asyncpg>=0.28.0; extra == "postgres"
40
+ Provides-Extra: mongodb
41
+ Requires-Dist: motor>=3.0.0; extra == "mongodb"
42
+ Requires-Dist: pymongo>=4.0.0; extra == "mongodb"
43
+ Dynamic: license-file
44
+
45
+ # PayPlus Python SDK
46
+
47
+ A comprehensive Python SDK for [PayPlus](https://www.payplus.co.il/) payment gateway with built-in subscription management for SaaS applications.
48
+
49
+ [![PyPI version](https://badge.fury.io/py/payplus-sdk.svg)](https://badge.fury.io/py/payplus-sdk)
50
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
52
+
53
+ ## Features
54
+
55
+ - 🔌 **Full PayPlus API Coverage** - Payment pages, transactions, recurring payments, tokenization
56
+ - 💳 **Subscription Management** - Stripe-like subscription handling for SaaS apps
57
+ - 🗄️ **Database Integration** - SQLAlchemy and MongoDB storage backends
58
+ - 🔔 **Webhook Handling** - Easy IPN/webhook integration with signature verification
59
+ - ⚡ **Async Support** - Full async/await support for modern Python apps
60
+ - 🏗️ **Type Safe** - Full type hints with Pydantic models
61
+
62
+ ## Installation
63
+
64
+ ```bash
65
+ pip install payplus-sdk
66
+ ```
67
+
68
+ With optional dependencies:
69
+
70
+ ```bash
71
+ # For FastAPI webhook integration
72
+ pip install payplus-sdk[fastapi]
73
+
74
+ # For PostgreSQL storage
75
+ pip install payplus-sdk[postgres]
76
+
77
+ # For MongoDB storage
78
+ pip install payplus-sdk[mongodb]
79
+
80
+ # All extras
81
+ pip install payplus-sdk[fastapi,postgres,mongodb]
82
+ ```
83
+
84
+ ## Quick Start
85
+
86
+ ### Basic Payment Link
87
+
88
+ ```python
89
+ from payplus import PayPlus
90
+
91
+ # Initialize client
92
+ client = PayPlus(
93
+ api_key="your_api_key",
94
+ secret_key="your_secret_key",
95
+ sandbox=True # Use sandbox for testing
96
+ )
97
+
98
+ # Generate a payment link
99
+ result = client.payment_pages.generate_link(
100
+ amount=100.00,
101
+ currency="ILS",
102
+ description="Premium Plan - Monthly",
103
+ customer_email="customer@example.com",
104
+ success_url="https://yourapp.com/success",
105
+ failure_url="https://yourapp.com/failure",
106
+ create_token=True # Save card for future charges
107
+ )
108
+
109
+ print(f"Payment URL: {result['data']['payment_page_link']}")
110
+ ```
111
+
112
+ ### Direct Card Charge (with token)
113
+
114
+ ```python
115
+ # Charge a saved card
116
+ result = client.transactions.charge(
117
+ token="card_token_from_payment",
118
+ amount=99.00,
119
+ currency="ILS",
120
+ description="Monthly subscription"
121
+ )
122
+
123
+ print(f"Transaction: {result['data']['transaction_uid']}")
124
+ ```
125
+
126
+ ### Recurring Payments
127
+
128
+ ```python
129
+ # Create a recurring payment
130
+ result = client.recurring.add(
131
+ token="card_token",
132
+ amount=49.00,
133
+ currency="ILS",
134
+ interval="month",
135
+ interval_count=1,
136
+ description="Pro Plan"
137
+ )
138
+
139
+ recurring_uid = result['data']['recurring_uid']
140
+
141
+ # Cancel recurring
142
+ client.recurring.cancel(recurring_uid)
143
+ ```
144
+
145
+ ## Subscription Management
146
+
147
+ The SDK includes a complete subscription management system for SaaS applications:
148
+
149
+ ```python
150
+ from payplus import PayPlus, SubscriptionManager
151
+ from payplus.subscriptions.storage import MongoDBStorage
152
+ from motor.motor_asyncio import AsyncIOMotorClient
153
+ from decimal import Decimal
154
+
155
+ # Setup
156
+ client = PayPlus(api_key="...", secret_key="...")
157
+ mongo = AsyncIOMotorClient("mongodb://localhost:27017")
158
+ storage = MongoDBStorage(mongo.your_database)
159
+
160
+ manager = SubscriptionManager(client, storage)
161
+
162
+ # Create pricing tiers
163
+ await manager.create_tier(
164
+ tier_id="free",
165
+ name="Free",
166
+ price=Decimal("0"),
167
+ features=[
168
+ {"feature_id": "projects", "name": "Projects", "included_quantity": 3},
169
+ {"feature_id": "storage", "name": "Storage", "included_quantity": 1},
170
+ ]
171
+ )
172
+
173
+ await manager.create_tier(
174
+ tier_id="pro",
175
+ name="Pro",
176
+ price=Decimal("79"),
177
+ trial_days=14,
178
+ features=[
179
+ {"feature_id": "projects", "name": "Projects", "included_quantity": None}, # Unlimited
180
+ {"feature_id": "storage", "name": "Storage", "included_quantity": 100},
181
+ {"feature_id": "priority_support", "name": "Priority Support"},
182
+ ]
183
+ )
184
+
185
+ # Create a customer
186
+ customer = await manager.create_customer(
187
+ email="user@example.com",
188
+ name="John Doe"
189
+ )
190
+
191
+ # Add payment method (from PayPlus token)
192
+ await manager.add_payment_method(
193
+ customer_id=customer.id,
194
+ token="card_token_from_payplus",
195
+ card_brand="Visa",
196
+ last_four="4242"
197
+ )
198
+
199
+ # Create subscription
200
+ subscription = await manager.create_subscription(
201
+ customer_id=customer.id,
202
+ tier_id="pro" # Will start with 14-day trial
203
+ )
204
+
205
+ print(f"Subscription: {subscription.id}")
206
+ print(f"Status: {subscription.status}") # "trialing"
207
+ print(f"Trial ends: {subscription.trial_end}")
208
+ ```
209
+
210
+ ### Subscription Lifecycle
211
+
212
+ ```python
213
+ # Upgrade/downgrade
214
+ await manager.change_tier(subscription.id, "enterprise")
215
+
216
+ # Pause subscription
217
+ await manager.pause_subscription(subscription.id)
218
+
219
+ # Resume subscription
220
+ await manager.resume_subscription(subscription.id)
221
+
222
+ # Cancel at period end
223
+ await manager.cancel_subscription(
224
+ subscription.id,
225
+ at_period_end=True,
226
+ reason="Customer requested"
227
+ )
228
+
229
+ # Cancel immediately
230
+ await manager.cancel_subscription(
231
+ subscription.id,
232
+ at_period_end=False
233
+ )
234
+ ```
235
+
236
+ ### Event Hooks
237
+
238
+ ```python
239
+ # Register event handlers
240
+ manager.on("subscription.created", lambda sub: print(f"New sub: {sub.id}"))
241
+ manager.on("payment.succeeded", lambda payment: print(f"Paid: {payment.amount}"))
242
+ manager.on("payment.failed", lambda payment: send_dunning_email(payment))
243
+ manager.on("subscription.canceled", lambda sub: handle_cancellation(sub))
244
+ ```
245
+
246
+ ### Billing Service (for scheduled jobs)
247
+
248
+ ```python
249
+ from payplus.subscriptions import BillingService
250
+
251
+ billing = BillingService(manager)
252
+
253
+ # Run daily via your scheduler (Celery, APScheduler, cron, etc.)
254
+ async def daily_billing_job():
255
+ # Process subscription renewals
256
+ renewed = await billing.process_due_renewals()
257
+
258
+ # Convert ending trials to paid
259
+ converted = await billing.process_trial_endings()
260
+
261
+ # Retry failed payments
262
+ retried = await billing.process_past_due()
263
+
264
+ # Finalize cancellations
265
+ canceled = await billing.process_cancellations()
266
+ ```
267
+
268
+ ## Webhook Handling
269
+
270
+ ```python
271
+ from fastapi import FastAPI, Request
272
+ from payplus import PayPlus
273
+ from payplus.webhooks import WebhookHandler
274
+
275
+ app = FastAPI()
276
+ client = PayPlus(api_key="...", secret_key="...")
277
+ webhooks = WebhookHandler(client)
278
+
279
+ @webhooks.on("payment.success")
280
+ async def handle_payment_success(event):
281
+ print(f"Payment succeeded: {event.transaction_uid}")
282
+ print(f"Amount: {event.amount} {event.currency}")
283
+ # Update your database, send confirmation email, etc.
284
+
285
+ @webhooks.on("payment.failure")
286
+ async def handle_payment_failure(event):
287
+ print(f"Payment failed: {event.status_description}")
288
+ # Send retry notification, update subscription status, etc.
289
+
290
+ @webhooks.on("recurring.charged")
291
+ async def handle_recurring_charge(event):
292
+ print(f"Recurring payment: {event.recurring_uid}")
293
+
294
+ @app.post("/webhooks/payplus")
295
+ async def payplus_webhook(request: Request):
296
+ payload = await request.body()
297
+ signature = request.headers.get("X-PayPlus-Signature", "")
298
+
299
+ event = await webhooks.handle_async(payload, signature)
300
+ return {"received": True}
301
+ ```
302
+
303
+ Or use the built-in router:
304
+
305
+ ```python
306
+ from payplus.webhooks import create_fastapi_webhook_router
307
+
308
+ router = create_fastapi_webhook_router(webhooks)
309
+ app.include_router(router)
310
+ ```
311
+
312
+ ## Storage Backends
313
+
314
+ ### MongoDB
315
+
316
+ ```python
317
+ from motor.motor_asyncio import AsyncIOMotorClient
318
+ from payplus.subscriptions.storage import MongoDBStorage
319
+
320
+ client = AsyncIOMotorClient("mongodb://localhost:27017")
321
+ storage = MongoDBStorage(client.your_database)
322
+
323
+ # Create indexes (run once)
324
+ await storage.create_indexes()
325
+ ```
326
+
327
+ ### SQLAlchemy (PostgreSQL, MySQL, SQLite)
328
+
329
+ ```python
330
+ from sqlalchemy.ext.asyncio import create_async_engine
331
+ from payplus.subscriptions.storage import SQLAlchemyStorage
332
+
333
+ engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
334
+ storage = SQLAlchemyStorage(engine)
335
+
336
+ # Create tables (run once)
337
+ await storage.create_tables()
338
+ ```
339
+
340
+ ## Models
341
+
342
+ The SDK provides Pydantic models for all entities:
343
+
344
+ ```python
345
+ from payplus.models import (
346
+ Customer,
347
+ Subscription, SubscriptionStatus, BillingCycle,
348
+ Payment, PaymentStatus,
349
+ Invoice, InvoiceStatus,
350
+ Tier, TierFeature,
351
+ )
352
+
353
+ # Create a tier programmatically
354
+ from payplus.models.tier import TierTemplates
355
+
356
+ free_tier = TierTemplates.free()
357
+ pro_tier = TierTemplates.pro(price=Decimal("99"))
358
+ ```
359
+
360
+ ## API Reference
361
+
362
+ ### PayPlus Client
363
+
364
+ | Module | Methods |
365
+ |--------|---------|
366
+ | `client.payment_pages` | `generate_link()`, `get_status()` |
367
+ | `client.transactions` | `charge()`, `get()`, `refund()`, `list()` |
368
+ | `client.recurring` | `add()`, `charge()`, `cancel()`, `get()`, `list()` |
369
+ | `client.payments` | `check_card()`, `tokenize()`, `get_token()`, `delete_token()` |
370
+
371
+ ### Subscription Manager
372
+
373
+ | Method | Description |
374
+ |--------|-------------|
375
+ | `create_customer()` | Create a new customer |
376
+ | `add_payment_method()` | Add a payment method to customer |
377
+ | `create_tier()` | Create a pricing tier |
378
+ | `create_subscription()` | Create a new subscription |
379
+ | `cancel_subscription()` | Cancel a subscription |
380
+ | `change_tier()` | Upgrade/downgrade subscription |
381
+ | `pause_subscription()` | Pause a subscription |
382
+ | `resume_subscription()` | Resume a paused subscription |
383
+
384
+ ## Configuration
385
+
386
+ ### Environment Variables
387
+
388
+ ```bash
389
+ PAYPLUS_API_KEY=your_api_key
390
+ PAYPLUS_SECRET_KEY=your_secret_key
391
+ PAYPLUS_TERMINAL_UID=your_terminal_uid # Optional
392
+ PAYPLUS_SANDBOX=true # For testing
393
+ ```
394
+
395
+ ### Sandbox vs Production
396
+
397
+ ```python
398
+ # Sandbox (testing)
399
+ client = PayPlus(
400
+ api_key="...",
401
+ secret_key="...",
402
+ sandbox=True # Uses restapidev.payplus.co.il
403
+ )
404
+
405
+ # Production
406
+ client = PayPlus(
407
+ api_key="...",
408
+ secret_key="...",
409
+ sandbox=False # Uses restapi.payplus.co.il
410
+ )
411
+ ```
412
+
413
+ ## Error Handling
414
+
415
+ ```python
416
+ from payplus.exceptions import (
417
+ PayPlusError,
418
+ PayPlusAPIError,
419
+ PayPlusAuthError,
420
+ SubscriptionError,
421
+ WebhookSignatureError,
422
+ )
423
+
424
+ try:
425
+ result = client.transactions.charge(token="...", amount=100)
426
+ except PayPlusAuthError:
427
+ print("Invalid API credentials")
428
+ except PayPlusAPIError as e:
429
+ print(f"API error [{e.status_code}]: {e.message}")
430
+ except PayPlusError as e:
431
+ print(f"General error: {e}")
432
+ ```
433
+
434
+ ## Contributing
435
+
436
+ Contributions are welcome! Please feel free to submit a Pull Request.
437
+
438
+ ## License
439
+
440
+ MIT License - see [LICENSE](LICENSE) for details.
441
+
442
+ ## Links
443
+
444
+ - [PayPlus Documentation](https://docs.payplus.co.il/)
445
+ - [GitHub Repository](https://github.com/Two-Solutions/payplus-python)
446
+ - [Issue Tracker](https://github.com/Two-Solutions/payplus-python/issues)