Skip to content

Instantly share code, notes, and snippets.

@kabir0st
Created June 6, 2025 10:01
Show Gist options
  • Save kabir0st/3d586e976eec6ab1eca325616ad7b986 to your computer and use it in GitHub Desktop.
Save kabir0st/3d586e976eec6ab1eca325616ad7b986 to your computer and use it in GitHub Desktop.

GTM Purchase Tracking Implementation

Overview

This document outlines the implementation of Google Tag Manager (GTM) purchase tracking for the Booksmandala frontend application. The goal was to add a JavaScript block to the /payments/success page that fires a purchase event with dynamic order data.

Problem Statement

The original request was to add this tracking script to the success page:

<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
  event: "purchase",
  transaction_id: "{{ order_id }}",
  value: {{ total_price }},
  currency: "NPR"
});
</script>

However, the challenge was that the success page (/payments/success) didn't have access to dynamic order data since it's a frontend-only Next.js application without server-side rendering of order details.

Solution Architecture

1. Data Flow Strategy

Since the application uses client-side routing and the success page doesn't receive order data via props or URL parameters, we implemented a sessionStorage-based data passing mechanism:

  1. Capture: Store order data in sessionStorage before redirecting to success page
  2. Retrieve: Read order data from sessionStorage on the success page
  3. Track: Fire GTM purchase event with the retrieved data
  4. Cleanup: Remove stored data to prevent duplicate tracking

2. Fallback Mechanism

For external payment gateways (like Khalti) that might redirect with URL parameters, we also implemented URL parameter parsing as a fallback.

Files Modified

1. Components/QRConfirm.js

Purpose: Handles QR payment verification and success redirects

Changes Made:

// Before redirect to success page
if (res.response.ok) {
  // Store order data for GTM tracking
  const orderData = {
    order_id: orderId,
    total_amount: paymentDetails?.amount || 0
  };
  sessionStorage.setItem('orderTrackingData', JSON.stringify(orderData));
  
  sessionStorage.removeItem(`paymentDetails-ORD${orderId}`);
  router.push("/payments/success");
}

Why: This component handles the most common payment flow (QR payments) and has access to both orderId and payment amount data.

Impact: Ensures order tracking data is available when users complete QR payments.

2. Components/GeneratedPaymentComponents/GeneratedPaymentQR.js

Purpose: Handles generated payment link QR verification

Changes Made:

if(res.response.ok){
  // Store order data for GTM tracking
  const orderData = {
    order_id: paymentDetails?.order_id || generatedPaymentId,
    total_amount: paymentDetails?.amount || amount || 0
  };
  sessionStorage.setItem('orderTrackingData', JSON.stringify(orderData));
  
  router.push('/payments/success')
}

Why: Generated payment flows also redirect to the success page and need tracking.

Impact: Covers payment tracking for generated payment links.

3. Components/CheckoutComponents/CheckoutQRCode.js

Purpose: Handles checkout process QR payments

Changes Made:

if(res.response.ok){
  // Store order data for GTM tracking
  const trackingData = {
    order_id: orderData?.orderId || orderData?.order_id,
    total_amount: orderData?.total_amount || paymentDetails?.amount || 0
  };
  sessionStorage.setItem('orderTrackingData', JSON.stringify(trackingData));
  
  router.push('/payments/success')
}

Why: Checkout QR payments are another path to the success page that needs tracking.

Impact: Ensures comprehensive coverage of all QR payment flows.

4. Pagecomponent/OrderPlacedPage.js

Purpose: The main success page component that displays order confirmation

Changes Made:

useEffect(() => {
  // Get order data from sessionStorage and fire GTM purchase event
  const orderTrackingData = sessionStorage.getItem('orderTrackingData');
  
  // Also check URL parameters for external payment gateway redirects
  const urlParams = new URLSearchParams(window.location.search);
  const urlOrderId = urlParams.get('order_id') || urlParams.get('transaction_id');
  const urlAmount = urlParams.get('amount') || urlParams.get('total_amount');
  
  let orderData = null;
  
  if (orderTrackingData) {
    try {
      orderData = JSON.parse(orderTrackingData);
      // Clean up the stored data
      sessionStorage.removeItem('orderTrackingData');
    } catch (error) {
      console.error('Error parsing order tracking data:', error);
    }
  } else if (urlOrderId) {
    // Fallback to URL parameters if sessionStorage data is not available
    orderData = {
      order_id: urlOrderId,
      total_amount: urlAmount ? parseFloat(urlAmount) : 0
    };
  }
  
  if (orderData && orderData.order_id) {
    // Initialize dataLayer if not exists
    window.dataLayer = window.dataLayer || [];
    
    // Push purchase event to GTM
    window.dataLayer.push({
      event: "purchase",
      transaction_id: orderData.order_id,
      value: orderData.total_amount,
      currency: "NPR"
    });
    
    console.log('GTM purchase event fired:', {
      transaction_id: orderData.order_id,
      value: orderData.total_amount,
      currency: "NPR"
    });
  }
}, []);

Why: This is where the actual GTM tracking happens. The component runs on the success page and fires the purchase event.

Impact: Centralizes all purchase tracking logic and provides robust error handling and fallback mechanisms.

Added Cash on Delivery (COD) Support

  • Modified TransactionMethodsfunction/TransactionMethods.js to accept and store order data for COD transactions
  • Updated all components that use COD (CheckoutStepThree, GeneratedPayments, PreorderModal) to pass order data
  • COD transactions now store order data before redirecting to external URLs

Enhanced Console Logging

Added detailed console logging throughout the implementation for easier testing and debugging:

Storage Events (when order data is saved):

  • QR Payment Success: "QR Payment Success: Storing order data for GTM tracking"
  • Generated Payment QR Success: "Generated Payment QR Success: Storing order data for GTM tracking"
  • Checkout QR Success: "Checkout QR Success: Storing order data for GTM tracking"
  • COD Transaction: "COD: Storing order data for GTM tracking"

GTM Event Firing (on success page):

  • Success Message: "🎯 GTM PURCHASE EVENT FIRED SUCCESSFULLY! 🎯"
  • Event Details: Complete GTM event object with transaction_id, value, and currency
  • DataLayer State: Current state of window.dataLayer after event push

Debug Information:

  • SessionStorage Data: Raw data retrieved from sessionStorage
  • URL Parameters: Fallback data from URL parameters
  • Data Parsing: Success/failure of JSON parsing
  • Cleanup Actions: Confirmation of sessionStorage cleanup

Testing Instructions

To test the implementation:

  1. Open Browser Developer Tools (F12)

  2. Go to Console Tab

  3. Complete a purchase using any payment method

  4. Look for console messages in this sequence:

    [Payment Method]: Storing order data for GTM tracking: {order_id: "...", total_amount: ..., source: "..."}
    OrderPlacedPage: Starting GTM tracking process...
    OrderPlacedPage: Retrieved sessionStorage data: {"order_id":"...","total_amount":...}
    OrderPlacedPage: Parsed sessionStorage order data: {order_id: "...", total_amount: ...}
    🎯 GTM PURCHASE EVENT FIRED SUCCESSFULLY! 🎯
    Event Details: {event: "purchase", transaction_id: "...", value: ..., currency: "NPR"}
    
  5. Verify GTM Event in GTM Preview Mode or Google Analytics

Error Handling

The implementation includes comprehensive error handling:

  • JSON Parsing Errors: Graceful fallback to URL parameters
  • Missing Data: Warning messages with available data details
  • Network Issues: Continues without breaking the user experience
  • Browser Compatibility: Works across all modern browsers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment