How it works
The short version: Stripe sends every payment to Mida. Mida finds the visitor who paid, saves the payment as a Purchase event on that visitor, and your experiment reads those events to show conversions, revenue, RPV and AOV per variant.
1. The connection
When you connect, Mida creates a webhook endpoint in your Stripe account. It is a URL unique to your project. Stripe calls it each time something happens in these four events:
Stripe event | What Mida uses it for |
| The money. The amount and currency of a successful payment. This is what creates the Purchase. |
| Who paid. The visitor ID, if you passed one, and the customer's email from Stripe Checkout. |
| Subscriptions. Tells a first payment from a renewal. |
| Refunds. Reduces the revenue of the original payment. |
Every request is signed by Stripe. Mida checks the signature with your webhook signing secret before it reads anything, and rejects anything it can't verify. Events from the other mode (test or live) are ignored.
Mida only uses a restricted, read-only key. It is encrypted at rest and is used to set up the webhook and to look up a Checkout session when needed. It can't charge customers or move money.
2. From payment to Purchase event
Stripe sends
payment_intent.succeededwith the amount.Mida keeps one record per payment, keyed by the Stripe PaymentIntent ID. Stripe can resend an event or deliver events out of order. Neither creates a duplicate, and if the visitor info arrives after the money, Mida fills it in then.
Mida looks up the visitor by ID inside your project. It never creates visitors, so a payment can't add to your monthly tracked users.
If a visitor is found, Mida writes a Purchase event on that visitor with the revenue, the currency and the payment ID. This is the same kind of event your own code or Shopify would send.
If no visitor is found, the payment is saved as Unattributed and is not scored.
3. How Mida knows who paid
Stripe doesn't know about your experiment, so the visitor's ID has to travel with the payment. These are the four ways it can:
Situation | What carries the ID |
Stripe Payment Link or buy button | The Mida tag adds |
Your own checkout | Your server reads the visitor ID (the |
Thank-you page | If the redirect URL ends with |
If the visitor was identified with their email earlier, Mida matches the buyer's email. This is the weakest method and it does not work on free plans. |
If more than one is present, Mida trusts them in this order: payment metadata, then the link ID, then the thank-you page, then email. A subscription renewal reuses the visitor from the subscription's first payment, so it needs no ID of its own.
4. How results are calculated
Your experiment's Revenue goal reads the Purchase events.
Mida joins each Purchase to the experiment by visitor. Only visitors who were exposed to the experiment, and who paid after they saw it, are counted.
Conversion is the share of exposed visitors with a Purchase. Revenue is the sum of their payments after refunds. RPV is revenue per exposed visitor. AOV is revenue per order.
Payments in other currencies are converted into your project currency when the report runs.
Reports update in 5-minute steps, so a new payment can take a few minutes to show up.
Steps to integrate
Before you start
The Mida tracking code is installed on your website.
You are an Owner or Admin in Mida.
You have a Stripe account. You can start in test mode.
Stripe works on all Mida plans.
Step 1. Create a Stripe key
Mida needs a restricted key. It can read payments and nothing else.
In Stripe, go to Developers → API keys → Create restricted key.
Set these permissions:
Permission | Access |
Payment Intents | Read |
Charges | Read |
Checkout Sessions | Read |
Invoices | Read |
Subscriptions | Read |
Customers | Read |
Account | Read |
Webhook Endpoints | Write |
Lastly, Create the key and copy it. It starts with rk_test_ (test mode) or rk_live_ (live mode).
The Webhook Endpoints: Write permission lets Mida set up the connection for you. Without it you can still connect. See No webhook permission? below.
Step 2. Connect Stripe in Mida
Go to Integrations and find Stripe under Payments.
Click Connect Stripe, then Enable Stripe Integration.
Open the Config tab, paste your key and click Test & Connect.
Mida checks the key and creates the webhook in your Stripe account. When it works you see a green Connected label.
This screen shows:
TEST or LIVE, depending on your key.
Attributed: payments matched to a visitor and counted.
Unattributed: payments Mida could not match. They are not counted.
Recent transactions: each payment, its status and the experiment it belongs to.
The payments in these screenshots are example data.
No webhook permission?
If your key can't create webhooks, Mida shows Waiting for webhook secret. Do this once:
Click Copy next to the URL.
In Stripe, go to Developers → Webhooks → Add endpoint and paste the URL.
Select these 4 events:
payment_intent.succeeded,checkout.session.completed,invoice.paid,charge.refunded.Save the endpoint, copy its signing secret (starts with
whsec_), paste it into Mida and click Save secret.
Payments are not recorded until the signing secret is saved.
Step 3. Pass the visitor ID
Mida can only count a payment if it knows which visitor paid. The visitor ID is called the Mida visitor ID (mida_uuid). Whether you have to pass it yourself depends on how your customers pay.
Find your case:
How customers pay | Do you pass the visitor ID? | What to do |
Payment Link or Buy button on a page that has the Mida code | No | Nothing. The Mida code adds the ID to the link for you. |
Payment Link opened from outside your site (email, ad, social post) | Can't | The link is not on a page with the Mida code, so it has no ID. The thank-you page backup below can still match the payment, but only if the visitor already browsed your site in that same browser. |
Stripe Checkout created by your server | Yes | Set |
Custom payment form (Stripe Elements or Payment Intents) | Yes | Set |
Subscriptions created by your server | Yes | Set |
Invoices, phone orders, charges created by your team | No visitor exists | These show as Unattributed, unless the customer's email matches a visitor you identified. |
Payment Links and Buy buttons (no code)
The Mida code finds every buy.stripe.com link and <stripe-buy-button> on the page and adds the visitor ID. It does this when the page loads, and again at the moment of click, so links added later are covered too.
For this to work:
The Stripe integration is enabled in Mida (Step 2).
The Mida code is on the page that has the link.
The visitor clicks the link from that page.
Where to get the visitor ID
You only need this for your own checkout, custom forms and subscriptions.
On your server: read the
optimize_uuidcookie, for examplereq.cookies.optimize_uuid.In the browser: call
await mida.uuid(), then send the value to your server with your checkout request.
If your site waits for cookie consent, the cookie may not exist yet. Use mida.uuid() in the browser and send it to your server.
// Browser: send the visitor ID with your checkout request
const midaUuid = await mida.uuid();
fetch('/create-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ midaUuid }),
});
Your own checkout
// Server: create the Checkout session
stripe.checkout.sessions.create({
// ...your usual settings
client_reference_id: midaUuid,
metadata: { mida_uuid: midaUuid },
payment_intent_data: { metadata: { mida_uuid: midaUuid } },
// Subscription mode only:
// subscription_data: { metadata: { mida_uuid: midaUuid } },
});
Custom payment form
stripe.paymentIntents.create({
// ...your usual settings
metadata: { mida_uuid: midaUuid },
});
Subscriptions
stripe.subscriptions.create({
// ...your usual settings
metadata: { mida_uuid: midaUuid },
});
The first payment is linked to the visitor. Each renewal reuses that visitor, so renewals need no ID of their own.
Backups
These catch payments the methods above missed. Set them up on top of the main method, not instead of it.
Thank-you page. In Stripe, set the after-payment redirect to a page that has the Mida code, ending with
?session_id={CHECKOUT_SESSION_ID}. When the visitor lands there, the Mida code sends the session ID and the visitor ID to Mida. It works for Payment Links and Checkout, in the same browser the visitor used to start.
Email. If you already call
mida.identify({ email })for your visitors, Mida can match the buyer's email. This is the weakest method and it does not work on free plans.
Check that it worked
Open your site in a browser where you are in an experiment.
Make a test payment with Stripe's test card
4242 4242 4242 4242.Open the Stripe card in Integrations and look at Recent transactions.
The payment should show Attributed, with the experiment and variant. The Link column shows how it was matched:
metadata,client_reference_id,handofforemail.
If it shows Unattributed, the ID didn't reach Stripe. Re-check your case in the table above.
How to use in Mida
1. Create a Revenue goal
Open the Stripe card in Integrations, go to the Info tab and click Use as conversion goal. This opens a Revenue goal for you. You can also go to Metrics and create a new goal with the Revenue type.
Keep the name "Stripe revenue", or change it. Click Save Goal.
2. Use it in your experiment
Set the Revenue goal as the experiment's main goal. You can also add it as a secondary metric to an experiment that has a different main goal.
Connecting Stripe does not change any experiment by itself. Only experiments that use the Revenue goal read Stripe payments.
3. Read your results
For each variant you see conversion rate, revenue, RPV and AOV. With a Revenue goal, a visitor counts as converted once a Stripe payment is linked to them.
A payment counts when all of these are true:
Mida matched it to a visitor.
That visitor was in the experiment and saw it before paying.
It is a first payment, or you turned on renewals (see below).
A 5-minute reporting step has passed. New payments show up within a few minutes.
Heads up
Use one source of purchases per project. If you also send Purchase events from Shopify or your own code, revenue is counted twice.
Refunds lower the revenue of that payment. The conversion stays. A full refund makes the revenue zero.
Subscriptions. The first payment always counts. Renewals do not count by default. To count them, switch on Count subscription renewals as revenue in the Settings box on the Config tab. It applies to payments received after you switch it on.
Test and live mode. Mida follows your key. A TEST connection only reads test payments and a LIVE connection only reads live payments. To switch, click Disconnect and connect again with the other key.
Currencies. Payments in other currencies are converted into your project currency.
Rotate key. Click Rotate key and paste a new key whenever you replace the key in Stripe.
Disconnect. Click Disconnect to delete the key from Mida. If Mida created the webhook for you, it is removed from Stripe too. If you added it by hand, delete it in Stripe yourself. Your payment history stays.
Safety. Your key is encrypted and never shown again, only the last 4 characters. Mida never stores card details, only the amount, currency, payment ID and a hidden version of the customer's email.
Troubleshooting
What you see | What to do |
Stripe rejected this key | Copy the key again. Check that it starts with |
Waiting for webhook secret | Follow No webhook permission? and save the signing secret. |
No payments show up | Check the label. A TEST connection ignores live payments, and a LIVE connection ignores test payments. |
Many Unattributed payments | The payment didn't carry the visitor ID. Check Step 3 and make sure the Mida code is on the page where the visitor pays. |
Payment is Attributed but the experiment shows no conversion | The experiment's goal must be the Revenue goal. Also check that the visitor saw the experiment before paying. |
Renewal (not counted) | Switch on Count subscription renewals as revenue if you want renewals counted. |
Revenue looks too high | Another Purchase source is connected to the same project. Turn one off. |
Still stuck? Contact us from the chat bubble and we'll help.






