> This page is for For Developers.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://whop.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://whop.ferndocs.com/_mcp/server.

# Accept payments

> Accept one-time and recurring payments across 195 countries with 100+ payment methods

#### [✨ Build this with AI](https://whop.com/network/examples?filter=checkouts)

Copy and paste our templates using your favorite editor.

Accept one-time and recurring payments using checkout links or an embedded checkout component. Whop supports **100+ payment methods** across **195 countries**, and the right ones appear automatically based on the buyer's location. [See all payment methods](/payments-and-billing/local-payment-methods).

> **Tip**
>
> Building an iOS app? The [Whop iOS Checkout SDK](/developer/guides/ios/overview) handles checkout natively with lower fees (2.7% + \$0.30 vs Apple's 15–30%). The SDK uses a scoped `iap:read` API key that's [safe to embed in your app](/developer/guides/ios/installation#step-3-create-an-api-key).

## Choose your integration

|                          | Checkout link              | Embedded checkout          |
| ------------------------ | -------------------------- | -------------------------- |
| **Effort**               | Low                        | Medium                     |
| **Customization**        | Limited                    | Full control               |
| **Best for**             | Sharing links, quick setup | Custom UX, dynamic pricing |
| **Server code required** | No (Dashboard) / Yes (API) | Yes                        |

## Option 1: Create a checkout link

Checkout links are the simplest way to accept payments. Create a plan to get a shareable checkout URL.

#### Dashboard

1. Go to your [Dashboard](https://whop.com/dashboard/links/checkout) > **Checkout links**
2. Click **+ Create checkout link**
3. Select a product and configure your pricing (free, one-time, or recurring)
4. Click **Create checkout link**

The generated link can be shared directly with customers or embedded on your website.

#### API

Create a plan via the API to get a `purchase_url`:

**`TypeScript`**

```typescript TypeScript
import Whop from "@whop/sdk";

const client = new Whop({
  apiKey: "Company API Key",
});

// company_id: find in Dashboard > Settings
// access_pass_id: find in Dashboard > Products
const plan = await client.plans.create({
  company_id: "biz_xxxxxxxxxxxxx",
  access_pass_id: "pass_xxxxxxxxxxxxx",
  initial_price: 10.0,
  plan_type: "one_time",
});

console.log(plan.purchase_url);
```

**`Python`**

```python Python
from whop_sdk import Whop

client = Whop(
    api_key="Company API Key",
)

# company_id: find in Dashboard > Settings
# access_pass_id: find in Dashboard > Products
plan = client.plans.create(
    company_id="biz_xxxxxxxxxxxxx",
    access_pass_id="pass_xxxxxxxxxxxxx",
    initial_price=10.0,
    plan_type="one_time",
)

print(plan.purchase_url)
```

Redirect customers to the `purchase_url` to complete payment on a Whop-hosted checkout page.

## Option 2: Embedded checkout

For a custom checkout experience, use the embedded checkout component with a checkout configuration.

### Step 1: Create a checkout configuration

Create a checkout configuration on your server with an inline plan:

**`TypeScript`**

```typescript TypeScript
import Whop from "@whop/sdk";

const client = new Whop({
  apiKey: "Company API Key",
});

const checkoutConfig = await client.checkoutConfigurations.create({
  company_id: "biz_xxxxxxxxxxxxx",
  plan: {
    initial_price: 10.0,
    plan_type: "one_time",
  },
  metadata: {
    order_id: "order_12345",
  },
});

console.log(checkoutConfig.id); // ch_xxxxxxxxxxxxx (session ID)
console.log(checkoutConfig.plan?.id); // plan_xxxxxxxxxxxxx (plan ID)
```

**`Python`**

```python Python
from whop_sdk import Whop

client = Whop(
    api_key="Company API Key",
)

checkout_config = client.checkout_configurations.create(
    company_id="biz_xxxxxxxxxxxxx",
    plan={
        "initial_price": 10.0,
        "plan_type": "one_time",
    },
    metadata={
        "order_id": "order_12345",
    },
)

print(checkout_config.id)  # ch_xxxxxxxxxxxxx (session ID)
print(checkout_config.plan.id)  # plan_xxxxxxxxxxxxx (plan ID)
```

In this example:

* `company_id` is your company ID
* `plan.initial_price` is the payment amount
* `plan.plan_type` is either `one_time` or `renewal` for subscriptions
* `metadata` stores custom data for your reference

### Step 2: Render the checkout

#### React

```tsx
import { WhopCheckoutEmbed } from "@whop/checkout/react";

export function Checkout({ sessionId }: { sessionId: string }) {
  return (
    <WhopCheckoutEmbed
      sessionId={sessionId}
      returnUrl="https://yoursite.com/checkout/complete"
      onComplete={(paymentId) => {
        console.log("Payment complete:", paymentId);
      }}
    />
  );
}
```

Pass the `checkoutConfig.id` from step 1 as the `sessionId` prop.

#### HTML / JavaScript

Add the loader script to your page:

```html
<script async defer src="https://js.whop.com/static/checkout/loader.js"></script>
```

Then add a checkout element with your plan ID and the `checkoutConfig.id` from step 1:

```html
<div
  data-whop-checkout-plan-id="plan_XXXXXXXXX"
  data-whop-checkout-session="ch_XXXXXXXXX"
  data-whop-checkout-return-url="https://yoursite.com/checkout/complete"
></div>
```

The `returnUrl` is required to handle redirects from external payment providers. When redirected, check the `status` query parameter:

* **success**: The payment succeeded. Use the receipt information to render a success page.
* **error**: The payment failed or was canceled. Remount the checkout so your customer can try again.

### Step 3: Customize the checkout

You can customize the checkout appearance and behavior:

| Prop (React)               | Attribute (HTML)                        | Description                                  |
| -------------------------- | --------------------------------------- | -------------------------------------------- |
| `theme`                    | `data-whop-checkout-theme`              | `"light"`, `"dark"`, or `"system"` (default) |
| `hidePrice`                | `data-whop-checkout-hide-price`         | Hide the price display                       |
| `themeOptions.accentColor` | `data-whop-checkout-theme-accent-color` | Custom accent color                          |

For the full list of customization options, see the [Embedded checkout reference](/payments/checkout-embed).

## Handle payment webhooks

Listen for webhooks to fulfill orders on your server. This example uses Next.js on Vercel, but the `whopsdk.webhooks.unwrap` pattern works with any framework:

```typescript
import { waitUntil } from "@vercel/functions";
import type { Payment } from "@whop/sdk/resources.js";
import type { NextRequest } from "next/server";
import { whopsdk } from "../../../lib/whop-sdk"; // your Whop SDK instance

export async function POST(request: NextRequest): Promise<Response> {
  const requestBodyText = await request.text();
  const headers = Object.fromEntries(request.headers);
  const webhookData = whopsdk.webhooks.unwrap(requestBodyText, { headers });

  if (webhookData.type === "payment.succeeded") {
    waitUntil(handlePaymentSucceeded(webhookData.data));
  }

  return new Response("OK", { status: 200 });
}

async function handlePaymentSucceeded(payment: Payment) {
  console.log("Payment succeeded:", payment.id);
}
```

Other useful webhook events: `membership.went_valid` (access granted), `payment.failed` (payment failed).

## See it work end-to-end

The fastest way to confirm everything's wired up is to run a test charge in sandbox and watch the result flow back to your server and dashboard.

### 1. Trigger a test charge

Switch your SDK and checkout to the sandbox environment, then run a checkout with a test card. The [sandbox guide](/developer/guides/sandbox) covers the URL changes and lists test cards for every payment outcome (succeeded, failed, requires action).

### 2. Inspect the webhook payload

When the test charge settles, your webhook handler receives a `payment.succeeded` event shaped like this:

```json
{
  "id": "msg_xxxxxxxxxxxxx",
  "api_version": "v1",
  "type": "payment.succeeded",
  "timestamp": "2026-05-12T18:42:11.041Z",
  "company_id": "biz_xxxxxxxxxxxxx",
  "data": {
    "id": "pay_xxxxxxxxxxxxx",
    "status": "succeeded",
    "amount_after_fees": 9.71,
    "currency": "usd",
    "paid_at": "2026-05-12T18:42:10Z",
    "payment_method_type": "card",
    "card_brand": "visa",
    "card_last4": "4242",
    "member": { "id": "mem_xxxxxxxxxxxxx" },
    "metadata": {
      "order_id": "order_12345"
    }
  }
}
```

The `metadata.order_id` you attached when creating the checkout configuration flows straight through to the webhook handler. That's the hook you use to map the payment back to your own order record.

### 3. Verify in the sandbox dashboard

Open the sandbox dashboard at `https://sandbox.whop.com/dashboard/<your_company_id>/payments`. The test charge appears in this payments list with its status and amount; use your server logs to confirm the `metadata.order_id` you set maps back to the same order.

![Sandbox dashboard Payments page showing the payments list](/_fern-img/c48eacd28e7627e0e228c3cfa14b406a18dcec812c0ff7bf254cdc2d545c4ba1.webp)

If you see both the dashboard row and the `payment.succeeded` event on your server, the integration is live. Switch your SDK and webhook URL to production when ready.

## Next steps

#### [Webhooks](/developer/guides/webhooks)

Handle payment events in real-time

#### [Save payment methods](/developer/guides/save-payment-methods)

Save and charge payment methods

#### [iOS payments](/developer/guides/ios/overview)

Accept payments in your iOS app with lower fees

#### [Payment methods](/payments-and-billing/local-payment-methods)

100+ payment methods across 195 countries