<!-- APIScout AI-readable guide source -->
<!-- Canonical: https://apiscout.dev/guides/whop-checkout-webhook-guide -->
<!-- Raw Markdown: https://apiscout.dev/guides/whop-checkout-webhook-guide/raw.md -->
<!-- Source path: content/guides/whop-checkout-webhook-guide.mdx -->

---
title: "Whop checkout: from a payment link to a verified order"
description: "Follow a documented Whop checkout flow: create a hosted payment link, guide the buyer, and verify signed payment webhooks before fulfilling an order."
date: "2026-11-10"
author: "APIScout Team"
tags: ["whop", "payments", "checkout", "webhooks", "tutorial"]
featured_api: "whop"
sponsored: true
sponsor_domains: ["whop.com"]
hide_sponsored_badge: true
---

In collaboration with Whop.

Create a checkout configuration on your server, send the buyer to its `purchase_url`, and fulfill the order only after a verified `payment.succeeded` webhook. This documented route suits an application that wants Whop to host checkout while keeping order fulfillment in its own backend. A return page helps the buyer understand progress; it does not establish that payment succeeded.

APIScout retains editorial control. This guide follows official documentation. The examples are documented, unexecuted adaptations. We have not created a checkout, processed a payment, or tested webhook delivery. Example identifiers and order references are illustrative.

| Integration decision | Use here | Application responsibility |
| --- | --- | --- |
| Where the buyer pays | Hosted checkout link | Send the buyer to the returned URL |
| What establishes payment | Signed server webhook | Match it to the intended order |
| What a return page does | Show progress | Keep access pending until backend confirmation |
| What happens on redelivery | Repeat event may arrive | Avoid repeating fulfillment |

## Prepare the account and order

Start with a Whop account and a server-held Account API key. Whop's <a href="https://docs.whop.com/developer/guides/auth-scoping" rel="sponsored nofollow noopener noreferrer">authentication guide</a> recommends narrowing permissions to the operations your application needs. Keep that key out of browser bundles and source control. Store a separate webhook signing secret for incoming events.

For this example, choose an existing variant and store its ID on your server. Whop's current terminology calls this a variant, although its identifier still starts with `plan_`. Before opening checkout, create a local pending order and associate it with your application's customer, intended variant and expected payment details. That record gives the webhook handler something to reconcile against.

API versioning matters here. The <a href="https://docs.whop.com/developer/api/versioning" rel="sponsored nofollow noopener noreferrer">version changelog</a> records the checkout rename from `plan_id` to `variant_id`, and from inline `plan` to `variant`. Older examples can therefore use different names legitimately. The following request pins an explicit API version; keep its body and version together.

## Create one hosted checkout

The <a href="https://docs.whop.com/api-reference/checkout-configurations/create-a-checkout-configuration" rel="sponsored nofollow noopener noreferrer">checkout-configuration reference</a> documents `POST /checkout_configurations`. It accepts an existing `variant_id` or an inline `variant`, which are mutually exclusive. Its response includes `purchase_url`; configuration metadata is copied to payments and memberships.

This server-side JavaScript adaptation uses an existing variant. It assumes you have already persisted the illustrative order and generated an idempotency key for this specific checkout-creation operation. We did not execute it.

```javascript
const token = process.env.WHOP_API_KEY;
const accountId = process.env.WHOP_ACCOUNT_ID;
const variantId = process.env.WHOP_VARIANT_ID;
const operationKey = process.env.CHECKOUT_OPERATION_KEY;
if (!token || !accountId || !variantId || !operationKey) {
  throw new Error("Missing server checkout configuration");
}

const response = await fetch(
  "https://api.whop.com/api/v1/checkout_configurations",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
      "Api-Version-Date": "2026-11-07",
      "Idempotency-Key": operationKey,
    },
    body: JSON.stringify({
      account_id: accountId,
      variant_id: variantId,
      metadata: { order_id: "demo_order_001" },
    }),
  },
);
if (!response.ok) throw new Error(`Whop HTTP ${response.status}`);
const checkout = await response.json();
if (typeof checkout.purchase_url !== "string") {
  throw new Error("Checkout URL missing");
}
console.log(checkout.purchase_url);
```

Choose the variant and order reference from trusted server state, rather than accepting a browser-supplied price or arbitrary order ID. Whop's <a href="https://docs.whop.com/developer/api/idempotency" rel="sponsored nofollow noopener noreferrer">idempotency guide</a> says to reuse the same key when retrying the same operation and use a fresh key for a distinct operation. This guards checkout creation; it does not replace deduplication in your webhook handler.

The example targets production. Whop's <a href="https://docs.whop.com/developer/quickstart" rel="sponsored nofollow noopener noreferrer">quickstart</a> warns that production checkout links take real money. Reading this example establishes neither a test environment nor permission to charge anyone.

To exercise the same request without moving money, send it to the sandbox API base, `https://sandbox-api.whop.com/api/v1`. The sandbox has its own dashboard at <a href="https://sandbox.whop.com" rel="sponsored nofollow noopener noreferrer">sandbox.whop.com</a> and its own API keys. Whop says the two environments share no accounts, keys, products or webhooks, so the account and variant IDs must also come from the sandbox. Its <a href="https://docs.whop.com/developer/guides/sandbox#test-cards" rel="sponsored nofollow noopener noreferrer">test card list</a> gives `4242 4242 4242 4242` for a successful payment.

## Follow the buyer without trusting the browser

Send the buyer to `purchase_url` through your application's checkout action. When configuring a return destination, keep its job limited to showing progress and offering a retry path. Read the application's order status from your backend before presenting access as available.

Whop's <a href="https://docs.whop.com/developer/guides/embed-checkout" rel="sponsored nofollow noopener noreferrer">checkout integration guide</a> explains why navigation and frontend completion callbacks are unsuitable fulfillment triggers: the buyer may close the page, redirects can fail, and URL parameters can be edited. Its specific callback and return-parameter examples describe the embedded Checkout element; they are not assumptions about every hosted-link return flow.

For a delayed outcome, show that confirmation is pending. A positive-looking URL must never unlock a download or customer account by itself.

## Verify the webhook before fulfilling

Register an HTTPS endpoint for `payment.succeeded` and store its signing secret as `WHOP_WEBHOOK_SECRET`. Whop's <a href="https://docs.whop.com/developer/guides/webhooks" rel="sponsored nofollow noopener noreferrer">webhook guide</a> documents Standard Webhooks signatures and an SDK helper that verifies the raw body before returning the parsed event.

Create a new v1 webhook using <a href="https://docs.whop.com/api-reference/webhooks/create-webhook" rel="sponsored nofollow noopener noreferrer">Create Webhook</a>: send `POST /api/v1/webhooks` with your HTTPS `url`, `events: ["payment.succeeded"]`, and `api_version_date: "2026-11-07"`, matching the checkout request's version pin. Do not send an `api_version` request field. Check that the created webhook reports `api_version: "v1"` and the intended `api_version_date`. The checkout request's REST header does not configure this webhook pin. <a href="https://docs.whop.com/developer/guides/webhooks#events-and-versions" rel="sponsored nofollow noopener noreferrer">Webhook versioning</a> independently controls the envelope and payment data; keep that contract aligned with the account and variant reconciliation below.

This TypeScript function illustrates signature verification. It is not a complete request handler, durable queue or fulfillment implementation, and it has not been executed.

```typescript
import { unwrapWebhook } from "@whop/sdk/helpers";

export async function verifyWhopEvent(request: Request) {
  const secret = process.env.WHOP_WEBHOOK_SECRET;
  if (!secret) throw new Error("Missing webhook signing secret");
  return unwrapWebhook(await request.text(), {
    headers: Object.fromEntries(request.headers),
    key: secret,
  });
}
```

Reject verification failures. For a verified payment event, reconcile the payment with your pending order, expected account, variant and payment details. Do not grant access solely because metadata contains a familiar order ID. This reconciliation is our implementation recommendation; a valid signature establishes the event's authenticity, not that it belongs to the order you meant to fulfill.

Persist accepted work durably before acknowledging it. Whop documents at-least-once delivery, a repeated `webhook-id` on retries, and a response requirement of less than five seconds. Use a unique event record and an idempotent order transition so duplicate deliveries cannot issue another entitlement. Queue email or other slow work after durable acceptance. Our [webhook failure guide](/guides/how-to-handle-webhook-failures-retries-2026) provides broader implementation context.

## Choose the next integration step

Keep the hosted link when your immediate requirement is one checkout and backend-controlled fulfillment. Evaluate the Checkout element when you need the checkout embedded, or Payment Elements when you need to own more of the form; Whop documents these as separate integration paths.

Testing needs a separate decision. The <a href="https://docs.whop.com/developer/guides/sandbox" rel="sponsored nofollow noopener noreferrer">sandbox page</a> provides API sandbox configuration and test cards. It also documents Whop Elements in the sandbox: set `environment` to `"sandbox"` when you create the Elements instance. We have not run an embedded test checkout, so confirm that path in the sandbox before you rely on it.

This documentation-led guide establishes a design, not an observed payment result. Before release, validate your chosen flow, permission set, signature failures and duplicate handling in an approved environment. For broader provider selection, use our [payment API evaluation guide](/guides/how-to-choose-payment-api) and [payments directory](/apis/payments). Start implementation with <a href="https://docs.whop.com/developer/quickstart" rel="sponsored nofollow noopener noreferrer">Whop's official quickstart</a>.

<!-- Source traceability: WH-S01, WH-S02, WH-S03, WH-S04, WH-S05, WH-S07, WH-S08, WH-S09, WH-S10, WH-S11 -->

<!-- Claim traceability: WH-C01, WH-C02, WH-C03, WH-C04, WH-C05, WH-C06, WH-C07, WH-C08, WH-C09, WH-C10, WH-C11, WH-C12, WH-C13, WH-C14, WH-C15, WH-C16, WH-C17, WH-C18, WH-C19, WH-C20, WH-C21, WH-C22, WH-C24, WH-C25, WH-C26, WH-C27, WH-C28, WH-C29, WH-C30, WH-C31, WH-C32 -->
