Connect Stripe

Connecting Stripe takes one read-only key. Klibon uses it to read your sales, renewals and refunds, and to list your products so each one can pay its own rate. If you would rather not let Klibon read your account's events, connect with a webhook instead.

This page is about reading your sales from Stripe. Managed payouts are a separate thing: they run on Klibon's own Stripe account, and give Klibon no access to your Stripe balance or your customers.

Create the key

  1. On your program's Setup page, choose Connect Stripe, then Create the key in Stripe. Stripe opens its restricted key form with the name and every permission already set.
  2. Click Create key at the bottom of that page, and copy the key.
  3. Paste it into Klibon and save.

Klibon checks the key with Stripe before saving it, imports your products, and shows the connection as connected straight away. New sales, renewals and refunds appear within a minute.

The key is restricted to reading these, and nothing else:

  • Products: Read
  • Prices: Read
  • Checkout Sessions: Read
  • Events: Read

It cannot charge, refund or change anything in your account, and it is stored encrypted. Setting the permissions by hand instead? Go to Developers → API keys → Create restricted key. Stripe files them in different sections: Prices under Billing, Checkout Sessions under Checkout, the rest under Core. If one is greyed out, set its section row to None first.

What the key can see

Stripe cannot limit a key to some events, so Events: Read lets it read every event on your account from the last 30 days, not only sales: new customers, disputes, payouts. Checkout Sessions: Read includes the customer details of each checkout. Klibon opens a checkout only to see which product was bought.

Klibon asks Stripe only for these four events, and only from the moment you save the key:

  • checkout.session.completed — A sale, once the session is paid.
  • checkout.session.async_payment_succeeded — The same sale, for payment methods that settle later.
  • invoice.paid — A sale on the first invoice of a subscription, a renewal on every one after.
  • charge.refunded — A refund, prorated against the commission it reverses.

Prefer a webhook?

With a webhook, Stripe sends Klibon those four events and nothing else, and the key only lists your products. On your program's Setup page, choose Connect Stripe, then Use a webhook instead.

  1. In Stripe: Developers → Webhooks → Add destination. Select the four events above.
  2. Choose Webhook endpoint and paste the endpoint shown on the Setup page. It looks like this, with the id of your program at the end:
    endpoint url
    https://klibon.com/api/webhooks/stripe/<program_id>
  3. Copy the signing secret Stripe shows you (it starts with whsec_) and paste it into Klibon.
  4. Create the key with the link on that form. It asks for these, without Events: Read: Products: Read, Prices: Read, Checkout Sessions: Read.

A program already on a webhook keeps it, and Klibon does not read its events a second time.

Verifying the webhook

Until Stripe sends something, Setup shows Waiting for a first event. The connection turns green on the first event Stripe signs with the secret you pasted, whichever of the four it is, even one that credits nobody. Usually that is your next sale or renewal. To verify it now:

  • Live mode. Stripe has no button that sends a test event to a live endpoint. Buy your own product, then refund it. A purchase without an affiliate link earns nobody anything.
  • Test mode. Add the endpoint in test mode too. It has its own signing secret, so paste that one. Then pay with a test card, or run stripe trigger checkout.session.completed with the Stripe CLI. When you switch to live, paste the live endpoint's secret: the connection waits again, for the first live event.
  • No purchase at all. The key-only setup above is verified as soon as Stripe answers for the key, usually within a minute.

If Setup shows Error instead, an event arrived but did not match the secret. Copy it again from Stripe: it changes if you delete and recreate the endpoint.

Getting the referral code to Stripe

The event tells Klibon a payment happened. What makes it attributed is the referral code riding along with it. There are four cases.

If your product has accounts, report signups first (Report signups): one line in the browser, and every later payment under that email is credited without touching your Stripe code. The cases below make the payment carry the code itself, which also covers a buyer who never signs up.

Payment Links

Nothing to do. The tracking script rewrites your buy.stripe.com links to carry client_reference_id, and Stripe puts it on the checkout session.

A Checkout Session you create yourself

Add one line: client_reference_id, set to the visitor's referral code. That one field credits the sale and every renewal after it, since renewals follow the Stripe customer. The code lives in the visitor's browser, so your page reads it when the checkout starts and sends it with the request:

browser
// In the browser, when the visitor starts a checkout. null for a visitor who was
// not referred, which is most of them.
const referral = window.Klibon ? window.Klibon.referral() : null

await fetch('https://api.example.com/checkout', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ priceId, referral })
})

Your server checks it and puts it on the session:

node
import Stripe from 'stripe'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)

// The code your page sent. It came from the browser, so keep it only if it has
// the shape of a Klibon code: 1 to 64 letters, digits, dashes or underscores.
const referral = req.body.referral
const code = typeof referral === 'string' && /^[A-Za-z0-9_-]{1,64}$/.test(referral)
  ? referral
  : undefined

const session = await stripe.checkout.sessions.create({
  mode: 'subscription',
  line_items: [{ price: 'price_123', quantity: 1 }],
  success_url: 'https://example.com/thanks',
  client_reference_id: code // the one line Klibon needs
})

This works wherever your checkout API runs, including on another origin. If it runs on the same origin as your pages, the browser also sends the _klb cookie with the request, and the server can read the code from it instead (Install the tracking script). Either way the code is per visitor, and absent for most: never hardcode one.

Already setting metadata.klibon or subscription_data.metadata.klibon? Klibon reads those too, on every Stripe API version. Already using client_reference_id for your own id? Leave it and set metadata.klibon on the session instead: Klibon reads that first. To check a sale, open the Checkout Session in Stripe: its client_reference_id should show the code.

A subscription created through the API

Set metadata.klibon on the subscription itself.

node
// A subscription created straight through the API never produces a Checkout Session.
// Without this metadata the first invoice has nothing to attribute.
// `code` is the checked referral your page sent, as for a Checkout Session.
await stripe.subscriptions.create({
  customer: customerId,
  items: [{ price: 'price_123' }],
  metadata: code ? { klibon: code } : {}
})

Renewals

Nothing to do, ever. A renewal is matched to the original referral by Stripe customer id, so it keeps paying the affiliate for as long as your terms say — long after the cookie expired.

Coupon codes

A sale can also be credited by the promotion code the customer used (Coupon codes). Stripe names it on the event by its promo_… id, not by the code, so register that id along with the code. On a Payment Link the script prefills it as prefilled_promo_code.

What each event becomes

  • Sale — checkout.session.completed or checkout.session.async_payment_succeeded, once payment_status is paid. A session in setup mode moves no money and is ignored.
  • Sale — invoice.paid with billing_reason=subscription_create. This is not redundant with the checkout session: a subscription started from the API or the dashboard never produces one. When both arrive for the same purchase, the second is recorded as a duplicate and pays nothing twice.
  • Renewal — invoice.paid with billing_reason=subscription_cycle.
  • Refund — charge.refunded. Stripe restates the running total refunded on the charge each time, and Klibon reverses the difference, prorated: refund half the payment and half the commission goes back.
  • Ignored on purpose — every other billing reason, including subscription_update for a mid-period plan change. Counting those would inflate how many payments a referral has earned and burn through a recurring program's duration.

What happens to the commission

  • An attributed sale writes a pending commission, approvable once your refund window has passed.
  • A scheduled job promotes it to approved after that date, unless it is flagged as a suspected self-referral — those wait for your decision.
  • A refund reduces the commission, or reverses it entirely.
  • A refund on a commission you have already paid cannot rewrite a settled statement, so it appears as a negative line on the next one.

Troubleshooting

No events are arriving

  • Check the connection on your program's Setup page. If Stripe refused the key — deleted, rolled, or missing Events: Read — it says so there, and on a live program Klibon emails you the moment it happens. Create a new key with the link and paste it in: Klibon catches up on everything you sold in the meantime, as far back as the 30 days Stripe keeps events for.
  • Check the key's mode. A live key reads live sales only, and a test key test sales only.

If your program uses a webhook endpoint instead:

  • Check the endpoint URL character for character. A mistyped id returns 404, which shows in Stripe's dashboard as a failed delivery.
  • Check the four event types. Stripe only sends what the endpoint subscribes to, and the most common failure is an endpoint listening for checkout.session.completed alone — every renewal and every refund then goes unrecorded.
  • Check the secret. A wrong or rotated whsec_ fails signature verification and Klibon answers 401; Stripe's own webhook log shows the response.

Events arrive but nothing is attributed

  • The payment carried no code. Check client_reference_id on the session in Stripe: if it is empty, the problem is on the page, not the connection — start with the script.
  • A renewal with nothing to match: the subscription has no metadata.klibon and its customer was never attributed to a referral.
  • A refund can arrive before the sale it reverses. Klibon holds it and retries for three days rather than dropping it.

Test mode versus live mode

Test-mode events are processed, but they never pay a real affiliate — a test payment attributed to a live affiliate is recorded and earns nothing. The affiliate you marked as a test one does earn on them, which is exactly what test mode is for: run one purchase end to end with your own test link before you invite anybody.

Test mode and live mode have separate keys in Stripe, and a key reads only its own mode. To run a test purchase, connect a test key (rk_test_…) first, then replace it with a live one.