# Referrals with Stripe

Stripe in one click with the Bolt Claw Stripe App, or a webhook and its signing secret: checkouts, invoices, refunds, disputes and codes.

If your checkout runs on Stripe, connect it in one of two ways. Both credit
paid checkouts, subscription invoices and renewals, refunds and disputes to the
partner whose link or code brought the customer.

- **The Bolt Claw Stripe App**, in one click: no webhook to create, no secret to
  paste. Recommended.
- **A webhook endpoint** in your Stripe account, with its signing secret.

## One click: the Stripe App

Open the program, **Setup**, **Billing**, **Stripe**, and press **Connect Stripe**.
Stripe asks you to approve the app on your account, and sends you back
connected. The app asks only for what it uses:

| Permission | For |
|---|---|
| `event_read` | the events below, sent to Bolt Claw by Stripe for you |
| `checkout_session_read` | paid checkouts, and what a one-time checkout sold |
| `invoice_read` | subscription payments and renewals |
| `charge_read` | refunds and disputes |
| `promotion_code_read` | discount codes typed at checkout |
| `coupon_write`, `promotion_code_write` | the friend's discount, and a code per partner, kept in step |
| `customer_write`, `subscription_write` | account credit and free months, given for you |

It holds no payout permission. One Stripe account can be connected to one
program at a time. Uninstalling the app in Stripe disconnects it here, and
**Disconnect** here uninstalls it in Stripe too; the conversions it recorded
stay.

## Or a webhook endpoint

1. On the program's **Setup** tab, add a Stripe **webhook** integration and copy its
   endpoint URL.
2. In the Stripe dashboard: **Developers**, **Webhooks**, **Add endpoint**. Paste
   the URL and choose **Snapshot** as the payload format (Thin events carry no
   object, so they cannot be read).
3. Select these events:

   `checkout.session.completed`, `checkout.session.async_payment_succeeded`,
   `invoice.paid`, `invoice_payment.paid`, `charge.refunded`,
   `charge.dispute.created`, `promotion_code.created`, `promotion_code.updated`,
   `customer.discount.created`, and `charge.succeeded` for the shared-card check
   (optional: without it every other check still runs)

4. Paste the endpoint's **Signing secret** (it starts `whsec_`) on the card. It is
   encrypted before it is stored and never shown again.

Each delivery is checked against that secret over the exact bytes Stripe sent,
within Stripe's five-minute window.

## Pass the referral to checkout

Put the code the tag kept in the Checkout Session's `client_reference_id` or
`metadata.boltclaw_ref`, and for a subscription in
`subscription_data.metadata.boltclaw_ref` too, so the first invoice is credited
whichever event Stripe delivers first. `boltclaw_customer_id` is your own id for
the customer, so a sale is matched to a signup you reported.

```js
const ref = req.cookies.bc_ref ?? null; // the tag's first-party cookie
await stripe.checkout.sessions.create({
  mode: "subscription",
  client_reference_id: ref ?? undefined,
  metadata: { boltclaw_ref: ref, boltclaw_customer_id: "acme-user-10442" },
  subscription_data: { metadata: { boltclaw_ref: ref, boltclaw_customer_id: "acme-user-10442" } },
  // ...line items, success_url
});
```

Selling through **Payment Links** or the **Buy Button**? Add
`data-payment-links="auto"` to the tag and it puts the code on every link and
button for you.

## Refunds and disputes

A refund takes back the same share of the commission as the share of the sale
refunded, against the running total; a dispute reverses the sale in full. A
refund names the payment intent; on Stripe API versions from `2025-03-31` an
invoice no longer does, and `invoice_payment.paid` links the two, so a refund of
a subscription invoice is matched whichever event arrives first. The setup
status warns when that event is not selected.

## Discount codes

Stripe sends a promotion code as an id, never its text. Bolt Claw learns each
code's id from `promotion_code.created` and `.updated`, so a sale made with a
partner's code alone is credited to them. With the app (or a restricted key),
**Create codes in Stripe** makes them for you. See
[Stripe codes and credits](https://boltclaw.ai/docs/referral-stripe-codes).

## Test mode

Every Stripe event says whether it is live. An event from test mode, or from the
app installed in a sandbox, is recorded as test data: credited to the program's
test partner, never paid, never counted.

## Check it works

On the program's **Setup** tab, the **Billing** row of the setup status turns
`Connected` with the first verified event, and says when a delivery was refused
(nearly always the wrong secret). Then press **Send a test sale** to see a
click, a signup, a sale and a refund go through your rules with test data, or
make a real test purchase in Stripe's test mode. See [Connect your billing system](https://boltclaw.ai/docs/referral-setup)
for the whole setup status and what test data never does.

<!-- https://boltclaw.ai/docs/referral-billing-stripe as markdown. Generated from openapi.yaml; API version 1.22.0. -->
<!-- Every /docs page answers as markdown: add .md to its path, or send Accept: text/markdown. -->
