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
- 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.
- 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 });
}); 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.
Client reference ID on a Payment Link
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.
-
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. - Install the RevScope tracker on that page.
-
In RevScope Settings, set
Payment success path to
/checkout/successand 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.
Verify the setup
- Complete a test payment through the flow you configured.
- In Stripe, inspect the completed Checkout Session, PaymentIntent, or Subscription and confirm that the expected metadata or client reference value is present.
- After the Stripe sync completes, confirm in RevScope that the payment has a referrer, campaign, or landing page from the test session.