# Stripe

Attribute Stripe Checkout and Payment Link payments to the visitor and channel that earned them.

VisitTrack reads Stripe's `checkout.session.completed` event, so it works for anything that goes through Stripe Checkout: Checkout Sessions you create from your server, and Payment Links.

## 1. Connect Stripe

1. In Stripe, create a **restricted key** with read-only access (Developers → API keys → Create restricted key). VisitTrack never needs write access.
2. Open **Settings → Revenue → Stripe** in VisitTrack and copy the webhook URL: `https://visitrack.app/api/stripe-webhook/YOUR_SITE_ID`.
3. In Stripe: Developers → Webhooks → **Add endpoint**. Paste the URL and select `checkout.session.completed` and `charge.refunded`.
4. Paste the restricted key and the endpoint's signing secret (`whsec_…`) into VisitTrack and press **Connect**.

## 2. Pass the visitor id

### Checkout Sessions

Send the visitor id from the browser to the endpoint that creates the session, and set it as `client_reference_id`.

Browser:
```
// The tracker stores one anonymous id per browser. Read it on the page
// that starts checkout and send it along with the checkout request.
const visitorId = window.visitrack?.visitorId?.() ?? localStorage.getItem("_ana_vid");

const res = await fetch("/api/checkout", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ priceId, visitorId }),
});
window.location.href = (await res.json()).url;
```

Node.js:
```
// app/api/checkout/route.ts
const { priceId, visitorId } = await req.json();

const session = await stripe.checkout.sessions.create({
  mode: "payment", // or "subscription"
  line_items: [{ price: priceId, quantity: 1 }],
  success_url: "https://example.com/thanks?session_id={CHECKOUT_SESSION_ID}",
  cancel_url: "https://example.com/pricing",
  client_reference_id: visitorId ?? undefined,
});

return Response.json({ url: session.url });
```

Python:
```
session = stripe.checkout.Session.create(
    mode="payment",
    line_items=[{"price": price_id, "quantity": 1}],
    success_url="https://example.com/thanks?session_id={CHECKOUT_SESSION_ID}",
    cancel_url="https://example.com/pricing",
    client_reference_id=visitor_id,
)
```

### Payment Links

Payment Links accept `client_reference_id` as a URL parameter. Append it to every link on the page:

pricing page:
```
<script>
  document.addEventListener("DOMContentLoaded", () => {
    const visitorId = localStorage.getItem("_ana_vid");
    if (!visitorId) return;
    document.querySelectorAll('a[href^="https://buy.stripe.com/"]').forEach((a) => {
      const url = new URL(a.href);
      url.searchParams.set("client_reference_id", visitorId);
      a.href = url.toString();
    });
  });
</script>
```

> **PaymentIntents and Invoices aren't attributed** — Only payments that go through Checkout (`checkout.session.completed`) are recorded. A PaymentIntent you confirm yourself, or an invoice paid outside Checkout, has no `client_reference_id`. Route those through Checkout, or send them with the [server-side events API](https://visitrack.app/docs/server-events).

## Events

| Event | What VisitTrack does |
| --- | --- |
| `checkout.session.completed` | Records the payment (`amount_total`, `currency`, customer email) against the visitor in `client_reference_id`. |
| `charge.refunded` | Subtracts the refunded amount from the original payment, including partial refunds. |
| anything else | Acknowledged with `200` and ignored. |

## Test it

1. Open your site in a normal browser tab so the tracker records a visit (localhost is ignored unless the script tag has `data-allow-local`).
2. Complete a checkout in test mode.
3. Open **Revenue** in the dashboard. The payment shows up within a few seconds, with the referrer, campaign and landing page that brought that visitor in.

> **Nothing showed up?** — See [Missing or unattributed payments](https://visitrack.app/docs/revenue-troubleshooting). The webhook response body always says why a payment was skipped.
