Payment return decided by the server only; encoded path ids; no sign-out on a two-tab refresh race
- ?orderId must be numeric; every id in an API path is encodeURIComponent'd.
- The payment sheet shows success only from GET /payment/orders/{id}; a failed lookup says the
status is unknown instead of "paid".
- A 401 from /auth/refresh is not a sign-out when another tab already stored a rotated token.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -144,29 +144,47 @@ function RegisterView({ onRegistered }: Pick<Props, 'onRegistered'>) {
|
||||
);
|
||||
}
|
||||
|
||||
/** نتیجهی برگشت از درگاه؛ وضعیتِ سفارش از خودِ سرور هم دوباره خوانده میشود. */
|
||||
/**
|
||||
* نتیجهی برگشت از درگاه. فقط پاسخِ سرور تعیین میکند پرداخت موفق بوده یا نه؛ `status` در آدرس را هر
|
||||
* لینکِ ساختگی میتواند «paid» بگذارد.
|
||||
*/
|
||||
function PaymentResult({ payment }: { payment: PaymentReturn }) {
|
||||
const { refreshSummary } = useAppData();
|
||||
const [open, setOpen] = useState(true);
|
||||
const [paid, setPaid] = useState(payment.status === 'paid');
|
||||
const [state, setState] = useState<'checking' | 'paid' | 'failed' | 'unknown'>('checking');
|
||||
|
||||
useEffect(() => {
|
||||
api.subscription
|
||||
.order(payment.orderId)
|
||||
.then((order) => setPaid(order.status === 'paid'))
|
||||
.catch(() => {});
|
||||
.then((order) => setState(order.status === 'paid' ? 'paid' : 'failed'))
|
||||
.catch((err: unknown) => {
|
||||
console.warn('payment result lookup failed', err);
|
||||
setState('unknown');
|
||||
});
|
||||
void refreshSummary();
|
||||
}, [payment.orderId, refreshSummary]);
|
||||
|
||||
if (state === 'checking') {
|
||||
return (
|
||||
<Sheet open={open} onClose={() => setOpen(false)}>
|
||||
<LoadingState text="دارم پرداخت رو بررسی میکنم…" />
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
const paid = state === 'paid';
|
||||
return (
|
||||
<Sheet open={open} onClose={() => setOpen(false)}>
|
||||
<div className="flex flex-col items-center gap-3 pb-2 text-center">
|
||||
{paid ? <CheckCircle2 size={52} className="text-mint" /> : <XCircle size={52} className="text-red-400" />}
|
||||
<h2 className="text-lg font-black text-ink">{paid ? 'پرداخت موفق بود! 🎉' : 'پرداخت انجام نشد'}</h2>
|
||||
<h2 className="text-lg font-black text-ink">
|
||||
{paid ? 'پرداخت موفق بود! 🎉' : state === 'unknown' ? 'وضعیتِ پرداخت معلوم نشد' : 'پرداخت انجام نشد'}
|
||||
</h2>
|
||||
<p className="text-sm leading-7 text-muted">
|
||||
{paid
|
||||
? 'اشتراکت فعال شد؛ از الان قلبهات نامحدوده.'
|
||||
: 'اگه مبلغی از حسابت کم شده، نگران نباش؛ طیِ چند ساعت خودکار برمیگرده یا اشتراکت فعال میشه.'}
|
||||
: state === 'unknown'
|
||||
? 'چند دقیقهی دیگه از «پرداختها و فاکتورها» در پروفایل نگاه کن. اگه مبلغی کم شده، یا خریدت فعال میشه یا پول خودکار برمیگرده.'
|
||||
: 'اگه مبلغی از حسابت کم شده، نگران نباش؛ طیِ چند ساعت خودکار برمیگرده یا اشتراکت فعال میشه.'}
|
||||
</p>
|
||||
<PrimaryButton color={paid ? '#5FBF8F' : '#ED6956'} onClick={() => setOpen(false)}>باشه</PrimaryButton>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user