Revenue
Connect Dodo Payments
Dodo Payments sends successful payments and refunds to a webhook signed with its secret. Checkout metadata links each payment to a visit.
Set up the webhook
Copy the webhook URL
In MetricRaven, open Settings → Integrations → Dodo Payments and copy the webhook URL.
Add a webhook in Dodo Payments
In the Dodo Payments dashboard, open Developer → Webhooks → Add Webhook. Paste the URL and select
payment.succeededandrefund.succeeded. For subscription goals, also select thesubscription.*events (active, on_hold, cancelled, expired, failed, plan_changed, updated).Paste the signing secret in MetricRaven
Copy the webhook's signing secret (
whsec_…), paste it into the Dodo Payments card and choose Connect Dodo Payments.
Pass the visitor ID at checkout
Dodo stores checkout session metadata on the payment, and subscription renewals carry the subscription's metadata. Use these keys:
| Key | Value |
|---|---|
| metricraven_visitor_id | The visitor ID from getVisitorId() (24 or 32 hex characters). |
| metricraven_user_id | Optional. Your signed-in user's ID, set on the server, matching the Identify API. |
| metricraven_plan | Optional. A plan name for reports; otherwise the product from the payment is used. |
Read the visitor ID in the browser
The MetricRaven script exposes
window.analytics.getVisitorId(). Read it when checkout starts and send it to your checkout code:script.js// Read this in the visitor's browser when they start checkout. const visitorId = window.analytics?.getVisitorId() ?? null; // Send visitorId to your own checkout endpoint alongside its usual request data. // Use the authenticated user ID from your backend; do not trust a browser-supplied user ID.Add it to the checkout
Checkout Sessions API// Server: create a checkout session with the Dodo Payments SDK (dodopayments). const session = await dodo.checkoutSessions.create({ product_cart: [{ product_id: "pdt_YOUR_PRODUCT_ID", quantity: 1 }], customer: user ? { email: user.email, name: user.name } : undefined, return_url: "https://example.com/success", metadata: { ...(visitorId ? { metricraven_visitor_id: visitorId } : {}), ...(user ? { metricraven_user_id: user.id } : {}), }, }); // Redirect the visitor to session.checkout_urlFor a static payment link, add
metadata_metricraven_visitor_id:Payment link// Browser: payment links turn ?metadata_<key>= into payment metadata. const url = new URL("https://checkout.dodopayments.com/buy/pdt_YOUR_PRODUCT_ID"); const visitorId = window.analytics?.getVisitorId(); if (visitorId) url.searchParams.set("metadata_metricraven_visitor_id", visitorId); window.location.href = url.toString();
MetricRaven links each payment to a visit using the first of these that is available:
- The
metricraven_visitor_idpassed through checkout. - The
metricraven_user_id(or the provider's customer external ID), matched to a visitor you linked with the Identify API. - An earlier payment from the same provider customer, so renewals keep the original attribution.
- The checkout email, matched to an email you sent to the Identify API.
- The checkout email, matched to a browser that called payment({ email }) within an hour of the payment.
Verify it works
After a delivery, the Dodo Payments card in Settings → Integrations shows Connected, the number of verified events and the last event type. A live payment appears in Revenue in its own currency; with a visitor ID it is credited to that visit's source in Traffic.
If the card says a delivery was rejected, the signing secret doesn't match. Paste it again or generate a replacement.
Details
- Revenue is the payment's
total_amount(including tax), converted from the currency's smallest unit. Eachrefund.succeededreduces its payment once. - Subscription payments without a checkout session are renewals. A cancellation is not a refund.
- Signature: Dodo uses Standard Webhooks headers
webhook-id,webhook-timestampandwebhook-signature. Deliveries older than five minutes are rejected. - Test mode and live mode have separate webhooks; connect test mode only to a test site.
- Each delivery is verified with an HMAC signature over the exact request body, compared in constant time. Unsigned or altered requests are rejected with
401and never change revenue. - Redelivered events are recognized and ignored, so provider retries never add a payment twice. Refunds reduce the original payment and never exceed it.
- The signing secret is stored encrypted, bound to this site, and never shown in reports, share links or logs. Disconnecting deletes it; imported revenue stays in your reports.
- Free trials and zero-amount orders are not revenue. A subscription cancellation is not a refund: revenue already paid stays.
- Webhooks deliver new events only. Payments from before you connected are not imported, and live MRR is currently available for Stripe only.
- Use one revenue path per purchase: don't also send the same orders through the Payments API.