Monetization
Stripe
The Stripe connector — Kaiten composes each invoice, Stripe numbers, sends and collects it. Connecting a restricted key, the options, how an invoice is pushed, reconciled and charged, the sync, currencies, 3-D Secure, the Stripe settings to check, and disconnecting.
Stripe is the payment provider Kaiten ships a connector for. Kaiten stays the reference for usage and for what each invoice says — its lines, its discounts, its total. Stripe numbers each invoice, presents and sends it, collects the payment, and computes the tax when the connector's automatic tax is on. Stripe holds no product, price, or subscription of yours: each invoice arrives there as an invoice whose items are the lines Kaiten composed.
| Kaiten owns | Stripe owns |
|---|---|
| The catalog and its prices | Tax, when automatic tax is on |
| What each instance is entitled to | Invoice numbers |
| The journal of usage | How an invoice looks and is sent |
| The subscription of each instance | Payment and receipts |
| What each invoice says | Retries and reminders |
A subscription chooses its payment provider when it starts: STRIPE, or NOOP — the default — for invoices you collect through your own systems, described in without a payment provider. Both can run side by side in one organization. Billing must be enabled for your organization (billing error families).
Connect Stripe
In the console, open Integrations → Connectors → Stripe, paste a restricted key of your Stripe account, choose the options below, and click Connect Stripe.
The key. A restricted key — rk_test_… or rk_live_… — created in Stripe under Developers → API keys, with these permissions, as the connector page lists them:
| Resource | Access |
|---|---|
| Customers, Invoices, Invoice items, Coupons, Payment methods, Checkout Sessions, Customer portal | Write |
| Events | Read |
A secret key (sk_…) gives Kaiten far more than it needs, and a publishable key (pk_…) cannot create an invoice: the console refuses both before sending anything. When a customer saves a payment method on Stripe's hosted page, Kaiten reads the checkout session back with its SetupIntent; if Stripe refuses that read for your key, add read access to SetupIntents.
Where it is kept. Kaiten checks the key against Stripe with a read-only call, stores it in Vault, and never shows it again: a read of the settings returns ***, and sending *** back keeps the stored key. A key Stripe refuses is never stored (422 UpdateConnectorSettings.CredentialsRejected). Once Kaiten has registered customers in Stripe, a new key must reach the same account: Kaiten reads one of those customers with it, and a key that cannot is refused with 409 UpdateConnectorSettings.AccountChanged. On a self-hosted installation, the connector needs a Vault — see secrets in Vault.
Test or live. The key decides the mode: an rk_test_ key reaches your Stripe account in test mode, an rk_live_ key in live mode. The connector page shows a Test mode or Live mode badge, and GET /billing/capabilities reports it as livemode on the Stripe provider.
Included in your License. The connector is activated under the connector-stripe entitlement of your organization's own Kaiten License on Kaiten Cloud; a self-hosted installation always grants it — see connectors.
The same connection over the API — saving the settings activates the connector, under the organizations scopes:
curl -X PUT "$KAITEN_API/connectors/kaiten.integration.billing.stripe/settings" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"settings": {
"stripeSecretKey": "<your rk_test_ key>",
"taxBehavior": "EXCLUSIVE",
"automaticTax": false,
"autoFinalize": true
}
}'BILLING_PROVIDER_CONNECTED is emitted, with livemode; it never carries the key.
The options
| Setting | Console | Default | What it does |
|---|---|---|---|
taxBehavior | Tax | EXCLUSIVE | Whether the amounts Kaiten composes leave tax out — Stripe adds it — or already include it |
automaticTax | Compute tax automatically | off | Stripe Tax computes the tax of every invoice. It needs a complete address on the customer in Stripe, or the push of the customer's invoices fails |
autoFinalize | Finalize invoices automatically | on | Off, each invoice stops as a draft in Stripe for someone to review; Kaiten finalizes it on request, from the invoice's page or with POST /invoices/{invoiceId}/retry-push |
Customers and how they pay
When a subscription starts on Stripe (POST /instances/{instanceSlug}/billing with "providerKind": "STRIPE"), Kaiten registers the customer in Stripe first. Two collection methods exist, chosen per subscription or taken from the organization's defaults:
- Send the invoice (
SEND_INVOICE) — the invoice goes to the customer for payment, at the billing e-mail Kaiten requires for it (422 SubscribeInstance.BillingEmailMissing), with its payment terms: the due date is the finalization plus the terms' days. - Charge automatically (
CHARGE_AUTOMATICALLY) — Kaiten charges the invoice off-session to the customer's default payment method, which the customer saved on a page Stripe hosts. Kaiten never sees card data: it keeps the labels Stripe returns — brand, last four digits, expiry.
To set up a payment method, POST /customers/{customerSlug}/billing/payment-method-session with a returnUrl opens Stripe's hosted page; the customer comes back with kaiten_setup_session appended, and POST …/payment-method-session/{sessionId}/complete has Kaiten check the session with Stripe — the redirect is never trusted — and make that payment method the one the customer's invoices are charged to. POST /customers/{customerSlug}/billing/portal-session opens Stripe's own portal, where the customer manages payment methods and invoices. On the customer's page in the console, the payment method card does both.
The organization's defaults — the collection method and the payment terms a subscription takes when it names none, 30 days unless you change it — are on Settings → Billing, beside the payment providers, the health of billing, and how long usage is kept.
How an invoice reaches Stripe
Kaiten composes each invoice at the subscription's boundary, then pushes it:
- Draft. Kaiten creates the invoice in Stripe with one item per line — its amount, its service period, and the coupons of the discounts it bears. Stripe refuses a negative item when it computes tax, so a discount reaches Stripe as a coupon on the item it reduces, never as a negative line; see discounts on Stripe. An invoice with more lines than a Stripe invoice takes, 250, is held rather than pushed.
- Finalize. Stripe numbers the invoice, computes the tax when automatic tax is on, and an invoice sent for payment goes out.
- Reconcile. Kaiten reads the invoice back and compares it with its own, to the minor unit: the same lines, the same discounts, the same total excluding tax. Kaiten never corrects itself from Stripe: a mismatch is reported, never charged, and remedied by voiding the invoice and composing it again. An invoice already paid when the mismatch shows cannot be voided in Stripe: settle the difference there, with a credit note and a refund.
- Charge. Under automatic collection, Kaiten charges a matched invoice once. From then on, Stripe may also collect it on its own, as its retry settings say.
A step that fails is retried with a doubling backoff, and POST /invoices/{invoiceId}/retry-push resumes it at once; a push that keeps failing appears in the health of billing and is announced as INSTANCE_INVOICE_PUSH_FAILED. The other steps are announced too: INSTANCE_INVOICE_ISSUED and INSTANCE_INVOICE_PUSHED at finalization, INSTANCE_INVOICE_RECONCILIATION_MISMATCH, then INSTANCE_INVOICE_PAID or INSTANCE_INVOICE_PAYMENT_FAILED. An invoice of zero is paid at issue and never pushed.
What Kaiten reads back
The sync. Every 15 minutes, Kaiten reads Stripe's events and mirrors what happened there: payments, voids, finalizations done in Stripe's dashboard. Sync now, in the health card of Settings → Billing, runs a pass at once (POST /billing/sync); Read from Stripe, on an invoice, reads that one invoice (POST /invoices/{invoiceId}/sync). Three failed passes in a row are announced as BILLING_PROVIDER_SYNC_FAILED, and the Stripe card of Settings → Billing says when the last sync succeeded. On a self-hosted installation, KAITEN_BILLING_PROVIDER_SYNC_INTERVAL sets the interval (environment variables).
3-D Secure and refused charges. A charge that needs the customer to authenticate fails with authentication_required: the customer confirms the payment on Stripe's hosted invoice page (provider.hostedInvoiceUrl on the invoice). Its subscription moves to PAST_DUE only once the invoice is still unpaid after the grace of automatic collection — two hours by default, KAITEN_BILLING_AUTO_COLLECTION_GRACE on a self-hosted installation. A refused charge — card_declined, expired_card, no_payment_method — makes the invoice PAYMENT_FAILED and the subscription PAST_DUE at once. PAST_DUE changes nothing to what the Instance may use, and the subscription leaves it once nothing of it is overdue.
Currencies
A subscription starts on Stripe in a currency Stripe can present an invoice in, provided Stripe counts its minor unit as ISO 4217 does: euros, US dollars, pounds, yen, and most others, HUF and TWD included. The three-decimal currencies, which Stripe offers to accounts in the United Arab Emirates only, are refused, and so are MGA, which Stripe counts in whole units, and ISK and UGX, which Stripe rounds. A refused currency answers 422 SubscribeInstance.UnsupportedCurrency, or 422 UpdateInstanceBilling.UnsupportedCurrency when a subscription moves to Stripe. Without a payment provider, every active ISO 4217 currency works.
Stripe settings to check
Kaiten finalizes invoices through Stripe's API; what Stripe does next follows your Stripe account's settings.
- The e-mail of a finalized invoice. Whether Stripe e-mails an invoice sent for payment once Kaiten finalizes it depends on your account's setting Email finalized invoices to customers. On a self-hosted installation where that setting is off,
KAITEN_BILLING_STRIPE_SEND_AFTER_FINALIZE=truehas Kaiten ask Stripe to send each such invoice right after finalizing it. With both on, the customer receives the invoice twice. Test mode sends no e-mail: confirm the behaviour on your first live invoice. - Retries of one-off invoices. Every Kaiten invoice is a one-off invoice in Stripe, so its automatic retries and reminders are those your Stripe account sets for invoices. A charge that needs the customer to authenticate is settled by the customer, on the hosted invoice page.
- Leave Kaiten's invoices as they are. A line or a coupon added to a Kaiten invoice in Stripe's dashboard makes its reconciliation fail.
Losing the connector-stripe entitlement
On Kaiten Cloud, if your organization's License stops granting connector-stripe, nothing new starts on Stripe: subscribing an instance on Stripe, or switching a subscription to it, answers 422 with .ProviderNotConnected and the reason NOT_ENTITLED in errors[0].value. What already routes to Stripe runs to its end: its invoices are still pushed, synced, and collected.
Disconnect
Disconnect, on the connector's page, stops Kaiten from pushing invoices to Stripe and reading them back. It is refused — 409 DeactivateConnector.BillingActive, with the number of subscriptions and invoices concerned in errors[0].value — while a subscription that is not canceled, or an invoice that is not settled, still routes to Stripe: cancel those subscriptions or move them to another provider, and settle the invoices, first. The key stays stored, so connecting again does not ask for it, and invoices already in Stripe are not touched. BILLING_PROVIDER_DISCONNECTED is emitted. Deleting the settings (DELETE /connectors/kaiten.integration.billing.stripe/settings) is refused under the same condition.
Reference
| Operation | Route | Scope |
|---|---|---|
| Connect, change the options | PUT /connectors/kaiten.integration.billing.stripe/settings | write:organizations |
| Disconnect | DELETE /connectors/kaiten.integration.billing.stripe/activation | write:organizations |
Providers, livemode, what billing can do | GET /billing/capabilities | read:billing |
| Health of billing | GET /billing/health | read:billing |
| Sync now | POST /billing/sync | write:billing |
| Read one invoice from Stripe; resume its push | POST /invoices/{invoiceId}/sync, /retry-push | write:billing |
| A customer's billing e-mail and payment method labels | GET /customers/{customerSlug}/billing | read:billing |
| Set up, remove a payment method | POST /customers/{customerSlug}/billing/payment-method-session, …/{sessionId}/complete; DELETE /customers/{customerSlug}/billing/payment-method | write:billing |
| Stripe's customer portal | POST /customers/{customerSlug}/billing/portal-session | write:billing |
Events: BILLING_PROVIDER_CONNECTED, BILLING_PROVIDER_DISCONNECTED, BILLING_PROVIDER_SYNC_FAILED, the invoice events above, and CUSTOMER_PAYMENT_METHOD_ATTACHED, _DETACHED, _EXPIRING — in the event catalog. Shapes: the API reference.
Where to go next
What Stripe receives, line by line: Invoices. The other way to collect: without a payment provider. Self-serve checkout, where customers save a card before they subscribe: Self-serve checkout.
Self-Serve Checkout
Selling from your own pages — the three public credentials (publishable key, checkout session, customer session), the anonymous checkout end to end from the Turnstile check to the completion, and what you see and receive once a visitor has bought.
Without a Payment Provider
Billing with no payment provider — Kaiten composes each invoice, records it as ready to bill, and hands it to your own accounting system through a pull queue — claim, book, acknowledge, then mark paid. Works on every installation, air-gapped ones included.