> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whop.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Payments

> Drives a payment collection surface against the Whop Payments API. Charge config is a plan id OR the inline currency/amount — a plan resolves client-side to the same shape, one path either way. Mount ONE of its faces — the payment element (the full method list), the fused card element, or the exploded card-fields unit — then `payments.createConfirmationToken({ billingDetails })` is the ONE confirm verb for all of them: it tokenizes the selection and mints a confirmation token your server confirms with a secret key; the payment's client_secret then drives any pending step via `handleNextAction`.

{/* whop-elements-docs-channel: upcoming */}

<Info>**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.</Info>

{/* whop-elements-since: unreleased */}*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:

<div data-whop-demo-shell style={{ position: "relative", minHeight: "480px", transition: "min-height 200ms ease" }}>
  <div data-whop-demo-skeleton style={{ position: "absolute", inset: "0", borderRadius: "12px", background: "rgba(140, 140, 140, 0.12)", pointerEvents: "none", transition: "opacity 200ms ease" }} />

  <iframe data-whop-demo src="https://js.whop.cloud/elements/amber/payments/_playground/index.html" title="Payments playground" height="480" allow="payment; publickey-credentials-get; clipboard-write" style={{ position: "relative", width: "100%", border: "0", borderRadius: "12px", background: "transparent", colorScheme: "normal" }} />
</div>

## Usage

<div data-whop-usage="payments/playground">
  <CodeGroup>
    ```tsx React theme={null}
    import { WhopElements, Payments } from "@whop/elements-react";
    import { loadWhop } from "@whop/elements";

    function Example() {
      return (
        <WhopElements elements={loadWhop()}>
          <Payments /* options */>
            {/* mount elements here */}
          </Payments>
        </WhopElements>
      );
    }
    ```

    ```html Vanilla theme={null}
    <script src="https://js.whop.cloud/elements/amber/elements.js" data-whop-elements></script>
    <script type="module">
      const payments = window.WhopElements().payments.create({ /* options */ });
    </script>
    ```
  </CodeGroup>
</div>

## Options

Pass these to `whop.payments.create({ … })` — or as props on `<Payments>` in React.

<ResponseField name="accountId" type="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).
</ResponseField>

<ResponseField name="plan" type="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.
</ResponseField>

<ResponseField name="returnUrl" type="string">
  Where the buyer lands after an off-site step (bank page, 3DS). Defaults to the mounting page.
</ResponseField>

<ResponseField name="setupFutureUsage" type="&#x22;off_session&#x22; | &#x22;on_session&#x22;">
  Attest the element should display save-consent and mint vault-capable tokens: `off_session` or `on_session`.
</ResponseField>

<ResponseField name="apiBaseUrl" type="string">
  Advanced: override the API origin (sandbox / staging testing).
</ResponseField>

<ResponseField name="currency" type="string">
  ISO currency the checkout charges in — filters the method matrix.
</ResponseField>

<ResponseField name="amount" type="number">
  Charge amount in minor units — methods whose bounds exclude it are not offered.
</ResponseField>

<ResponseField name="paymentMethodConfiguration" type="{ 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.
</ResponseField>

<ResponseField name="appearance" type="Appearance">
  Visual customization for this group's elements — overrides the global `WhopElements({ appearance })`; change it live with `update({ appearance })`.
</ResponseField>

<ResponseField name="locale" type="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.
</ResponseField>

<Note>The options are a union — provide **exactly one** of these shapes (plus the shared event callbacks below):</Note>

* `{ 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:

<CardGroup cols={2}>
  <Card title="AddressElement" href="/elements/upcoming/payments/address">
    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.
  </Card>
</CardGroup>

**Alternatives** — these are exclusive; mount one at a time (destroy it to mount another):

<CardGroup cols={2}>
  <Card title="PaymentElement" href="/elements/upcoming/payments/payment">
    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.
  </Card>

  <Card title="CardElement" href="/elements/upcoming/payments/card">
    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).
  </Card>

  <Card title="CardFields" href="/elements/upcoming/payments/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)*
  </Card>
</CardGroup>

## 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.
