import { type PlanCatalog } from "./plans.js"; import type { BillingInterval } from "./plans.js"; import type { BillingConfig } from "./types.js"; export type Quantities = Record; export type CheckoutSessionResult = { sessionId: string; customerId: string; /** Elements mode: pass to BillingCheckoutSessionProvider. Null under * `uiMode: "hosted"`, which has no elements to mount. */ clientSecret: string | null; /** Hosted mode: Stripe's own payment page. Null in elements mode — a session * the browser confirms itself has no page to send anyone to. */ url: string | null; }; /** * Create a seat Checkout Session whose tax Stripe calculates itself. * * Nothing is charged and no subscription exists yet: the session is an open * basket the browser confirms. Read the result with `checkoutSessionOutcome` * afterwards — never trust the client's word that payment happened. * * Quantities of 0 are dropped rather than sent (Stripe rejects a zero-quantity * line, and "no Premium seats" must mean "no Premium line"). */ export declare function createCheckoutSession(opts: { plans: PlanCatalog; plan: string; interval: BillingInterval; seats: Quantities; /** Where Stripe returns the browser after an off-site step (3DS, bank app), and * the `success_url` under `uiMode: "hosted"`. */ returnUrl: string; /** * Who draws the payment form, and therefore what comes back. * * `"elements"` (default) mounts our own layout and returns a **client secret**. * `"hosted"` is Stripe's page and returns a **URL** — which is the only form a * caller with no browser can use, so it is what an agent-facing tool wants: an * MCP caller handed a client secret cannot do anything with it, and the first * purchase is exactly where that caller arrives. Hand-rolling the hosted session * to get a URL is how a consumer ends up with a second checkout that inherits * neither the deployment's tax nor its payment-method configuration. */ uiMode?: "elements" | "hosted"; /** Hosted mode only: where Stripe sends a customer who backs out. Defaults to * `returnUrl`. */ cancelUrl?: string; /** Reuse an existing customer, else one is created from `email`. */ customerId?: string; email?: string; currency?: string; /** * Stripe Tax. OFF unless you ask for it. * * Off is the default because the library ships its own calculation * (`taxRatesFor` — `eu-vat-rates-data` + VIES, no per-transaction fee) and because * "on" is the more expensive mistake: Stripe Tax with no active registration * computes ZERO tax rather than erroring, so the total silently drops to the * pre-tax amount and the seller owes the difference. Opting in is a statement * that the registrations exist; inheriting it from an unset field never was. * * Mutually exclusive with `taxRates` — Stripe rejects both, and it would tax * the same line twice. */ automaticTax?: boolean; /** * Apply these Stripe TaxRate ids instead of Stripe Tax (see src/tax.ts). * * The rate has to be chosen before the customer types an address, so pass the * one for your own country and re-apply with `updateCheckoutSessionTaxRates` * when the billing country turns out to be different. That handoff is the * work Stripe Tax would otherwise do for 0.5%. */ taxRates?: string[]; /** Collect a business tax ID (VAT number). On by default. */ taxIdCollection?: boolean; /** * REQUIRE a tax id, not just offer the field. Off by default. * * Two Stripe constraints make this blunter than it looks, and both matter before you * reach for it: * * 1. It is **all-or-nothing across countries**. Stripe's only value is * `"if_supported"` — required wherever Stripe supports a tax id type for the * customer's country — so there is NO "require it for UK addresses only". Turning * it on also forces a French or Italian consumer to produce a VAT number, which * blocks every legitimate B2C sale. * 2. It is **unavailable in elements mode** (`ui_mode: "custom"`). Stripe rejects the * parameter there, so a deployment that owns its own form has to enforce it itself. * * So "the address is in a country where I am not registered, therefore the VAT number * becomes mandatory" is not expressible in ONE hosted session: the address is typed * inside Stripe's form, after the session was created with this flag already fixed. * The two shapes that work are to ask for the country in your own step first and then * create the session accordingly, or to register where you owe and charge the rate — * which needs no tax id from the customer at all. */ taxIdRequired?: boolean; /** * Which payment methods the form offers. Defaults to CARD ONLY — no Link, * Klarna, wallets — because that's what most subscription checkouts want, and * Checkout otherwise shows every method enabled on the account. Pass a list to * add methods (e.g. ["card", "klarna"]) or "automatic" to defer to the * account's dashboard settings. */ paymentMethods?: string[] | "automatic"; /** * A payment-method configuration id (see `ensurePaymentMethodConfig`). Replaces * `payment_method_types` (Stripe rejects both) and is the only way to remove * Link, whose inline signup ignores the method list. */ paymentMethodConfiguration?: string; metadata?: Record; /** * Hand back the session already open for this exact basket instead of opening * another one. * * `checkout.sessions.create` costs 400-500ms at Stripe, and it is the last * thing standing between a customer arriving and a payment form existing. The * same customer asking for the same basket twice — a reload, a back-button, a * router prefetch followed by the click it was prefetching for — does not need * two sessions, and creating them anyway leaves a trail of abandoned ones. * * Keyed on EVERYTHING that shapes the session (customer, plan, interval, * seats, currency, return url, tax rates, metadata, …), so a reused session is * one Stripe would have created identically. Requires `customerId`: without * one every call mints a new customer, and there is nothing stable to key on. * * Off by default — reusing anything payment-related should be a decision, not * a surprise. Dropped as soon as the session is paid (`checkoutSessionOutcome`) * or expired, and after `ttlMs` (default 30 min, well inside Stripe's ~24h * session lifetime). */ reuse?: boolean | { ttlMs?: number; }; /** * The deployment's `BillingConfig` — where tax comes from when this call names * none, and where `paymentMethods.link` is read. * * The body has always read it; the TYPE did not have it, so no TypeScript caller * could pass it and every seat session fell back to "no declaration": mode * `local` with the origin guessed from the Stripe account's country. Which is * right by luck when the account and the establishment are the same country, and * silently wrong for `mode: "none"`, `mode: "stripe"`, `registrations` and `oss` * — the whole point of declaring tax once. */ config?: BillingConfig; }): Promise; /** * Stop handing out a session. * * Called automatically once `checkoutSessionOutcome` sees it paid and by * `expireCheckoutSession`; call it directly if payment is confirmed some other * way. Handing a completed session to the next visitor would mount a form that * cannot be confirmed. */ export declare function forgetCheckoutSession(sessionId: string): void; /** * What actually happened, read from Stripe. * * The server-side proof of payment: a caller could post any session id, so the * subscription is required to be live as well as the session complete. */ export declare function checkoutSessionOutcome(sessionId: string): Promise<{ paid: boolean; subscriptionId: string | null; customerId: string | null; /** * The metadata the session was created with. * * This is what makes fulfilment possible from a WEBHOOK and not just from the * browser: whatever provisioning needs — a workspace name, the user it belongs * to — has to travel with the session, because the tab that knew it may be * gone by the time payment completes. */ metadata: Record; /** "subscription" | "payment" | "setup". */ mode: string | null; }>; /** Close an abandoned session. Idempotent: one already completed or expired is * left as-is. Optional — Stripe expires open sessions by itself (~24h) and an * unconfirmed session has created nothing. */ export declare function expireCheckoutSession(sessionId: string): Promise; //# sourceMappingURL=checkout.d.ts.map