Upcoming — generated from the latest merged element source; documents unreleased development. Use the channel picker at the top of the sidebar for the docs of a published release.
Playground
Assemble the elements with sample data — drive the controls, add and arrange elements, and watch events fire live:Usage
Options
Pass these towhop.payments.create({ … }) — or as props on <Payments> in React.
string
OPTIONLESS — the statics rail (
WhopElements.payments.handleNextAction) boots the namespace handle with no options; no collection face can be created on such a handle (they refuse loudly — pass a plan or currency+amount to collect).string
An existing plan id (
plan_…) — resolved client-side into the inline shape (currency, amount, method configuration); a plan is a preset for the same one config. Pass a plan OR the inline currency/amount, never both.string
Where the buyer lands after an off-site step (bank page, 3DS). Defaults to the mounting page.
"off_session" | "on_session"
Attest the element should display save-consent and mint vault-capable tokens:
off_session or on_session.WalletAvailability
Presentation-only wallet facts for deterministic previews. Live checkouts leave this unset and use the host probe.
string
ISO currency the checkout charges in — filters the method matrix.
number
Charge amount in minor units — methods whose bounds exclude it are not offered.
{ enabled?: string[] | undefined; disabled?: string[] | undefined; include_platform_defaults?: boolean | undefined; }
Seller method filtering in the API’s own shape (
enabled, disabled, include_platform_defaults): disabled types are removed, enabled re-adds against the account’s offered set, and with include_platform_defaults: false only enabled types remain. A plan carries its own.Appearance
Visual customization for this group’s elements — overrides the global
WhopElements({ appearance }); change it live with update({ appearance }).WhopElementsLocale
Locale for this group’s element UI text — one of the app’s built locales, overriding the global config; any other value falls back to the default locale.
The options are a union — provide exactly one of these shapes (plus the shared event callbacks below):
{ accountId: string; plan: string; returnUrl?: string; setupFutureUsage?: "off_session" | "on_session"; previewWalletAvailability?: WalletAvailability }{ accountId: string; currency: string; amount: number; paymentMethodConfiguration?: { enabled?: string[] | undefined; disabled?: string[] | undefined; include_platform_defaults?: boolean | undefined; }; returnUrl?: string; setupFutureUsage?: "off_session" | "on_session"; previewWalletAvailability?: WalletAvailability }{ accountId?: undefined; plan?: undefined; currency?: undefined; amount?: undefined; paymentMethodConfiguration?: undefined; returnUrl?: string; setupFutureUsage?: "off_session" | "on_session"; previewWalletAvailability?: WalletAvailability }
Events
Pass a callback in the create options / React props.onConfirmationToken
A confirmation token was minted (also returned by createConfirmationToken()). Confirm it server-side with your secret key.
Signature: ((payload: { id: string; type: string; }) => void)
onLoadingChange
Fired when the grouped loading state changes — true while any mounted element is still loading.
Signature: ((loading: boolean) => void)
Methods
Call these on the Payments handle — the return ofwhop.payments.create({ … }) (vanilla) or usePayments() (React).
createConfirmationToken
Mint a confirmation token from whatever the buyer selected — the ONE confirm verb, for every face, called from YOUR pay button on the payments handle. A card selection tokenizes inside the mounted face (pass the billing details your checkout collected — email required; card also requires name and address with line1 + country). Returns { confirmationToken, type }; confirm it server-side with your secret key, then call WhopElements.payments.handleNextAction(...). Runs on your page (host-side), not in the controller iframe.
Signature: (input: { billingDetails?: { email?: string | undefined; name?: string | undefined; address?: { country?: string | undefined; line1?: string | undefined; line2?: string | undefined; city?: string | undefined; state?: string | undefined; postal_code?: string | undefined; } | undefined; } | undefined; }) => Promise<{ confirmationToken: string; type: string; }>
update
Update the handle live: any of its options (appearance and locale included), propagated to every mounted element. React consumers never call it — updating the namespace component’s props does the same.
Signature: (options: Partial<PaymentsOptions>) => void
Static methods
Available before anything mounts — call them on the namespace accessor:whop.payments.<method>(…) (vanilla, where whop = WhopElements(…)) or useWhop().payments.<method>(…) (React). Useful when no element is on the page, e.g. handling a post-redirect next action.
handleNextAction
Drive a confirmed payment’s pending step to a resting state with NO element mounted — the universal completion surface. The clientSecret alone identifies the payment — its tag is embedded in the secret, so there is no separate payment id to pass (or mismatch). A frameable step (next_action.render includes inline — 3DS challenges, most wallet/redirect rails) runs in a dismissible dialog on YOUR page and the promise resolves in place with the payment’s fresh status (redirected: false; a dismissed dialog resolves with the still-pending status — re-offer at will). A full-page-only step redirects THIS tab (redirected: true) and the hosted return landing brings the buyer back to your return_url — or parks them on a hosted receipt when you never set one. Pass returnUrl to set or override that destination right here: it’s PATCHed onto the payment under the client_secret before the step runs (the innermost layer of the layered return_url; only a full-page step navigates to it — the dialog resolves in place, and a failed PATCH rejects before any step runs). No pending step resolves immediately. Called as WhopElements.payments.handleNextAction(...).
Signature: (input: { clientSecret: string; returnUrl?: string | undefined; pollIntervalMs?: number | undefined; }) => Promise<{ status: string; redirected: boolean; lastPaymentError: { code?: string | null | undefined; decline_code?: string | null | undefined; message?: string | null | undefined; } | null; }>
Returned objects
Live objects returned by the methods above — hold the reference and call it on your page; these are capabilities, not serializable data.WalletAvailability
Host-probed wallet facts (payment-request.ts canMakePayment reduced to serializable data): matrix single-type reads ∩ device capability ∩ (Google) origin clearance.
applePay
Signature: boolean
googlePay
Signature: boolean
Elements
The elements this group mounts — each has its own page:AddressElement
Collects a billing or shipping address with per-country formats — the field set, order, required flags, and postal-code validation adapt to the selected country — plus address autocomplete on the street field. Drive your own forms off
onChange, or call validate() / getValues() on the handle.EmailElement
Collects the buyer’s email — the one email truth for the checkout: while this element is mounted its value rides
payments.createConfirmationToken automatically (explicit billingDetails.email input still wins), exactly like the address element owns the billing address. It also recognizes returning buyers (Link-style): once a complete email matches a Whop account, a “Welcome back” sign-in affordance appears; choosing it pops a verification-code dialog, and a verified sign-in unlocks the buyer’s saved payment methods in the payment element. Signing in is always optional — a guest checkout proceeds on the email alone.PaymentElement
The drop-in payment method picker: shows the payment methods this seller accepts for the charge — filtered live by currency, amount, and seller configuration — and collects what the chosen method needs: card fields, billing address, method-specific inputs, and any required disclosures. Drive your pay button off the
change event, call payments.createConfirmationToken() on click, confirm the token server-side with your secret key, then pass any pending step to payments.handleNextAction(...). The addressChange event reports the billing address it collects as the buyer fills it in, so your checkout can quote tax, rate shipping, or update anything else that depends on the address. Two arrangements via layout: a stacked list (default) or a horizontal tile row.CardElement
The drop-in card block: number, expiration, and security code pre-arranged as one field group. Reached through
payments.create("card"); drive your pay button off onChange, then confirm with payments.createConfirmationToken(). Card numbers never touch your page or ours. Two arrangements via layout: ‘stacked’ (default) or ‘compact’ (one row).CardFields
The exploded card unit: three individually mountable, PCI-isolated fields (number, expiration, security code) under one completeness surface — for layouts the drop-in card element can’t express. Reached through
payments.create("cardFields"): place each field yourself, drive your pay button off onChange, confirm with payments.createConfirmationToken(). Card numbers never touch your page or ours. (sub-controller — 3 elements)Flow surfaces
These internal elements open automatically during flows (for example a confirmation or verification step). They are not part of the consumer API — you never mount, configure, or subscribe to them directly.- AddressSuggestionsElement (
addressSuggestions) — The address autocomplete overlay — suggestion rows plus “Enter address manually”, anchored beside the street field. - BillingAddressSuggestionsElement (
billingAddressSuggestions) — The billing address autocomplete overlay — suggestion rows plus “Enter address manually”, anchored beside the billing street field. - VerificationCodeElement (
verificationCode) — The buyer sign-in code dialog — popped by the payments controller when a recognized buyer chooses to sign in (payment-api.md §6). Collects the one-time code, verifies it against the pending session with the session’s own client_secret, offers a resend behind the session’s resend window, and surfaces every verification failure honestly (wrong code, expired code, rate limit, an account that must finish sign-in from its emailed link). On success the session token flows to the controller internally and the dialog closes. - CompletePaymentElement (
completePayment) — The hosted completion surfacepayments.handleNextActionpresents as a detached modal: it drives a pending payment step — a framed PSP flow, bank-transfer instructions, a confirmation wait — to a resting state, polling the payment status under its scoped client_secret, and closes itself when the payment rests. Never mounted directly.

