import React, { useEffect, useState } from 'react'; import { createPaymentOrder, recordCodPayment, verifyPayment, } from '../core/checkout-api'; import type { PaymentStepProps } from '../core/types'; declare global { interface Window { Razorpay?: new (options: Record) => { open: () => void }; } } const RAZORPAY_SCRIPT = 'https://checkout.razorpay.com/v1/checkout.js'; function loadRazorpayScript(): Promise { if (window.Razorpay) return Promise.resolve(); return new Promise((resolve, reject) => { const existing = document.querySelector(`script[src="${RAZORPAY_SCRIPT}"]`); if (existing) { existing.addEventListener('load', () => resolve()); return; } const script = document.createElement('script'); script.src = RAZORPAY_SCRIPT; script.async = true; script.onload = () => resolve(); script.onerror = () => reject(new Error('Failed to load Razorpay')); document.body.appendChild(script); }); } const PaymentStep: React.FC = ({ checkoutConfig, sessionToken, sessionId, amountPaise, enableCod = false, codAllowed = false, addressRisk, theme = 'light', onComplete, onBack, onEvent, orderConfirmationUrl, }) => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const isDark = theme === 'dark'; useEffect(() => { onEvent?.('payment_step_viewed'); }, [onEvent]); const handlePrepaid = async () => { setLoading(true); setError(null); try { await loadRazorpayScript(); const order = await createPaymentOrder(checkoutConfig, sessionToken, amountPaise); await new Promise((resolve, reject) => { if (!window.Razorpay) { reject(new Error('Razorpay unavailable')); return; } const rzp = new window.Razorpay({ key: order.razorpay_key_id, amount: order.amount_paise, currency: order.currency, order_id: order.razorpay_order_id, name: 'Checkout', handler: async (response: { razorpay_payment_id: string; razorpay_order_id: string; razorpay_signature: string; }) => { try { const result = await verifyPayment(checkoutConfig, sessionToken, { razorpay_order_id: response.razorpay_order_id, razorpay_payment_id: response.razorpay_payment_id, razorpay_signature: response.razorpay_signature, payment_method: 'prepaid', }); onEvent?.('payment_method_selected', result.payment_method); onComplete({ paymentMethod: result.payment_method, razorpayOrderId: result.razorpay_order_id, razorpayPaymentId: result.razorpay_payment_id, }); if (orderConfirmationUrl) { const url = orderConfirmationUrl.replace('{{sessionId}}', sessionId ?? ''); window.location.href = url; } resolve(); } catch (e) { reject(e); } }, modal: { ondismiss: () => reject(new Error('Payment cancelled')), }, }); rzp.open(); }); } catch (e) { setError(e instanceof Error ? e.message : 'Payment failed'); } finally { setLoading(false); } }; const handleCod = async () => { setLoading(true); setError(null); try { const result = await recordCodPayment(checkoutConfig, sessionToken); onEvent?.('payment_method_selected', result.payment_method); onComplete({ paymentMethod: result.payment_method }); } catch (e) { setError(e instanceof Error ? e.message : 'Failed to select COD'); } finally { setLoading(false); } }; const amountDisplay = `₹${(amountPaise / 100).toLocaleString('en-IN')}`; const codBlockedByRisk = addressRisk?.interventions.includes('hide_cod') ?? false; const riskTone = addressRisk?.level === 'high' ? 'high' : addressRisk?.level === 'medium' ? 'medium' : 'low'; return (
{onBack ? ( ) : null}

Choose payment

Order total: {amountDisplay}

{error ?

{error}

: null} {addressRisk && addressRisk.level !== 'low' ? (
{addressRisk.level === 'high' ? 'Address needs extra confirmation' : 'Address confidence check'} {addressRisk.reasons[0]} {codBlockedByRisk ? ( Cash on Delivery is disabled for this order. Online payment is still available. ) : null}
) : null} {enableCod && codAllowed && !codBlockedByRisk ? ( ) : null} {sessionId ? (

Session {sessionId.slice(0, 8)}…

) : null}
); }; export default PaymentStep;