Sign up

Revenue

Attribute payments by email

On this page

Some checkouts can't carry the visitor ID: Podia, Kajabi, Ghost, hosted pages and third-party tools. On the page buyers see after paying, tell MetricRaven which email this visitor paid with. A payment from your connected provider with that customer email is then credited to the visit.

Call payment() on the thank-you page

  1. Connect your payment provider

    Payments still come from Stripe, a provider webhook (Lemon Squeezy, Polar, Paddle, Dodo Payments) or the Payments API. The browser call only says who paid; it never creates revenue.

  2. Pass the buyer's email after a successful payment

    script.js
    // On the page buyers see after paying, with the email they paid with.
    window.metricraven.payment({ email: "customer@example.com" });

    window.analytics.payment({ email }) is the same call. With the npm package:

    @metricraven/web
    import { payment } from "@metricraven/web";
    
    payment({ email: user.email });
  3. Make the call safe before the script loads (recommended)

    Add this to the page's <head>, above other scripts. Calls made before the MetricRaven script loads are queued and sent as soon as it does.

    Queue stub
    <script>
      // Keeps the call until the MetricRaven script has loaded, then replays it.
      window.metricraven = window.metricraven || { q: [] };
      window.metricraven.payment = window.metricraven.payment || function () {
        window.metricraven.q.push(["payment"].concat([].slice.call(arguments)));
      };
    </script>

Verify it works

Make a test purchase from a fresh browser. Once the provider's payment arrives, it appears in Revenue credited to that visit's source instead of Unattributed. The order doesn't matter: the call can come before or after the payment reaches MetricRaven.

Send the email from your server

If you record payments with the Payments API, add the customer's email (or customer_email) when you have no visitor or user ID:

terminal
curl --request POST 'https://metricraven.com/api/v1/payments' \
  --header "Authorization: Bearer $METRICRAVEN_SECRET_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "amount": 49,
  "currency": "USD",
  "transaction_id": "order_123",
  "email": "customer@example.com"
}'

With @metricraven/node, pass email to payment():

Server
import { payment } from "@metricraven/node";

await payment({
  amount: 49,
  currency: "USD",
  transactionId: order.id,
  email: order.customerEmail, // matched, never stored as text
});

The email first matches a visitor you linked with a server-side identify call, then a browser that called payment() around the payment time.

How matching works

RuleWhat it means
Last resort onlyA claim is used only for a payment with no visitor ID, no user ID and no email that matches a server-side identify. It never replaces any of those.
One-hour windowIt matches payments created up to an hour before or after the call, by the provider's payment time.
First claim winsRepeat calls from the same browser are ignored and don't move the window. After the hour, a new call starts a new window.
Contested claims match nothingIf a second browser claims the same email within the hour, nobody gets the payment, and anything already credited through that claim goes back to Unattributed.
Renewals followOnce a customer's payment is matched, their later renewals and subscription goals keep the same visit, as with any other link.
Rate limitedEach visitor can vouch for at most 3 emails a day, and each IP address can make 5 calls an hour.
ProviderWhere the customer email comes from
StripeThe customer's email, else the charge's billing or receipt email.
Lemon SqueezyThe order's user_email.
PolarThe order's customer email.
PaddleTransactions only carry a customer ID. Select customer.created and customer.updated in your notification destination so MetricRaven can match them.
Dodo PaymentsThe payment's customer email.
Payments APIemail or customer_email.

Privacy

  • Emails are never stored in plain text. MetricRaven keeps a keyed hash (HMAC-SHA256 with a server secret, different for every site), for three days.
  • Nothing is stored in the browser, and no event is recorded for the call.
  • The response is the same whether or not anything matched, so the call can't be used to test whether an email bought something.
  • Deleting a person by email or visitor ID removes their claims too.

Security model

The call is self-asserted, like browser identify: anyone who can run JavaScript on your pages can make it. The rules above keep the damage small.

A malicious browserCan it?
See revenue, payments or whether an email bought somethingNo. The call returns nothing about payments.
Create, change or refund revenueNo. Revenue comes only from your provider or your server.
Override a visitor ID passed at checkout, a user ID, or a server-side identifyNo. Claims only fill in payments that have no other link.
Credit someone's payment to its own visitOnly if it knows the buyer's email, claims it within an hour of their purchase, and the buyer's own thank-you page never makes the call. If the buyer's page does call, the claim is contested and the payment stays unattributed.

The worst case is a payment credited to the wrong traffic source, never lost or extra revenue. When you can, pass the visitor ID at checkout or identify buyers from your server: both are exact and can't be claimed by a browser.

Details

  • Emails are compared trimmed and case-insensitively. The browser call accepts a string or { email } and returns false for an invalid email.
  • Without analytics consent the visitor ID is daily; a payment matched to it still counts toward that visit's source. When consent is granted later the same day, the claim moves to the persistent ID.
  • Guides: Podia, Kajabi, Ghost, Lemon Squeezy, Polar and Paddle.