Skip to main content
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.
In development — not yet part of a stable release.

Playground

Assemble the elements with sample data — drive the controls, add and arrange elements, and watch events fire live:

Usage

Options

Pass these to whop.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.
string
Advanced: override the API origin (sandbox / staging testing).
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"; apiBaseUrl?: string }
  • { 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"; apiBaseUrl?: string }
  • { accountId?: undefined; plan?: undefined; currency?: undefined; amount?: undefined; paymentMethodConfiguration?: undefined; returnUrl?: string; setupFutureUsage?: "off_session" | "on_session"; apiBaseUrl?: string }

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 of whop.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; apiBaseUrl?: string | undefined; returnUrl?: string | undefined; pollIntervalMs?: number | undefined; }) => Promise<{ status: string; redirected: boolean; lastPaymentError: { code?: string | undefined; decline_code?: string | null | undefined; message?: string | undefined; } | null; }>

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.
Alternatives — these are exclusive; mount one at a time (destroy it to mount another):

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(...). 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.
  • CompletePaymentElement (completePayment) — The hosted completion surface payments.handleNextAction presents 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.