--- title: Analyze your conversion funnel subtitle: Analyze your Stripe Checkout conversion funnel with Google Analytics 4. route: /payments/checkout/analyze-conversion-funnel --- # Analyze your conversion funnel Analyze your Stripe Checkout conversion funnel with Google Analytics 4. # Stripe-hosted page > This is a Stripe-hosted page for when payment-ui is stripe-hosted. View the original doc at https://docs.stripe.com/payments/checkout/analyze-conversion-funnel?payment-ui=stripe-hosted. Use Google Analytics 4 (GA4) to track users as they progress through your Stripe Checkout purchase funnel. Before you begin, set up a [GA4 account](https://support.google.com/analytics/answer/9304153) and add a [GA4 property](https://support.google.com/analytics/answer/9744165?hl=en#zippy=%2Cin-this-article). ## Set up your site 1. Create a product page with a **Checkout** button: ```html Buy cool new product
``` 1. Create a server-side endpoint that creates a Checkout Session and serves the pages: ```javascript // This example sets up endpoints using the Express framework. // Watch this video to get started: https://youtu.be/rPR2aJ6XnAc. const express = require("express"); require("dotenv").config(); const app = express(); // Set your secret key. Remember to switch to your live key in production! // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const request = require("request"); app.post( "/create-checkout-session", express.urlencoded({ extended: false }), async (req, res) => { const session = await stripe.checkout.sessions.create({ payment_method_types: ["card"], line_items: [ { price_data: { currency: "usd", product_data: { name: "T-shirt", }, unit_amount: 2000, }, quantity: 1, }, ], mode: "payment", success_url: req.get("origin") + "/success", cancel_url: req.get("origin") + "/cancel", }); res.json({ url: session.url }); } ); app.get("/product", function (req, res) { res.sendFile(__dirname + "/product.html"); }); app.get("/success", function (req, res) { res.sendFile(__dirname + "/success.html"); }); app.get("/cancel", function (req, res) { res.sendFile(__dirname + "/cancel.html"); }); app.listen(4242, () => console.log(`Listening on port ${4242}!`)); ``` 1. Create a success page: ```html Thanks for your order!

Thanks for your order!

We appreciate your business! If you have any questions, please email orders@example.com.

``` 1. Create a canceled page: ```html Order Canceled!

Start another order.

``` ## Instrumentation walkthrough In the following example, we assume your customer has: - Viewed your product page. - Clicked the **Buy** button and was redirected to Stripe Checkout. - Completed the payment and was redirected to the success page. ### Quick summary | Method | Viewed product | Clicked **Buy** button | Completed purchase | | --------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | Client-side | [Add Google Analytics tag](#record-pageview-metrics) to `product` page | [Record event before redirecting to Stripe Checkout](#client-side-event-before-redirect) | [Add Google Analytics tag](#record-pageview-metrics) to `success` page | | Server-side | NA | [Record event before redirecting to Stripe Checkout](#server-side-redirect) | [Record event when you receive the `checkout.session.completed` webhook](#server-side-event-recording) | | Server-side with stored client ID | NA | NA | Record event when you receive the `checkout.session.completed` webhook, and [link to client side events](#link-client-and-server-side-events) | ### Add instrumentation 1. Add `checkout.stripe.com` to your referral exclusion list. 1. Add Google Analytics tags to your product, success, and canceled pages. Tags automatically fire an event on page load. ```html Buy cool new product
``` ```html Thanks for your order!

Thanks for your order!

We appreciate your business! If you have any questions, please email orders@example.com.

``` ```html Order Canceled!

Start another order.

``` 1. Fire an event just before redirecting to Stripe Checkout: ```html Buy cool new product
``` ### Analyze your conversion funnel metrics After you add the proper instrumentation, you can see the metrics corresponding to each step defined in your conversion funnel: - **product page views:** The number of page visitors who viewed the product page. - **begin\_checkout event count:** The number of page visitors who clicked the **Buy** button and were redirected to Stripe Checkout. - **success page views:** The number of page visitors who completed the purchase and were redirected to the success page. Using these numbers, you can see where visitors are dropping off in your conversion funnel. ## Server-side event recording In this example, to track the completion of a purchase, we consider the scenario where your user reaches the success page. While this is generally suitable for most use cases, it might not offer a comprehensive solution for some, such as: - Checkout flows without a designated success page. - Instances where it’s important to log the purchase completion metric, even when the redirection to the success page fails. - Situations in which customers frequently refresh a success URL due to it containing useful information (for example, shipping progress or a confirmation number). To record the purchase completion metric without a success page, [set up an event handler](https://docs.stripe.com/checkout/fulfillment.md) and record a metric whenever you get the `checkout.session.completed` event. After you add the highlighted following code, you can use the `purchase` metric to analyze the number of visitors that completed a purchase, instead of the number of success page views: ```javascript // This example sets up endpoints using the Express framework. // Watch this video to get started: https://youtu.be/rPR2aJ6XnAc. const express = require("express"); require("dotenv").config(); const app = express(); // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const request = require("request"); app.post( "/create-checkout-session", express.urlencoded({ extended: false }), async (req, res) => { const session = await stripe.checkout.sessions.create({ payment_method_types: ["card"], line_items: [ { price_data: { currency: "usd", product_data: { name: "T-shirt", }, unit_amount: 2000, }, quantity: 1, }, ], mode: "payment", success_url: req.get("origin") + "/success", cancel_url: req.get("origin") + "/cancel", }); res.json({ url: session.url }); } ); app.get("/product", function (req, res) { res.sendFile(__dirname + "/product.html"); }); app.get("/success", function (req, res) { res.sendFile(__dirname + "/success.html"); }); app.get("/cancel", function (req, res) { res.sendFile(__dirname + "/cancel.html"); }); app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => { const payload = req.body; const sig = req.headers["stripe-signature"]; let event; try { event = stripe.webhooks.constructEvent( payload, sig, process.env.STRIPE_WEBHOOK_SECRET ); } catch (err) { return res.status(400).send(`Webhook Error: ${err.message}`); } if (event.type === "checkout.session.completed") { // Record metrics using the Google Analytics Measurement Protocol // See https://developers.google.com/analytics/devguides/collection/protocol/ga4 const MEASUREMENT_ID = ; // GA4 Measurement ID const API_SECRET = ; // GA4 Measurement Protocol API secret fetch("https://www.google-analytics.com/mp/collect?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}", { method: "POST", body: JSON.stringify({ client_id: 'XXXXXXXXXX.YYYYYYYYYY', // Client ID events: [{ name: "purchase", params: {}, }] }) }); } res.status(200); }); app.listen(4242, () => console.log(`Listening on port ${4242}!`)); ``` ## Linking client and server-side events In [Server-side event recording](https://docs.stripe.com/payments/checkout/analyze-conversion-funnel.md#server-side-event-recording), you recorded the server-side metrics against an anonymous client ID. That ID is different from the one associated with the page view metrics, so Google Analytics has no way to link the page view and purchase metrics. To maintain a link between the client-side and server side metrics: 1. Choose an ID unique to the visitor (for example, the Google Analytics client ID) and send it to the server: ```html Buy cool new product
``` 1. Read the ID from the request. 1. Store the ID in the Checkout Session’s metadata. 1. Retrieve the ID and send it with the request to record the event. ```javascript // This example sets up endpoints using the Express framework. // Watch this video to get started: https://youtu.be/rPR2aJ6XnAc. const express = require("express"); require("dotenv").config(); const app = express(); // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const request = require("request"); app.post( "/create-checkout-session", express.urlencoded({ extended: false }), async (req, res) => { const session = await stripe.checkout.sessions.create({ payment_method_types: ["card"], line_items: [ { price_data: { currency: "usd", product_data: { name: "T-shirt", }, unit_amount: 2000, }, quantity: 1, }, ], mode: "payment", success_url: req.get("origin") + "/success", cancel_url: req.get("origin") + "/cancel", metadata: { analyticsClientId: req.body.analyticsClientId, }, }); res.json({ url: session.url }); } ); app.get("/product", function (req, res) { res.sendFile(__dirname + "/product.html"); }); app.get("/success", function (req, res) { res.sendFile(__dirname + "/success.html"); }); app.get("/cancel", function (req, res) { res.sendFile(__dirname + "/cancel.html"); }); app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => { const payload = req.body; const sig = req.headers["stripe-signature"]; let event; try { event = stripe.webhooks.constructEvent( payload, sig, process.env.STRIPE_WEBHOOK_SECRET ); } catch (err) { return res.status(400).send(`Webhook Error: ${err.message}`); } if (event.type === "checkout.session.completed") { // Record metrics using the Google Analytics Measurement Protocol // See https://developers.google.com/analytics/devguides/collection/protocol/ga4 const params = new URLSearchParams({ v: "1", // Version tid: , // Tracking ID / Property ID. cid: "555", // Anonymous Client ID cid: event.data.object.metadata.analyticsClientId, // Client ID t: "event", // Event hit type ec: "ecommerce", // Event Category ea: "purchase", // Event Action }); request(`https://www.google-analytics.com/batch?${params.toString()}`, { method: "POST", }); } res.status(200); }); app.listen(4242, () => console.log(`Listening on port ${4242}!`)); ``` ## Server-side redirects In this example, we assume that redirects to Stripe happen on the client. If you need to redirect to Stripe from your server, record the `'begin_checkout'` metric on the server, just before redirecting to Stripe Checkout: ```javascript // This example sets up endpoints using the Express framework. // Watch this video to get started: https://youtu.be/rPR2aJ6XnAc. const express = require("express"); require("dotenv").config(); const app = express(); // Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')('<>'); const request = require("request"); app.post( "/create-checkout-session", express.urlencoded({ extended: false }), async (req, res) => { const session = await stripe.checkout.sessions.create({ payment_method_types: ["card"], line_items: [ { price_data: { currency: "usd", product_data: { name: "T-shirt", }, unit_amount: 2000, }, quantity: 1, }, ], mode: "payment", success_url: req.get("origin") + "/success", cancel_url: req.get("origin") + "/cancel", }); res.json({ url: session.url }); // Record metrics using the Google Analytics Measurement Protocol // See https://developers.google.com/analytics/devguides/collection/protocol/ga4 const MEASUREMENT_ID = ; // GA4 Measurement ID const API_SECRET = ; // GA4 Measurement Protocol API secret fetch("https://www.google-analytics.com/mp/collect?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}", { method: "POST", body: JSON.stringify({ client_id: 'XXXXXXXXXX.YYYYYYYYYY', // Client ID events: [{ name: "begin_checkout", params: {}, }] }) }); res.redirect(303, session.url); } ); app.get("/product", function (req, res) { res.sendFile(__dirname + "/product.html"); }); app.get("/success", function (req, res) { res.sendFile(__dirname + "/success.html"); }); app.get("/cancel", function (req, res) { res.sendFile(__dirname + "/cancel.html"); }); app.listen(4242, () => console.log(`Listening on port ${4242}!`)); ``` ```html Buy cool new product
``` # Embedded form > This is a Embedded form for when payment-ui is embedded-form. View the original doc at https://docs.stripe.com/payments/checkout/analyze-conversion-funnel?payment-ui=embedded-form. Conversion funnel analytics aren’t available when using Embedded Checkout. # Embedded components > This is a Embedded components for when payment-ui is embedded-components. View the original doc at https://docs.stripe.com/payments/checkout/analyze-conversion-funnel?payment-ui=embedded-components. Conversion funnel analytics aren’t available when using Elements.