raqm-core 0.2.5__tar.gz → 0.3.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.
- {raqm_core-0.2.5 → raqm_core-0.3.0}/PKG-INFO +48 -18
- {raqm_core-0.2.5 → raqm_core-0.3.0}/README.md +47 -17
- {raqm_core-0.2.5 → raqm_core-0.3.0}/pyproject.toml +1 -1
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/headers/jazzcash.py +2 -5
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/jazzcash.py +5 -1
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/schemas/jazzcash.py +12 -19
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core.egg-info/PKG-INFO +48 -18
- {raqm_core-0.2.5 → raqm_core-0.3.0}/tests/test_easypaisa.py +7 -54
- {raqm_core-0.2.5 → raqm_core-0.3.0}/tests/test_jazzcash.py +4 -39
- {raqm_core-0.2.5 → raqm_core-0.3.0}/setup.cfg +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/__init__.py +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/easypaisa.py +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/exceptions/exceptions.py +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/headers/__init__.py +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/headers/easypaisa.py +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/schemas/__init__.py +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core/schemas/easypaisa.py +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core.egg-info/SOURCES.txt +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core.egg-info/dependency_links.txt +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core.egg-info/requires.txt +0 -0
- {raqm_core-0.2.5 → raqm_core-0.3.0}/src/raqm_core.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: raqm-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Unified Python SDK for Pakistani payment gateways (EasyPaisa, JazzCash, HBL, ABL, UBL, etc.)
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.9
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -19,7 +19,7 @@ Requires-Dist: pydantic>=2.0
|
|
|
19
19
|
|
|
20
20
|
One async, type-safe interface across multiple processors — EasyPaisa, JazzCash, HBL, ABL, UBL, and more.
|
|
21
21
|
|
|
22
|
-
> ⚠️ **Work in progress.**
|
|
22
|
+
> ⚠️ **Work in progress.** EasyPaisa and JazzCash (MWALLET) are implemented. Contributions welcome!
|
|
23
23
|
>
|
|
24
24
|
> ⚠️ **Breaking change in v0.2.4** — methods now **raise `PaymentError`** on business-level failures instead of returning a result with a non-success response code. Wrap calls in try/except to handle errors gracefully.
|
|
25
25
|
|
|
@@ -30,7 +30,7 @@ One async, type-safe interface across multiple processors — EasyPaisa, JazzCas
|
|
|
30
30
|
| Gateway | Status |
|
|
31
31
|
|---------|--------|
|
|
32
32
|
| [EasyPaisa](https://easypaisa.com.pk) | ✅ Live |
|
|
33
|
-
| [JazzCash](https://jazzcash.com.pk) |
|
|
33
|
+
| [JazzCash](https://jazzcash.com.pk) | ✅ Live (MWALLET) |
|
|
34
34
|
| HBL | 📅 Planned |
|
|
35
35
|
| ABL | 📅 Planned |
|
|
36
36
|
| UBL | 📅 Planned |
|
|
@@ -231,6 +231,7 @@ src/
|
|
|
231
231
|
└── jazzcash.py # JazzCash Pydantic models
|
|
232
232
|
|
|
233
233
|
tests/
|
|
234
|
+
├── conftest.py # Shared fixtures & mock helpers
|
|
234
235
|
├── test_easypaisa.py # EasyPaisa integration tests
|
|
235
236
|
├── test_jazzcash.py # JazzCash integration tests
|
|
236
237
|
└── headers/
|
|
@@ -303,24 +304,53 @@ class HBL:
|
|
|
303
304
|
|
|
304
305
|
### 5. Write integration tests
|
|
305
306
|
|
|
306
|
-
`tests/test_<gateway>.py` — use `httpx.MockTransport` to mock responses.
|
|
307
|
+
`tests/test_<gateway>.py` — use `httpx.MockTransport` to mock responses. Add shared helpers to `tests/conftest.py`.
|
|
307
308
|
|
|
308
|
-
|
|
309
|
-
# tests/test_hbl.py
|
|
310
|
-
import httpx
|
|
311
|
-
import pytest
|
|
312
|
-
from raqm_core import HBL
|
|
309
|
+
**conftest.py** — add a success body constant and a factory:
|
|
313
310
|
|
|
311
|
+
```python
|
|
312
|
+
# tests/conftest.py
|
|
313
|
+
from raqm_core.hbl import HBL
|
|
314
|
+
|
|
315
|
+
HBL_SUCCESS_BODY = {
|
|
316
|
+
"orderId": "abc123",
|
|
317
|
+
"responseCode": "0000",
|
|
318
|
+
"responseDesc": "SUCCESS",
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
def make_hbl(response_body: dict) -> HBL:
|
|
322
|
+
return HBL(
|
|
323
|
+
api_key="test",
|
|
324
|
+
sandbox=True,
|
|
325
|
+
client=make_client(response_body),
|
|
326
|
+
)
|
|
327
|
+
```
|
|
314
328
|
|
|
315
|
-
|
|
316
|
-
async def test_pay_success():
|
|
317
|
-
def handler(request: httpx.Request) -> httpx.Response:
|
|
318
|
-
return httpx.Response(status_code=200, json={...})
|
|
329
|
+
**test file** — import from `conftest`:
|
|
319
330
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
331
|
+
```python
|
|
332
|
+
# tests/test_hbl.py
|
|
333
|
+
import pytest
|
|
334
|
+
from raqm_core.exceptions.exceptions import PaymentError
|
|
335
|
+
from tests.conftest import HBL_SUCCESS_BODY, make_client, make_hbl
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class TestPay:
|
|
339
|
+
@pytest.mark.asyncio
|
|
340
|
+
async def test_success(self):
|
|
341
|
+
hbl = make_hbl(HBL_SUCCESS_BODY)
|
|
342
|
+
result = await hbl.pay(...)
|
|
343
|
+
assert result.responseCode == "0000"
|
|
344
|
+
|
|
345
|
+
@pytest.mark.asyncio
|
|
346
|
+
async def test_network_error(self):
|
|
347
|
+
hbl = HBL(
|
|
348
|
+
api_key="test",
|
|
349
|
+
sandbox=True,
|
|
350
|
+
client=make_client({"error": "timeout"}, status_code=500),
|
|
351
|
+
)
|
|
352
|
+
with pytest.raises(NetworkError):
|
|
353
|
+
await hbl.pay(...)
|
|
324
354
|
```
|
|
325
355
|
|
|
326
356
|
### 6. Update this README
|
|
@@ -332,7 +362,7 @@ Add the new gateway to the **Supported Gateways** table with the appropriate sta
|
|
|
332
362
|
## Roadmap
|
|
333
363
|
|
|
334
364
|
- [x] EasyPaisa (MA, OTC, inquiry)
|
|
335
|
-
- [
|
|
365
|
+
- [x] JazzCash client + schemas (MWALLET)
|
|
336
366
|
- [ ] HBL payment gateway
|
|
337
367
|
- [ ] ABL payment gateway
|
|
338
368
|
- [ ] UBL payment gateway
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
One async, type-safe interface across multiple processors — EasyPaisa, JazzCash, HBL, ABL, UBL, and more.
|
|
6
6
|
|
|
7
|
-
> ⚠️ **Work in progress.**
|
|
7
|
+
> ⚠️ **Work in progress.** EasyPaisa and JazzCash (MWALLET) are implemented. Contributions welcome!
|
|
8
8
|
>
|
|
9
9
|
> ⚠️ **Breaking change in v0.2.4** — methods now **raise `PaymentError`** on business-level failures instead of returning a result with a non-success response code. Wrap calls in try/except to handle errors gracefully.
|
|
10
10
|
|
|
@@ -15,7 +15,7 @@ One async, type-safe interface across multiple processors — EasyPaisa, JazzCas
|
|
|
15
15
|
| Gateway | Status |
|
|
16
16
|
|---------|--------|
|
|
17
17
|
| [EasyPaisa](https://easypaisa.com.pk) | ✅ Live |
|
|
18
|
-
| [JazzCash](https://jazzcash.com.pk) |
|
|
18
|
+
| [JazzCash](https://jazzcash.com.pk) | ✅ Live (MWALLET) |
|
|
19
19
|
| HBL | 📅 Planned |
|
|
20
20
|
| ABL | 📅 Planned |
|
|
21
21
|
| UBL | 📅 Planned |
|
|
@@ -216,6 +216,7 @@ src/
|
|
|
216
216
|
└── jazzcash.py # JazzCash Pydantic models
|
|
217
217
|
|
|
218
218
|
tests/
|
|
219
|
+
├── conftest.py # Shared fixtures & mock helpers
|
|
219
220
|
├── test_easypaisa.py # EasyPaisa integration tests
|
|
220
221
|
├── test_jazzcash.py # JazzCash integration tests
|
|
221
222
|
└── headers/
|
|
@@ -288,24 +289,53 @@ class HBL:
|
|
|
288
289
|
|
|
289
290
|
### 5. Write integration tests
|
|
290
291
|
|
|
291
|
-
`tests/test_<gateway>.py` — use `httpx.MockTransport` to mock responses.
|
|
292
|
+
`tests/test_<gateway>.py` — use `httpx.MockTransport` to mock responses. Add shared helpers to `tests/conftest.py`.
|
|
292
293
|
|
|
293
|
-
|
|
294
|
-
# tests/test_hbl.py
|
|
295
|
-
import httpx
|
|
296
|
-
import pytest
|
|
297
|
-
from raqm_core import HBL
|
|
294
|
+
**conftest.py** — add a success body constant and a factory:
|
|
298
295
|
|
|
296
|
+
```python
|
|
297
|
+
# tests/conftest.py
|
|
298
|
+
from raqm_core.hbl import HBL
|
|
299
|
+
|
|
300
|
+
HBL_SUCCESS_BODY = {
|
|
301
|
+
"orderId": "abc123",
|
|
302
|
+
"responseCode": "0000",
|
|
303
|
+
"responseDesc": "SUCCESS",
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
def make_hbl(response_body: dict) -> HBL:
|
|
307
|
+
return HBL(
|
|
308
|
+
api_key="test",
|
|
309
|
+
sandbox=True,
|
|
310
|
+
client=make_client(response_body),
|
|
311
|
+
)
|
|
312
|
+
```
|
|
299
313
|
|
|
300
|
-
|
|
301
|
-
async def test_pay_success():
|
|
302
|
-
def handler(request: httpx.Request) -> httpx.Response:
|
|
303
|
-
return httpx.Response(status_code=200, json={...})
|
|
314
|
+
**test file** — import from `conftest`:
|
|
304
315
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
316
|
+
```python
|
|
317
|
+
# tests/test_hbl.py
|
|
318
|
+
import pytest
|
|
319
|
+
from raqm_core.exceptions.exceptions import PaymentError
|
|
320
|
+
from tests.conftest import HBL_SUCCESS_BODY, make_client, make_hbl
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class TestPay:
|
|
324
|
+
@pytest.mark.asyncio
|
|
325
|
+
async def test_success(self):
|
|
326
|
+
hbl = make_hbl(HBL_SUCCESS_BODY)
|
|
327
|
+
result = await hbl.pay(...)
|
|
328
|
+
assert result.responseCode == "0000"
|
|
329
|
+
|
|
330
|
+
@pytest.mark.asyncio
|
|
331
|
+
async def test_network_error(self):
|
|
332
|
+
hbl = HBL(
|
|
333
|
+
api_key="test",
|
|
334
|
+
sandbox=True,
|
|
335
|
+
client=make_client({"error": "timeout"}, status_code=500),
|
|
336
|
+
)
|
|
337
|
+
with pytest.raises(NetworkError):
|
|
338
|
+
await hbl.pay(...)
|
|
309
339
|
```
|
|
310
340
|
|
|
311
341
|
### 6. Update this README
|
|
@@ -317,7 +347,7 @@ Add the new gateway to the **Supported Gateways** table with the appropriate sta
|
|
|
317
347
|
## Roadmap
|
|
318
348
|
|
|
319
349
|
- [x] EasyPaisa (MA, OTC, inquiry)
|
|
320
|
-
- [
|
|
350
|
+
- [x] JazzCash client + schemas (MWALLET)
|
|
321
351
|
- [ ] HBL payment gateway
|
|
322
352
|
- [ ] ABL payment gateway
|
|
323
353
|
- [ ] UBL payment gateway
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
import hmac
|
|
1
2
|
from hashlib import sha256
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def generate_secure_hash(hash_key: str, params: dict) -> str:
|
|
5
|
-
|
|
6
6
|
values = "&".join(param[1] for param in sorted(params.items()) if param[1])
|
|
7
7
|
my_str = hash_key + ("&" + values if values else "")
|
|
8
|
-
|
|
9
|
-
my_bytes = str.encode(my_str)
|
|
10
|
-
my_sha256 = sha256(my_bytes)
|
|
11
|
-
return my_sha256.hexdigest()
|
|
8
|
+
return hmac.new(hash_key.encode(), my_str.encode(), sha256).hexdigest()
|
|
@@ -45,6 +45,7 @@ class JazzCash:
|
|
|
45
45
|
|
|
46
46
|
async def pay_via_mwallet(
|
|
47
47
|
self,
|
|
48
|
+
cnic: str,
|
|
48
49
|
mobile_number: str,
|
|
49
50
|
amount: str,
|
|
50
51
|
description: str,
|
|
@@ -71,13 +72,16 @@ class JazzCash:
|
|
|
71
72
|
pp_Description=description,
|
|
72
73
|
pp_TxnExpiryDateTime=pp_TxnExpiryDateTime,
|
|
73
74
|
pp_MobileNumber=mobile_number,
|
|
75
|
+
pp_CNIC=cnic,
|
|
74
76
|
) # type: ignore
|
|
75
77
|
|
|
76
78
|
payload = request.model_dump()
|
|
77
79
|
secure_hash = generate_secure_hash(hash_key=self.hash_key, params=payload)
|
|
78
80
|
|
|
79
81
|
payload["pp_SecureHash"] = secure_hash
|
|
80
|
-
data = await self._post(
|
|
82
|
+
data = await self._post(
|
|
83
|
+
endpoint="Purchase/DoMWalletTransaction", payload=payload
|
|
84
|
+
)
|
|
81
85
|
|
|
82
86
|
result = JazzcashResponse(**data)
|
|
83
87
|
|
|
@@ -16,9 +16,6 @@ class JazzCashResponseCode(str, Enum):
|
|
|
16
16
|
INSUFFICIENT_BALANCE = "405"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
# ── REQUEST MODELS ────────────────────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
|
|
22
19
|
class JazzcashRequest(BaseModel):
|
|
23
20
|
pp_Version: str = Field("1.1", description="Payment Portal Version.")
|
|
24
21
|
pp_Language: str = Field("EN", description="Display language. Fixed value 'EN'.")
|
|
@@ -29,10 +26,6 @@ class JazzcashRequest(BaseModel):
|
|
|
29
26
|
"", description="Sub merchant ID, leave empty if unused."
|
|
30
27
|
)
|
|
31
28
|
pp_Password: str = Field(..., description="Password assigned by JazzCash.")
|
|
32
|
-
pp_BankID: str = Field("", description="Bank identifier, leave empty if unused.")
|
|
33
|
-
pp_ProductID: str = Field(
|
|
34
|
-
"", description="Product identifier, leave empty if unused."
|
|
35
|
-
)
|
|
36
29
|
pp_TxnRefNo: str = Field(..., description="Unique transaction reference number.")
|
|
37
30
|
pp_Amount: str = Field(..., description="Transaction amount, no decimal places.")
|
|
38
31
|
pp_TxnCurrency: str = Field(
|
|
@@ -56,15 +49,17 @@ class JazzcashRequest(BaseModel):
|
|
|
56
49
|
class JazzcashMWalletRequest(JazzcashRequest):
|
|
57
50
|
pp_TxnType: str = Field("MWALLET", description="Transaction type.")
|
|
58
51
|
pp_MobileNumber: str = Field(..., description="Customer's JazzCash mobile number.")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
pp_CNIC: str = Field(
|
|
53
|
+
..., min_length=6, max_length=6, description="Last 6 digits of customer's CNIC."
|
|
54
|
+
)
|
|
55
|
+
pp_DiscountedAmount: str = Field(
|
|
56
|
+
"", description="Discounted amount, leave empty if unused."
|
|
57
|
+
)
|
|
62
58
|
|
|
63
59
|
|
|
64
60
|
class JazzcashResponse(BaseModel):
|
|
65
61
|
pp_Amount: str = Field(..., description="Transaction amount.")
|
|
66
62
|
pp_AuthCode: Optional[str] = None
|
|
67
|
-
pp_BankID: Optional[str] = None
|
|
68
63
|
pp_BillReference: Optional[str] = None
|
|
69
64
|
pp_Language: Optional[str] = None
|
|
70
65
|
pp_MerchantID: str = Field(..., description="Merchant ID.")
|
|
@@ -72,19 +67,17 @@ class JazzcashResponse(BaseModel):
|
|
|
72
67
|
..., description="Response code."
|
|
73
68
|
)
|
|
74
69
|
pp_ResponseMessage: str = Field(..., description="Response message.")
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
pp_RetrievalReferenceNo: Optional[str] = None # fixed JazzCash typo from v1
|
|
71
|
+
pp_RetreivalReferenceNo: Optional[str] = None # kept for v1 backwards compat
|
|
72
|
+
pp_SubMerchantID: Optional[str] = None
|
|
77
73
|
pp_TxnCurrency: Optional[str] = None
|
|
78
74
|
pp_TxnDateTime: Optional[str] = None
|
|
79
75
|
pp_TxnRefNo: str = Field(..., description="Transaction reference number.")
|
|
80
|
-
pp_SettlementExpiry: Optional[str] = None
|
|
81
76
|
pp_TxnType: Optional[str] = None
|
|
82
77
|
pp_Version: Optional[str] = None
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
ppmbf_4: Optional[str] = None
|
|
87
|
-
ppmbf_5: Optional[str] = None
|
|
78
|
+
pp_MobileNumber: Optional[str] = None
|
|
79
|
+
pp_CNIC: Optional[str] = None
|
|
80
|
+
pp_DiscountedAmount: Optional[str] = None
|
|
88
81
|
ppmpf_1: Optional[str] = None
|
|
89
82
|
ppmpf_2: Optional[str] = None
|
|
90
83
|
ppmpf_3: Optional[str] = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: raqm-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Unified Python SDK for Pakistani payment gateways (EasyPaisa, JazzCash, HBL, ABL, UBL, etc.)
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.9
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -19,7 +19,7 @@ Requires-Dist: pydantic>=2.0
|
|
|
19
19
|
|
|
20
20
|
One async, type-safe interface across multiple processors — EasyPaisa, JazzCash, HBL, ABL, UBL, and more.
|
|
21
21
|
|
|
22
|
-
> ⚠️ **Work in progress.**
|
|
22
|
+
> ⚠️ **Work in progress.** EasyPaisa and JazzCash (MWALLET) are implemented. Contributions welcome!
|
|
23
23
|
>
|
|
24
24
|
> ⚠️ **Breaking change in v0.2.4** — methods now **raise `PaymentError`** on business-level failures instead of returning a result with a non-success response code. Wrap calls in try/except to handle errors gracefully.
|
|
25
25
|
|
|
@@ -30,7 +30,7 @@ One async, type-safe interface across multiple processors — EasyPaisa, JazzCas
|
|
|
30
30
|
| Gateway | Status |
|
|
31
31
|
|---------|--------|
|
|
32
32
|
| [EasyPaisa](https://easypaisa.com.pk) | ✅ Live |
|
|
33
|
-
| [JazzCash](https://jazzcash.com.pk) |
|
|
33
|
+
| [JazzCash](https://jazzcash.com.pk) | ✅ Live (MWALLET) |
|
|
34
34
|
| HBL | 📅 Planned |
|
|
35
35
|
| ABL | 📅 Planned |
|
|
36
36
|
| UBL | 📅 Planned |
|
|
@@ -231,6 +231,7 @@ src/
|
|
|
231
231
|
└── jazzcash.py # JazzCash Pydantic models
|
|
232
232
|
|
|
233
233
|
tests/
|
|
234
|
+
├── conftest.py # Shared fixtures & mock helpers
|
|
234
235
|
├── test_easypaisa.py # EasyPaisa integration tests
|
|
235
236
|
├── test_jazzcash.py # JazzCash integration tests
|
|
236
237
|
└── headers/
|
|
@@ -303,24 +304,53 @@ class HBL:
|
|
|
303
304
|
|
|
304
305
|
### 5. Write integration tests
|
|
305
306
|
|
|
306
|
-
`tests/test_<gateway>.py` — use `httpx.MockTransport` to mock responses.
|
|
307
|
+
`tests/test_<gateway>.py` — use `httpx.MockTransport` to mock responses. Add shared helpers to `tests/conftest.py`.
|
|
307
308
|
|
|
308
|
-
|
|
309
|
-
# tests/test_hbl.py
|
|
310
|
-
import httpx
|
|
311
|
-
import pytest
|
|
312
|
-
from raqm_core import HBL
|
|
309
|
+
**conftest.py** — add a success body constant and a factory:
|
|
313
310
|
|
|
311
|
+
```python
|
|
312
|
+
# tests/conftest.py
|
|
313
|
+
from raqm_core.hbl import HBL
|
|
314
|
+
|
|
315
|
+
HBL_SUCCESS_BODY = {
|
|
316
|
+
"orderId": "abc123",
|
|
317
|
+
"responseCode": "0000",
|
|
318
|
+
"responseDesc": "SUCCESS",
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
def make_hbl(response_body: dict) -> HBL:
|
|
322
|
+
return HBL(
|
|
323
|
+
api_key="test",
|
|
324
|
+
sandbox=True,
|
|
325
|
+
client=make_client(response_body),
|
|
326
|
+
)
|
|
327
|
+
```
|
|
314
328
|
|
|
315
|
-
|
|
316
|
-
async def test_pay_success():
|
|
317
|
-
def handler(request: httpx.Request) -> httpx.Response:
|
|
318
|
-
return httpx.Response(status_code=200, json={...})
|
|
329
|
+
**test file** — import from `conftest`:
|
|
319
330
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
331
|
+
```python
|
|
332
|
+
# tests/test_hbl.py
|
|
333
|
+
import pytest
|
|
334
|
+
from raqm_core.exceptions.exceptions import PaymentError
|
|
335
|
+
from tests.conftest import HBL_SUCCESS_BODY, make_client, make_hbl
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class TestPay:
|
|
339
|
+
@pytest.mark.asyncio
|
|
340
|
+
async def test_success(self):
|
|
341
|
+
hbl = make_hbl(HBL_SUCCESS_BODY)
|
|
342
|
+
result = await hbl.pay(...)
|
|
343
|
+
assert result.responseCode == "0000"
|
|
344
|
+
|
|
345
|
+
@pytest.mark.asyncio
|
|
346
|
+
async def test_network_error(self):
|
|
347
|
+
hbl = HBL(
|
|
348
|
+
api_key="test",
|
|
349
|
+
sandbox=True,
|
|
350
|
+
client=make_client({"error": "timeout"}, status_code=500),
|
|
351
|
+
)
|
|
352
|
+
with pytest.raises(NetworkError):
|
|
353
|
+
await hbl.pay(...)
|
|
324
354
|
```
|
|
325
355
|
|
|
326
356
|
### 6. Update this README
|
|
@@ -332,7 +362,7 @@ Add the new gateway to the **Supported Gateways** table with the appropriate sta
|
|
|
332
362
|
## Roadmap
|
|
333
363
|
|
|
334
364
|
- [x] EasyPaisa (MA, OTC, inquiry)
|
|
335
|
-
- [
|
|
365
|
+
- [x] JazzCash client + schemas (MWALLET)
|
|
336
366
|
- [ ] HBL payment gateway
|
|
337
367
|
- [ ] ABL payment gateway
|
|
338
368
|
- [ ] UBL payment gateway
|
|
@@ -1,61 +1,14 @@
|
|
|
1
|
-
import httpx
|
|
2
1
|
import pytest
|
|
3
2
|
|
|
4
3
|
from src.raqm_core.exceptions.exceptions import NetworkError, PaymentError
|
|
5
4
|
from src.raqm_core.easypaisa import EasyPaisa
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"responseDesc": "SUCCESS",
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
OTC_SUCCESS_BODY = {
|
|
17
|
-
"orderId": "abc123",
|
|
18
|
-
"storeId": 43,
|
|
19
|
-
"transactionDateTime": "11/08/2021 11:30 PM",
|
|
20
|
-
"responseCode": "0000",
|
|
21
|
-
"responseDesc": "SUCCESS",
|
|
22
|
-
"paymentToken": "tok_abc123",
|
|
23
|
-
"paymentTokenExpiryDateTime": "12/08/2021 11:30 PM",
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
INQUIRE_SUCCESS_BODY = {
|
|
27
|
-
"orderId": "abc123",
|
|
28
|
-
"storeId": 43,
|
|
29
|
-
"transactionId": "253184",
|
|
30
|
-
"transactionDateTime": "11/08/2021 11:30 PM",
|
|
31
|
-
"responseCode": "0000",
|
|
32
|
-
"responseDesc": "SUCCESS",
|
|
33
|
-
"accountNum": "123456789",
|
|
34
|
-
"storeName": "Test Store",
|
|
35
|
-
"paymentToken": None,
|
|
36
|
-
"transactionStatus": "COMPLETED",
|
|
37
|
-
"transactionAmount": "1.23",
|
|
38
|
-
"paymentTokenExpiryDateTime": None,
|
|
39
|
-
"msisdn": "03458508726",
|
|
40
|
-
"paymentMode": "MA",
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def make_client(response_body: dict, status_code: int = 200) -> httpx.AsyncClient:
|
|
45
|
-
def handler(request: httpx.Request) -> httpx.Response:
|
|
46
|
-
return httpx.Response(status_code=status_code, json=response_body)
|
|
47
|
-
|
|
48
|
-
return httpx.AsyncClient(transport=httpx.MockTransport(handler))
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def make_ep(response_body: dict) -> EasyPaisa:
|
|
52
|
-
return EasyPaisa(
|
|
53
|
-
store_id="43",
|
|
54
|
-
username="admin",
|
|
55
|
-
password="123",
|
|
56
|
-
sandbox=True,
|
|
57
|
-
client=make_client(response_body),
|
|
58
|
-
)
|
|
5
|
+
from tests.conftest import (
|
|
6
|
+
INQUIRE_SUCCESS_BODY,
|
|
7
|
+
MA_SUCCESS_BODY,
|
|
8
|
+
OTC_SUCCESS_BODY,
|
|
9
|
+
make_client,
|
|
10
|
+
make_ep,
|
|
11
|
+
)
|
|
59
12
|
|
|
60
13
|
|
|
61
14
|
class TestPayViaMA:
|
|
@@ -1,46 +1,8 @@
|
|
|
1
|
-
import httpx
|
|
2
1
|
import pytest
|
|
3
2
|
|
|
4
3
|
from src.raqm_core.exceptions.exceptions import NetworkError, PaymentError
|
|
5
4
|
from src.raqm_core.jazzcash import JazzCash
|
|
6
|
-
|
|
7
|
-
MWALLET_SUCCESS_BODY = {
|
|
8
|
-
"pp_Amount": "1000",
|
|
9
|
-
"pp_AuthCode": "123456",
|
|
10
|
-
"pp_BankID": "",
|
|
11
|
-
"pp_BillReference": "bill123",
|
|
12
|
-
"pp_Language": "EN",
|
|
13
|
-
"pp_MerchantID": "MERCH123",
|
|
14
|
-
"pp_ResponseCode": "000",
|
|
15
|
-
"pp_ResponseMessage": "Success",
|
|
16
|
-
"pp_RetreivalReferenceNo": "RET123",
|
|
17
|
-
"pp_SubMerchantId": "SUBMERCH123",
|
|
18
|
-
"pp_TxnCurrency": "PKR",
|
|
19
|
-
"pp_TxnDateTime": "20260101120000",
|
|
20
|
-
"pp_TxnRefNo": "TXN123456",
|
|
21
|
-
"pp_SettlementExpiry": "20260101130000",
|
|
22
|
-
"pp_TxnType": "MWALLET",
|
|
23
|
-
"pp_Version": "1.1",
|
|
24
|
-
"pp_SecureHash": "abc123hash",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def make_client(response_body: dict, status_code: int = 200) -> httpx.AsyncClient:
|
|
29
|
-
def handler(request: httpx.Request) -> httpx.Response:
|
|
30
|
-
return httpx.Response(status_code=status_code, json=response_body)
|
|
31
|
-
|
|
32
|
-
return httpx.AsyncClient(transport=httpx.MockTransport(handler))
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def make_jc(response_body: dict) -> JazzCash:
|
|
36
|
-
return JazzCash(
|
|
37
|
-
merchant_id="MERCH123",
|
|
38
|
-
sub_merchant_id="SUBMERCH123",
|
|
39
|
-
hash_key="test_hash_key",
|
|
40
|
-
password="test_password",
|
|
41
|
-
sandbox=True,
|
|
42
|
-
client=make_client(response_body),
|
|
43
|
-
)
|
|
5
|
+
from tests.conftest import MWALLET_SUCCESS_BODY, make_client, make_jc
|
|
44
6
|
|
|
45
7
|
|
|
46
8
|
class TestPayViaMWallet:
|
|
@@ -48,6 +10,7 @@ class TestPayViaMWallet:
|
|
|
48
10
|
async def test_success(self):
|
|
49
11
|
jc = make_jc(MWALLET_SUCCESS_BODY)
|
|
50
12
|
result = await jc.pay_via_mwallet(
|
|
13
|
+
cnic="123456",
|
|
51
14
|
mobile_number="03001234567",
|
|
52
15
|
amount="1000",
|
|
53
16
|
description="Test payment",
|
|
@@ -77,6 +40,7 @@ class TestPayViaMWallet:
|
|
|
77
40
|
jc = make_jc(payload)
|
|
78
41
|
with pytest.raises(PaymentError) as exc_info:
|
|
79
42
|
await jc.pay_via_mwallet(
|
|
43
|
+
cnic="123456",
|
|
80
44
|
mobile_number="03001234567",
|
|
81
45
|
amount="1000",
|
|
82
46
|
description="Test payment",
|
|
@@ -97,6 +61,7 @@ class TestPayViaMWallet:
|
|
|
97
61
|
)
|
|
98
62
|
with pytest.raises(NetworkError):
|
|
99
63
|
await jc.pay_via_mwallet(
|
|
64
|
+
cnic="123456",
|
|
100
65
|
mobile_number="03001234567",
|
|
101
66
|
amount="1000",
|
|
102
67
|
description="Test payment",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|