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.
- payplus_python-0.1.2/LICENSE +21 -0
- payplus_python-0.1.2/PKG-INFO +446 -0
- payplus_python-0.1.2/README.md +402 -0
- payplus_python-0.1.2/examples/basic_payment.py +29 -0
- payplus_python-0.1.2/examples/fastapi_webhooks.py +130 -0
- payplus_python-0.1.2/examples/subscription_saas.py +206 -0
- payplus_python-0.1.2/payplus/__init__.py +30 -0
- payplus_python-0.1.2/payplus/api/__init__.py +15 -0
- payplus_python-0.1.2/payplus/api/base.py +37 -0
- payplus_python-0.1.2/payplus/api/payment_pages.py +176 -0
- payplus_python-0.1.2/payplus/api/payments.py +117 -0
- payplus_python-0.1.2/payplus/api/recurring.py +216 -0
- payplus_python-0.1.2/payplus/api/transactions.py +203 -0
- payplus_python-0.1.2/payplus/client.py +211 -0
- payplus_python-0.1.2/payplus/exceptions.py +57 -0
- payplus_python-0.1.2/payplus/models/__init__.py +23 -0
- payplus_python-0.1.2/payplus/models/customer.py +136 -0
- payplus_python-0.1.2/payplus/models/invoice.py +242 -0
- payplus_python-0.1.2/payplus/models/payment.py +179 -0
- payplus_python-0.1.2/payplus/models/subscription.py +193 -0
- payplus_python-0.1.2/payplus/models/tier.py +226 -0
- payplus_python-0.1.2/payplus/subscriptions/__init__.py +11 -0
- payplus_python-0.1.2/payplus/subscriptions/billing.py +231 -0
- payplus_python-0.1.2/payplus/subscriptions/manager.py +600 -0
- payplus_python-0.1.2/payplus/subscriptions/storage.py +571 -0
- payplus_python-0.1.2/payplus/webhooks/__init__.py +10 -0
- payplus_python-0.1.2/payplus/webhooks/handler.py +370 -0
- payplus_python-0.1.2/payplus_python.egg-info/PKG-INFO +446 -0
- payplus_python-0.1.2/payplus_python.egg-info/SOURCES.txt +34 -0
- payplus_python-0.1.2/payplus_python.egg-info/dependency_links.txt +1 -0
- payplus_python-0.1.2/payplus_python.egg-info/requires.txt +26 -0
- payplus_python-0.1.2/payplus_python.egg-info/top_level.txt +4 -0
- payplus_python-0.1.2/pyproject.toml +79 -0
- payplus_python-0.1.2/setup.cfg +4 -0
- payplus_python-0.1.2/tests/__init__.py +1 -0
- 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
|
+
[](https://badge.fury.io/py/payplus-sdk)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](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)
|