Skip to content

Stripe Billing

If you bill your customers through Stripe, you can wire up OpenPartner attribution without emitting op.event() calls from your app. OpenPartner listens to your Stripe webhooks and maps customer.subscription.created / invoice.paid events into the attribution pipeline automatically.

What you’ll need

  • A Stripe account (the same one you use for billing customers)
  • The OpenPartner browser SDK already initialized on your marketing pages
  • Access to your Stripe Dashboard to add a webhook endpoint

1. Add the webhook endpoint in Stripe

In your Stripe Dashboard → Developers → Webhooks → Add endpoint.

  • Endpoint URL: https://api.your-openpartner-host.com/webhooks/stripe (for the OpenPartner-hosted version: https://api.openpartner.dev/webhooks/stripe)
  • Events to send:
    • checkout.session.completed
    • customer.subscription.created
    • invoice.paid

Copy the Signing secret Stripe shows you and set it as STRIPE_WEBHOOK_SECRET in your OpenPartner environment.

2. Pass the referral on Stripe Checkout

In your checkout-session creation code, set client_reference_id to the visitor’s referral token. The OpenPartner browser SDK exposes it as op.getReferral():

import { OpenPartner } from '@openpartner/sdk';
const op = OpenPartner.init({ apiUrl: 'https://api.openpartner.dev' });
// When the user clicks "Subscribe":
const session = await fetch('/api/create-checkout-session', {
method: 'POST',
body: JSON.stringify({
referral: op.getReferral(),
}),
});

Then on your server, drop it into the Stripe Checkout session:

const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: 'price_xxx', quantity: 1 }],
client_reference_id: req.body.referral ?? undefined,
success_url: 'https://yourapp.com/welcome',
cancel_url: 'https://yourapp.com/pricing',
});

That’s it. No op.identify() call needed for Stripe-billed customers — the client_reference_id rides through Stripe to our webhook, and we stitch the attribution on checkout.session.completed.

What happens under the hood

  1. A visitor clicks yoursite.com/?cref=PARTNER_REF. The OpenPartner SDK captures the ref and stashes it in localStorage.
  2. The visitor completes a Stripe Checkout. Your server passes client_reference_id: op.getReferral() to Stripe.
  3. Stripe fires checkout.session.completed to OpenPartner with the client_reference_id and the new Stripe customer ID attached.
  4. OpenPartner stitches an Identity row linking the click to the Stripe customer, then emits a signup Event. Attribution runs automatically.
  5. Every subsequent invoice.paid from Stripe is mapped to a revenue event for the same customer and attributed to the same partner — for the lifetime of the subscription.

When to use the SDK path instead

The Stripe Billing flow only covers events Stripe knows about: subscription creation, recurring invoices, and Checkout sessions. If you want to track non-Stripe events (demo_booked, trial_started, custom milestones), or you bill through Paddle / Chargebee / Lemon Squeezy / your own server, use the SDK event API:

op.event('demo_booked', { dealValue: 5000 });

The two paths coexist — you can use Stripe Billing for revenue events and the SDK for custom milestones.

Troubleshooting

Conversions aren’t attributing.

  • Check Stripe Dashboard → Webhooks → your endpoint → Recent deliveries. You should see checkout.session.completed events with 200 responses.
  • Verify the client_reference_id is present on the Checkout session. If it’s empty, the visitor either didn’t arrive via a partner link or the SDK didn’t initialize before checkout creation.

Multiple signup events for the same customer.

  • If you have both customer.created and checkout.session.completed configured AND you set openpartner_user_id in customer metadata yourself, you’ll get two signup events. Pick one path — for the Stripe Billing flow, just checkout.session.completed is enough.

invoice.paid events aren’t attributing.

  • The first invoice.paid in a subscription usually arrives within seconds of checkout.session.completed. If the order is reversed, OpenPartner falls back to a database-side Identity lookup so attribution still works — but if it persists, check that the customer’s metadata was successfully backfilled (Stripe Dashboard → Customer → Metadata should show openpartner_user_id).