cosmic-python-sdk 0.1.1__tar.gz → 0.2.0__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.
- cosmic_python_sdk-0.2.0/PKG-INFO +358 -0
- cosmic_python_sdk-0.2.0/README.md +328 -0
- cosmic_python_sdk-0.2.0/cosmic_python_sdk.egg-info/PKG-INFO +358 -0
- cosmic_python_sdk-0.2.0/cosmic_python_sdk.egg-info/SOURCES.txt +31 -0
- cosmic_python_sdk-0.2.0/cosmic_python_sdk.egg-info/requires.txt +6 -0
- {cosmic_python_sdk-0.1.1 → cosmic_python_sdk-0.2.0}/cosmic_sdk/__init__.py +1 -1
- cosmic_python_sdk-0.2.0/cosmic_sdk/client.py +117 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/config.py +41 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/errors.py +52 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/logger.py +51 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/resources/__init__.py +0 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/resources/base.py +76 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/resources/customers.py +24 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/resources/payments.py +36 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/resources/refunds.py +26 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/resources/transactions.py +13 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/types/__init__.py +0 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/types/common.py +47 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/types/customers.py +12 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/types/payments.py +23 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/types/refunds.py +13 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/validation.py +70 -0
- cosmic_python_sdk-0.2.0/cosmic_sdk/webhooks.py +31 -0
- cosmic_python_sdk-0.2.0/pyproject.toml +44 -0
- cosmic_python_sdk-0.2.0/setup.cfg +4 -0
- cosmic_python_sdk-0.2.0/tests/test_client.py +80 -0
- cosmic_python_sdk-0.2.0/tests/test_config.py +32 -0
- cosmic_python_sdk-0.2.0/tests/test_errors.py +39 -0
- cosmic_python_sdk-0.2.0/tests/test_logger.py +15 -0
- cosmic_python_sdk-0.2.0/tests/test_validation.py +103 -0
- cosmic_python_sdk-0.2.0/tests/test_webhooks.py +40 -0
- cosmic_python_sdk-0.1.1/PKG-INFO +0 -28
- cosmic_python_sdk-0.1.1/README.md +0 -17
- cosmic_python_sdk-0.1.1/cosmic_python_sdk.egg-info/PKG-INFO +0 -28
- cosmic_python_sdk-0.1.1/cosmic_python_sdk.egg-info/SOURCES.txt +0 -11
- cosmic_python_sdk-0.1.1/cosmic_python_sdk.egg-info/requires.txt +0 -1
- cosmic_python_sdk-0.1.1/cosmic_sdk/client.py +0 -31
- cosmic_python_sdk-0.1.1/pyproject.toml +0 -3
- cosmic_python_sdk-0.1.1/setup.cfg +0 -20
- cosmic_python_sdk-0.1.1/tests/test_client.py +0 -16
- {cosmic_python_sdk-0.1.1 → cosmic_python_sdk-0.2.0}/cosmic_python_sdk.egg-info/dependency_links.txt +0 -0
- {cosmic_python_sdk-0.1.1 → cosmic_python_sdk-0.2.0}/cosmic_python_sdk.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cosmic-python-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Official Python SDK for the Cosmic Gateway payment processing API
|
|
5
|
+
Author: Emmanuel Mnanka Samo
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/cosmic-payment/cosmic-gateway
|
|
8
|
+
Project-URL: Documentation, https://github.com/cosmic-payment/cosmic-gateway
|
|
9
|
+
Project-URL: Source, https://github.com/cosmic-payment/cosmic-gateway
|
|
10
|
+
Project-URL: Issues, https://github.com/cosmic-payment/cosmic-gateway/issues
|
|
11
|
+
Keywords: payment,gateway,cosmic,api,sdk,payments
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: requests>=2.28
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-mock>=3.0; extra == "dev"
|
|
29
|
+
Requires-Dist: responses>=0.23; extra == "dev"
|
|
30
|
+
|
|
31
|
+
# Cosmic Python SDK
|
|
32
|
+
|
|
33
|
+
Official Python client SDK for the [Cosmic Gateway](https://cosmicgateway.com) payment processing API.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Complete payment lifecycle** — charge, capture, refund, and query payments
|
|
38
|
+
- **Customer management** — create, retrieve, and list customers
|
|
39
|
+
- **Transaction history** — paginated transaction listing with filters
|
|
40
|
+
- **Webhook verification** — HMAC-SHA256 signature verification for incoming webhooks
|
|
41
|
+
- **Idempotency** — automatic idempotency key generation to safely retry requests
|
|
42
|
+
- **Retry with backoff** — automatic retry on 5xx errors and rate limits (429)
|
|
43
|
+
- **Request timeout** — configurable timeout per request
|
|
44
|
+
- **Environment switching** — built-in sandbox and production endpoints
|
|
45
|
+
- **Input validation** — pre-flight validation of amounts, currencies, emails, and more
|
|
46
|
+
- **Typed request/response models** — dataclass-based types for all API objects
|
|
47
|
+
- **Sensitive data masking** — automatic masking of API keys, PAN, and CVC in logs
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install cosmic-python-sdk
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Requires Python 3.8+.
|
|
56
|
+
|
|
57
|
+
### Development installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install cosmic-python-sdk[dev]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from cosmic_sdk import CosmicGateway
|
|
67
|
+
|
|
68
|
+
client = CosmicGateway(
|
|
69
|
+
api_key="sk_test_...",
|
|
70
|
+
environment="sandbox", # or "production"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Charge a payment
|
|
74
|
+
result = client.payments.charge({
|
|
75
|
+
"amount": 1999, # $19.99 in cents
|
|
76
|
+
"currency": "usd",
|
|
77
|
+
"source": "tok_visa_4242",
|
|
78
|
+
"description": "Test payment",
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
if result["success"]:
|
|
82
|
+
print("Payment ID:", result["transactionId"])
|
|
83
|
+
print("New balance:", result["newBalance"])
|
|
84
|
+
else:
|
|
85
|
+
print("Payment failed:", result["status"])
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Configuration
|
|
89
|
+
|
|
90
|
+
| Option | Type | Default | Description |
|
|
91
|
+
|--------|------|---------|-------------|
|
|
92
|
+
| `api_key` | `str` | **required** | Your secret API key |
|
|
93
|
+
| `environment` | `str` | `'production'` | API environment: `'sandbox'` or `'production'` |
|
|
94
|
+
| `base_url` | `str` | environment default | Custom API base URL (overrides environment) |
|
|
95
|
+
| `timeout` | `int` | `30000` | Request timeout in milliseconds |
|
|
96
|
+
|
|
97
|
+
### Environment URLs
|
|
98
|
+
|
|
99
|
+
| Environment | URL |
|
|
100
|
+
|-------------|-----|
|
|
101
|
+
| `sandbox` | `https://api.sandbox.cosmicgateway.com` |
|
|
102
|
+
| `production` | `https://api.cosmicgateway.com` |
|
|
103
|
+
|
|
104
|
+
### Retry Configuration
|
|
105
|
+
|
|
106
|
+
The SDK retries failed requests with exponential backoff. Configure via `RetryConfig`:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from cosmic_sdk import CosmicGateway
|
|
110
|
+
from cosmic_sdk.config import RetryConfig
|
|
111
|
+
|
|
112
|
+
client = CosmicGateway(
|
|
113
|
+
api_key="sk_test_...",
|
|
114
|
+
environment="sandbox",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
client.config.retry = RetryConfig(
|
|
118
|
+
max_retries=5,
|
|
119
|
+
base_delay_ms=200,
|
|
120
|
+
max_delay_ms=10000,
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## API Reference
|
|
125
|
+
|
|
126
|
+
### Payments
|
|
127
|
+
|
|
128
|
+
#### `client.payments.charge(request)`
|
|
129
|
+
|
|
130
|
+
Process a payment charge.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
result = client.payments.charge({
|
|
134
|
+
"amount": 1999,
|
|
135
|
+
"currency": "usd",
|
|
136
|
+
"source": "tok_abc123",
|
|
137
|
+
"description": "Order #1234",
|
|
138
|
+
"metadata": {"orderId": "ORD-12345"},
|
|
139
|
+
"idempotencyKey": "uuid-v4", # optional — auto-generated if omitted
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Success response:**
|
|
144
|
+
```python
|
|
145
|
+
{
|
|
146
|
+
"success": True,
|
|
147
|
+
"transactionId": "txn_...",
|
|
148
|
+
"referenceId": "ref_...",
|
|
149
|
+
"processor": "stripe",
|
|
150
|
+
"region": "us",
|
|
151
|
+
"amount": 1999,
|
|
152
|
+
"currency": "usd",
|
|
153
|
+
"settlement": { # present for cross-currency payments
|
|
154
|
+
"from": "usd",
|
|
155
|
+
"to": "kes",
|
|
156
|
+
"originalAmount": 1999,
|
|
157
|
+
"convertedAmount": 289855,
|
|
158
|
+
"rate": 145.0,
|
|
159
|
+
},
|
|
160
|
+
"newBalance": 50000,
|
|
161
|
+
"fraudScore": 0.02,
|
|
162
|
+
"kycVerified": "verified",
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Failed response:**
|
|
167
|
+
```python
|
|
168
|
+
{
|
|
169
|
+
"success": False,
|
|
170
|
+
"transactionId": "txn_...",
|
|
171
|
+
"referenceId": None,
|
|
172
|
+
"processor": "stripe",
|
|
173
|
+
"region": "us",
|
|
174
|
+
"status": "failed",
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### `client.payments.retrieve(payment_id)`
|
|
179
|
+
|
|
180
|
+
Retrieve payment details by ID.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
payment = client.payments.retrieve("pay_abc123")
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### `client.payments.list(params=None)`
|
|
187
|
+
|
|
188
|
+
List payments with optional filtering.
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
result = client.payments.list({
|
|
192
|
+
"page": 1,
|
|
193
|
+
"limit": 20,
|
|
194
|
+
"status": "SUCCESS",
|
|
195
|
+
"startDate": "2026-01-01T00:00:00Z",
|
|
196
|
+
"endDate": "2026-12-31T23:59:59Z",
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Refunds
|
|
201
|
+
|
|
202
|
+
#### `client.refunds.create(request)`
|
|
203
|
+
|
|
204
|
+
Create a refund for a payment.
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
result = client.refunds.create({
|
|
208
|
+
"paymentId": "pay_abc123",
|
|
209
|
+
"amount": 500, # partial refund (optional, defaults to full)
|
|
210
|
+
"reason": "Customer requested refund",
|
|
211
|
+
"metadata": {"ticketId": "TKT-999"},
|
|
212
|
+
})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### `client.refunds.retrieve(refund_id)`
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
refund = client.refunds.retrieve("ref_abc123")
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### `client.refunds.list(params=None)`
|
|
222
|
+
|
|
223
|
+
### Customers
|
|
224
|
+
|
|
225
|
+
#### `client.customers.create(request)`
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
customer = client.customers.create({
|
|
229
|
+
"email": "user@example.com",
|
|
230
|
+
"name": "John Doe",
|
|
231
|
+
"phone": "+254700000000",
|
|
232
|
+
"metadata": {"signupSource": "web"},
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### `client.customers.retrieve(customer_id)`
|
|
237
|
+
|
|
238
|
+
#### `client.customers.list(params=None)`
|
|
239
|
+
|
|
240
|
+
### Transactions
|
|
241
|
+
|
|
242
|
+
#### `client.transactions.list(params=None)`
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
result = client.transactions.list({
|
|
246
|
+
"page": 1,
|
|
247
|
+
"limit": 20,
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### `client.transactions.retrieve(transaction_id)`
|
|
252
|
+
|
|
253
|
+
### Webhook Verification
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from cosmic_sdk.webhooks import verify_webhook_signature
|
|
257
|
+
|
|
258
|
+
payload = request.body # raw bytes
|
|
259
|
+
signature = request.headers.get("X-Cosmic-Signature") # "sha256=..."
|
|
260
|
+
secret = "whsec_..."
|
|
261
|
+
|
|
262
|
+
event = verify_webhook_signature(payload, signature, secret)
|
|
263
|
+
# {"event": "payment.succeeded", "transactionId": "txn_1", ...}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The SDK verifies the `sha256=` HMAC signature using **timing-safe comparison** (`hmac.compare_digest`).
|
|
267
|
+
|
|
268
|
+
Raises `WebhookVerificationError` if the signature is missing, malformed, or doesn't match.
|
|
269
|
+
|
|
270
|
+
### Health Check
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
health = client.health()
|
|
274
|
+
# {"status": "ok"}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Raw HTTP Methods
|
|
278
|
+
|
|
279
|
+
The SDK exposes `get()` and `post()` for direct API access if needed:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
# Raw GET
|
|
283
|
+
resp = client.get("/customers", params={"limit": 10})
|
|
284
|
+
|
|
285
|
+
# Raw POST
|
|
286
|
+
resp = client.post("/customers", json={"email": "user@example.com"})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Error Handling
|
|
290
|
+
|
|
291
|
+
The SDK raises typed exceptions for all failure modes:
|
|
292
|
+
|
|
293
|
+
| Exception | HTTP Status | When |
|
|
294
|
+
|-----------|-------------|------|
|
|
295
|
+
| `ValidationError` | 400 | Invalid input |
|
|
296
|
+
| `AuthenticationError` | 401 | Missing or invalid credentials |
|
|
297
|
+
| `PaymentFailedError` | 402 | Payment was declined |
|
|
298
|
+
| `NotFoundError` | 404 | Resource not found |
|
|
299
|
+
| `RateLimitError` | 429 | Too many requests |
|
|
300
|
+
| `ServerError` | 500 | Gateway internal error |
|
|
301
|
+
| `ServiceUnavailableError` | 503 | Service temporarily down |
|
|
302
|
+
| `WebhookVerificationError` | — | Webhook signature invalid |
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
from cosmic_sdk.errors import PaymentFailedError, ValidationError
|
|
306
|
+
|
|
307
|
+
try:
|
|
308
|
+
result = client.payments.charge({
|
|
309
|
+
"amount": -1,
|
|
310
|
+
"currency": "usd",
|
|
311
|
+
"source": "tok_abc",
|
|
312
|
+
})
|
|
313
|
+
except ValidationError as e:
|
|
314
|
+
print("Bad input:", e)
|
|
315
|
+
except PaymentFailedError as e:
|
|
316
|
+
print("Payment declined:", e.transaction_id)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Retry Behavior
|
|
320
|
+
|
|
321
|
+
- **5xx errors** — retried with exponential backoff (base delay doubles each attempt)
|
|
322
|
+
- **429 rate limits** — respects the `Retry-After` header
|
|
323
|
+
- **Network errors** — retried as transient failures
|
|
324
|
+
- **4xx errors (except 429)** — **not** retried (these are client errors)
|
|
325
|
+
- Configurable via `RetryConfig`
|
|
326
|
+
|
|
327
|
+
## Idempotency
|
|
328
|
+
|
|
329
|
+
All mutation endpoints (`charge`, `create`, etc.) accept an optional `idempotency_key`. If omitted, a UUID v4 is generated automatically. The gateway uses the `api_key + idempotency_key` pair to detect duplicate requests and return the cached response.
|
|
330
|
+
|
|
331
|
+
## Sensitive Data Masking
|
|
332
|
+
|
|
333
|
+
The logger automatically masks sensitive fields (API keys, PAN, CVC, auth tokens) in logs. Set the log level to `DEBUG` to inspect request/response shapes without exposing secrets:
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
import logging
|
|
337
|
+
client._logger = GatewayLogger(level=logging.DEBUG)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Development
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
# Clone and install with dev dependencies
|
|
344
|
+
pip install -e ".[dev]"
|
|
345
|
+
|
|
346
|
+
# Run tests
|
|
347
|
+
python3 -m pytest tests/
|
|
348
|
+
|
|
349
|
+
# Run with coverage
|
|
350
|
+
python3 -m pytest tests/ --cov=cosmic_sdk
|
|
351
|
+
|
|
352
|
+
# Build distribution
|
|
353
|
+
python3 -m build
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
MIT
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
# Cosmic Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python client SDK for the [Cosmic Gateway](https://cosmicgateway.com) payment processing API.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Complete payment lifecycle** — charge, capture, refund, and query payments
|
|
8
|
+
- **Customer management** — create, retrieve, and list customers
|
|
9
|
+
- **Transaction history** — paginated transaction listing with filters
|
|
10
|
+
- **Webhook verification** — HMAC-SHA256 signature verification for incoming webhooks
|
|
11
|
+
- **Idempotency** — automatic idempotency key generation to safely retry requests
|
|
12
|
+
- **Retry with backoff** — automatic retry on 5xx errors and rate limits (429)
|
|
13
|
+
- **Request timeout** — configurable timeout per request
|
|
14
|
+
- **Environment switching** — built-in sandbox and production endpoints
|
|
15
|
+
- **Input validation** — pre-flight validation of amounts, currencies, emails, and more
|
|
16
|
+
- **Typed request/response models** — dataclass-based types for all API objects
|
|
17
|
+
- **Sensitive data masking** — automatic masking of API keys, PAN, and CVC in logs
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install cosmic-python-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requires Python 3.8+.
|
|
26
|
+
|
|
27
|
+
### Development installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install cosmic-python-sdk[dev]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from cosmic_sdk import CosmicGateway
|
|
37
|
+
|
|
38
|
+
client = CosmicGateway(
|
|
39
|
+
api_key="sk_test_...",
|
|
40
|
+
environment="sandbox", # or "production"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Charge a payment
|
|
44
|
+
result = client.payments.charge({
|
|
45
|
+
"amount": 1999, # $19.99 in cents
|
|
46
|
+
"currency": "usd",
|
|
47
|
+
"source": "tok_visa_4242",
|
|
48
|
+
"description": "Test payment",
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
if result["success"]:
|
|
52
|
+
print("Payment ID:", result["transactionId"])
|
|
53
|
+
print("New balance:", result["newBalance"])
|
|
54
|
+
else:
|
|
55
|
+
print("Payment failed:", result["status"])
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
| Option | Type | Default | Description |
|
|
61
|
+
|--------|------|---------|-------------|
|
|
62
|
+
| `api_key` | `str` | **required** | Your secret API key |
|
|
63
|
+
| `environment` | `str` | `'production'` | API environment: `'sandbox'` or `'production'` |
|
|
64
|
+
| `base_url` | `str` | environment default | Custom API base URL (overrides environment) |
|
|
65
|
+
| `timeout` | `int` | `30000` | Request timeout in milliseconds |
|
|
66
|
+
|
|
67
|
+
### Environment URLs
|
|
68
|
+
|
|
69
|
+
| Environment | URL |
|
|
70
|
+
|-------------|-----|
|
|
71
|
+
| `sandbox` | `https://api.sandbox.cosmicgateway.com` |
|
|
72
|
+
| `production` | `https://api.cosmicgateway.com` |
|
|
73
|
+
|
|
74
|
+
### Retry Configuration
|
|
75
|
+
|
|
76
|
+
The SDK retries failed requests with exponential backoff. Configure via `RetryConfig`:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from cosmic_sdk import CosmicGateway
|
|
80
|
+
from cosmic_sdk.config import RetryConfig
|
|
81
|
+
|
|
82
|
+
client = CosmicGateway(
|
|
83
|
+
api_key="sk_test_...",
|
|
84
|
+
environment="sandbox",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
client.config.retry = RetryConfig(
|
|
88
|
+
max_retries=5,
|
|
89
|
+
base_delay_ms=200,
|
|
90
|
+
max_delay_ms=10000,
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## API Reference
|
|
95
|
+
|
|
96
|
+
### Payments
|
|
97
|
+
|
|
98
|
+
#### `client.payments.charge(request)`
|
|
99
|
+
|
|
100
|
+
Process a payment charge.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
result = client.payments.charge({
|
|
104
|
+
"amount": 1999,
|
|
105
|
+
"currency": "usd",
|
|
106
|
+
"source": "tok_abc123",
|
|
107
|
+
"description": "Order #1234",
|
|
108
|
+
"metadata": {"orderId": "ORD-12345"},
|
|
109
|
+
"idempotencyKey": "uuid-v4", # optional — auto-generated if omitted
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Success response:**
|
|
114
|
+
```python
|
|
115
|
+
{
|
|
116
|
+
"success": True,
|
|
117
|
+
"transactionId": "txn_...",
|
|
118
|
+
"referenceId": "ref_...",
|
|
119
|
+
"processor": "stripe",
|
|
120
|
+
"region": "us",
|
|
121
|
+
"amount": 1999,
|
|
122
|
+
"currency": "usd",
|
|
123
|
+
"settlement": { # present for cross-currency payments
|
|
124
|
+
"from": "usd",
|
|
125
|
+
"to": "kes",
|
|
126
|
+
"originalAmount": 1999,
|
|
127
|
+
"convertedAmount": 289855,
|
|
128
|
+
"rate": 145.0,
|
|
129
|
+
},
|
|
130
|
+
"newBalance": 50000,
|
|
131
|
+
"fraudScore": 0.02,
|
|
132
|
+
"kycVerified": "verified",
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Failed response:**
|
|
137
|
+
```python
|
|
138
|
+
{
|
|
139
|
+
"success": False,
|
|
140
|
+
"transactionId": "txn_...",
|
|
141
|
+
"referenceId": None,
|
|
142
|
+
"processor": "stripe",
|
|
143
|
+
"region": "us",
|
|
144
|
+
"status": "failed",
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### `client.payments.retrieve(payment_id)`
|
|
149
|
+
|
|
150
|
+
Retrieve payment details by ID.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
payment = client.payments.retrieve("pay_abc123")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### `client.payments.list(params=None)`
|
|
157
|
+
|
|
158
|
+
List payments with optional filtering.
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
result = client.payments.list({
|
|
162
|
+
"page": 1,
|
|
163
|
+
"limit": 20,
|
|
164
|
+
"status": "SUCCESS",
|
|
165
|
+
"startDate": "2026-01-01T00:00:00Z",
|
|
166
|
+
"endDate": "2026-12-31T23:59:59Z",
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Refunds
|
|
171
|
+
|
|
172
|
+
#### `client.refunds.create(request)`
|
|
173
|
+
|
|
174
|
+
Create a refund for a payment.
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
result = client.refunds.create({
|
|
178
|
+
"paymentId": "pay_abc123",
|
|
179
|
+
"amount": 500, # partial refund (optional, defaults to full)
|
|
180
|
+
"reason": "Customer requested refund",
|
|
181
|
+
"metadata": {"ticketId": "TKT-999"},
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### `client.refunds.retrieve(refund_id)`
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
refund = client.refunds.retrieve("ref_abc123")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### `client.refunds.list(params=None)`
|
|
192
|
+
|
|
193
|
+
### Customers
|
|
194
|
+
|
|
195
|
+
#### `client.customers.create(request)`
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
customer = client.customers.create({
|
|
199
|
+
"email": "user@example.com",
|
|
200
|
+
"name": "John Doe",
|
|
201
|
+
"phone": "+254700000000",
|
|
202
|
+
"metadata": {"signupSource": "web"},
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### `client.customers.retrieve(customer_id)`
|
|
207
|
+
|
|
208
|
+
#### `client.customers.list(params=None)`
|
|
209
|
+
|
|
210
|
+
### Transactions
|
|
211
|
+
|
|
212
|
+
#### `client.transactions.list(params=None)`
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
result = client.transactions.list({
|
|
216
|
+
"page": 1,
|
|
217
|
+
"limit": 20,
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### `client.transactions.retrieve(transaction_id)`
|
|
222
|
+
|
|
223
|
+
### Webhook Verification
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from cosmic_sdk.webhooks import verify_webhook_signature
|
|
227
|
+
|
|
228
|
+
payload = request.body # raw bytes
|
|
229
|
+
signature = request.headers.get("X-Cosmic-Signature") # "sha256=..."
|
|
230
|
+
secret = "whsec_..."
|
|
231
|
+
|
|
232
|
+
event = verify_webhook_signature(payload, signature, secret)
|
|
233
|
+
# {"event": "payment.succeeded", "transactionId": "txn_1", ...}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The SDK verifies the `sha256=` HMAC signature using **timing-safe comparison** (`hmac.compare_digest`).
|
|
237
|
+
|
|
238
|
+
Raises `WebhookVerificationError` if the signature is missing, malformed, or doesn't match.
|
|
239
|
+
|
|
240
|
+
### Health Check
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
health = client.health()
|
|
244
|
+
# {"status": "ok"}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Raw HTTP Methods
|
|
248
|
+
|
|
249
|
+
The SDK exposes `get()` and `post()` for direct API access if needed:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
# Raw GET
|
|
253
|
+
resp = client.get("/customers", params={"limit": 10})
|
|
254
|
+
|
|
255
|
+
# Raw POST
|
|
256
|
+
resp = client.post("/customers", json={"email": "user@example.com"})
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Error Handling
|
|
260
|
+
|
|
261
|
+
The SDK raises typed exceptions for all failure modes:
|
|
262
|
+
|
|
263
|
+
| Exception | HTTP Status | When |
|
|
264
|
+
|-----------|-------------|------|
|
|
265
|
+
| `ValidationError` | 400 | Invalid input |
|
|
266
|
+
| `AuthenticationError` | 401 | Missing or invalid credentials |
|
|
267
|
+
| `PaymentFailedError` | 402 | Payment was declined |
|
|
268
|
+
| `NotFoundError` | 404 | Resource not found |
|
|
269
|
+
| `RateLimitError` | 429 | Too many requests |
|
|
270
|
+
| `ServerError` | 500 | Gateway internal error |
|
|
271
|
+
| `ServiceUnavailableError` | 503 | Service temporarily down |
|
|
272
|
+
| `WebhookVerificationError` | — | Webhook signature invalid |
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
from cosmic_sdk.errors import PaymentFailedError, ValidationError
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
result = client.payments.charge({
|
|
279
|
+
"amount": -1,
|
|
280
|
+
"currency": "usd",
|
|
281
|
+
"source": "tok_abc",
|
|
282
|
+
})
|
|
283
|
+
except ValidationError as e:
|
|
284
|
+
print("Bad input:", e)
|
|
285
|
+
except PaymentFailedError as e:
|
|
286
|
+
print("Payment declined:", e.transaction_id)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Retry Behavior
|
|
290
|
+
|
|
291
|
+
- **5xx errors** — retried with exponential backoff (base delay doubles each attempt)
|
|
292
|
+
- **429 rate limits** — respects the `Retry-After` header
|
|
293
|
+
- **Network errors** — retried as transient failures
|
|
294
|
+
- **4xx errors (except 429)** — **not** retried (these are client errors)
|
|
295
|
+
- Configurable via `RetryConfig`
|
|
296
|
+
|
|
297
|
+
## Idempotency
|
|
298
|
+
|
|
299
|
+
All mutation endpoints (`charge`, `create`, etc.) accept an optional `idempotency_key`. If omitted, a UUID v4 is generated automatically. The gateway uses the `api_key + idempotency_key` pair to detect duplicate requests and return the cached response.
|
|
300
|
+
|
|
301
|
+
## Sensitive Data Masking
|
|
302
|
+
|
|
303
|
+
The logger automatically masks sensitive fields (API keys, PAN, CVC, auth tokens) in logs. Set the log level to `DEBUG` to inspect request/response shapes without exposing secrets:
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
import logging
|
|
307
|
+
client._logger = GatewayLogger(level=logging.DEBUG)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Development
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# Clone and install with dev dependencies
|
|
314
|
+
pip install -e ".[dev]"
|
|
315
|
+
|
|
316
|
+
# Run tests
|
|
317
|
+
python3 -m pytest tests/
|
|
318
|
+
|
|
319
|
+
# Run with coverage
|
|
320
|
+
python3 -m pytest tests/ --cov=cosmic_sdk
|
|
321
|
+
|
|
322
|
+
# Build distribution
|
|
323
|
+
python3 -m build
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## License
|
|
327
|
+
|
|
328
|
+
MIT
|