python-getpaid-paynow 0.1.0__tar.gz → 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.
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/PKG-INFO +3 -3
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/README.md +2 -2
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/getting-started.md +2 -2
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/pyproject.toml +1 -1
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/src/getpaid_paynow/processor.py +22 -3
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/tests/test_callback.py +22 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/tests/test_processor.py +2 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/uv.lock +1 -1
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/.gitignore +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/.readthedocs.yml +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/CODE_OF_CONDUCT.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/CONTRIBUTING.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/LICENSE +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/changelog.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/codeofconduct.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/concepts.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/conf.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/configuration.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/contributing.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/index.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/license.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/reference.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/docs/requirements.txt +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/src/getpaid_paynow/__init__.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/src/getpaid_paynow/client.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/src/getpaid_paynow/py.typed +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/src/getpaid_paynow/types.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/tests/__init__.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/tests/conftest.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/tests/test_client.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-0.1.2}/tests/test_types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-getpaid-paynow
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Paynow payment gateway integration for python-getpaid ecosystem.
|
|
5
5
|
Project-URL: Homepage, https://github.com/django-getpaid/python-getpaid-paynow
|
|
6
6
|
Project-URL: Repository, https://github.com/django-getpaid/python-getpaid-paynow
|
|
@@ -61,7 +61,7 @@ The plugin is split into two layers:
|
|
|
61
61
|
### Standalone Client
|
|
62
62
|
|
|
63
63
|
```python
|
|
64
|
-
import
|
|
64
|
+
import anyio
|
|
65
65
|
from decimal import Decimal
|
|
66
66
|
from getpaid_paynow import PaynowClient
|
|
67
67
|
|
|
@@ -83,7 +83,7 @@ async def main():
|
|
|
83
83
|
redirect_url = response["redirectUrl"]
|
|
84
84
|
print(f"Redirect buyer to: {redirect_url}")
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
anyio.run(main)
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
### With django-getpaid
|
|
@@ -38,7 +38,7 @@ The plugin is split into two layers:
|
|
|
38
38
|
### Standalone Client
|
|
39
39
|
|
|
40
40
|
```python
|
|
41
|
-
import
|
|
41
|
+
import anyio
|
|
42
42
|
from decimal import Decimal
|
|
43
43
|
from getpaid_paynow import PaynowClient
|
|
44
44
|
|
|
@@ -60,7 +60,7 @@ async def main():
|
|
|
60
60
|
redirect_url = response["redirectUrl"]
|
|
61
61
|
print(f"Redirect buyer to: {redirect_url}")
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
anyio.run(main)
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
### With django-getpaid
|
|
@@ -32,7 +32,7 @@ The `PaynowClient` is an async HTTP client that wraps the Paynow V3 REST API.
|
|
|
32
32
|
Use it as an async context manager for connection reuse:
|
|
33
33
|
|
|
34
34
|
```python
|
|
35
|
-
import
|
|
35
|
+
import anyio
|
|
36
36
|
from decimal import Decimal
|
|
37
37
|
from getpaid_paynow import PaynowClient
|
|
38
38
|
|
|
@@ -56,7 +56,7 @@ async def main():
|
|
|
56
56
|
redirect_url = response["redirectUrl"]
|
|
57
57
|
print(f"Redirect buyer to: {redirect_url}")
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
anyio.run(main)
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
## Using with django-getpaid
|
|
@@ -110,8 +110,24 @@ class PaynowProcessor(BaseProcessor):
|
|
|
110
110
|
:param headers: HTTP headers (must include 'Signature').
|
|
111
111
|
:raises InvalidCallbackError: On missing/invalid signature.
|
|
112
112
|
"""
|
|
113
|
-
raw_body
|
|
114
|
-
|
|
113
|
+
raw_body = kwargs.get("raw_body")
|
|
114
|
+
if raw_body is None:
|
|
115
|
+
raise InvalidCallbackError(
|
|
116
|
+
"Missing raw_body in callback kwargs. "
|
|
117
|
+
"The framework adapter must pass the raw HTTP body."
|
|
118
|
+
)
|
|
119
|
+
if isinstance(raw_body, (bytes, bytearray)):
|
|
120
|
+
raw_body = raw_body.decode("utf-8")
|
|
121
|
+
if not isinstance(raw_body, str):
|
|
122
|
+
raise InvalidCallbackError(
|
|
123
|
+
"raw_body must be a str or bytes value."
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
received_sig = ""
|
|
127
|
+
for key, value in headers.items():
|
|
128
|
+
if key.lower() == "signature":
|
|
129
|
+
received_sig = value
|
|
130
|
+
break
|
|
115
131
|
|
|
116
132
|
if not received_sig:
|
|
117
133
|
raise InvalidCallbackError(
|
|
@@ -224,10 +240,13 @@ class PaynowProcessor(BaseProcessor):
|
|
|
224
240
|
"""Start a refund via Paynow API."""
|
|
225
241
|
client = self._get_client()
|
|
226
242
|
refund_amount = amount or self.payment.amount_paid
|
|
227
|
-
await client.create_refund(
|
|
243
|
+
response = await client.create_refund(
|
|
228
244
|
payment_id=self.payment.external_id,
|
|
229
245
|
amount=refund_amount,
|
|
230
246
|
)
|
|
247
|
+
refund_id = response.get("refundId", "")
|
|
248
|
+
if refund_id:
|
|
249
|
+
self.payment.external_refund_id = refund_id
|
|
231
250
|
return refund_amount
|
|
232
251
|
|
|
233
252
|
async def cancel_refund(self, **kwargs) -> bool:
|
|
@@ -86,6 +86,28 @@ class TestVerifyCallback:
|
|
|
86
86
|
raw_body=raw_body,
|
|
87
87
|
)
|
|
88
88
|
|
|
89
|
+
async def test_lowercase_signature_header_is_accepted(self):
|
|
90
|
+
data, raw_body = _notification()
|
|
91
|
+
signature = _sign_body(raw_body)
|
|
92
|
+
headers = {"signature": signature}
|
|
93
|
+
processor = _make_processor()
|
|
94
|
+
await processor.verify_callback(
|
|
95
|
+
data=data,
|
|
96
|
+
headers=headers,
|
|
97
|
+
raw_body=raw_body.encode("utf-8"),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def test_missing_raw_body_raises(self):
|
|
101
|
+
data, raw_body = _notification()
|
|
102
|
+
signature = _sign_body(raw_body)
|
|
103
|
+
headers = {"Signature": signature}
|
|
104
|
+
processor = _make_processor()
|
|
105
|
+
with pytest.raises(InvalidCallbackError, match="Missing raw_body"):
|
|
106
|
+
await processor.verify_callback(
|
|
107
|
+
data=data,
|
|
108
|
+
headers=headers,
|
|
109
|
+
)
|
|
110
|
+
|
|
89
111
|
async def test_bad_signature_raises(self):
|
|
90
112
|
data, raw_body = _notification()
|
|
91
113
|
headers = {"Signature": "bad_signature"}
|
|
@@ -312,6 +312,7 @@ class TestStartRefund:
|
|
|
312
312
|
amount=Decimal("50.00"),
|
|
313
313
|
)
|
|
314
314
|
assert result == Decimal("50.00")
|
|
315
|
+
assert payment.external_refund_id == "REF-456"
|
|
315
316
|
|
|
316
317
|
async def test_start_refund_full_amount(self, respx_mock):
|
|
317
318
|
url = f"{SANDBOX_URL}/v3/payments/PAY-123/refunds"
|
|
@@ -324,6 +325,7 @@ class TestStartRefund:
|
|
|
324
325
|
processor = _make_processor(payment=payment)
|
|
325
326
|
result = await processor.start_refund()
|
|
326
327
|
assert result == Decimal("100.00")
|
|
328
|
+
assert payment.external_refund_id == "REF-456"
|
|
327
329
|
|
|
328
330
|
async def test_start_refund_sends_correct_body(self, respx_mock):
|
|
329
331
|
url = f"{SANDBOX_URL}/v3/payments/PAY-123/refunds"
|
|
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
|
|
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
|