feat(backend): migrate auth and notifications off email
Some checks failed
Backend CI/CD / test (push) Has been cancelled
Backend CI/CD / deploy (push) Has been cancelled

This commit is contained in:
2026-05-21 10:28:04 +03:30
parent b4903f7cb1
commit b7b21a6cc6
35 changed files with 2784 additions and 1390 deletions

View File

@@ -8,12 +8,44 @@ import requests
from apps.payments.models import Payment, DiscountCode
from apps.events.models import Event, Registration
from apps.notifications.services import notify_user
from apps.users.tasks import send_critical_sms
from core.authentication import jwt_auth
from apps.payments.api.schemas import CouponVerifyIn, CouponVerifyOut, CreatePaymentIn, CreatePaymentOut, PaymentDetailOut
payments_router = Router(tags=["Payments"])
def _event_action_url(event: Event) -> str:
root = getattr(settings, "FRONTEND_ROOT", "/") or "/"
if not root.endswith("/"):
root = f"{root}/"
return f"{root}events/{event.slug or event.id}"
def _notify_payment_status(payment: Payment, *, title: str, message: str, level: str):
notify_user(
payment.user_id,
{
"type": "payment_status",
"title": title,
"message": message,
"level": level,
"action_url": _event_action_url(payment.event),
"entity_type": "payment",
"entity_id": payment.id,
"meta": {
"event_id": payment.event_id,
"payment_id": payment.id,
"ref_id": payment.ref_id,
"status": payment.status,
},
},
)
if payment.user.mobile and payment.user.is_mobile_verified:
send_critical_sms.delay(payment.user.mobile, "payment_status", payment.event.title)
@payments_router.post("create", response=CreatePaymentOut, auth=jwt_auth)
def create_payment(request, payload: CreatePaymentIn):
event = get_object_or_404(Event, pk=payload.event_id)
@@ -145,10 +177,18 @@ def callback(request, Authority: str | None = None, Status: str | None = None):
pay = Payment.objects.filter(authority=Authority).select_related("event","user","discount_code").first()
if not pay:
raise HttpError(404, "Payment not found")
previous_status = pay.status
if Status != "OK":
pay.status = Payment.OrderStatusChoices.CANCELED
pay.save(update_fields=["status"])
if previous_status != Payment.OrderStatusChoices.CANCELED:
_notify_payment_status(
pay,
title=f"پرداخت {pay.event.title} لغو شد",
message="پرداخت شما کامل نشد و ثبت‌نام نهایی انجام نگرفت.",
level="warning",
)
return redirect(f"{settings.FRONTEND_CALLBACK_URL}?status=failed&event_id={pay.event_id}")
verify_body = {
@@ -168,6 +208,13 @@ def callback(request, Authority: str | None = None, Status: str | None = None):
except Exception:
pay.status = Payment.OrderStatusChoices.FAILED
pay.save(update_fields=["status"])
if previous_status != Payment.OrderStatusChoices.FAILED:
_notify_payment_status(
pay,
title=f"پرداخت {pay.event.title} ناموفق بود",
message="خطا در تأیید پرداخت رخ داد. در صورت نیاز دوباره تلاش کنید.",
level="error",
)
return redirect(f"{settings.FRONTEND_CALLBACK_URL}?status=failed&event_id={pay.event_id}")
vcode = (vjd.get("data") or {}).get("code")
@@ -193,10 +240,25 @@ def callback(request, Authority: str | None = None, Status: str | None = None):
updates.append("final_price")
registration.save(update_fields=updates)
if previous_status != Payment.OrderStatusChoices.PAID:
_notify_payment_status(
pay,
title=f"پرداخت {pay.event.title} تأیید شد",
message="پرداخت شما با موفقیت ثبت شد و ثبت‌نام رویداد تکمیل شده است.",
level="success",
)
return redirect(f"{settings.FRONTEND_CALLBACK_URL}?status=success&event_id={pay.event_id}&ref_id={pay.ref_id}")
pay.status = Payment.OrderStatusChoices.FAILED
pay.save(update_fields=["status"])
if previous_status != Payment.OrderStatusChoices.FAILED:
_notify_payment_status(
pay,
title=f"پرداخت {pay.event.title} ناموفق بود",
message="تراکنش شما توسط درگاه تأیید نشد. در صورت نیاز دوباره پرداخت را انجام دهید.",
level="error",
)
return redirect(f"{settings.FRONTEND_CALLBACK_URL}?status=failed&event_id={pay.event_id}")
@payments_router.get("by-ref/{ref_id}", response=PaymentDetailOut)