# Trace each sale to its visit

> How odo credits each sale to the visit that brought it: Stripe metadata, checkout links tagged for you, Shopify, Paddle and more, and first or last touch.

When a payment source and a website share a project, odo traces each sale to the visitor who made it and credits the visit that brought them. Every channel, campaign, page and country then shows revenue, and you see revenue per visitor, conversion rate, and the time from a first visit to a first payment.

## How it works

The script keeps one id per visitor, `odo_vid`. Your checkout passes it to the payment platform, the platform keeps it on the sale, and odo reads it back. odo never guesses: a sale without a visitor shows as not traced.

The sale is credited to the visitor’s **last touch** by default: their most recent visit that came from somewhere other than Direct. Switch to **first touch**, the visit that first brought them, with *Credit* on the Revenue card.

## What each platform needs

Each platform carries the visitor its own documented way, and the script tags checkout links on your pages for you.

| Platform | How the visitor travels | What you do |
| --- | --- | --- |
| Stripe | A Payment Link’s `client_reference_id`, or `odo_visitor_id` in metadata | Nothing for Payment Links; pass it from your own checkout (below) |
| Lemon Squeezy | `checkout[custom][odo_visitor_id]`, sent back in its webhooks | Turn on instant updates, with a signing secret |
| Polar | Checkout links’ `reference_id`, kept in the order’s metadata | Nothing for checkout links; API checkouts pass `metadata.odo_visitor_id` |
| Paddle | `custom_data.odo_visitor_id` on the transaction | Nothing for `paddle_button` launchers; `Paddle.Checkout.open()` passes `customData` |
| Gumroad | A parameter on the product link, sent back in its Ping | Turn on instant updates (the Ping) |
| Dodo Payments | Payment links’ `metadata_odo_visitor_id` | Nothing for payment links; API checkouts pass `metadata.odo_visitor_id` |
| Shopify | The cart attribute `odo_visitor_id` | One line in your theme (shown in its setup); cart permalinks are tagged |
| WooCommerce | The order meta `_odo_visitor_id` | One snippet in `functions.php` (shown in its setup) |

The script only touches links on these platforms’ own checkout addresses, adds one parameter, and never one a link already has.

## Stripe, from your own checkout

For Checkout Sessions or PaymentIntents your server makes, pass the visitor into `metadata.odo_visitor_id`. On your server it’s the `odo_vid` cookie:

```js
// Server: pass the visitor to Stripe Checkout
const session = await stripe.checkout.sessions.create({
  // …your line items, mode and URLs
  metadata: { odo_visitor_id: req.cookies.odo_vid },
});
```

In the browser it’s `window.odo.visitorId`, for a checkout that starts there:

```js
fetch('/checkout', { method: 'POST', body: JSON.stringify({ odo: window.odo?.visitorId }) });
```

PaymentIntents, Customers and Subscriptions work the same way, with `metadata.odo_visitor_id`. For odo to read it back, a restricted key needs read access to Charges, Customers, Invoices, Subscriptions and Checkout Sessions; the **Sales from visits** card says when it’s missing.

After each Stripe sync, odo looks up the new payments, newest first, and refunds follow their payment. Other platforms keep the visitor on the sale itself, so their sales are matched right after each sync, and one whose visitor odo hasn’t seen yet is tried again for three days.

## Anything else

Send the purchase from the page:

```js
odo('Purchase', { revenue: 49, currency: 'USD' })
```

or from your server, with a [project key](https://odo.gg/docs/api#payments) and the buyer’s `odo_vid` as `visitor`. Both are self-reported.

## One sale, one credit

- A sale traced through a Verified source keeps its seal. Gumroad’s unsigned Ping, a Lemon Squeezy webhook without a signing secret, WooCommerce and test-mode sources are traced too, but never shown as verified.
- Refunds and disputes take back from the visits their payment was credited to.
- When a platform traces a sale your page also reported with `odo('Purchase')` (the same visitor and currency, about the same amount, within two hours), the platform’s replaces the page’s.
- Cookieless sites, and browsers that send Global Privacy Control, have no visitor id to pass on, so their sales can’t be traced.

## Who paid

For a Stripe sale traced to a visit, odo keeps the buyer’s name, or their email when Stripe has no name, as your Stripe knows them, so your dashboard (and your own AI assistant, which sees what you see) can say who paid. Shared views and the REST API never show it. No other platform’s buyer is kept, and a Stripe sale that isn’t traced keeps none.
