DEV Community

Lokesh
Lokesh

Posted on

Firefox Navigation Bug with Razorpay Payment Gateway - Here's the Fix

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);
  }
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)