fix(payments): make checkout attempts idempotent and reconcilable
This commit is contained in:
48
apps/payments/services.py
Normal file
48
apps/payments/services.py
Normal 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"]
|
||||
Reference in New Issue
Block a user