Why are payments appearing twice?
If the same payment shows up more than once in your Flowsery Analytics dashboard, there is no need to worry -- this is a common configuration issue with a straightforward solution.
What is going on
Flowsery Analytics offers two methods for connecting revenue to your traffic sources:
-
Automatic (URL parameters) -- After a customer completes a purchase, your payment provider (Stripe, LemonSqueezy, Polar) redirects them back to your site with a special parameter in the URL (e.g.,
?session_id=...). The Flowsery Analytics script detects this parameter and logs the payment automatically. -
Server-side (metadata) -- When you create a checkout session in code, you include the visitor's
flowsery_visitor_idandflowsery_session_idas metadata. Once the payment succeeds, Flowsery Analytics receives it through a webhook and attributes it using that metadata.
The root cause: When both methods are active simultaneously, a single payment gets recorded twice -- once via the URL parameter when the customer arrives at your success page, and once via the server-side webhook.
How to resolve it
Choose one of the two approaches below. Do not use both at the same time.
Option 1: Keep server-side attribution, turn off automatic detection (recommended)
Server-side attribution is more dependable because it functions even when the customer closes their browser before the redirect completes, opens the success page on another device, or uses an ad blocker. This is the recommended path if you already have server-side attribution configured.
Add data-disable-payments="true" to your Flowsery Analytics script tag:
<script
defer
data-fl-website-id="fl_******"
data-domain="your_domain.com"
data-disable-payments="true"
src="https://analytics.flowsery.com/js/script.js"
></script>
That is all you need to do. The script will no longer read payment parameters from URLs, while your server-side attribution continues to operate normally.
window.flowsery("payment", { email }) calls remain functional with this setting enabled. Only the automatic URL parameter detection is disabled.
Option 2: Keep automatic detection, remove server-side attribution
If you prefer not to modify any code and want the zero-code approach, remove the flowsery_visitor_id and flowsery_session_id metadata from your checkout session creation logic. Automatic URL parameter attribution will keep working independently.
For instance, if you set up Stripe Checkout server-side attribution, you would remove the metadata object:
// Before (server-side attribution active)
const session = await stripe.checkout.sessions.create({
// ...
metadata: {
flowsery_visitor_id: cookieStore.get('flowsery_visitor_id')?.value,
flowsery_session_id: cookieStore.get('flowsery_session_id')?.value,
},
});
// After (server-side attribution removed)
const session = await stripe.checkout.sessions.create({
// ...
// no metadata -- automatic URL attribution takes care of it
});
Be aware that automatic attribution depends on the customer being redirected back to your site on the same device and browser. If they close the tab or the redirect does not complete, the payment will not be attributed.
Which option is right for you?
- Server-side (option 1) is the more robust choice. Payments get attributed even when the customer never returns to your site after paying. If server-side attribution is already in place, simply add
data-disable-payments="true"to your script tag. - Automatic (option 2) requires no code changes, making setup easier. However, it only works when the customer actually reaches the redirect URL in the same browser session.
In short -- if server-side attribution is already configured, go with option 1. Add data-disable-payments="true" to your script and you are all set.