121 lines
4.5 KiB
TypeScript
121 lines
4.5 KiB
TypeScript
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<boolean> => {
|
||
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 };
|
||
};
|