fix(payments): make checkout attempts idempotent and reconcilable

This commit is contained in:
2026-09-30 21:14:16 +03:30
parent 77567178ad
commit 1daa755543
9 changed files with 862 additions and 173 deletions

48
apps/payments/services.py Normal file
View File

@@ -0,0 +1,48 @@
"""Gateway operations whose responses must be interpreted conservatively."""
from django.conf import settings
import requests
class PaymentVerificationUncertain(Exception):
"""The gateway response does not prove either payment success or failure."""
def request_payment_verification(payment):
try:
response = requests.post(
settings.ZARINPAL_VERIFY_URL,
json={
"merchant_id": settings.ZARINPAL_MERCHANT_ID,
"amount": payment.amount,
"authority": payment.authority,
},
headers={"accept": "application/json", "content-type": "application/json"},
timeout=15,
)
status_code = getattr(response, "status_code", 200)
if isinstance(status_code, int) and status_code >= 400:
raise PaymentVerificationUncertain(
f"ZarinPal verification returned HTTP {status_code}"
)
body = response.json()
except PaymentVerificationUncertain:
raise
except Exception as exc:
raise PaymentVerificationUncertain("ZarinPal verification request failed") from exc
if not isinstance(body, dict) or not isinstance(body.get("data"), dict):
raise PaymentVerificationUncertain("ZarinPal returned an invalid verification response")
code = body["data"].get("code")
try:
code = int(code)
except (TypeError, ValueError) as exc:
raise PaymentVerificationUncertain("ZarinPal returned no verification code") from exc
if code not in (100, 101, -51):
raise PaymentVerificationUncertain(
f"ZarinPal returned an inconclusive verification code: {code}"
)
return code, body["data"]