"use client"; import { useId, type ReactNode } from "react"; import { QRCodeSVG } from "qrcode.react"; import { ModalPortal } from "@/app/_components/core/modal-portal"; import type { PaymentLaunchFlow } from "@/app/_hooks/use-payment-launch-flow"; import { LazyStripePaymentDialog } from "./lazy-stripe-payment-dialog"; import { stripePaymentDialogStyles as styles } from "./stripe-payment-dialog.styles"; type PaymentLaunchDialogFlow = Pick< PaymentLaunchFlow, | "handleEzpayCancel" | "handleEzpayConfirm" | "handleRegionalQrClose" | "handleStripeClose" | "handleStripeConfirmed" | "isConfirmingEzpay" | "regionalQrErrorMessage" | "regionalQrPayment" | "regionalQrStatus" | "shouldShowEzpayConfirmDialog" | "shouldShowRegionalQrDialog" | "shouldShowStripeDialog" | "stripeClientSecret" >; export interface PaymentLaunchDialogsProps { currentOrderId: string | null; externalCheckoutAnalyticsKey: string; ezpayDescription: ReactNode; launch: PaymentLaunchDialogFlow; stripeReturnPath?: string; } export function PaymentLaunchDialogs({ currentOrderId, externalCheckoutAnalyticsKey, ezpayDescription, launch, stripeReturnPath, }: PaymentLaunchDialogsProps) { return ( <> {launch.shouldShowEzpayConfirmDialog ? ( ) : null} {launch.shouldShowRegionalQrDialog && launch.regionalQrPayment ? ( ) : null} {launch.shouldShowStripeDialog && launch.stripeClientSecret ? ( ) : null} ); } interface RegionalQrPaymentDialogProps { errorMessage: string | null; onClose: () => void; payment: NonNullable; status: PaymentLaunchFlow["regionalQrStatus"]; } function formatRegionalQrAmount(amountCents: number, currency: string): string { const normalizedCurrency = currency.trim().toUpperCase() || "IDR"; try { return new Intl.NumberFormat( normalizedCurrency === "PHP" ? "en-PH" : "id-ID", { style: "currency", currency: normalizedCurrency, maximumFractionDigits: normalizedCurrency === "IDR" ? 0 : 2, }, ).format(amountCents / 100); } catch { return `${normalizedCurrency} ${(amountCents / 100).toFixed(2)}`; } } function regionalQrStatusMessage( status: PaymentLaunchFlow["regionalQrStatus"], errorMessage: string | null, paymentName: string, ): string { if (status === "failed") { return errorMessage || "Payment failed. Please try again."; } if (status === "expired") { return errorMessage || `This ${paymentName} order has expired.`; } return "Waiting for payment"; } function RegionalQrPaymentDialog({ errorMessage, onClose, payment, status, }: RegionalQrPaymentDialogProps) { const titleId = useId(); const copy = regionalQrCopy(payment.experience); const statusMessage = regionalQrStatusMessage( status, errorMessage, copy.paymentName, ); return (

{copy.title}

{copy.description}

{payment.qrData ? ( ) : (

{copy.unavailableMessage}

)}

Order No. {payment.orderId}

{formatRegionalQrAmount(payment.amountCents, payment.currency)}

{statusMessage}

); } function regionalQrCopy( experience: NonNullable["experience"], ) { if (experience === "gcashQrPh") { return { paymentName: "GCash / QR Ph", title: "Pay with GCash / QR Ph", description: "Open GCash or another QR Ph-compatible app and scan this code.", qrTitle: "GCash / QR Ph payment QR code", unavailableMessage: "GCash / QR Ph data is unavailable. Please close this dialog and try again.", }; } if (experience === "qris") { return { paymentName: "QRIS", title: "Scan to pay with QRIS", description: "Open a QRIS-compatible banking or wallet app and scan this code.", qrTitle: "QRIS payment QR code", unavailableMessage: "QRIS data is unavailable. Please close this dialog and try again.", }; } return { paymentName: "payment QR", title: "Scan to pay", description: "Open a compatible banking or wallet app and scan this code.", qrTitle: "Payment QR code", unavailableMessage: "Payment QR data is unavailable. Please close this dialog and try again.", }; } interface EzpayRedirectConfirmDialogProps { description: ReactNode; externalCheckoutAnalyticsKey: string; isConfirming: boolean; onCancel: () => void; onConfirm: () => void; orderId: string; } function EzpayRedirectConfirmDialog({ description, externalCheckoutAnalyticsKey, isConfirming, onCancel, onConfirm, orderId, }: EzpayRedirectConfirmDialogProps) { const titleId = useId(); return (

Continue to payment?

{description}

Order No. {orderId}

); }