"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" | "handleQrisClose" | "handleStripeClose" | "handleStripeConfirmed" | "isConfirmingEzpay" | "qrisErrorMessage" | "qrisPayment" | "qrisStatus" | "shouldShowEzpayConfirmDialog" | "shouldShowQrisDialog" | "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.shouldShowQrisDialog && launch.qrisPayment ? ( ) : null} {launch.shouldShowStripeDialog && launch.stripeClientSecret ? ( ) : null} ); } interface QrisPaymentDialogProps { errorMessage: string | null; onClose: () => void; payment: NonNullable; status: PaymentLaunchFlow["qrisStatus"]; } function formatQrisAmount(amountCents: number, currency: string): string { const normalizedCurrency = currency.trim().toUpperCase() || "IDR"; try { return new Intl.NumberFormat("id-ID", { style: "currency", currency: normalizedCurrency, maximumFractionDigits: normalizedCurrency === "IDR" ? 0 : 2, }).format(amountCents / 100); } catch { return `${normalizedCurrency} ${(amountCents / 100).toFixed(2)}`; } } function qrisStatusMessage( status: PaymentLaunchFlow["qrisStatus"], errorMessage: string | null, ): string { if (status === "failed") return errorMessage || "Payment failed. Please try again."; if (status === "expired") return errorMessage || "This QRIS order has expired."; return "Waiting for payment"; } function QrisPaymentDialog({ errorMessage, onClose, payment, status, }: QrisPaymentDialogProps) { const titleId = useId(); const statusMessage = qrisStatusMessage(status, errorMessage); return (

Scan to pay with QRIS

Open a QRIS-compatible banking or wallet app and scan this code.

{payment.qrData ? ( ) : (

QRIS data is unavailable. Please close this dialog and try again.

)}

Order No. {payment.orderId}

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

{statusMessage}

); } 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}

); }