Try it (its real) 👇
React setup
Step 1: Install the package
npm install @whop/checkout
import { WhopExpressCheckoutButton } from "@whop/checkout/react";
export default function PricingCard() {
return (
<WhopExpressCheckoutButton
planId="plan_XXXXXXXXX"
returnUrl="https://yoursite.com/checkout/complete"
/>
);
}
The returnUrl is required on the one click checkout button (unlike the regular embedded checkout, where it’s optional). Payments started here may complete inside the Whop Pay dialog via a redirect-based payment method (3DS, Sezzle, etc.) and the user needs somewhere to land that closes the loop on your page.
When the customer is redirected back, check the status query parameter:
- success: The payment succeeded.
- error: The payment failed or was canceled.
On success, the redirect also includes:
payment_id — the id of the payment. Use it to look up the payment on your backend or render a confirmation.
setup_intent_id — set instead of payment_id when checkout didn’t charge anything (e.g. saving a card for future use).
state_id — the checkout session id.
planId
Required (or checkoutConfigurationId) - The plan id you want to checkout. Provide either this or checkoutConfigurationId, not both.
checkoutConfigurationId
Required (or planId) - An API-created checkout configuration id. Use this to mount the button against a pre-configured checkout (e.g. with attached metadata, a pre-applied promo, or a multi-line cart) instead of creating a new session at mount time. Provide either this or planId, not both.
returnUrl
Required - The URL to return to after payment authorization flows.
methods
Optional - Which methods the button is allowed to render, in priority order. Defaults to ["apple-pay", "whop-pay"].
apple-pay — the native Apple Pay / Google Pay button (auto-resolved per browser).
whop-pay — a Whop Pay button that opens checkout in a dialog.
<WhopExpressCheckoutButton
methods={["apple-pay"]}
planId="plan_XXXXXXXXX"
returnUrl="https://yoursite.com/checkout/complete"
/>
If you only allow apple-pay and the browser doesn’t support it, nothing renders and the onExpressMethodResolved callback fires with { rendered: "none" }.
theme
Optional - The theme for the button and the dialog. Possible values are light, dark, or system.
themeOptions
Optional - Theme tuning for the Whop Pay dialog.
<WhopExpressCheckoutButton
planId="plan_XXXXXXXXX"
returnUrl="https://yoursite.com/checkout/complete"
theme="dark"
themeOptions={{ accentColor: "violet", highContrast: true }}
/>
prefill
Optional - Prefill the email or address inside the Whop Pay dialog. Same shape as the regular embedded checkout — see prefill options.
<WhopExpressCheckoutButton
planId="plan_XXXXXXXXX"
returnUrl="https://yoursite.com/checkout/complete"
prefill={{ email: "example@domain.com" }}
/>
affiliateCode
Optional - Attribute the sale to an affiliate.
Optional - Apply a promo code on the session.
adaptivePricing
Optional - Set to true to present prices in the buyer’s local currency where supported. Defaults to false.
skipRedirect
Optional - Set to true to skip the final redirect and keep your page loaded. Automatically true when onComplete is provided.
onComplete
Optional - Fires when the checkout completes successfully.
Setting this implies skipRedirect = true.
<WhopExpressCheckoutButton
planId="plan_XXXXXXXXX"
returnUrl="https://yoursite.com/checkout/complete"
onComplete={(planId, receiptId) => {
console.log("paid", planId, receiptId);
}}
/>
onExpressMethodResolved
Optional - Fires once the button decides which method it will render.
<WhopExpressCheckoutButton
planId="plan_XXXXXXXXX"
returnUrl="https://yoursite.com/checkout/complete"
onExpressMethodResolved={({ rendered }) => {
// rendered: "apple-pay" | "google-pay" | "whop-pay" | "none"
if (rendered === "none") hidePricingCard();
}}
/>
rendered: "none" means the button won’t show anything — typically because the only requested method is apple-pay and the browser can’t render it. Use this to hide the surrounding slot so you don’t leave an empty space on the page.
What renders
The button picks one method per browser, in this order:
| Browser | Renders | Notes |
|---|
| Safari (macOS/iOS) with an Apple Pay card set up | Native Apple Pay button | Submits inline; no dialog |
| Chrome / Android with Google Pay | Native Google Pay button | Submits inline; no dialog |
| Anything else | Whop Pay button | Opens checkout in a dialog over your page |
If you only request apple-pay and the browser can’t render it, the button signals back to your page (via the express-method-resolved event) so you can hide the slot.
Other websites
Step 1: Add the script tag
<script
async
defer
src="https://js.whop.com/static/checkout/loader.js"
></script>
The one click checkout button is a custom element. Once the script loads it registers <whop-express-checkout-button>:
<whop-express-checkout-button
plan-id="plan_XXXXXXXXX"
return-url="https://yoursite.com/checkout/complete"
></whop-express-checkout-button>
| Attribute | Required | Description |
|---|
plan-id | yes (or checkout-configuration-id) | The plan to check out. |
checkout-configuration-id | yes (or plan-id) | An API-created checkout configuration to resume instead of creating a new session at mount. |
return-url | yes | Where to return after redirect-based authorization. |
methods | no | Comma-separated list — apple-pay, whop-pay. Defaults to both. |
theme | no | light, dark, or system. |
theme-accent-color | no | Frosted UI accent color name (e.g. violet). |
theme-high-contrast | no | "true" or "false". |
prefill-email | no | Prefill the buyer’s email. |
prefill-name | no | Prefill the buyer’s name. |
prefill-address-line1 | no | Prefill billing address line 1. |
prefill-address-line2 | no | Prefill billing address line 2. |
prefill-address-city | no | Prefill billing city. |
prefill-address-state | no | Prefill billing state. |
prefill-address-country | no | Prefill billing country code. |
prefill-address-postal-code | no | Prefill billing postal code. |
prefill-shipping-name | no | Prefill shipping name. |
prefill-shipping-address-line1 | no | Prefill shipping address line 1. |
prefill-shipping-address-line2 | no | Prefill shipping address line 2. |
prefill-shipping-address-city | no | Prefill shipping city. |
prefill-shipping-address-state | no | Prefill shipping state. |
prefill-shipping-address-country | no | Prefill shipping country code. |
prefill-shipping-address-postal-code | no | Prefill shipping postal code. |
affiliate-code | no | Affiliate attribution. |
promo-code | no | Apply a promo code on the session. |
state-id | no | Resume a previously-created session. |
adaptive-pricing | no | "true" to present buyer-local currency. |
skip-redirect | no | "true" to keep your page loaded after payment. |
setup-future-usage | no | Set to off_session to save the payment method for future charges. |
environment | no | Override the API environment (rarely needed). |
wuid | no | Stable per-buyer identifier for analytics. |
Step 4: Listening for events
The custom element dispatches DOM events you can listen for:
<whop-express-checkout-button
id="my-button"
plan-id="plan_XXXXXXXXX"
return-url="https://yoursite.com/checkout/complete"
></whop-express-checkout-button>
<script>
const button = document.getElementById("my-button");
button.addEventListener("express-method-resolved", (event) => {
// event.detail.rendered: "apple-pay" | "google-pay" | "whop-pay" | "none"
if (event.detail.rendered === "none") button.style.display = "none";
});
button.addEventListener("complete", (event) => {
// event.detail: { planId, receiptOrSetupIntentId }
console.log("paid", event.detail);
});
button.addEventListener("overlay-open", () => {
// the Whop Pay dialog opened
});
button.addEventListener("overlay-close", () => {
// the Whop Pay dialog closed (cancelled or completed)
});
</script>
| Event | Fires when |
|---|
ready | The button has mounted and measured itself. |
express-method-resolved | The button decided which method it will render. event.detail.rendered is "apple-pay", "google-pay", "whop-pay", or "none". |
overlay-open | The Whop Pay dialog opened. |
overlay-close | The Whop Pay dialog closed (for any reason). |
complete | Payment completed successfully. event.detail has planId and receiptOrSetupIntentId. |