import { useState, useCallback } from 'react'; import { Alert } from 'react-native'; // @ts-ignore import RazorpayCheckout from 'react-native-razorpay'; import { PlaceOrderResponse } from '@interfaces'; import { verifyPaymentApi } from '@api'; import { useAppSelector } from '@store'; import { ENV } from '../../../../config'; /** * Custom hook that encapsulates the entire Razorpay UPI payment flow: * 1. Extract session details (gatewayOrderId, amount) from the place-order response * 2. Configure Razorpay checkout options with prefilled customer profile details * 3. Present the Razorpay Checkout overlay to the user * 4. Verify the payment signature on the backend via /payments/process * * Returns: * - processRazorpayPayment: async function to execute the Razorpay flow * - isPaymentProcessing: loading flag for UPI payment step */ export const useRazorPayment = () => { const [isPaymentProcessing, setIsPaymentProcessing] = useState(false); const user = useAppSelector( state => state.customerProfile.customerDetails?.user, ); /** * Runs the complete Razorpay payment flow. * @param response – the PlaceOrderResponse from the backend * @returns `true` if payment was completed and verified successfully, `false` otherwise */ const processRazorpayPayment = useCallback( async ( response: PlaceOrderResponse, paymentMethod: 'upi' | 'card' | undefined = undefined, ): Promise => { try { setIsPaymentProcessing(true); // ── 1. Extract session details ───────────────────────────────────── const session = response.checkoutSessions?.[0]; if (!session) { Alert.alert('Error', 'Payment session not found.'); return false; } const { gatewayOrderId, amount } = session; if (!gatewayOrderId) { Alert.alert('Error', 'Razorpay Order ID not found.'); return false; } // Amount in checkoutSessions represents standard rupees. // Fallback to first order's total amount if needed. const amountVal = amount ?? parseFloat(response.orders?.[0]?.totalAmount || '0'); // Razorpay SDK requires the amount in the smallest currency sub-units (paise for INR) const amountInPaise = Math.round(amountVal * 100); // ── 2. Configure Checkout Options ────────────────────────────────── const options = { key: ENV.RAZORPAY_KEY_ID, amount: amountInPaise, currency: 'INR', name: 'SG Delivery', description: `Payment for Order #${ session.orderNumber || response.orders?.[0]?.orderNumber || '' }`, order_id: gatewayOrderId, method: 'upi', prefill: { name: user?.name || '', email: user?.email || '', contact: user?.phone || '', }, theme: { color: '#05824C' }, // Matching brand green primary color }; // ── 3. Present Razorpay Checkout SDK ─────────────────────────────── const rzpData = await RazorpayCheckout.open(options); // ── 4. Verify payment cryptographically on backend ──────────────── const verifyRes = await verifyPaymentApi({ gatewayOrderId: rzpData.razorpay_order_id || gatewayOrderId, gatewayPaymentId: rzpData.razorpay_payment_id, gatewaySignature: rzpData.razorpay_signature, }); if (verifyRes.success) { return true; } else { Alert.alert( 'Verification Failed', verifyRes.message || 'Unable to confirm payment.', ); return false; } } catch (error: any) { // Razorpay Checkout library throws an error if user cancels or transaction fails if (error && error.code) { Alert.alert( 'Payment Cancelled', error.description || 'You cancelled the payment process.', ); } else { Alert.alert( 'Payment Error', error?.message || 'Something went wrong during payment.', ); } return false; } finally { setIsPaymentProcessing(false); } }, [user], ); return { processRazorpayPayment, isPaymentProcessing }; };