Stripe

Connect Stripe to sync payments, invoices, subscriptions, refunds, and disputes into RevScope. Then choose one of the methods below to connect each payment or subscription to the browser session that generated it.

Connect Stripe

  1. In the Stripe Dashboard, open Developers → API keys and create a restricted key with read access to Balance, Charges, Checkout Sessions, Coupons, Disputes, Invoices, PaymentIntents, Refunds, and Subscriptions. Stripe recommends restricted keys so each integration has only the permissions it needs. See Stripe's API key best practices.
  2. Open RevScope Settings, select Stripe under Integrations, paste the key into Secret key, and save.

Choose an attribution method

There are different ways to attribute Stripe payments and subscriptions depending how you've integrated with Stripe.

Method Accuracy and tradeoffs
Shared unique metadata Recommended. Exact attribution via a shared id between session and Stripe. Requires code changes, you must pass a shared id between browser and server code.
Checkout ID in the success URL Simple attribution for checkout and payment links with minimal code changes, but misses a small percent of sessions which don't record the pageview on the checkout success page.
Payment link client reference Exact attribution for payment links only by appending ?client_reference_id= to the payment url. Simple solution for payment links.
Payment success path A useful fallback but gets less accurate as payment volume increases. This is the only method that can attribute historical imported traffic from another analytics service.

Shared unique metadata (recommended)

Share a unique value between the current RevScope session and Stripe. Either generate a value and save it as RevScope metadata, or use the RevScope session ID directly. RevScope matches Stripe metadata by value, so the key can be any name.

Secret-key Stripe calls must run on your server with the Node.js SDK (npm install stripe). The browser records the value with RevScope, then sends it to your backend (for example with fetch). Confirming a PaymentIntent in the browser uses Stripe.js (npm install @stripe/stripe-js).

Use a generated UUID

The examples use client_reference_id only because its purpose is clear. Commands queue safely until the tracker initializes; the get_session callback provides the continuation point for starting the payment flow after the metadata command.

Checkout Session

Browser — create the value, attach it to the session, and send it to your server:

function startCheckout() {
  const attributionId = crypto.randomUUID();

  // Attach the value to the current RevScope session.
  dispatch(
    "metadata",
    { client_reference_id: attributionId }
  );

  dispatch(
    "get_session",
    async function () {
      // Send it after the tracker has initialized.
      const response = await fetch("/api/create-checkout", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ attributionId }),
      });

      const { url } = await response.json();
      window.location.href = url;
    }
  );
}

Server (Node.js) — create the Checkout Session with the Stripe Node SDK. This works for both mode: "payment" and mode: "subscription":

import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

// POST /api/create-checkout
app.post("/api/create-checkout", async (req, res) => {
  const { attributionId } = req.body;

  const session = await stripe.checkout.sessions.create({
    mode: "payment", // Use "subscription" for recurring products.
    line_items: [{ price: "price_123", quantity: 1 }],
    metadata: {
      client_reference_id: attributionId,
    },
    success_url: "https://example.com/checkout/success",
    cancel_url: "https://example.com/pricing",
  });

  res.json({ url: session.url });
});

RevScope reads completed Checkout Session metadata and associates it with the linked payment or subscription.

PaymentIntent

If you create PaymentIntents without Checkout, put the value on the PaymentIntent. The browser still owns the attribution ID and sends it to your server; Stripe.js confirms the payment client-side.

Browser — record metadata, create the PaymentIntent via your backend, then confirm with Stripe.js:

import { loadStripe } from "@stripe/stripe-js";

function startPayment() {
  const attributionId = crypto.randomUUID();

  dispatch(
    "metadata",
    { client_reference_id: attributionId }
  );

  dispatch(
    "get_session",
    async function () {
      const response = await fetch("/api/create-payment-intent", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ attributionId }),
      });
      const { clientSecret } = await response.json();

      const stripe = await loadStripe("pk_live_...");
      const elements = stripe.elements({ clientSecret });
      // Mount elements.create("payment") into your form, then:
      const { error } = await stripe.confirmPayment({
        elements,
        confirmParams: {
          return_url: "https://example.com/checkout/success",
        },
      });
      if (error) {
        // Show error.message to the customer.
      }
    }
  );
}

Server (Node.js) — create the PaymentIntent with the shared metadata:

import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

// POST /api/create-payment-intent
app.post("/api/create-payment-intent", async (req, res) => {
  const { attributionId } = req.body;

  const paymentIntent = await stripe.paymentIntents.create({
    amount: 2000,
    currency: "usd",
    automatic_payment_methods: { enabled: true },
    metadata: {
      client_reference_id: attributionId,
    },
  });

  res.json({ clientSecret: paymentIntent.client_secret });
});

Subscription

Browser — same pattern: record the value and POST it to your server:

function startSubscription() {
  const attributionId = crypto.randomUUID();

  dispatch(
    "metadata",
    { client_reference_id: attributionId }
  );

  dispatch(
    "get_session",
    async function () {
      const response = await fetch("/api/create-subscription", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ attributionId }),
      });

      // Handle the response for your subscription flow
      // (client secret, Checkout URL, etc.).
      const subscription = await response.json();
    }
  );
}

Server (Node.js) — put the value on the Subscription:

import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

// POST /api/create-subscription
app.post("/api/create-subscription", async (req, res) => {
  const { attributionId } = req.body;

  const subscription = await stripe.subscriptions.create({
    customer: "cus_123",
    items: [{ price: "price_123" }],
    metadata: {
      client_reference_id: attributionId,
    },
  });

  res.json({ subscriptionId: subscription.id });
});
Use a unique value. RevScope checks every metadata value, not only client_reference_id. Never set on a common value such as currency: "USD", lang: "en", or a plan name on both, that will attribute revenue to the wrong visitor. UUIDs and RevScope session IDs are good choices. Customer email, account or user id could work if you have that information prior to checkout.

Or use the RevScope session ID

Instead of generating a UUID, read RevScope's current session ID and put it in Stripe metadata. RevScope recognizes its own session ID and can match it directly.

Browser — read the session ID and send it to your server:

dispatch(
  "get_session",
  async function (sessionId) {
    const response = await fetch("/api/create-checkout", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ attributionId: sessionId }),
    });

    const { url } = await response.json();
    window.location.href = url;
  }
);

Server (Node.js) — use req.body.attributionId in the Checkout Session, PaymentIntent, or Subscription metadata exactly as shown in the previous section.

get_session is callback-only. The command queues safely when dispatched before the tracker initializes, then invokes the callback with the current session ID.

Checkout ID in the success URL

Stripe replaces {CHECKOUT_SESSION_ID} with the completed Checkout Session ID. Put that ID in a checkout_id query parameter and make sure the RevScope tracker is installed on the success page.

Server (Node.js) — include the placeholder in success_url when creating the Checkout Session:

import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

const session = await stripe.checkout.sessions.create({
  mode: "payment",
  line_items: [{ price: "price_123", quantity: 1 }],
  success_url:
    "https://example.com/checkout/success?checkout_id={CHECKOUT_SESSION_ID}",
  cancel_url: "https://example.com/pricing",
});

// Redirect the browser to session.url, or return it from your API
// for the client to navigate.

For a Payment Link, open the link in the Stripe Dashboard, select After the payment, choose to redirect customers to your website, and use:

https://example.com/checkout/success?checkout_id={CHECKOUT_SESSION_ID}

See Stripe's post-payment redirect guide. This method is exact when the paying browser reaches the success page. It cannot connect the payment if the customer closes Checkout, switches devices, or otherwise never loads that page.

Note. A small percentage of sessions complete checkout but never finish the redirect or never record a pageview on the success page. Those payments are not attributed to a session.

Stripe Payment Links accept a client_reference_id query parameter. Set it to either the current RevScope session ID or a UUID that you also record as session metadata. This runs entirely in the browser; no Stripe SDK is required.

Use the RevScope session ID

Browser:

dispatch(
  "get_session",
  function (sessionId) {
    const paymentLink = new URL(
      "https://buy.stripe.com/your_payment_link"
    );
    paymentLink.searchParams.set("client_reference_id", sessionId);
    window.location.href = paymentLink.toString();
  }
);

Or use shared UUID metadata

Browser:

const attributionId = crypto.randomUUID();
dispatch(
  "metadata",
  { client_reference_id: attributionId }
);

dispatch(
  "get_session",
  function () {
    const paymentLink = new URL(
      "https://buy.stripe.com/your_payment_link"
    );
    paymentLink.searchParams.set("client_reference_id", attributionId);
    window.location.href = paymentLink.toString();
  }
);

The UUID version uses the get_session callback only as a safe continuation after the queued metadata command. Use URL.searchParams as shown so the value is encoded correctly. Stripe allows letters, numbers, dashes, and underscores up to 200 characters. See Stripe's Payment Link URL parameter guide.

Payment success path fallback

If you cannot pass an exact identifier, RevScope can correlate a payment with a session that viewed your success page near the payment time. This is less accurate than the methods above, but works well at low payment volume and can recover attribution when another method was missing or misconfigured.

  1. In Stripe Checkout or your Payment Link's After the payment settings, redirect successful payments to a dedicated page such as https://example.com/checkout/success.
  2. Install the RevScope tracker on that page.
  3. In RevScope Settings, set Payment success path to /checkout/success and save. Enter only the path, without the domain or query string.

During a full sync, RevScope first tries exact IDs and metadata. If a payment or subscription is still unattributed, it uses the closest unmatched session that viewed this path within 15 minutes of the Stripe event. Payments close together in time can therefore be assigned to the wrong session.

Note. A small percentage of sessions complete checkout but never finish the redirect or never record a pageview on the success page. Those payments are not attributed to a session.

Verify the setup

  1. Complete a test payment through the flow you configured.
  2. In Stripe, inspect the completed Checkout Session, PaymentIntent, or Subscription and confirm that the expected metadata or client reference value is present.
  3. After the Stripe sync completes, confirm in RevScope that the payment has a referrer, campaign, or landing page from the test session.
See all integrations, or build a funnel from first visit to payment.