Usermaven
Dark mode

How to track Stripe purchases as conversions in Usermaven

Accurately tracking Stripe purchases in Usermaven allows you to measure sales, attribute revenue to marketing efforts, and understand your customer's journey to conversion. This guide details several methods to ensure every Stripe purchase is captured, regardless of your checkout flow.

Choosing your tracking method

Select the method(s) that best fit your Stripe integration and technical comfort level:

  • Redirect to custom success page (for Stripe Checkout): Ideal if you use Stripe-hosted Checkout pages.

  • Client-side event tracking (for Stripe Elements/Payment Element): Best for on-site payment forms where you want rich data and immediate feedback.

  • Server-side webhook tracking (most reliable): Guarantees every purchase is tracked, even if the user closes their browser early.

  • Google Tag Manager (GTM) integration: Suitable if you manage your website tags through GTM.


Option 1: Redirect to a custom success page (via success_url)

Why this method?

Stripe Checkout pages (hosted by Stripe) don't allow third-party scripts. Redirecting users to a custom "Thank You" page on your site after payment allows the Usermaven pixel to fire and record the conversion.

Best for: Users of Stripe Checkout.

Step-by-step instructions

  1. Set the success_url in your Stripe Checkout Session:

When creating your Stripe Checkout Session on your server, include the success_url parameter, pointing to a page on your domain. You can include {CHECKOUT_SESSION_ID} to pass the session ID.

const session = await stripe.checkout.sessions.create({
  payment_method_types: ['card'],
  line_items: [
    /* ...your line items... */
  ],
  mode: 'payment',
  success_url:
    'https://yourdomain.com/stripe-payment-success?session_id={CHECKOUT_SESSION_ID}&utm_source=your_source',
  cancel_url: 'https://yourdomain.com/cart',
});
  1. Create your custom success page:

Build a "Thank You" or "Order Confirmation" page on your site (e.g., yourdomain.com/stripe-payment-success).

Ensure the Usermaven tracking pixel is installed and active on this page.

  1. Create a conversion goal in Usermaven:

In Usermaven, navigate to Configure > Conversion Goals.

Create a new conversion goal based on Page URL condition.

Enter the URL of your success page (e.g., /stripe-payment-success) and name it (e.g., "Purchase").


Option 2: Client-side event tracking (with Stripe Elements / Payment Element)

Why this method?

If you build your payment form directly on your domain using Stripe Elements, particularly the modern Payment Element, you can track conversions client-side. This usually involves redirecting to a return URL where payment status is confirmed.

Best for: On-site payment forms built with Stripe Elements or the Payment Element.

Initialize Stripe.js and mount the Payment Element

Ensure Stripe.js is loaded and you've created and mounted the Payment Element on your checkout page.

Handle payment submission and redirection

const form = document.getElementById('payment-form');
form.addEventListener('submit', async (event) => {
  event.preventDefault();
  const { error } = await stripe.confirmPayment({
    elements,
    confirmParams: {
      return_url: 'https://yourdomain.com/order-confirmation',
    },
  });
  if (error) {
    console.error('Stripe Payment Error:', error.message);
  }
});

Confirm payment status on your return_url page and fire Usermaven event

document.addEventListener('DOMContentLoaded', async () => {
  const urlParams = new URLSearchParams(window.location.search);
  const clientSecret = urlParams.get('payment_intent_client_secret');

  if (!clientSecret) {
    console.log('No client secret found. User may have navigated directly.');
    return;
  }

  const { paymentIntent, error } = await stripe.retrievePaymentIntent(clientSecret);

  if (error) {
    console.error('Error retrieving PaymentIntent:', error.message);
  } else if (paymentIntent && paymentIntent.status === 'succeeded') {
    const amountInMajorUnits = paymentIntent.amount / 100;
    const currencyCode = paymentIntent.currency.toUpperCase();

    usermaven('track', 'stripe_purchase_elements', {
      value: amountInMajorUnits,
      currency: currencyCode,
      orderId: paymentIntent.id,
    });
  } else if (paymentIntent) {
    console.log('PaymentIntent status:', paymentIntent.status);
  }
});

Note on older Card Element: If using the older single Card Element and stripe.confirmCardPayment, the logic can be more direct if no redirect is needed, but handling the return_url is still a robust pattern.


Option 3: Server-side tracking via Stripe webhooks (most reliable)

Why this method?

Client-side tracking can sometimes be missed. Webhooks from Stripe to your server provide a reliable way to capture completed purchases directly from Stripe.

Best for: Ensuring accurate purchase tracking. Requires server-side development.


Step-by-step instructions

  1. Create a webhook endpoint on your server.

  2. Register the webhook endpoint in Stripe.
    In your Stripe Dashboard, go to Developers > Webhooks and add an endpoint.

  3. Listen for the relevant Stripe events based on your payment flow:

For Stripe Checkout:

  • checkout.session.completed

  • Optional: checkout.session.async_payment_succeeded

For direct PaymentIntent flows:

  • payment_intent.succeeded

Important:

Stripe can emit both checkout.session.completed and payment_intent.succeeded for the same Checkout purchase, so if you listen to both, deduplicate purchases before sending them to Usermaven.

const fetch = require("node-fetch");

app.post(
  "/stripe-webhook",
  express.raw({ type: "application/json" }),
  async (request, response) => {
    const sig = request.headers["stripe-signature"];
    let event;

    try {
      event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
    } catch (err) {
      return response.sendStatus(400);
    }

    let sessionOrPaymentIntent;
    let eventNameSuffix = "";
    let eventTimestamp;

    if (
      event.type === "checkout.session.completed" ||
      event.type === "checkout.session.async_payment_succeeded"
    ) {
      sessionOrPaymentIntent = event.data.object;
      eventNameSuffix = "_checkout";
      eventTimestamp = sessionOrPaymentIntent.created;
    } else if (event.type === "payment_intent.succeeded") {
      sessionOrPaymentIntent = event.data.object;
      eventNameSuffix = "_pi";
      eventTimestamp = sessionOrPaymentIntent.created;
    } else {
      return response.sendStatus(200);
    }

    const amount =
      sessionOrPaymentIntent.amount_total || sessionOrPaymentIntent.amount;

    const amountInMajorUnits = amount / 100;
    const currencyCode = sessionOrPaymentIntent.currency.toUpperCase();

    const usermavenToken = `${USERMAVEN_API_KEY}.${USERMAVEN_SERVER_TOKEN}`;

    const usermavenPayload = {
      api_key: USERMAVEN_API_KEY,
      event_type: `stripe_purchase_webhook${eventNameSuffix}`,
      timestamp: eventTimestamp * 1000,
      user: {
        id:
          sessionOrPaymentIntent.customer ||
          `stripe_guest_${sessionOrPaymentIntent.id}`,
      },
      event_attributes: {
        value: amountInMajorUnits,
        currency: currencyCode,
        orderId: sessionOrPaymentIntent.id,
        stripe_event_type: event.type,
      },
    };

    try {
      const usermavenResponse = await fetch(
        `https://events.usermaven.com/api/v1/s2s/event?token=${usermavenToken}`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(usermavenPayload),
        }
      );

      if (!usermavenResponse.ok) {
        const errorBody = await usermavenResponse.text();
        console.error(
          `Error sending event to Usermaven: ${usermavenResponse.status} ${usermavenResponse.statusText}`,
          errorBody
        );
      }
    } catch (apiError) {
      console.error("Exception when calling Usermaven API:", apiError);
    }

    response.sendStatus(200);
  }
);

Important notes

For Stripe webhook events, use Usermaven's server-to-server event API endpoint because the event is sent from your backend server, not from a browser.

Use your Usermaven API key and Server Token for backend Stripe webhook tracking. Combine them with a dot:

API_KEY.SERVER_TOKEN

Then pass the combined value in the token query parameter:

https://events.usermaven.com/api/v1/s2s/event?token=API_KEY.SERVER_TOKEN

Do not use an Authorization: Bearer header for this example.

Use timestamp for the server-to-server event payload instead of created_at.

The payload should use a nested user object:

{
  "user": {
    "id": "stripe_customer_id"
  }
}

Example payload:

{
  "api_key": "API_KEY",
  "event_type": "stripe_purchase_webhook",
  "user": {
    "id": "stripe_customer_id"
  },
  "event_attributes": {
    "value": 99,
    "currency": "usd",
    "orderId": "stripe_session_or_payment_intent_id"
  }
}

Do not send user_id as a top-level field.

Stripe purchase details should remain under event_attributes, including:

  • value

  • currency

  • orderId

  • stripe_event_type


Option 4: Google Tag Manager (GTM) integration

Why this method?

If you manage tags through GTM, push a dataLayer event on purchase success (from your success page or client-side callback) and use GTM to fire your Usermaven tracking.

Best for: Users already leveraging GTM.

Step-by-step instructions

  1. Push data to the dataLayer on purchase success:

const amountInMajorUnits = paymentIntent.amount / 100;
const currencyCode = paymentIntent.currency.toUpperCase();
window.dataLayer = window.dataLayer || [];
dataLayer.push({
  event: 'stripe_purchase_successful',
  transactionValue: amountInMajorUnits,
  transactionCurrency: currencyCode,
  transactionId: paymentIntent.id,
});
  1. Configure GTM:

  • Create Data Layer Variables: transactionValue, transactionCurrency, transactionId

  • Create a Custom Event Trigger: Event name stripe_purchase_successful

  • Create a Usermaven Tag (Custom HTML):

<script>
  usermaven("track", "Purchase", {
    value: {{transactionValue}},
    currency: {{transactionCurrency}},
    orderId: {{transactionId}}
  });
</script>

Fire it on the custom event trigger. Then publish your GTM container.

Was this article helpful?