How to Track Stripe Revenue in Web Analytics (Step by Step)
A copy-paste guide to sending Stripe payments into your analytics with full acquisition context: webhook setup, the track() call, and the attribution join.
Stripe knows exactly how much money you made. Your analytics knows exactly where visitors came from. Until the two are joined, 'which channel drives revenue?' is a guess. This guide wires Stripe payments into Clycyo step by step — about twenty minutes including the test payment — and the pattern transfers to any analytics tool with a track() API and identity merging.
Prerequisites
- The Clycyo tracker installed on your site (the quickstart covers it — one script tag).
- identify() on signup, plus the one line that makes server-side attribution possible: persist the visitor ID on your user record.
// After successful signup
window.webanalytics.identify(user.email);
// Store the visitor_id — webhooks will need it to re-attach revenue
await api.post('/me', {
clycyo_visitor_id: window.webanalytics.getVisitorId(),
});Step 1: create the webhook endpoint
In the Stripe Dashboard → Developers → Webhooks, add an endpoint (e.g. /api/stripe-webhook) subscribed to invoice.paid — the event that fires for both first payments and renewals. For one-time purchases, add checkout.session.completed.
Step 2: verify and handle the event
// Node / Express example
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
app.post('/api/stripe-webhook',
express.raw({ type: 'application/json' }),
async (req, res) => {
const event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET,
);
if (event.type === 'invoice.paid') {
const invoice = event.data.object;
await sendRevenueEvent(invoice);
}
res.json({ received: true });
});Always verify the signature — an unverified revenue endpoint is an invitation to corrupt your own MRR numbers.
Step 3: send the revenue event
async function sendRevenueEvent(invoice) {
const customer = await loadCustomer(invoice.customer);
if (!customer.clycyo_visitor_id) return; // signed up before wiring
await fetch('https://clycyo.com/api/collect', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
tracking_id: process.env.CLYCYO_TRACKING_ID,
type: 'event',
visitor_id: customer.clycyo_visitor_id, // the join key
event_name: 'subscription_paid',
event_properties: {
revenue: invoice.amount_paid / 100,
currency: invoice.currency.toUpperCase(),
plan: invoice.lines.data[0]?.price?.nickname,
billing_provider: 'stripe',
order_id: invoice.id, // idempotency
},
}),
});
}Three details that save you debugging later:
- visitor_id is the entire join. It is the ID you persisted at signup via getVisitorId(). No stored visitor_id means no attribution — which is why that one line in the prerequisites matters more than everything else on this page.
- amount_paid is in cents. Divide by 100, or enjoy a 100× MRR week.
- order_id makes retries safe. Stripe redelivers webhooks; your numbers should not care.
Step 4: test end to end
- Visit your site through a tagged link: ?utm_source=test&utm_campaign=stripe_wiring.
- Sign up with a test email; confirm identify() fired.
- Complete a payment with Stripe's test card (4242…).
- Open the visitor's journey in the dashboard: you should see the tagged first pageview, the signup, and the revenue event on one timeline — with the campaign credited.
What this unlocks
Once payments flow in with acquisition context, the questions that used to take a spreadsheet afternoon become single filters: revenue by utm_source, revenue by landing page, time from first visit to first payment, and which channels bring customers who renew versus churn. That last one routinely reorders a marketing budget — traffic volume and revenue quality are rarely the same ranking.
The broader strategy behind these mechanics is covered in Revenue Attribution for SaaS. And if you want to see journeys with revenue events in a real dashboard before wiring anything, the live demo is the actual product running on this site's data.