From 1441027cf34b3111bee16f23d242009915ab3b4a Mon Sep 17 00:00:00 2001 From: sonpiaz Date: Sun, 18 Oct 2026 10:41:27 -0700 Subject: [PATCH 1/3] fix: Stripe invoice events, refunded invoices and empty refund token in docs Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/api-reference/errors.mdx | 2 +- content/docs/api-reference/integrations/stripe.mdx | 4 ++-- content/docs/api-reference/track-refund.mdx | 4 ++-- .../docs/brand/tracking/connect-stripe-account.mdx | 4 ++-- content/docs/use-cases/subscriptions.mdx | 4 ++-- scripts/retired-claims.json | 12 ++++++++++++ 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/content/docs/api-reference/errors.mdx b/content/docs/api-reference/errors.mdx index 70e69e52..938a6b37 100644 --- a/content/docs/api-reference/errors.mdx +++ b/content/docs/api-reference/errors.mdx @@ -114,7 +114,7 @@ When you contact support about a failed request, include the `X-Request-ID`. It | Status | Error message | Condition | Fix | |--------|---------------|-----------|-----| | `401` | `Missing or malformed Authorization header. Expected: Bearer ` | `Authorization` header missing or does not start with `Bearer ` | Add `Authorization: Bearer YOUR_PROGRAM_API_KEY` to the request | -| `401` | `Missing API token` | Header present but the token is empty after trimming (sale only; refund answers `Invalid API token`) | Check for trailing spaces or an empty env variable | +| `401` | `Missing API token` (sale), `Empty Bearer token` (refund) | Header present but the token is empty after trimming | Check for trailing spaces or an empty env variable | | `401` | `Invalid API token` | The token matches no program: a wrong key, a replaced key, or a key from a different program | Compare what you are sending against the full key you stored when it was issued. If you no longer hold that value, ask a workspace owner to [regenerate the key](/docs/brand/settings#step-3-regenerate-the-key) and deploy the new one — the dashboard only ever shows it masked afterwards | The rows above apply to the endpoints that require a Bearer token: `POST /api/v1/track/sale` and `POST /api/v1/track/refund`. diff --git a/content/docs/api-reference/integrations/stripe.mdx b/content/docs/api-reference/integrations/stripe.mdx index 2a5f6a0c..6c951ca3 100644 --- a/content/docs/api-reference/integrations/stripe.mdx +++ b/content/docs/api-reference/integrations/stripe.mdx @@ -2,7 +2,7 @@ title: "Stripe" description: "Canonical recipe for attributing Stripe payments to Affitor partners — one-time and recurring." kind: guide -last_updated: 2026-10-17 +last_updated: 2026-10-18 --- This guide is the **canonical reference for the Stripe sale path**. Other framework guides (Next.js, Express, etc.) link here for Step 3. If you need click capture or signup tracking first, start with the [Next.js guide](/docs/api-reference/integrations/nextjs) or the framework guide for your stack. @@ -95,7 +95,7 @@ Affitor processes two Stripe webhook events: | Event | When it fires | What Affitor does | |---|---|---| | `checkout.session.completed` | A one-time payment | Records the sale. A subscription checkout records no sale here: its first invoice does | -| `invoice.payment_succeeded` | A subscription's first payment and every renewal | Records the subscription sale | +| `invoice.payment_succeeded`, `invoice.paid` | A subscription's first payment and every renewal | Records the subscription sale once, from whichever of the two arrives first | **Attribution fallback chain.** Affitor resolves the partner in this order: diff --git a/content/docs/api-reference/track-refund.mdx b/content/docs/api-reference/track-refund.mdx index db099e44..5fae6c84 100644 --- a/content/docs/api-reference/track-refund.mdx +++ b/content/docs/api-reference/track-refund.mdx @@ -2,7 +2,7 @@ title: Track Refund description: Reverse a commission when a sale is refunded kind: reference -last_updated: 2026-10-17 +last_updated: 2026-10-18 --- `POST /api/v1/track/refund` @@ -151,7 +151,7 @@ Errors use the [platform envelope](/docs/api-reference/errors#the-platform-envel | 400 | `invalid_request` | `transaction_id` is missing, or `reason` is not `refund` or `chargeback`. | | 401 | `unauthorized` | `Authorization` header is missing, malformed, or the API token is invalid. | | 404 | `not_found` | No sale record found for the given `transaction_id` in the authenticated program. | -| 500 | `internal_error` | Internal error during commission reversal. | +| 500 | `internal_error` | Internal error during commission reversal. Nothing was applied: retry the same request with the same `refund_id`. | --- diff --git a/content/docs/brand/tracking/connect-stripe-account.mdx b/content/docs/brand/tracking/connect-stripe-account.mdx index d1fc49ce..bc652ab4 100644 --- a/content/docs/brand/tracking/connect-stripe-account.mdx +++ b/content/docs/brand/tracking/connect-stripe-account.mdx @@ -2,7 +2,7 @@ title: Connect a new Stripe account description: "Create a separate Stripe account under the login you already have, connect it to an Affitor program, test it with Stripe test mode, and check that the connection is right." kind: guide -last_updated: 2026-10-17 +last_updated: 2026-10-18 --- Create a second Stripe account under your existing Stripe login, activate it, connect it to your Affitor program, and test it before partners send real customers. Affitor connects through Stripe Connect, so you do not create an API key or a webhook for it. @@ -72,7 +72,7 @@ If Stripe is connected and your server also sends sales through the Server API, | Stripe event | What Affitor does | |---|---| | `checkout.session.completed` | Records a one-time purchase | -| `invoice.payment_succeeded` | Records a subscription's first payment and each renewal | +| `invoice.payment_succeeded`, `invoice.paid` | Records a subscription's first payment and each renewal, once per invoice | | `charge.refunded` | Reverses the commission ([refund handling](/docs/brand/quickstart/commission-approval-cash-flow#refund-handling)) | | `charge.dispute.funds_withdrawn` | Reverses the commission when a chargeback takes the money | | `charge.dispute.funds_reinstated`, `charge.dispute.closed` (won) | Sends you a notification to review the commission. It stays reversed: Affitor does not re-credit the partner on its own | diff --git a/content/docs/use-cases/subscriptions.mdx b/content/docs/use-cases/subscriptions.mdx index 43ec263a..4cdc8922 100644 --- a/content/docs/use-cases/subscriptions.mdx +++ b/content/docs/use-cases/subscriptions.mdx @@ -2,7 +2,7 @@ title: Subscriptions, credit partners on renewals description: Stripe tells Affitor about each paid invoice of a subscription, and each one earns the partner who referred the customer for as long as your commission type says. Refunds and disputes take the commission back. kind: guide -last_updated: 2026-10-17 +last_updated: 2026-10-18 use_case: name: Subscriptions strip: [Partner link, Subscription, Renewals] @@ -44,7 +44,7 @@ sequenceDiagram Each paid invoice of the subscription is a sale: the first one, then every renewal. Renewals go to the partner of the first payment, through the click passed at checkout or the buyer's email or Stripe customer ID. The attribution window applies only to that first payment: once the customer has paid, renewals are credited however old the click is. The rate is the same for every plan, price and billing interval. -Canceling, pausing or changing the plan writes nothing in Affitor: a canceled subscription stops earning because it stops paying, and after a plan change each paid invoice earns at the same rate. A refund or dispute is matched to its invoice, except when one payment paid several invoices or one invoice was paid by several charges: those are not taken back automatically. If you bill subscriptions outside Stripe, your server reports each payment and each refund ([Any payment provider](/docs/use-cases/any-payment-provider)). +Canceling, pausing or changing the plan writes nothing in Affitor: a canceled subscription stops earning because it stops paying, and after a plan change each paid invoice earns at the same rate. A refund or dispute is matched to its invoice and reverses the commission. An invoice that is already refunded when Affitor processes it earns no commission. An invoice paid by several charges, or by one payment that also paid other invoices, is not recorded automatically. If you bill subscriptions outside Stripe, your server reports each payment and each refund ([Any payment provider](/docs/use-cases/any-payment-provider)). ## Steps diff --git a/scripts/retired-claims.json b/scripts/retired-claims.json index 745da7e2..566ac783 100644 --- a/scripts/retired-claims.json +++ b/scripts/retired-claims.json @@ -94,5 +94,17 @@ "why": "CLI 0.4.5 reads stripe_account_bound for a certain runtime key. A true binding skips injection; unknown/false or uncertain env keeps unattended webhook unchanged. Existing calls are not removed.", "since": "2026-10-18", "allow": [] + }, + { + "pattern": "not taken back automatically", + "why": "Since CMS v3.16.139 an invoice paid by several charges, or by a payment shared with other invoices, is not recorded as a sale at all, and an invoice already refunded when processed earns nothing.", + "since": "2026-10-18", + "allow": [] + }, + { + "pattern": "refund answers `Invalid API token`", + "why": "An empty Bearer token on POST /api/v1/track/refund answers 401 `Empty Bearer token` (CMS v3.16.140).", + "since": "2026-10-18", + "allow": [] } ] From 130677584fe3d241e7d800557538393436976f78 Mon Sep 17 00:00:00 2001 From: sonpiaz Date: Sun, 18 Oct 2026 10:45:42 -0700 Subject: [PATCH 2/3] fix: scope refunded and split invoices to new sales, name both invoice events everywhere Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/api-reference/attribution.mdx | 4 ++-- content/docs/api-reference/errors.mdx | 2 +- content/docs/api-reference/integrations/node-express.mdx | 4 ++-- content/docs/api-reference/integrations/stripe.mdx | 6 +++--- content/docs/api-reference/track-refund.mdx | 2 +- content/docs/brand/tracking/connect-stripe-account.mdx | 2 +- content/docs/brand/tracking/payment-tracking-stripe.mdx | 6 +++--- content/docs/brand/tracking/quickstart-integration.mdx | 4 ++-- content/docs/use-cases/subscriptions.mdx | 2 +- scripts/retired-claims.json | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/content/docs/api-reference/attribution.mdx b/content/docs/api-reference/attribution.mdx index 9d3e4e2f..94488373 100644 --- a/content/docs/api-reference/attribution.mdx +++ b/content/docs/api-reference/attribution.mdx @@ -2,7 +2,7 @@ title: Attribution Mechanics description: How Affitor assigns credit to partners — the cookie, the click ID, the attribution windows, and what happens on re-click. kind: concept -last_updated: 2026-10-17 +last_updated: 2026-10-18 --- > **Beta** — The Affitor SDKs and CLI are in active development. The documented happy-path works; edge cases may change. Report issues on GitHub. @@ -104,7 +104,7 @@ For subscription products, every recurring charge can generate a commission with | `cps_lifetime` | `null` | Commission paid on every renewal indefinitely | | `cps_one_time` | `0` | Commission paid once on the first payment only | -With Stripe connected, renewals need no call from you: Affitor records each renewal from `invoice.payment_succeeded` and matches it to the partner through the click in the subscription's metadata, or else by the buyer's email or Stripe customer ID (saved when the first payment was attributed). If you report sales yourself with `/api/v1/track/sale`, a renewal is attributed like any sale, by `customer_key` and then `click_id`; `is_recurring: true` and `subscription_id` mark it as a subscription charge. +With Stripe connected, renewals need no call from you: Affitor records each renewal from the invoice events (`invoice.payment_succeeded`, `invoice.paid`) and matches it to the partner through the click in the subscription's metadata, or else by the buyer's email or Stripe customer ID (saved when the first payment was attributed). If you report sales yourself with `/api/v1/track/sale`, a renewal is attributed like any sale, by `customer_key` and then `click_id`; `is_recurring: true` and `subscription_id` mark it as a subscription charge. --- diff --git a/content/docs/api-reference/errors.mdx b/content/docs/api-reference/errors.mdx index 938a6b37..0ae6cc08 100644 --- a/content/docs/api-reference/errors.mdx +++ b/content/docs/api-reference/errors.mdx @@ -140,7 +140,7 @@ The CLI's program-key routes word these differently; see [CLI endpoint errors](# The success body is identical in both modes, so a `2xx` on its own does not prove your key was read. The one exception is a program configured with `lead_auth: server_required`, where a browser-mode lead comes back with `"provisional": true`. -Such a lead settles when your backend sends the server-side lead. A qualifying Stripe payment webhook can also settle it, through the same path the lead endpoint uses, but only when Affitor can resolve the customer from that event — one-off payments through `checkout.session.completed`, and subscriptions through `invoice.payment_succeeded`, because subscription checkouts are deliberately left to the invoice event. Send the server-side lead rather than relying on a payment to settle it. +Such a lead settles when your backend sends the server-side lead. A qualifying Stripe payment webhook can also settle it, through the same path the lead endpoint uses, but only when Affitor can resolve the customer from that event — one-off payments through `checkout.session.completed`, and subscriptions through the invoice events (`invoice.payment_succeeded`, `invoice.paid`), because subscription checkouts are deliberately left to the invoice event. Send the server-side lead rather than relying on a payment to settle it. --- diff --git a/content/docs/api-reference/integrations/node-express.mdx b/content/docs/api-reference/integrations/node-express.mdx index 66a55c7a..a856c3e8 100644 --- a/content/docs/api-reference/integrations/node-express.mdx +++ b/content/docs/api-reference/integrations/node-express.mdx @@ -2,7 +2,7 @@ title: "Node / Express" description: "Server-side Affitor integration with @affitor/sdk/server — track signups and sales from your Express backend." kind: guide -last_updated: 2026-10-17 +last_updated: 2026-10-18 --- This guide wires Affitor into a Node.js / Express app. Because Express is a pure server runtime, the click is captured by a frontend script tag, then the `affitor_click_id` is forwarded to your server so all tracking calls — lead and sale — happen server-side with `@affitor/sdk/server`. @@ -111,7 +111,7 @@ Sales must always be tracked server-side — never from the browser. ### Option A — Stripe integration (recommended for Stripe Checkout) -Connect your Stripe account under **Program settings** › **Payments**, then attach Affitor metadata when you create the Checkout Session. Affitor receives the account's `checkout.session.completed` and `invoice.payment_succeeded` events through Stripe Connect (you add no webhook) and attributes the sale automatically, with no extra `trackSale` call needed. +Connect your Stripe account under **Program settings** › **Payments**, then attach Affitor metadata when you create the Checkout Session. Affitor receives the account's `checkout.session.completed` and invoice events (`invoice.payment_succeeded`, `invoice.paid`) through Stripe Connect (you add no webhook) and attributes the sale automatically, with no extra `trackSale` call needed. ```js // server — creating the Checkout Session diff --git a/content/docs/api-reference/integrations/stripe.mdx b/content/docs/api-reference/integrations/stripe.mdx index 6c951ca3..9bf00b88 100644 --- a/content/docs/api-reference/integrations/stripe.mdx +++ b/content/docs/api-reference/integrations/stripe.mdx @@ -58,7 +58,7 @@ Affitor listens to the `checkout.session.completed` webhook event and attributes ### Subscription (`mode: 'subscription'`) -For subscriptions, put the same fields in `subscription_data.metadata`. Affitor records a subscription's first payment and each renewal from `invoice.payment_succeeded`, and an invoice carries the subscription's metadata, not the Checkout Session's. Renewals do not depend on it: once the first payment is attributed, Affitor saves the buyer's Stripe customer ID and matches each renewal by the buyer's email or that Stripe customer ID. +For subscriptions, put the same fields in `subscription_data.metadata`. Affitor records a subscription's first payment and each renewal from the invoice events (`invoice.payment_succeeded`, `invoice.paid`), and an invoice carries the subscription's metadata, not the Checkout Session's. Renewals do not depend on it: once the first payment is attributed, Affitor saves the buyer's Stripe customer ID and matches each renewal by the buyer's email or that Stripe customer ID. ```ts const session = await stripe.checkout.sessions.create({ @@ -90,12 +90,12 @@ const session = await stripe.checkout.sessions.create({ ## 3. How Affitor attributes payments -Affitor processes two Stripe webhook events: +Affitor processes these Stripe webhook events for sales: | Event | When it fires | What Affitor does | |---|---|---| | `checkout.session.completed` | A one-time payment | Records the sale. A subscription checkout records no sale here: its first invoice does | -| `invoice.payment_succeeded`, `invoice.paid` | A subscription's first payment and every renewal | Records the subscription sale once, from whichever of the two arrives first | +| `invoice.payment_succeeded`, `invoice.paid` | A subscription's first payment and every renewal | Records the subscription sale. Both events go to the same handler, which records each invoice at most once | **Attribution fallback chain.** Affitor resolves the partner in this order: diff --git a/content/docs/api-reference/track-refund.mdx b/content/docs/api-reference/track-refund.mdx index 5fae6c84..500aa719 100644 --- a/content/docs/api-reference/track-refund.mdx +++ b/content/docs/api-reference/track-refund.mdx @@ -151,7 +151,7 @@ Errors use the [platform envelope](/docs/api-reference/errors#the-platform-envel | 400 | `invalid_request` | `transaction_id` is missing, or `reason` is not `refund` or `chargeback`. | | 401 | `unauthorized` | `Authorization` header is missing, malformed, or the API token is invalid. | | 404 | `not_found` | No sale record found for the given `transaction_id` in the authenticated program. | -| 500 | `internal_error` | Internal error during commission reversal. Nothing was applied: retry the same request with the same `refund_id`. | +| 500 | `internal_error` | Internal error during commission reversal. Retry the same request with the same `refund_id`: a refund that was already applied is skipped. | --- diff --git a/content/docs/brand/tracking/connect-stripe-account.mdx b/content/docs/brand/tracking/connect-stripe-account.mdx index bc652ab4..5d3edaab 100644 --- a/content/docs/brand/tracking/connect-stripe-account.mdx +++ b/content/docs/brand/tracking/connect-stripe-account.mdx @@ -72,7 +72,7 @@ If Stripe is connected and your server also sends sales through the Server API, | Stripe event | What Affitor does | |---|---| | `checkout.session.completed` | Records a one-time purchase | -| `invoice.payment_succeeded`, `invoice.paid` | Records a subscription's first payment and each renewal, once per invoice | +| `invoice.payment_succeeded`, `invoice.paid` | Records a subscription's first payment and each renewal, at most once per invoice | | `charge.refunded` | Reverses the commission ([refund handling](/docs/brand/quickstart/commission-approval-cash-flow#refund-handling)) | | `charge.dispute.funds_withdrawn` | Reverses the commission when a chargeback takes the money | | `charge.dispute.funds_reinstated`, `charge.dispute.closed` (won) | Sends you a notification to review the commission. It stays reversed: Affitor does not re-credit the partner on its own | diff --git a/content/docs/brand/tracking/payment-tracking-stripe.mdx b/content/docs/brand/tracking/payment-tracking-stripe.mdx index 443c2eb4..9a6392b0 100644 --- a/content/docs/brand/tracking/payment-tracking-stripe.mdx +++ b/content/docs/brand/tracking/payment-tracking-stripe.mdx @@ -2,7 +2,7 @@ title: Payment Tracking description: Track completed sales with server-side tracking or Stripe integration kind: guide -last_updated: 2026-10-17 +last_updated: 2026-10-18 --- Payment tracking records revenue after a customer pays. Affitor supports two sale-tracking paths: @@ -161,7 +161,7 @@ const session = await stripe.checkout.sessions.create({ ### Why subscriptions need `subscription_data.metadata` - One-time payments are handled from Stripe checkout session completion -- Subscription commissions, for the first payment and each renewal, are created from `invoice.payment_succeeded` +- Subscription commissions, for the first payment and each renewal, are created from the invoice events (`invoice.payment_succeeded`, `invoice.paid`) - An invoice carries the subscription's metadata (`subscription_data.metadata`), not the Checkout Session's `metadata`, so that is where the click reaches the subscription - Renewals do not depend on it: once the first payment is attributed, Affitor saves the buyer's Stripe customer ID and matches each renewal by the buyer's email or that Stripe customer ID - Without it, the subscription is attributed only if the buyer's email or Stripe customer ID matches a customer Affitor already has @@ -327,7 +327,7 @@ Check: Check: - the subscription's first payment was attributed: renewals follow it through the buyer's email or Stripe customer ID - `subscription_data.metadata` contains all three fields -- the program's Stripe card under **Program settings** › **Payments** reads **Connected**, and the subscription bills on that Stripe account. Affitor receives `invoice.payment_succeeded` through Stripe Connect; you do not add a webhook +- the program's Stripe card under **Program settings** › **Payments** reads **Connected**, and the subscription bills on that Stripe account. Affitor receives the invoice events (`invoice.payment_succeeded`, `invoice.paid`) through Stripe Connect; you do not add a webhook - the same customer key was used at signup and in Stripe metadata --- diff --git a/content/docs/brand/tracking/quickstart-integration.mdx b/content/docs/brand/tracking/quickstart-integration.mdx index c8134798..04d38b27 100644 --- a/content/docs/brand/tracking/quickstart-integration.mdx +++ b/content/docs/brand/tracking/quickstart-integration.mdx @@ -2,7 +2,7 @@ title: 3-Step Integration Guide description: Get Affitor tracking live with the supported click, signup, and server-side tracking flows kind: quickstart -last_updated: 2026-10-17 +last_updated: 2026-10-18 --- Three steps get Affitor tracking live: clicks → signups → sales. Steps 1 and 2 establish attribution; step 3 records revenue. @@ -279,7 +279,7 @@ const session = await stripe.checkout.sessions.create({ ### Stripe integration rules - **One-time payments** are attributed from Stripe checkout metadata and webhook processing -- **Subscriptions and renewals** rely on `invoice.payment_succeeded` +- **Subscriptions and renewals** rely on the invoice events (`invoice.payment_succeeded`, `invoice.paid`) - If `subscription_data.metadata` is missing, the click does not reach the subscription's invoices (first payment and renewals), and Affitor attributes the subscription only by the buyer's email or Stripe customer ID - Metadata fields to set on every checkout (recommended; none is strictly required): - `affitor_click_id` diff --git a/content/docs/use-cases/subscriptions.mdx b/content/docs/use-cases/subscriptions.mdx index 4cdc8922..a14aa860 100644 --- a/content/docs/use-cases/subscriptions.mdx +++ b/content/docs/use-cases/subscriptions.mdx @@ -44,7 +44,7 @@ sequenceDiagram Each paid invoice of the subscription is a sale: the first one, then every renewal. Renewals go to the partner of the first payment, through the click passed at checkout or the buyer's email or Stripe customer ID. The attribution window applies only to that first payment: once the customer has paid, renewals are credited however old the click is. The rate is the same for every plan, price and billing interval. -Canceling, pausing or changing the plan writes nothing in Affitor: a canceled subscription stops earning because it stops paying, and after a plan change each paid invoice earns at the same rate. A refund or dispute is matched to its invoice and reverses the commission. An invoice that is already refunded when Affitor processes it earns no commission. An invoice paid by several charges, or by one payment that also paid other invoices, is not recorded automatically. If you bill subscriptions outside Stripe, your server reports each payment and each refund ([Any payment provider](/docs/use-cases/any-payment-provider)). +Canceling, pausing or changing the plan writes nothing in Affitor: a canceled subscription stops earning because it stops paying, and after a plan change each paid invoice earns at the same rate. A refund or dispute is matched to its invoice and reverses the commission; some older sales, such as one payment shared by several invoices, are held for manual reconciliation instead. A new invoice whose payment was already refunded, even in part, before Affitor processed it is not recorded as a sale. A new invoice paid by several charges, or by one payment that also paid other invoices, is not recorded automatically. If you bill subscriptions outside Stripe, your server reports each payment and each refund ([Any payment provider](/docs/use-cases/any-payment-provider)). ## Steps diff --git a/scripts/retired-claims.json b/scripts/retired-claims.json index 566ac783..12060055 100644 --- a/scripts/retired-claims.json +++ b/scripts/retired-claims.json @@ -97,7 +97,7 @@ }, { "pattern": "not taken back automatically", - "why": "Since CMS v3.16.139 an invoice paid by several charges, or by a payment shared with other invoices, is not recorded as a sale at all, and an invoice already refunded when processed earns nothing.", + "why": "Since CMS v3.16.139 a new invoice paid by several charges, or by a payment shared with other invoices, is not recorded as a sale, and a new invoice refunded before processing is not recorded; older such sales are held for manual reconciliation.", "since": "2026-10-18", "allow": [] }, From 934b722877990e2adc14961d0c243fae91662046 Mon Sep 17 00:00:00 2001 From: sonpiaz Date: Sun, 18 Oct 2026 10:49:49 -0700 Subject: [PATCH 3/3] fix: name both invoice events on the pay-as-you-go note, drop an em dash Co-Authored-By: Claude Opus 5.5 (1M context) --- content/docs/api-reference/errors.mdx | 2 +- content/docs/brand/tracking/payment-tracking-stripe.mdx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/docs/api-reference/errors.mdx b/content/docs/api-reference/errors.mdx index 0ae6cc08..063e89e6 100644 --- a/content/docs/api-reference/errors.mdx +++ b/content/docs/api-reference/errors.mdx @@ -140,7 +140,7 @@ The CLI's program-key routes word these differently; see [CLI endpoint errors](# The success body is identical in both modes, so a `2xx` on its own does not prove your key was read. The one exception is a program configured with `lead_auth: server_required`, where a browser-mode lead comes back with `"provisional": true`. -Such a lead settles when your backend sends the server-side lead. A qualifying Stripe payment webhook can also settle it, through the same path the lead endpoint uses, but only when Affitor can resolve the customer from that event — one-off payments through `checkout.session.completed`, and subscriptions through the invoice events (`invoice.payment_succeeded`, `invoice.paid`), because subscription checkouts are deliberately left to the invoice event. Send the server-side lead rather than relying on a payment to settle it. +Such a lead settles when your backend sends the server-side lead. A qualifying Stripe payment webhook can also settle it, through the same path the lead endpoint uses, but only when Affitor can resolve the customer from that event: one-off payments through `checkout.session.completed`, and subscriptions through the invoice events (`invoice.payment_succeeded`, `invoice.paid`), because subscription checkouts are deliberately left to the invoice event. Send the server-side lead rather than relying on a payment to settle it. --- diff --git a/content/docs/brand/tracking/payment-tracking-stripe.mdx b/content/docs/brand/tracking/payment-tracking-stripe.mdx index 9a6392b0..189879c2 100644 --- a/content/docs/brand/tracking/payment-tracking-stripe.mdx +++ b/content/docs/brand/tracking/payment-tracking-stripe.mdx @@ -178,8 +178,8 @@ Every `checkout.session.completed` event with `mode: 'payment'` is processed ind :::note -**`invoice.payment_succeeded` is not involved here.** -Affitor uses `invoice.payment_succeeded` only for subscriptions: a subscription's first payment and each renewal. Pay-as-you-go purchases use `checkout.session.completed`, one event per purchase, each attributed and commissioned separately. +**The invoice events are not involved here.** +Affitor uses the invoice events (`invoice.payment_succeeded`, `invoice.paid`) only for subscriptions: a subscription's first payment and each renewal. Pay-as-you-go purchases use `checkout.session.completed`, one event per purchase, each attributed and commissioned separately. :::