Source: https://datafa.st/docs/fix-duplicate-payments
Markdown source: https://datafa.st/docs/fix-duplicate-payments.md
Description: Seeing double payments in your DataFast dashboard? This usually happens when both automatic URL attribution and server-side attribution are active at the same time.

# Why am I seeing duplicate payments?

If you see the same payment appearing twice in your DataFast dashboard, don't panic — it's a common setup issue with a simple fix.

## What's happening

DataFast has **two ways** to attribute revenue to your traffic sources:

1. **Automatic (URL parameters)** — When a customer completes a purchase, your payment provider (Stripe, LemonSqueezy, Polar) redirects them back to your website with a special parameter in the URL (like `?session_id=...`). The DataFast script on your page sees this parameter and records the payment automatically. This is what happens when you set up [Stripe Payment Links](/docs/stripe-payment-links), [LemonSqueezy Payment Links](/docs/lemonsqueezy-payment-links), or [Polar Checkout Links](/docs/polar-checkout-links).

2. **Server-side (metadata)** — When you create a checkout session in your code, you pass the visitor's `datafast_visitor_id` and `datafast_session_id` as metadata. When the payment succeeds, DataFast receives the payment via webhook and attributes it using that metadata. This is what happens when you follow the [Stripe Checkout API](/docs/stripe-checkout-api), [LemonSqueezy Checkout API](/docs/lemonsqueezy-checkout-api), or [Polar Checkout API](/docs/polar-checkout-api) guides.

**The problem:** If you have **both** set up at the same time, the same payment gets recorded twice — once from the URL parameter when the customer lands on your success page, and once from the webhook on the server.

## How to fix it

You have two options. Pick one, not both.

### Option 1: Keep server-side, disable automatic (recommended)

Server-side attribution is more accurate because it works even if the customer closes the browser before the redirect, opens the success page on a different device, or has an ad blocker. **We recommend this option if you have server-side set up.**

Add `data-disable-payments="true"` to your DataFast script tag:

```html
<script
  defer
  data-website-id="dfid_******"
  data-domain="your_domain.com"
  data-disable-payments="true"
  src="https://datafa.st/js/script.js"
></script>
```

That's it. The script will stop reading payment parameters from URLs. Your server-side attribution keeps working as before.

> `window.datafast("payment", { email })` calls still work when this is enabled. Only the automatic URL parameter detection is turned off. See [script configuration](/docs/script-configuration) for all available options.

### Option 2: Keep automatic, remove server-side

If you don't want to touch your code and prefer the no-code approach, remove the `datafast_visitor_id` and `datafast_session_id` metadata from your checkout session creation code. The automatic URL parameter method will continue working on its own.

For example, if you followed the [Stripe Checkout API guide](/docs/stripe-checkout-api), you would remove the `metadata` object:

```javascript
// Before (server-side attribution enabled)
const session = await stripe.checkout.sessions.create({
  // ...
  metadata: {
    datafast_visitor_id: cookieStore.get('datafast_visitor_id')?.value,
    datafast_session_id: cookieStore.get('datafast_session_id')?.value,
  },
});

// After (server-side attribution removed)
const session = await stripe.checkout.sessions.create({
  // ...
  // no metadata — automatic URL attribution handles it
});
```

> Keep in mind that automatic attribution relies on the customer being redirected back to your site on the same device and browser. If they close the tab or the redirect fails, the payment won't be attributed.

## Which option should I choose?

- **Server-side** (option 1) is more reliable. Payments are attributed even if the customer never returns to your website after paying. If you already have server-side set up, keep it and just add `data-disable-payments="true"` to your script.

- **Automatic** (option 2) is easier to set up since it doesn't require any code. But it only works when the customer actually lands on the redirect URL in the same browser.

> **TL;DR** — If you've already set up server-side attribution, go with option 1. Add `data-disable-payments="true"` to your script and you're done.

## Related documentation

- [Revenue attribution guide](/docs/revenue-attribution-guide) — choose the right attribution method for your setup
- [Script configuration reference](/docs/script-configuration) — all script attributes including `data-disable-payments`
- [Stripe Checkout API](/docs/stripe-checkout-api) — server-side attribution for Stripe
- [Stripe Payment Links](/docs/stripe-payment-links) — automatic attribution for Stripe
- [LemonSqueezy Checkout API](/docs/lemonsqueezy-checkout-api) — server-side attribution for LemonSqueezy
- [LemonSqueezy Payment Links](/docs/lemonsqueezy-payment-links) — automatic attribution for LemonSqueezy
- [Polar Checkout API](/docs/polar-checkout-api) — server-side attribution for Polar
- [Polar Checkout Links](/docs/polar-checkout-links) — automatic attribution for Polar
