# DataFast shows revenue as direct traffic? Pass the visitor id into checkout metadata

> DataFast attributes a payment by reading datafast_visitor_id from the checkout's metadata. Read the cookie on the server when you create the Stripe, Polar, Lemon Squeezy or Dodo checkout.

You connected Stripe in DataFast. Revenue shows up. Every dollar of it says
"Direct / None". Your Twitter thread that sold 40 licences gets no credit.

The payment reached DataFast. The visitor did not come with it.

## How DataFast joins a payment to a visit

The script gives every browser a random id in a first-party cookie,
`datafast_visitor_id`, plus a `datafast_session_id`. DataFast knows where that
visitor came from: referrer, UTM tags, landing page.

The payment provider knows nothing about that cookie. It knows a customer and
an amount. DataFast joins the two only if the checkout carries the visitor id
in its metadata. No id, no join, "direct".

## The fix: read the cookie where you create the checkout

The cookie is first-party, so your server receives it on every request. Read it
in the server action or route handler that creates the checkout.

**Stripe Checkout**

```ts
import { cookies } from "next/headers";

const jar = await cookies();
const session = await stripe.checkout.sessions.create({
  mode: "subscription",
  line_items: [{ price: priceId, quantity: 1 }],
  metadata: {
    userId: user.id,
    datafast_visitor_id: jar.get("datafast_visitor_id")?.value ?? "",
    datafast_session_id: jar.get("datafast_session_id")?.value ?? "",
  },
  success_url: `${origin}/billing?checkout=success`,
});
```

Put it on the Checkout Session's own `metadata`, not only on
`subscription_data.metadata`. The session is what DataFast reads.

**Polar**

```ts
await polar.checkouts.create({
  products: [productId],
  metadata: { userId: user.id, datafast_visitor_id: visitorId },
});
```

**Lemon Squeezy** uses `checkoutData.custom` instead of `metadata`:

```ts
await createCheckout(storeId, variantId, {
  checkoutData: { custom: { datafast_visitor_id: visitorId } },
});
```

**Dodo Payments** takes `metadata` the same way. Dodo also needs a webhook to
DataFast: in Dodo, Developer, Webhooks, add endpoint, choose DataFast, paste a
DataFast API key.

For Stripe, Polar and Lemon Squeezy, no webhook is needed. Connect the provider
in DataFast (Website settings, Revenue) and it reads the metadata itself.

## Make it one helper

Three payment providers, one rule. Wrap it once:

```ts
export async function withCheckoutAttribution<T extends { metadata?: Record<string, string> }>(
  params: T,
): Promise<T & { metadata: Record<string, string> }> {
  const jar = await cookies();
  const metadata: Record<string, string> = { ...params.metadata };
  const visitor = jar.get("datafast_visitor_id")?.value;
  if (visitor && /^[A-Za-z0-9_-]{1,128}$/.test(visitor)) {
    metadata.datafast_visitor_id ??= visitor;
  }
  return { ...params, metadata };
}
```

Two details in there are deliberate:

- **Validate the cookie.** It is client-writable. Anything that does not look
  like an id is dropped, not copied into your payment provider.
- **Never replace a key.** Your webhook probably reads `metadata.userId`. The
  helper only adds.

## Mistakes that look like they work

- **Taking the id from the request body.** A client can post any id and credit
  any channel. Read the cookie on the server.
- **Also sending a `payment` event from the browser.** The provider connection
  already records it. DataFast's manual `payment` call exists for flows with no
  connected provider, and it identifies by email. Pick one path, and prefer the
  one that keeps customer emails out of your analytics.
- **Tracking revenue as a custom goal.** `purchase_completed` with an amount is
  a second, worse copy of the revenue DataFast already has.
- **Creating the checkout from a background job.** No request, no cookie. Store
  the visitor id on the user row at signup and pass it from there.
- **Testing with a fresh browser that never loaded a page.** No page view, no
  cookie, nothing to attribute.

## Check it

1. Visit the site from a link with `?ref=test`.
2. Buy something in test mode.
3. In the provider's dashboard, open the session. `datafast_visitor_id` is in
   its metadata.
4. In DataFast, the payment shows with `test` as its source.

If step 3 passes and step 4 fails, the provider is not connected in DataFast.

---

Agentic Boilerplate: A Next.js repo your agent already knows. Free during launch, then $99 once.

- Site map for agents: https://agenticboilerplate.com/llms.txt
- Public API: https://agenticboilerplate.com/openapi.json
- Contact: agenticstudio@gmail.com
