# Stripe

> Connect the Stripe webhook, and see which channel, page and campaign produced each checkout, invoice and refund.

Section: Revenue attribution  
Canonical page: https://statsy.co/docs/revenue/stripe  
Last updated: 2026-09-15

Stripe reports every completed checkout, every subscription invoice and every refund to Statsy over a signed webhook. Setup is two copies and a paste.

## Connect Stripe

1. Copy the webhook URLIn Statsy, open **Site settings › Integrations** and press **Connect** on the Stripe tile. The dialog shows the URL for your site, ending in `/api/hooks/stripe/` and your `ws_` site key, with a copy button.
2. Add it as a webhook in StripePaste the URL into a new webhook endpoint in Stripe and subscribe it to the events listed below.
3. Paste the signing secret backStripe shows a signing secret when you create the endpoint, under "Reveal". Paste it into the second field in the dialog and press **Save and connect**. It is stored encrypted and never shown again, so keep your own copy.

## Which events to subscribe to

| Stripe event | What Statsy does with it |
| --- | --- |
| `checkout.session.completed` | Records a one-time payment, or the first payment of a subscription. |
| `invoice.paid` | Records a renewal. Only invoices whose billing reason is `subscription_cycle` count, so the first invoice is not counted twice. |
| `charge.refunded` | Records a refund, which reduces net revenue. |
| `customer.subscription.deleted` | Marks the customer as churned. No money changes. |

Any other event you happen to send is accepted and ignored, so subscribing to too much is harmless — just noisier.

## Passing the visitor through

For **Payment Links and hosted Checkout** — links to `checkout.stripe.com` or `buy.stripe.com` — the script adds the visitor id to the link as `client_reference_id` when it is clicked. Nothing to do.

For **Checkout Sessions you create on your server**, read the id in the browser and send it along:

```js
// In the browser, before you call your own API:
const vid = statsy.visitorId(); // undefined for cookieless visitors

// On your server:
await stripe.checkout.sessions.create({
  mode: 'subscription',
  line_items: [{ price: 'price_123', quantity: 1 }],
  client_reference_id: vid,          // or: metadata: { statsy_vid: vid }
  success_url: 'https://example.com/thanks',
});
```

Invoices and refunds are matched through `metadata.statsy_vid`, so if you attribute renewals, set that metadata on the subscription as well as the session.

> **Note.** Statsy accepts the webhook within five minutes of the timestamp Stripe signed. A wildly wrong server clock, or a replayed old request, fails the signature check.

## Test mode

Statsy does not distinguish Stripe's test mode from live mode. A test-mode endpoint pointed at your production site key puts fake money in your real dashboard. Point test mode at a throwaway site, or accept the noise deliberately.

Refunds, duplicate webhooks and unmatched payments are covered in [Payments not showing up](https://statsy.co/docs/revenue/troubleshooting).

Previous: [How revenue attribution works](https://statsy.co/docs/revenue)  
Next: [Lemon Squeezy](https://statsy.co/docs/revenue/lemon-squeezy)