import { type PartialMessages } from "../i18n.js"; import { AddressElement, PaymentElement, TaxIdElement, useElements, useStripe } from "@stripe/react-stripe-js"; import { BillingAddressElement, CheckoutElementsProvider, PaymentElement as CheckoutPaymentElement, TaxIdElement as CheckoutTaxIdElement, useCheckoutElements } from "@stripe/react-stripe-js/checkout"; import { type Appearance, type StripeCheckoutTaxIdType, type StripeElementLocale } from "@stripe/stripe-js"; import * as React from "react"; /** * Start loading Stripe.js NOW, before there is a client secret to mount. * * Nothing about the SDK depends on the session, but the provider is what * normally triggers the download — so the ~400ms of script + iframes is spent * AFTER the server round trip that created the session, one after the other, * with the customer watching. Called on mount (an effect, or the module scope of * a client component) it overlaps with that round trip instead. * * Idempotent and cached, so calling it and then rendering the provider loads * Stripe once — pass the same `locale`/`taxIdBeta` the provider will use, or the * warm instance won't be the one it asks for. */ export declare function preloadStripe(publishableKey: string, opts?: { locale?: StripeElementLocale; taxIdBeta?: boolean; }): void; export type BillingCheckoutProviderProps = { /** Stripe publishable key (pk_…). Safe in the browser by design. */ publishableKey: string; /** Client secret of the PaymentIntent/SetupIntent the SERVER created. */ clientSecret: string; /** Stripe Elements appearance, so the form inherits the host app's theme. */ appearance?: Appearance; /** e.g. "it" — defaults to the browser's locale. */ locale?: StripeElementLocale; /** * Load the beta that the Tax ID Element needs. Set this on the provider AND * pass `collectTaxId` to BillingPaymentForm — the element cannot render unless * Stripe.js was instantiated with the beta, so the two go together. */ taxIdBeta?: boolean; children: React.ReactNode; }; /** Wraps Stripe's with the bits every consumer would otherwise repeat. */ export declare function BillingCheckoutProvider({ publishableKey, clientSecret, appearance, locale, taxIdBeta, children, }: BillingCheckoutProviderProps): React.JSX.Element; export type BillingPaymentFormProps = { /** * Collect a billing address. Required when the subscription was created with * automatic_tax: Stripe needs a location to compute VAT, and without it the * tax line stays 0 and the total silently disagrees with the summary above. */ collectAddress?: boolean; /** * Render Stripe's Tax ID Element — the "Business tax ID (Optional)" field with * its type selector. * * Requires `taxIdBeta` on the provider (it is a public-preview feature). Paired * with `collectAddress`, Stripe infers the tax ID type and whether to show the * field at all from the country, so an Italian customer is offered IT VAT * rather than a global list. * * NOTE: collecting a tax ID does not by itself change what is charged. Reverse * charge is applied only when the IDs reach a Stripe Tax calculation — with a * fixed tax rate the ID is recorded on the invoice and nothing more. */ collectTaxId?: boolean; /** Where Stripe returns the browser after an off-site step (3DS, bank redirect). */ returnUrl: string; /** * Overrides for the few words this form supplies itself. English by default — * this package ships English only. Stripe's own error messages already follow * the Elements locale, so these are only the no-message fallback. */ messages?: PartialMessages; /** * What the client secret refers to. `"setup"` saves a card for later without * charging it — a SetupIntent — which is what an "add a card" screen needs; * `"payment"` (the default) takes the money now. * * It has to be told: a PaymentIntent and a SetupIntent are confirmed by * different Stripe calls, and the form is given the secret by its provider * rather than holding it. */ intent?: "payment" | "setup"; /** Rendered as the submit button. Receives the live submitting state. */ children: (state: { submitting: boolean; }) => React.ReactNode; /** Called after the intent is confirmed without a redirect. */ /** * Prefill the billing address (and cardholder name) from what the org already * has on file — its billing profile. * * Worth doing because the alternative is not "empty", it is WRONG: with no * default the Address Element guesses a country from the browser, so an Italian * workspace was offered France. The customer then either corrects it or saves a * card whose billing country is not theirs, which is a tax question, not a * cosmetic one. */ defaultAddress?: { name?: string | null; line1?: string | null; line2?: string | null; city?: string | null; state?: string | null; postal_code?: string | null; country?: string | null; } | null; /** * Called after a successful confirm, WITH what was confirmed. * * The argument is what lets a caller act on the specific card that was just * saved — setting it as the default, copying its billing address onto the * customer. Without it the app has to guess by listing payment methods and * taking the newest, which is a race the moment two tabs are open. */ onSuccess?: (result?: { setupIntent?: { id: string; payment_method?: unknown; } | null; paymentIntent?: { id: string; payment_method?: unknown; } | null; }) => void; /** Called with a human-readable message when Stripe declines or validation fails. */ onError?: (message: string) => void; className?: string; }; /** * The card/payment-method form: Stripe's PaymentElement plus confirmation. * * The button is passed in as a render prop rather than styled here, so the host * app's own Button keeps the design system consistent — this component owns the * Stripe wiring, not the look. * * `redirect: "if_required"` keeps the common card case on-page and only leaves * for methods that genuinely need it (3DS challenge, bank redirects). */ export declare function BillingPaymentForm({ collectAddress, collectTaxId, defaultAddress, returnUrl, intent, messages, children, onSuccess, onError, className, }: BillingPaymentFormProps): React.JSX.Element; export { AddressElement, PaymentElement, TaxIdElement, useElements, useStripe }; /** Amounts as Stripe computed them for the current session. */ export type CheckoutTotals = { /** Pre-tax, in the smallest currency unit. */ subtotalCents: number; /** Tax added on top. Zero until Stripe can compute it — see `taxPending`. */ taxCents: number; /** What will actually be charged. */ totalCents: number; /** e.g. "eur". */ currency: string; /** Stripe's own formatted strings, already localised. */ formatted: { subtotal: string; tax: string; total: string; }; /** * Stripe has no location yet, so `taxCents` is not the real tax — the customer * hasn't entered an address. Show "calculated at payment" rather than "€0,00". */ taxPending: boolean; /** Tax rate Stripe applied, when it computed one (e.g. 22). */ taxPercent?: number; }; export type BillingCheckoutSessionProviderProps = { /** Stripe publishable key (pk_…). Safe in the browser by design. */ publishableKey: string; /** * `clientSecret` from `createCheckoutSession`, or a PROMISE of one. Cannot be * changed once set — to price a new basket, create a new session and remount * (key on the basket). * * Pass the promise. Stripe awaits it internally, which means the provider * mounts at hydration and starts loading Stripe.js and rendering its skeleton * while the session is still being created, instead of the app holding a * spinner until it resolves and only then beginning. Two waits in sequence * become one in parallel, and it is the difference between a payment form * appearing in ~1.5s and in ~0.5s. */ clientSecret: string | Promise; /** Stripe Elements appearance, so the form inherits the host app's theme. */ appearance?: Appearance; /** * e.g. "it" — defaults to the browser's locale. In elements mode this is a * Stripe.js load-time option, not an Elements one, so it is baked into the * cached Stripe instance rather than passed per-provider. */ locale?: StripeElementLocale; /** * Load the beta the Tax ID Element needs (public preview). Defaults to ON, * because `createCheckoutSession` enables tax ID collection by default; pass * false to drop the beta along with the element. */ taxIdBeta?: boolean; /** * Two-letter country to start the billing address on, e.g. "IT". * * Set this for a single-market product. Without it Stripe geolocates by IP, * which is right often enough to be dangerous: when it guesses wrong the * customer is quoted their WRONG country's tax — and if it lands on a country * you have no registration in, Stripe computes ZERO tax and the total silently * drops to the pre-tax amount unless they notice the dropdown. * * It reaches the SESSION, not just the field, so tax is computed from the first * render (`tax.status: "ready"`) instead of after the address is filled in. The * customer can still change it. */ defaultCountry?: string; /** * Prefill the whole billing address, not just the country — the org's own * billing profile, typically. * * Same mechanism and the same reason as `defaultCountry`: it reaches the * SESSION, so tax is computed from the first render. A customer paying for a * team they already have an invoicing address for should not retype it, and * should not be nudged into paying with a different one by an empty form. * Ignored unless `line1` and `country` are both present. */ defaultAddress?: { name?: string | null; line1?: string | null; line2?: string | null; city?: string | null; state?: string | null; postal_code?: string | null; country?: string | null; } | null; children: React.ReactNode; }; /** Wraps Stripe's with the publishable-key singleton * and options shape every consumer would otherwise repeat. */ export declare function BillingCheckoutSessionProvider({ publishableKey, clientSecret, appearance, locale, taxIdBeta, defaultCountry, defaultAddress, children, }: BillingCheckoutSessionProviderProps): React.JSX.Element; /** * Live totals for the surrounding session, or null while it loads. * * Call from inside BillingCheckoutSessionProvider — including from the order * summary, which is the point: the summary and the payment form then read the * same numbers from the same place. */ export declare function useCheckoutTotals(): CheckoutTotals | null; export type BillingCheckoutSessionFormProps = { /** * Collect a full billing address. ON by default: Stripe Tax needs a location, * and `createCheckoutSession` sets `billing_address_collection: "required"`, * which this element is what satisfies. */ collectAddress?: boolean; /** * Render Stripe's Tax ID Element — the "Business tax ID (optional)" field. * * ON by default, matching `taxIdCollection` on the server. Unlike the * fixed-rate path, the ID reaches a real Stripe Tax calculation here, so a valid * EU VAT number from another member state actually applies reverse charge. * * The element is a public preview and is SKIPPED unless the account has been * granted it (see `taxIdAvailable` below) — leaving this on costs nothing if it * hasn't been. Measured on an account without access: the checkout SDK exposes * `createPaymentElement` but `createTaxIdElement` is undefined, WITH the beta * flag as well as without it. So a missing tax ID field is account access to * request from Stripe, never a client-side option to find. */ collectTaxId?: boolean; /** * Offer Link — Stripe's one-click wallet, which appears INSIDE the card form as * a "save my info for faster checkout" block asking for email, phone and name. * * OFF by default, to match `createCheckoutSession`'s card-only default. It is a * separate switch because Link is not a payment method type: restricting * `payment_method_types` to ["card"] does not remove it, so a checkout that * asked for card-only was still showing a Link signup and collecting a phone * number nobody asked for. */ link?: boolean; /** Rendered as the submit button. Receives the live submitting state. */ /** * Charge a payment method the customer already has, by id. * * The Payment Element and the address field are then NOT rendered — there is * nothing to type — and the caller shows whatever it likes instead (it listed the * card, so it has the brand and the last four). `validateElements` is skipped for * the same reason: validating a form that does not exist would fail a purchase * with nothing wrong with it. */ paymentMethod?: string; children: (state: { submitting: boolean; }) => React.ReactNode; /** Called once the session is confirmed without a redirect. */ onSuccess?: () => void; /** Called with a human-readable message when Stripe declines or validation fails. */ onError?: (message: string) => void; className?: string; }; /** * The payment form for a Checkout Session: address, payment method, tax ID. * * The button is passed in as a render prop rather than styled here, so the host * app's own Button keeps the design system consistent — this component owns the * Stripe wiring, not the look. * * `redirect: "if_required"` keeps the common card case on-page and only leaves for * methods that genuinely need it (3DS challenge, bank redirects); the session's * `return_url` is where Stripe comes back to. */ export declare function BillingCheckoutSessionForm({ collectAddress, collectTaxId, link, paymentMethod, children, onSuccess, onError, className, }: BillingCheckoutSessionFormProps): React.JSX.Element; export { BillingAddressElement, CheckoutElementsProvider, CheckoutPaymentElement, CheckoutTaxIdElement, useCheckoutElements, }; export type BillingTaxIdState = { /** Stripe's own element is rendering the field — render nothing yourself. */ handledByStripe: boolean; /** The type for the current billing country, or null when there is none to * collect (also null before a country is chosen). Hide the field when null. */ type: StripeCheckoutTaxIdType | null; /** Two-letter country the type was derived from, for labelling. */ country: string | null; value: string; setValue: (value: string) => void; /** * Push the value to the session — call on blur, not per keystroke. Resolves to * an error message (Stripe's own, e.g. an invalid VAT format) or null on * success. An empty value clears the tax ID rather than erroring. */ apply: (businessName?: string) => Promise; /** Last error from `apply`, cleared as soon as the value changes. */ error: string | null; applying: boolean; /** Already accepted by Stripe for this session. */ applied: boolean; }; /** Collect a business tax ID without Stripe's preview element. Call inside * BillingCheckoutSessionProvider. */ export declare function useBillingTaxId(): BillingTaxIdState; /** Where a session is in its lifecycle. Shared with the consumer's own render, which is why * it is exported: a form that submits while `syncing` submits a stale total. */ export type CheckoutStatus = "idle" | "syncing" | "ready" | "error"; export type CheckoutSessionIntent = { clientSecret: string; publishableKey: string; sessionId: string; }; export type CheckoutSessionSync = ({ ok: true; } & CheckoutSessionIntent) | { ok: false; error: string; }; /** A session plus the basket it was created for. */ type InitialSession = ({ basket: string; } & CheckoutSessionIntent) | null; export declare function useCheckoutSession(opts: { /** Identity of the current basket (seats + interval). A new value re-syncs; * the app computes it, so the hook needs no pricing. */ basket: string; /** Open a Checkout Session for the current basket. */ create: () => Promise; /** * A session the server already created, with the basket it was created for — * so the first basket costs no client round trip at all. * * Pass the PROMISE (unawaited, straight from a server component) to get both: * the page shell renders immediately and the session is already in flight, * rather than starting after hydration. It is awaited in an effect, never * `use`d, so it suspends nothing. */ initial?: InitialSession | PromiseLike; /** Publishable key to warm Stripe.js with before the first session arrives. * Optional — without it the preload starts as soon as a session does. */ publishableKey?: string; /** Debounce before re-syncing a CHANGED basket, ms. Default 500. The first * sync never waits. */ debounceMs?: number; /** Skip syncing while true — e.g. an empty basket below the minimum. */ paused?: boolean; }): { /** The session to mount the provider against — key the provider on its * `clientSecret`. Stays mounted while a newer basket syncs, so the form * doesn't disappear and remount on every stepper click. */ session: CheckoutSessionIntent | null; /** The mounted session no longer prices the current basket: it is about to be * replaced, so disable submit until it is. */ stale: boolean; status: CheckoutStatus; error: string | null; }; export type CheckoutTaxInput = { country: string; state?: string | null; taxNumber?: string | null; }; export type CheckoutRetaxResult = { ok: true; percent: number; reverseCharge: boolean; } | { ok: false; error: string; }; export type CheckoutTaxState = { /** Applied rate, e.g. 22. Null until the first calculation lands. */ percent: number | null; /** The buyer accounts for the VAT — cross-border EU B2B with a valid number. */ reverseCharge: boolean; /** A recalculation is in flight; the totals on screen are the previous ones. */ pending: boolean; error: string | null; }; /** * Re-tax the session whenever the billing country, state or tax number changes. * * Call inside `BillingCheckoutSessionProvider`. The tax number comes from the * session by default, so rendering `useBillingTaxId`'s field anywhere in the * tree is enough to make reverse charge work. With no number the buyer is * treated as a consumer — the safe default, since it charges tax rather than * exempting. */ export declare function useCheckoutTax(opts: { retax: (input: CheckoutTaxInput) => Promise; /** Override the tax number. Omit to use the one on the session. */ taxNumber?: string | null; /** Wait before recalculating, ms. Default 400. */ debounceMs?: number; }): CheckoutTaxState; export { INVOICE_EMAIL_MAX, COMPANY_NAME_MAX } from "./limits.js"; export { BillingAddressForm, type AddressValue } from "./address.js"; export { INVOICE_LOCALES, type InvoiceLocale } from "./locales.js"; export { TAX_ID_TYPES, splitTaxIdType, type TaxIdType } from "./tax-id-types.js"; export { SessionProvider, useSession, ANONYMOUS_SESSION, type BillingSession, type SessionUser, } from "./session.js"; //# sourceMappingURL=index.d.ts.map