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.
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 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; 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.
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(...). 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 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.