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.
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.
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:
- Capture: Store order data in sessionStorage before redirecting to success page
- Retrieve: Read order data from sessionStorage on the success page
- Track: Fire GTM purchase event with the retrieved data
- Cleanup: Remove stored data to prevent duplicate tracking
For external payment gateways (like Khalti) that might redirect with URL parameters, we also implemented URL parameter parsing as a fallback.
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.
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.
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.
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.
- 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
Added detailed console logging throughout the implementation for easier testing and debugging:
- 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"
- 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
- 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
To test the implementation:
-
Open Browser Developer Tools (F12)
-
Go to Console Tab
-
Complete a purchase using any payment method
-
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"}
-
Verify GTM Event in GTM Preview Mode or Google Analytics
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