Tracking and billing

Stripe setup and webhooks

Connect Stripe with a restricted key or OAuth. See the webhook events OpenPromoter reads, how to test locally and how Connect pays partners.

Stripe is the most complete integration: attribution, subscription lifecycle, refunds, chargebacks and partner payouts.

Connecting

  • Cloud: click Connect Stripe in setup or in Settings > Integrations and approve access in Stripe.
  • Self-host: create a restricted key and paste it in the same place. OpenPromoter checks the key and registers a webhook endpoint for you.

Restricted key permissions

In the Stripe Dashboard go to Developers > API keys > Create restricted key and set:

Resource Permission Why
Customers Read Match referred customers and import history
Subscriptions Read Recurring commissions, upgrades and cancellations
Invoices Read Commissions are booked on paid invoices
Charges Read Refunds
Disputes Read Chargebacks
Checkout Sessions Read client_reference_id attribution
Products and Prices Read Program designer and import
Promotion codes Write Read codes used at checkout and create partner coupon codes
Coupons Write Create the coupon behind each partner's discount code
Webhook Endpoints Write Register the OpenPromoter endpoint

For partner payouts through Stripe Connect, also add Accounts, Account Links and Transfers with Write access. Skip these if you pay partners with PayPal, Wise or manually.

Stripe doesn't let accounts registered in some countries, India among them, pay connected accounts this way. Founders there pay partners with Wise or manually. OpenPromoter says so when a partner tries to connect Stripe.

Stripe's own guidance is to use restricted keys wherever possible. Keys are stored encrypted with your ENCRYPTION_KEY.

Webhook events

The endpoint is https://<APP_URL>/api/webhooks/stripe/<workspaceId>. OpenPromoter subscribes to:

Event What OpenPromoter does
customer.created Links the customer to a partner from metadata or the lead email
checkout.session.completed Reads client_reference_id and promotion codes. A paid one-time payment books its commission here
checkout.session.async_payment_succeeded Books one-time payments made with a delayed method such as a bank debit
invoice.paid Books the commission (first payment and every renewal in the window)
invoice_payment.paid Links the invoice to the charge that paid it so refunds and disputes find the commission
charge.refunded Reduces or reverses the commission; claws back if already paid
charge.dispute.created Holds the commission
charge.dispute.closed Releases it if the dispute was won and reverses it if lost
customer.subscription.created Tracks trials and plan
customer.subscription.updated Updates MRR on upgrades and downgrades
customer.subscription.deleted Stops recurring commissions

Commissions are booked on invoice.paid rather than invoice.payment_succeeded because it also covers invoices marked paid outside Stripe. Since API version 2025-03 an invoice no longer names its charge. invoice_payment.paid supplies that link, in either order with invoice.paid. Endpoints registered before these events were added need invoice_payment.paid and checkout.session.async_payment_succeeded switched on in the Stripe Dashboard.

Stripe does not deliver events in order. A paid invoice that arrives before the checkout naming its partner is kept for two days and booked when the partner is known. A refund or dispute that arrives before its sale waits the same way. The amount excludes tax and uses what the customer actually paid after discounts.

Every event is checked against the Stripe-Signature header with a 5-minute tolerance and processed once: duplicates are ignored and events can arrive in any order.

Local development

Automatic webhook registration fails on a local instance because Stripe cannot reach localhost. Use the Stripe CLI instead:

stripe listen --forward-to localhost:3000/api/webhooks/stripe/<workspaceId>

Copy the whsec_… signing secret it prints into Settings > Integrations > Webhook signing secret. Your workspace id is shown on the same screen. Then open a partner link and buy through a test-mode Payment Link to see the commission arrive.

Test mode

You can try the whole flow with Stripe test mode because its events are processed like live ones. The referrals and commissions they create are tagged Test. Before going live, click Delete test data in Settings > Integrations to remove test referrals and unpaid test commissions.

Importing history

When you connect, OpenPromoter imports products and prices, customers with lifetime value and active subscriptions. Existing customers whose Stripe metadata already holds op_ref, op_click or via are linked to their partner.

Partner payouts with Connect Express

Your Stripe account acts as the Connect platform:

  1. The partner chooses Stripe in their portal and completes Stripe's hosted Express onboarding.
  2. When you approve a payout, OpenPromoter creates a Transfer from your Stripe balance to the partner's account. Each payout carries an idempotency key so a retry never pays twice.
  3. Stripe pays the partner's bank on their own payout schedule.

OpenPromoter uses Connect Express (v1) accounts with the transfers capability. Partners outside your country are onboarded with Stripe's recipient service agreement where Stripe supports it.

Stripe charges its own Connect fees. OpenPromoter adds nothing on top.

See Commissions and payouts for approvals, holds and clawbacks.

Edit this page on GitHub