feat: add dedicated TopUpResult page for payment return flow

Replace toast-based payment return with animated result page showing
pending/success/failed/timeout states. Extract shared Spinner,
AnimatedCheckmark and AnimatedCrossmark components. Add sessionStorage
persistence with validation and TTL for cross-redirect payment data.
Unify ProtectedRoute with withLayout prop, add aria-live accessibility.
This commit is contained in:
Fringg
2026-03-09 03:58:40 +03:00
parent d7b1ff1052
commit b59122818c
14 changed files with 608 additions and 130 deletions

View File

@@ -12,6 +12,7 @@ import { useHaptic, usePlatform } from '@/platform';
import { staggerContainer, staggerItem } from '@/components/motion/transitions';
import type { PaymentMethod } from '../types';
import BentoCard from '../components/ui/BentoCard';
import { saveTopUpPendingInfo } from '../utils/topUpStorage';
// Icons
const StarIcon = () => (
@@ -202,6 +203,20 @@ export default function TopUpAmount() {
const redirectUrl = data.payment_url || data.invoice_url;
if (redirectUrl) {
setPaymentUrl(redirectUrl);
// Save payment info for the result page
if (method && data.payment_id) {
const methodKey = method.id.toLowerCase().replace(/-/g, '_');
const displayName =
t(`balance.paymentMethods.${methodKey}.name`, { defaultValue: '' }) || method.name;
saveTopUpPendingInfo({
amount_kopeks: data.amount_kopeks,
method_id: method.id,
method_name: displayName,
payment_id: data.payment_id,
created_at: Date.now(),
});
}
}
},
onError: (err: unknown) => {
@@ -296,8 +311,8 @@ export default function TopUpAmount() {
await navigator.clipboard.writeText(paymentUrl);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (e) {
console.warn('Failed to copy:', e);
} catch {
// Clipboard write failed silently
}
};