fix(payments): reuse checkout attempts and show pending status

This commit is contained in:
2026-09-30 21:14:19 +03:30
parent 450b387228
commit 9f3f3631ac
4 changed files with 191 additions and 67 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useNavigate, useParams } from "@/lib/router";
import { api } from "@/lib/api";
import type * as Types from "@/lib/types";
@@ -76,6 +76,8 @@ export default function EventDetail({ initialEvent = null }: EventDetailProps) {
const [submitting, setSubmitting] = useState(false);
const [alreadyRegistered, setAlreadyRegistered] = useState(false);
const [nowTs, setNowTs] = useState(() => Date.now());
const checkoutInProgressRef = useRef(false);
const checkoutIdempotencyKeyRef = useRef<string | null>(null);
const basePrice = Number(event?.price ?? 0);
const isFree = useMemo(() => basePrice <= 0, [basePrice]);
@@ -163,6 +165,9 @@ export default function EventDetail({ initialEvent = null }: EventDetailProps) {
return;
}
if (checkoutInProgressRef.current) return;
checkoutInProgressRef.current = true;
try {
setSubmitting(true);
const reg = await api.registerForEvent(event.id, coupon);
@@ -185,9 +190,10 @@ export default function EventDetail({ initialEvent = null }: EventDetailProps) {
event_id: event.id,
description: `پرداخت رویداد: ${event.title}`,
discount_code: (coupon ?? "").trim() || null,
idempotency_key: checkoutIdempotencyKeyRef.current ??= crypto.randomUUID(),
});
if (!result?.start_pay_url || Number(result.amount) === 0) {
if (Number(result?.amount ?? 0) === 0) {
window.sessionStorage.setItem(
"payment:last",
buildPaymentSnapshot(event, eventThumb, {
@@ -200,6 +206,20 @@ export default function EventDetail({ initialEvent = null }: EventDetailProps) {
return;
}
if (result.status === "failed" || result.status === "canceled") {
checkoutIdempotencyKeyRef.current = null;
setOpen(false);
navigate(`/payments/result?status=${result.status}&event_id=${event.id}&payment_id=${result.payment_id ?? ""}`);
return;
}
if (!result.start_pay_url) {
setOpen(false);
const resultStatus = result.status === "paid" ? "success" : "pending";
navigate(`/payments/result?status=${resultStatus}&event_id=${event.id}&payment_id=${result.payment_id ?? ""}`);
return;
}
window.sessionStorage.setItem(
"payment:last",
buildPaymentSnapshot(event, eventThumb, {
@@ -223,6 +243,7 @@ export default function EventDetail({ initialEvent = null }: EventDetailProps) {
});
} finally {
setSubmitting(false);
checkoutInProgressRef.current = false;
setOpen(false);
}
};