Skip to content

Commit 400fd6a

Browse files
authored
Merge pull request #46 from gopaycommunity/hotfix/GPOMA-2221-new-qr-payment-feature
GPOMA-2221: New QR payment feature
2 parents 9a6258c + 2e2c6da commit 400fd6a

5 files changed

Lines changed: 138 additions & 3 deletions

File tree

‎gopay/enums.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ class ContentType(StrEnum):
276276
FORM = "application/x-www-form-urlencoded"
277277
JSON = "application/json"
278278

279+
280+
class QrCodeFormat(StrEnum):
281+
PNG = "png"
282+
SVG = "svg"
283+
279284
class BnplType(StrEnum):
280285
DEFERRED_PAYMENT = "DEFERRED_PAYMENT"
281286
PAY_IN_THREE = "PAY_IN_THREE"

‎gopay/payments.py‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from dataclasses import dataclass
44

55
from gopay.api import GoPay, Response
6-
from gopay.enums import ContentType, Currency
6+
from gopay.enums import ContentType, Currency, QrCodeFormat
77

88

99
@dataclass
@@ -35,6 +35,21 @@ def get_status(self, payment_id: str | int) -> Response:
3535
"""
3636
return self.gopay.call("GET", f"/payments/payment/{payment_id}")
3737

38+
def get_qr_payment(
39+
self,
40+
payment_id: int | str,
41+
format: QrCodeFormat | None = None,
42+
) -> Response:
43+
"""
44+
Retrieves QR payment information for a given payment.
45+
GET /api/payments/payment/{id}/qr-payment
46+
Optional query parameter: format (png | svg), defaults to png.
47+
"""
48+
path = f"/payments/payment/{payment_id}/qr-payment"
49+
if format is not None:
50+
path = f"{path}?format={format}"
51+
return self.gopay.call("GET", path)
52+
3853
def refund_payment(self, payment_id: int | str, amount: int) -> Response:
3954
"""
4055
https://doc.gopay.com#payment-refund

‎gopay/utils.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
VERSION = "2.2.6"
1+
VERSION = "2.3.0"
22
DEFAULT_USER_AGENT = "GoPay Python " + VERSION

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ name = "gopay"
1414
packages = [{include = "gopay"}]
1515
readme = "README.md"
1616
repository = "https://github.com/gopaycommunity/gopay-python-api"
17-
version = "2.2.6"
17+
version = "2.3.0"
1818

1919
[tool.poetry.dependencies]
2020
deprecated = "^1.2.14"

‎tests/test_api_qr_payment.py‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import logging
2+
3+
from gopay import Payments
4+
from gopay.enums import QrCodeFormat
5+
6+
7+
class TestQrPayment:
8+
"""
9+
Integration tests for the QR payment endpoint.
10+
11+
Prerequisites:
12+
- Set the GOID, CLIENT_ID, CLIENT_SECRET and GATEWAY_URL environment variables.
13+
- The `payments` and `base_payment` fixtures are provided by conftest.py.
14+
"""
15+
16+
_payment_id: int | str
17+
18+
def test_create_payment_for_qr(self, payments: Payments, base_payment: dict):
19+
"""
20+
Creates a standard payment that will be used for subsequent QR payment tests.
21+
Stores the payment id on the class so the other tests can re-use it.
22+
"""
23+
response = payments.create_payment(base_payment)
24+
assert response.success, f"Payment creation failed: {response.json}"
25+
26+
body = response.json
27+
logging.info(f"Created payment: {body}")
28+
29+
assert "errors" not in body
30+
assert "id" in body
31+
assert body["state"] == "CREATED"
32+
33+
# Store the payment id for the other tests in this class
34+
TestQrPayment._payment_id = body["id"]
35+
36+
def test_get_qr_payment_default_format(self, payments: Payments):
37+
"""
38+
Calls GET /api/payments/payment/{id}/qr-payment without specifying format.
39+
Expects a successful response containing amount, currency and qr_code.
40+
"""
41+
payment_id = TestQrPayment._payment_id
42+
response = payments.get_qr_payment(payment_id)
43+
44+
logging.info(f"QR payment response (default format): {response.json}")
45+
assert response.success, f"QR payment request failed: {response.json}"
46+
47+
body = response.json
48+
assert "errors" not in body
49+
assert "amount" in body
50+
assert "currency" in body
51+
assert "qr_code" in body
52+
53+
# qr_code must contain at least one of the known QR types
54+
qr_code = body["qr_code"]
55+
assert any(
56+
key in qr_code for key in ("spayd", "paybysquare", "sepa", "mnb_qr")
57+
), f"Unexpected qr_code structure: {qr_code}"
58+
59+
def test_get_qr_payment_png_format(self, payments: Payments):
60+
"""
61+
Calls GET /api/payments/payment/{id}/qr-payment?format=png.
62+
"""
63+
payment_id = TestQrPayment._payment_id
64+
response = payments.get_qr_payment(payment_id, format=QrCodeFormat.PNG)
65+
66+
logging.info(f"QR payment response (PNG): {response.json}")
67+
assert response.success, f"QR payment PNG request failed: {response.json}"
68+
69+
body = response.json
70+
assert "errors" not in body
71+
assert "qr_code" in body
72+
73+
def test_get_qr_payment_svg_format(self, payments: Payments):
74+
"""
75+
Calls GET /api/payments/payment/{id}/qr-payment?format=svg.
76+
"""
77+
payment_id = TestQrPayment._payment_id
78+
response = payments.get_qr_payment(payment_id, format=QrCodeFormat.SVG)
79+
80+
logging.info(f"QR payment response (SVG): {response.json}")
81+
assert response.success, f"QR payment SVG request failed: {response.json}"
82+
83+
body = response.json
84+
assert "errors" not in body
85+
assert "qr_code" in body
86+
87+
def test_get_qr_payment_recipient_structure(self, payments: Payments):
88+
"""
89+
Validates the structure of the recipient block in the QR payment response.
90+
"""
91+
payment_id = TestQrPayment._payment_id
92+
response = payments.get_qr_payment(payment_id)
93+
assert response.success
94+
95+
body = response.json
96+
assert "recipient" in body
97+
98+
recipient = body["recipient"]
99+
# name is expected
100+
assert "name" in recipient
101+
102+
# bank_account block (local + international) is expected
103+
if "bank_account" in recipient:
104+
bank_account = recipient["bank_account"]
105+
if "local" in bank_account:
106+
local = bank_account["local"]
107+
assert "account_number" in local
108+
assert "bank_code" in local
109+
if "international" in bank_account:
110+
international = bank_account["international"]
111+
assert "iban" in international
112+
# bic is not always present (depends on currency/payment type)
113+
assert any(
114+
key in international for key in ("bic", "reference")
115+
), f"Unexpected international bank_account structure: {international}"

0 commit comments

Comments
 (0)