ब्लॉग पर वापस जाएँ

ब्लॉग

बिना शॉप प्लेटफ़ॉर्म के ई-कॉमर्स इवेंट: अपने कस्टम चेकआउट से GA4 को रिपोर्ट कैसे कराएँ

प्रकाशित · अपडेट किया गया · पढ़ने में 7 मिनट

शॉप प्लेटफ़ॉर्म कम से कम आपको जूझने के लिए एक प्लगइन तो देता है। ख़ुद बनाया चेकआउट — एक React ऐप, एक बुकिंग फ़्लो, Stripe वाला कोई SaaS साइन अप — आपको कुछ नहीं देता: जब तक आपका अपना कोड कोई इवेंट पुश न करे, कोई इवेंट फ़ायर नहीं होता। अच्छी ख़बर यह है कि ज़रूरतें थोड़ी सी हैं: मुट्ठी भर नाम, ऑब्जेक्ट का एक ढाँचा, और कुछ नियम कि हर पुश कहाँ होना चाहिए। यह रहा पूरा ब्योरा, उस हिस्से समेत जिसे ज़्यादातर गाइड छोड़ देती हैं — वे ख़रीदारियाँ जो तब होती हैं जब आपके पेज पर कोई नहीं होता।

बुनियादी शर्त: एक नाम और एक ecommerce ऑब्जेक्ट

यहाँ सब कुछ Analytics तक एक ही तरह पहुँचता है। आपका पेज window.dataLayer में एक इवेंट उसके GA4 नाम से पुश करता है, साथ में GA4 के ढाँचे वाला एक ecommerce ऑब्जेक्ट। Tag Manager का एक ट्रिगर नाम से मैच करता है, और एक GA4 इवेंट टैग ऑब्जेक्ट पढ़कर उसे आपके सर्वर कंटेनर को आगे भेजता है। हर SignalHost कंटेनर के वेब कंटेनर में ये ट्रिगर और टैग पहले से मौजूद होते हैं, SH प्रीफ़िक्स वाले नामों के साथ — नीचे दिए शॉप इवेंट के लिए, और साइन अप, ट्रायल व लीड के लिए भी। बचता है सिर्फ़ पुश।

दो नियम ज़्यादातर ग़लतियाँ पकड़ लेते हैं। पहले पिछला ecommerce ऑब्जेक्ट साफ़ करें, वरना तीन पेज पहले का view_item अगले इवेंट के अंदर साथ चला आता है। और पैसे से जुड़े हर इवेंट में value को नंबर के रूप में और currency को ISO कोड के रूप में भेजें: string के रूप में value या ग़ायब currency बिना किसी शिकायत के स्वीकार हो जाती है और चुपचाप रेवेन्यू से बाहर रह जाती है।

window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
  event: 'begin_checkout',
  ecommerce: {
    currency: 'EUR',
    value: 49.00,
    items: [
      { item_id: 'plan-pro', item_name: 'Pro', price: 49.00, quantity: 1 }
    ]
  }
});

कौन-सा इवेंट, और वह कहाँ होना चाहिए

कस्टम फ़्लो में भी वही पड़ाव होते हैं जो किसी शॉप प्लेटफ़ॉर्म में। बस आपको उन्हें अपने कोड में ढूँढना होता है।

  • view_item_list — कोई पेज या कंपोनेंट जो कई प्रोडक्ट या प्लान दिखाता है। सूची दिखने पर एक बार, हर स्क्रॉल पर नहीं।
  • view_item — किसी एक प्रोडक्ट या प्लान का पेज।
  • add_to_cart और remove_from_cart — बटन हैंडलर में, आपका कार्ट सच में बदल जाने के बाद।
  • view_cart — जब कार्ट दिखाया जाए: उसका पेज, या ड्रॉअर का खुलना।
  • begin_checkout — जिस पल विज़िटर चेकआउट में आता है: चेकआउट पेज का पहला रेंडर, या वह क्लिक जो चेकआउट डायलॉग खोलता है।
  • add_shipping_info — जब शिपिंग का तरीक़ा चुना जाए। अगर आप कुछ भी शिप नहीं करते, तो इसे छोड़ दें।
  • add_payment_info — जब भुगतान की जानकारी स्वीकार हो जाए, फ़ॉर्म दिखने पर नहीं।
  • purchase — एक बार, जब ऑर्डर कन्फ़र्म हो। इस लेख का ज़्यादातर हिस्सा इसी को सही करने के बारे में है।
  • sign_up और start_trial — सब्सक्रिप्शन बिज़नेस के लिए, पैसों का लेन-देन होने से पहले के कन्वर्ज़न। SignalHost शॉप इवेंट के साथ-साथ इन दोनों के लिए भी टैग बनाता है, और लीड के लिए भी: generate_lead, contact, schedule, submit_application और subscribe।

पुश उस हैंडलर से करें जिसने बदलाव किया, रेंडर से नहीं। सिंगल-पेज ऐप बार-बार री-रेंडर होता है, और डेवलपमेंट में React इफ़ेक्ट दो बार चलाता है: इफ़ेक्ट के अंदर किया गया पुश डेव बिल्ड में दोगुना हो जाता है और हर बार कंपोनेंट के दोबारा माउंट होने पर दोहराया जाता है।

आपके कन्फ़र्मेशन पेज पर purchase: ऑर्डर अपने सर्वर से पढ़ें

जब विज़िटर भुगतान के बाद आपके किसी पेज पर लौटता है, तो purchase की सही जगह वही पेज है — दो शर्तों के साथ। ऑर्डर अपने बैकएंड से लें, URL से नहीं: क्वेरी पैरामीटर बदले जा सकते हैं, रीडायरेक्ट में हट जाते हैं, और फ़ेल हुए भुगतानों पर भी आते हैं। उदाहरण के लिए, Stripe भुगतान सफल हो या न हो, उसी पते पर लौटाता है, और अपने एक अलग पैरामीटर में बताता है कि क्या हुआ। और इसे सिर्फ़ एक बार पुश करें: रीलोड, बैक बटन या बुकमार्क किया गया रसीद पेज, सब इसी पेज पर वापस लाते हैं।

// On your confirmation page. The order comes from YOUR server, by an id the
// page already knows — never from price or status parameters in the URL.
const order = await fetch(`/api/orders/${orderId}`).then((r) => r.json());

const key = `purchase-sent-${order.id}`;
if (order.status === 'paid' && !localStorage.getItem(key)) {
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({ ecommerce: null });
  window.dataLayer.push({
    event: 'purchase',
    ecommerce: {
      transaction_id: order.id,
      value: order.total,
      tax: order.tax,
      currency: order.currency,
      items: order.items.map((i) => ({
        item_id: i.sku, item_name: i.name, price: i.price, quantity: i.quantity
      }))
    }
  });
  localStorage.setItem(key, '1');
}

transaction_id एक सुरक्षा जाल है, रणनीति नहीं। Analytics एक ही सेशन में दोहराई गई transaction_id को हटा देता है, लेकिन अगली सुबह रसीद पेज के रीलोड को नहीं।

किसी और के पेज पर भुगतान: Stripe Checkout, PayPal, बैंक रीडायरेक्ट

कस्टम बिल्ड के लिए होस्टेड पेमेंट पेज आम विकल्प है, और इसमें एक जोखिम भरा पहलू है: हो सकता है विज़िटर कभी लौटे ही नहीं। वे भुगतान करते हैं, टैब बंद कर देते हैं, और आपका कन्फ़र्मेशन पेज कभी रेंडर नहीं होता। कार्ड के मामले में यह कुछ प्रतिशत ख़रीदारियों के साथ होता है। बैंक ट्रांसफ़र, इनवॉइस और पे-लेटर तरीक़ों में यह ज़्यादातर ख़रीदारियों के साथ हो सकता है, क्योंकि पैसा घंटों या दिनों बाद आता है।

इसलिए हर भुगतान तरीक़े के लिए तय करें कि सही जानकारी कहाँ है। अगर आपके बैकएंड को भुगतान की ख़बर वेबहुक से मिलती है, तो उसे रिपोर्ट करने की भरोसेमंद जगह वेबहुक है — अगला सेक्शन देखें — और तब कन्फ़र्मेशन पेज को purchase पुश नहीं करना चाहिए, वरना हर कार्ड भुगतान दो बार गिना जाएगा। अगर इसकी ख़बर आपको सिर्फ़ रिटर्न पेज पर मिलती है, तो वहीं रिपोर्ट करें और इस कमी को स्वीकार करें।

वे ख़रीदारियाँ जिनके लिए कोई ब्राउज़र मौजूद नहीं: उन्हें अपने सर्वर से भेजें

सब्सक्रिप्शन में इससे बचा नहीं जा सकता। ट्रायल सात दिन बाद कन्वर्ट होता है, रिन्यूअल हर महीने होता है, इनवॉइस का भुगतान अगले हफ़्ते ट्रांसफ़र से होता है। हर एक ख़रीदारी है, और इनमें से कोई भी किसी ब्राउज़र के सामने नहीं होती। उपाय है इवेंट को सर्वर-से-सर्वर भेजना, उसी वेबहुक से जिसे भुगतान की ख़बर मिलती है, उसी टैगिंग एंडपॉइंट पर जिसका उपयोग आपके पेज करते हैं — आपका SignalHost होस्टनेम या आपका अपना डोमेन — ताकि यह बाक़ी हर हिट की तरह आपके सर्वर कंटेनर से होकर गुज़रे।

इसके लिए चेकआउट से एक चीज़ चाहिए: विज़िटर की Analytics क्लाइंट ID, ताकि ख़रीदारी उसी व्यक्ति के खाते में जाए जिसकी विज़िट से यह हुई। यह _ga कुकी में होती है, जो आपकी चेकआउट रिक्वेस्ट के साथ पहले से आती है, जब वह आपके अपने डोमेन पर जाती है। इसे ऑर्डर के साथ सेव करें। _ga कुकी न होने का मतलब है कि विज़िटर ने एनालिटिक्स सहमति नहीं दी: हिट को किसी अस्थायी ID के साथ सहमति-अस्वीकृत (consent-denied) के रूप में भेजें, या बिल्कुल न भेजें — उनके लिए कभी कोई पहचान न गढ़ें। अगर आप कन्वर्ज़न Google Ads, Meta, TikTok या दूसरे विज्ञापन प्लेटफ़ॉर्म को भी भेजते हैं, तो विज्ञापन को लेकर विज़िटर की पसंद भी ऑर्डर के साथ सेव करें और अनुमति मिली हो तो G111 भेजें: G101 वाली ख़रीदारी Analytics तक पहुँचती है, लेकिन किसी भी विज्ञापन प्लेटफ़ॉर्म तक नहीं, और Google Ads उसे किसी क्लिक से नहीं जोड़ सकता।

// In your checkout request handler: keep the visitor's Analytics id with the order.
const ga = req.cookies._ga;                        // "GA1.1.1234567890.1700000000"
order.gaClientId = ga ? ga.split('.').slice(-2).join('.') : null;

// Later, in your payment webhook — a trial converting, an invoice paid:
const params = new URLSearchParams({
  v: '2',
  tid: 'G-XXXXXXXXXX',                              // your GA4 measurement ID
  cid: order.gaClientId ?? `${Date.now()}.${Math.floor(Math.random() * 1e9)}`,
  gcs: order.gaClientId ? 'G101' : 'G100',          // no _ga cookie = no consent
  en: 'purchase',
  'ep.transaction_id': invoice.id,
  'epn.value': '49.00',
  cu: 'EUR',
  pr1: 'idplan-pro~nmPro~pr49.00~qt1',
});
await fetch(`https://metrics.example.com/g/collect?${params}`, { method: 'POST' });

हर इवेंट के लिए एक ही स्रोत चुनें और उसी पर टिके रहें। signalhost.io अपनी बिक्री ठीक इसी तरह मापता है: ब्राउज़र sign_up, begin_checkout और add_payment_info पुश करता है; हमारा सर्वर start_trial तभी भेजता है जब ट्रायल सच में शुरू हो जाए, ताकि टैब बंद होने से यह खो न जाए; और पहले इनवॉइस का भुगतान होने पर Stripe वेबहुक purchase भेजता है। किसी भी इवेंट के दो स्रोत नहीं हैं। Google का Measurement Protocol भी काम करता, लेकिन वह सीधे Google तक जाता है और आपके सर्वर कंटेनर को बायपास करता है, इसलिए आपका कोई भी सर्वर-साइड टैग वह बिक्री कभी नहीं देखता।

GA4 आपकी ऑर्डर सूची से कम क्यों गिनता है, इसके चार कारणों में से एक देर से होने वाली ख़रीदारियाँ हैं: बाक़ी तीन कारण, और उनमें से किन्हें आप ठीक कर सकते हैं।

सहमति के लिए आपको अलग से कुछ नहीं करना पड़ता

विज़िटर ने जो भी चुना हो, इवेंट पुश करें। टैग Consent Mode का पालन करते हैं: सहमति होने पर वे सामान्य रूप से भेजते हैं; सहमति न होने पर वे कुकी-रहित पिंग भेजते हैं, जिन्हें Analytics रिपोर्ट करने के बजाय मॉडल करता है। ख़ुद पुश रोकने से बस वे कन्वर्ज़न उस मॉडलिंग से छिप जाते हैं। जो चीज़ सच में मायने रखती है, वह है क्रम — Tag Manager स्निपेट आपके सहमति बैनर की स्क्रिप्ट के बाद आता है, ताकि पहला इवेंट भेजे जाने से पहले सहमति के डिफ़ॉल्ट मौजूद हों।

जाँचें कि यह काम कर रहा है

  • SignalHost में कंटेनर खोलें। इवेंट कार्ड नाम के साथ बताता है कि क्या पहुँचा, और उसका कितना हिस्सा सहमति के साथ आया। जो नाम आपने पुश किया और वहाँ नहीं दिखता, वह कभी आपके टैगिंग सर्वर तक पहुँचा ही नहीं।
  • Tag Manager के Preview मोड में अपने चेकआउट से गुज़रकर देखें। यह हर पुश दिखाता है, कौन-सा टैग फ़ायर हुआ, और कौन-सा ecommerce ऑब्जेक्ट भेजा गया।
  • Analytics में DebugView इवेंट को आते ही दिखाता है। स्टैंडर्ड रिपोर्ट में एक दिन लग सकता है।
  • कन्फ़र्मेशन पेज को दो बार रीलोड करें और ख़रीदारियाँ गिनें। संख्या तब भी एक ही होनी चाहिए।

यहाँ की सारी गड़बड़ियाँ चुपचाप होती हैं: ग़लत केस में लिखा नाम, string के रूप में भेजी गई value, एक लेवल ज़्यादा गहराई में रखे items। इनमें से कोई भी कहीं कोई एरर नहीं देता। और हर एक रिपोर्ट में एक छेद छोड़ जाता है।

ऐड ब्लॉकर्स की वजह से डेटा खोना बंद करें

मुफ़्त शुरू करें। कुछ ही मिनटों में पहला कंटेनर चालू करें।