Revenue attribution
Connect Stripe, Polar or Dodo Payments per website, and link every sale to the visit that produced it.
How it works
The script gives each visitor a first-party id (cookies datacheap_visitor_id and datacheap_session_id on your domain). When that id reaches the payment, DataBull knows which visit, source and campaign produced the sale. It gets there in one of three ways:
- Payment links are tagged by the script automatically. Nothing to do.
- Server-created checkouts: pass the two ids as metadata (examples below).
- Success page: when the buyer lands back on your site with the provider's checkout id in the URL, the script links it.
Each website connects its own payment account. A dedicated account can send every payment to one site (new products included), or a shared account can split by product.
Overviews for each provider: Stripe, Polar, Dodo Payments.
Stripe
- In the website's setup, choose Stripe and click the link to create a restricted key. The permissions are pre-filled: read access to Checkout Sessions, Charges, PaymentIntents, Invoices, Subscriptions, Products, Prices and Customers, plus write access to Webhook Endpoints.
- Paste the key (
rk_live_…). Leave "Instant updates" on to create the webhook. - Choose "Every payment in this account" or pick products.
Payment Links
Nothing to do: the script adds client_reference_id to every buy.stripe.com link on your site. Optional backup: set the link's after-payment redirect to https://yoursite.com/thanks?session_id={CHECKOUT_SESSION_ID}.
Checkout Sessions
const cookie = (name) => request.headers.get('cookie')?.match(new RegExp('(?:^|; )' + name + '=([^;]+)'))?.[1];
const metadata = {
databull_visitor_id: cookie('datacheap_visitor_id'),
databull_session_id: cookie('datacheap_session_id'),
};
const session = await stripe.checkout.sessions.create({
mode: 'payment', // or 'subscription'
line_items: [{ price: 'price_…', quantity: 1 }],
metadata,
subscription_data: { metadata }, // subscriptions: renewals keep the visitor
success_url: 'https://yoursite.com/thanks?session_id={CHECKOUT_SESSION_ID}',
});
Subscriptions: the first invoice is credited to the checkout's visitor, and every renewal inherits it. Custom PaymentIntent flows work when the intent carries the same metadata.
Polar
- In Polar: Settings → Developers → New token, no expiration, with the scopes
organizations:read products:read orders:read subscriptions:read customers:read refunds:read checkouts:read webhooks:read webhooks:write. - Paste the token (
polar_oat_…), choose Production or Sandbox, keep "Instant updates" on. - Choose every payment or specific products.
Checkout links
Nothing to do: the script adds reference_id to every buy.polar.sh link, and Polar copies it onto the order. Optional backup: success URL https://yoursite.com/thanks?checkout_id={CHECKOUT_ID}.
Checkout API
const checkout = await polar.checkouts.create({
products: ['<product id>'],
metadata, // same two ids as above
successUrl: 'https://yoursite.com/thanks?checkout_id={CHECKOUT_ID}',
});
Dodo Payments
- Dodo → Developer → API keys: create a key and paste it (Live or Test).
- Pick this website's products. One key covers the whole business; other sites can use the same connection with different products.
Payment links (/buy/pdt_…) are tagged automatically. For checkout sessions, pass the same metadata object, and set return_url to a page on your site.
Switching from DataFast
Checkout metadata named datafast_visitor_id / datafast_session_id is read too, so existing checkout code keeps working.
Weighing the two up? See DataBull vs DataFast.