I recently encountered a frustrating bug while integrating Razorpay payment gateway in a Next.js application. After completing a payment, Firefox would automatically navigate back to the previous page (flight results in my case), while Chrome, Safari, and Edge worked perfectly fine.
Symptoms:
- Payment completes successfully
- Verification loading spinner appears briefly
- Firefox suddenly navigates back to the previous page
- Success message never displays
- Issue is Firefox-specific only
Console Warnings (Red Herrings)
Firefox threw several warnings that initially seemed related:
However, these were not the root cause - the real issue was Firefox's handling of window.history
when the Razorpay modal closes.
Root Cause ๐
Firefox treats the Razorpay modal closure differently than other browsers. When the payment modal closes, Firefox interprets it as a navigation event and triggers a popstate
event, causing the browser to go back in history.
The Solution โ
The fix involves preventing Firefox from navigating back by managing the browser history state:
typescript
const onPayClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
try {
// Your existing order creation and Razorpay script loading...
const order = await createOrder(fixedAmount, id);
const loaded = await loadRazorpayScript();
if (!loaded) throw new Error('Razorpay SDK failed to load');
// ๐ฅ FIX: Prevent Firefox back navigation
const preventBackNav = (e: PopStateEvent) => {
window.history.pushState(null, '', window.location.href);
};
// Push initial state
window.history.pushState(null, '', window.location.href);
window.addEventListener('popstate', preventBackNav);
const options = {
key: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID,
amount: order.amount,
currency: order.currency,
name: 'Your Company',
order_id: order.id,
handler: async (response: any) => {
// โ
Clean up listener on success
window.removeEventListener('popstate', preventBackNav);
setIsVerifying(true);
try {
// Your payment verification logic...
const data = await verifyPayment(response, id);
if (data.success) {
setBookingDetails(data);
// Show success message
}
} catch (err) {
console.error('Verification error:', err);
} finally {
setIsVerifying(false);
}
},
modal: {
ondismiss: () => {
// โ
Clean up listener on modal dismiss
window.removeEventListener('popstate', preventBackNav);
console.log('Checkout closed by user');
},
},
};
const rzp = new window.Razorpay(options);
rzp.on('payment.failed', (resp: any) => {
// โ
Clean up listener on payment failure
window.removeEventListener('popstate', preventBackNav);
console.error('Payment failed:', resp.error);
});
rzp.open();
} catch (err) {
console.error('Payment flow error:', err);
}
};
Top comments (0)