Metrickle

Revenue from Stripe and RevenueCat

Last updated

Connect your Stripe account or RevenueCat project to an app and its purchases, renewals, refunds and cancellations arrive in Metrickle on their own, on the same person your SDK identified. You don't need to send them as custom events. A refund then takes back the task that person completed, a cancellation can be a protected flow's revoke step, and revenue kept is a real number. You set it up on the app's Integrations page, under Revenue. You need to be an owner or admin, or have the integrations.manage permission.

Tie payments to people

Metrickle can only put a payment on the right person if the payment provider knows the same user id you pass to identify(). Do this first.

Stripe

Give Stripe the user id as Checkout's client_reference_id, or as metadata.metrickle_user_id on the customer, subscription or PaymentIntent. Once Metrickle has seen a customer with a user id, later payments from that customer are matched automatically.

// Client: identify the person after sign-in.
metrickle.identify(user.id);

// Server, when you create the Checkout Session: the same id.
const session = await stripe.checkout.sessions.create({
  mode: "subscription", // or "payment"
  line_items: [{ price: "price_…", quantity: 1 }],
  client_reference_id: user.id,
  metadata: { metrickle_user_id: user.id },
  subscription_data: { metadata: { metrickle_user_id: user.id } }, // subscriptions only
  success_url: "https://example.com/welcome",
});

// Without Checkout (Payment Element): put it on the PaymentIntent, or on the customer once.
await stripe.paymentIntents.create({ amount: 4900, currency: "usd", customer: customer.id, metadata: { metrickle_user_id: user.id } });
await stripe.customers.update(customer.id, { metadata: { metrickle_user_id: user.id } });

From a server that sends events over the HTTP ingest API:

# Send events with "userId" set to your user's id, and give their Stripe customer the same id:
curl https://api.stripe.com/v1/customers/cus_… \
  -H "Authorization: Bearer $STRIPE_SECRET_KEY" \
  -d "metadata[metrickle_user_id]=user-123"

# Checkout Sessions: client_reference_id=user-123 (or metadata[metrickle_user_id]=user-123)

RevenueCat

Log in to RevenueCat with the same user id you pass to identify(). If RevenueCat's app user id has to be something else, set the metrickle_user_id attribute instead.

iOS

import RevenueCat

// After sign-in, the same id for both:
Metrickle.shared?.identify(user.id)
_ = try await Purchases.shared.logIn(user.id)

// If RevenueCat's app user id has to stay something else, name the Metrickle user instead:
// Purchases.shared.attribution.setAttributes(["metrickle_user_id": user.id])

Android

import com.revenuecat.purchases.Purchases
import com.revenuecat.purchases.logInWith

// After sign-in, the same id for both:
Metrickle.identify(user.id)
Purchases.sharedInstance.logInWith(user.id)

// If RevenueCat's app user id has to stay something else, name the Metrickle user instead:
// Purchases.sharedInstance.setAttributes(mapOf("metrickle_user_id" to user.id))

Flutter

import 'package:purchases_flutter/purchases_flutter.dart';

// After sign-in, the same id for both:
Metrickle.instance.identify(user.id);
await Purchases.logIn(user.id);

// If RevenueCat's app user id has to stay something else, name the Metrickle user instead:
// await Purchases.setAttributes({'metrickle_user_id': user.id});

React Native

import Purchases from "react-native-purchases";

// After sign-in, the same id for both:
metrickle.identify(user.id);
await Purchases.logIn(user.id);

// If RevenueCat's app user id has to stay something else, name the Metrickle user instead:
// Purchases.setAttributes({ metrickle_user_id: user.id });

Connect Stripe

You can connect Stripe with a restricted key, a webhook, or both. Both is best: the webhook brings events within seconds, and the key fills gaps.

  1. Restricted key (recommended). In Stripe, go to Developers → API keys → Create restricted key. Give it Read on Charges, Refunds, Customers, Subscriptions, Checkout Sessions and Events, and nothing else. Paste it into Metrickle. It backfills the last 30 days, looks up who a customer is, and reads new events every 15 minutes. Keys that can change your account are refused.

  2. Webhook. In Metrickle, choose Show the endpoint URL. In Stripe, go to Developers → Webhooks → Add destination, paste the URL and select these events:

    checkout.session.completed
    checkout.session.async_payment_succeeded
    invoice.paid
    payment_intent.succeeded
    charge.refunded
    customer.subscription.created
    customer.subscription.updated
    customer.subscription.deleted
    

    Then paste the endpoint's signing secret (whsec_…) into Metrickle. Every delivery's Stripe-Signature is checked against it.

Connect RevenueCat

  1. In Metrickle, choose Create webhook details. You get a webhook URL and an Authorization header value. The value is shown once.
  2. In RevenueCat, go to Project settings → Integrations → Webhooks → Add new configuration. Paste the URL and the Authorization value, choose Production (and Sandbox, to test), then send a test event.
  3. Optionally, add a RevenueCat v2 secret key with read access, so Metrickle can show which project is connected.

If you lose the Authorization value, issue a new one in Metrickle and update it in RevenueCat. The old one stops working straight away.

Settings

  • Report revenue in: a 3-letter currency code. Amounts in other currencies are converted at the day's rate. Use the currency your other revenue events are in.
  • Record test events: includes Stripe test mode or RevenueCat sandbox purchases, for trying the setup. Turn it off afterwards.

What arrives

Metrickle eventRevenueMeans
purchaseYesA one-off payment: Checkout in payment mode, a paid invoice, an in-app purchase
subscription_startedYesThe first payment of a subscription
subscription_renewedYesA renewal payment
trial_startedNoA free trial began
refundNegativeMoney returned. Takes back a task completed in the 7 days before it
subscription_canceledNoThe person chose to cancel, or a payment failed for good. Use it as a protected flow's revoke step
subscription_endedNoAccess ran out after a cancel. It takes nothing back

Each payment is counted once, however many provider events describe it. A redelivered webhook, or the same event read again through the key, changes nothing.

Check it's working

Each connection shows when the last event arrived. What arrived lists the last 50 provider events and what each became:

StatusMeans
RecordedStored as a Metrickle event
Waiting for userMetrickle doesn't know the customer's user id yet. It's recorded as soon as a later event names the customer
No user foundNo user id turned up within 7 days. Set metadata.metrickle_user_id on the customer, then run a backfill
SkippedNot something Metrickle records, a test event while test events are off, or already recorded
FailedMetrickle couldn't read the event

A webhook delivery Metrickle refuses, such as one with a wrong signature, shows on the connection with the reason.

Don't count revenue twice

If your app already sends revenue on a purchase event from the client, remove it there once a provider is connected. Otherwise the same payment is counted twice.

Keys and privacy

Keys and webhook secrets are encrypted and never shown again. Free-text cancellation comments aren't stored. Disconnecting a provider removes its log, but keeps the events already recorded.