Some flows have nowhere to put metadata and no return URL to read: an invoice you send from the Stripe dashboard, a subscription created by a third-party tool, a reseller, a payment taken over the phone. The payment still reaches your dashboard — it simply arrives with no visitor attached.
If the customer does land back on your site, use a
Payment Link with session_id — that is
stronger than anything on this page. What follows is for when they do not.
Match on the e-mail address
Tell us the address the customer paid with, and we match it against the payment Stripe reports.
window.truestat("payment", { email: "customer@example.com" });Fire this on your own success page, with the address used at checkout.
Why this is weaker, and when it is enough
The match needs the address to be identical. A customer who pays with a work address and signed up with a personal one stays unattributed, and there is no way for us to know the two are the same person.
It also cannot tell one visit from another. Metadata carries a session id as well as a visitor id, so it can say which visit produced the sale; e-mail matching can only say which person did.
Firing it twice is safe. Only a payment that is still unattributed is ever filled in, so a success page the customer reloads changes nothing after the first time.
What it cannot do
It cannot attribute a renewal. The success page fires once, when the customer first buys; the renewals that follow arrive from Stripe with no e-mail event beside them. Subscriptions attributed this way show their first payment against a channel and their renewals as unattributed.
That is the strongest argument for metadata where you have the choice: it carries forward on its own, for the life of the subscription. Both the Checkout Session and the PaymentIntent API pages set it.