74 lines
2.7 KiB
Python
74 lines
2.7 KiB
Python
"""Bounded retries for payment verification after an inconclusive callback."""
|
|
|
|
import logging
|
|
|
|
from celery import shared_task
|
|
from django.shortcuts import get_object_or_404
|
|
|
|
from apps.payments.models import Payment
|
|
from apps.payments.services import PaymentVerificationUncertain, request_payment_verification
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@shared_task(bind=True, max_retries=5, name="apps.payments.reconcile_payment")
|
|
def reconcile_payment(self, payment_id: int):
|
|
payment = get_object_or_404(
|
|
Payment.objects.select_related("event", "user"),
|
|
pk=payment_id,
|
|
)
|
|
if payment.status in (Payment.OrderStatusChoices.PAID, Payment.OrderStatusChoices.FAILED):
|
|
return payment.get_status_display().lower()
|
|
if not payment.authority:
|
|
logger.error(
|
|
"Payment %s remains pending without a gateway authority; manual review is required",
|
|
payment.id,
|
|
)
|
|
return "pending_manual_review"
|
|
|
|
# Imported at task runtime to avoid a view/task import cycle.
|
|
from apps.payments.api.views import (
|
|
_mark_payment_paid,
|
|
_mark_payment_pending,
|
|
_mark_payment_unsuccessful,
|
|
_notify_payment_status,
|
|
)
|
|
|
|
try:
|
|
code, data = request_payment_verification(payment)
|
|
except PaymentVerificationUncertain as exc:
|
|
_mark_payment_pending(payment.id)
|
|
if self.request.retries >= self.max_retries:
|
|
logger.error(
|
|
"Payment %s verification remains uncertain after bounded retries; manual review is required",
|
|
payment.id,
|
|
exc_info=exc,
|
|
)
|
|
return "pending_manual_review"
|
|
countdown = min(60 * (2 ** self.request.retries), 15 * 60)
|
|
raise self.retry(exc=exc, countdown=countdown)
|
|
|
|
if code in (100, 101):
|
|
payment, newly_verified = _mark_payment_paid(payment.id, data)
|
|
if newly_verified:
|
|
_notify_payment_status(
|
|
payment,
|
|
title=f"پرداخت رویداد «{payment.event.title}» تأیید شد",
|
|
message="پرداخت شما با موفقیت ثبت شد و ثبتنام رویداد تکمیل شده است.",
|
|
level="success",
|
|
)
|
|
return "paid"
|
|
|
|
payment, result, transitioned = _mark_payment_unsuccessful(
|
|
payment.id,
|
|
Payment.OrderStatusChoices.FAILED,
|
|
)
|
|
if transitioned:
|
|
_notify_payment_status(
|
|
payment,
|
|
title=f"پرداخت رویداد «{payment.event.title}» ناموفق بود",
|
|
message="تراکنش شما توسط درگاه تأیید نشد. در صورت نیاز دوباره پرداخت را انجام دهید.",
|
|
level="error",
|
|
)
|
|
return result
|