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

# Create Checkout Session

> Generate payment links programmatically with custom parameters

<Accordion title="✨ Install with AI" icon="sparkles">
  Copy this prompt to your AI code editor (Cursor, Copilot, etc.) to create checkout integration:

  ```text theme={"system"}
  Add Waffo Pancake checkout to my project using the official TypeScript SDK.

  npm install @waffo/pancake-ts

  Create a WaffoPancake client with WAFFO_MERCHANT_ID and WAFFO_PRIVATE_KEY.
  Use client.checkout.createSession({ productId, currency: "USD" })
  to get a checkoutUrl, then redirect the user with res.redirect(checkoutUrl).

  For webhooks, use verifyWebhook(rawBody, signature) from the SDK — it has embedded public keys,
  no secret needed. Handle events: order.completed, subscription.activated, subscription.canceled.

  Read https://waffo.mintlify.app/llms-full.txt for full API reference.
  ```
</Accordion>

***

## What You'll Build

A checkout session is a dynamically generated payment page. Instead of sharing a static product link, you can:

* Pre-fill customer email
* Track orders with custom metadata
* Set custom success URLs
* Enable trial periods for subscriptions

***

## Prerequisites

* Waffo Pancake account
* API key (Dashboard → API & Development)
* At least one product created

***

## Basic Checkout Session

The simplest checkout session needs a product ID and currency.

### Using the TypeScript SDK (Recommended)

```bash theme={"system"}
npm install @waffo/pancake-ts
```

```typescript theme={"system"}
import { WaffoPancake } from "@waffo/pancake-ts";

const client = new WaffoPancake({
  merchantId: process.env.WAFFO_MERCHANT_ID!,
  privateKey: process.env.WAFFO_PRIVATE_KEY!,
});

const session = await client.checkout.createSession({
  productId: "prod_xxx",
  currency: "USD",
});

// Redirect the user to the checkout page
res.redirect(session.checkoutUrl);
```

### Using REST API Directly

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST https://api.waffo.ai/v1/actions/checkout/create-session \
    -H "Content-Type: application/json" \
    -H "X-Store-Slug: your-store-slug" \
    -H "X-Environment: test" \
    -d '{
      "productId": "your-product-uuid",
      "currency": "USD"
    }'
  ```

  ```python Python theme={"system"}
  import requests

  response = requests.post(
      'https://api.waffo.ai/v1/actions/checkout/create-session',
      headers={
          'Content-Type': 'application/json',
          'X-Store-Slug': 'your-store-slug',
          'X-Environment': 'test',
      },
      json={
          'productId': 'your-product-uuid',
          'currency': 'USD',
      }
  )

  checkout_url = response.json()['data']['checkoutUrl']
  # Redirect customer to checkout_url
  ```
</CodeGroup>

**Response:**

```json theme={"system"}
{
  "data": {
    "sessionId": "cs_xxx",
    "checkoutUrl": "https://checkout.waffo.ai/your-store/checkout/cs_xxx",
    "expiresAt": "2024-01-22T12:00:00Z"
  }
}
```

Redirect your customer to `checkoutUrl` to complete payment.

***

## Redirect Best Practices

<Warning>
  **Do not use `window.open()` to open the checkout page.** Safari and many mobile browsers block popup windows opened from asynchronous callbacks (e.g., after an API call). This causes checkout to silently fail for your customers.
</Warning>

**Recommended approaches:**

| Method               | When to Use               | Example                              |
| -------------------- | ------------------------- | ------------------------------------ |
| Server-side redirect | API route / server action | `res.redirect(checkoutUrl)`          |
| Client-side redirect | SPA / React               | `window.location.href = checkoutUrl` |
| Link element         | Static links              | `<a href={checkoutUrl}>`             |

```typescript theme={"system"}
// ✅ Recommended: server-side redirect
export async function POST(req: NextRequest) {
  const session = await client.checkout.createSession({ ... });
  return NextResponse.redirect(session.checkoutUrl);
}

// ✅ Also fine: client-side redirect
const { checkoutUrl } = await res.json();
window.location.href = checkoutUrl;

// ❌ Avoid: window.open — blocked by Safari and mobile browsers
window.open(checkoutUrl, "_blank"); // Will be blocked!
```

After payment, the customer is redirected back to your `successUrl`. Use the `{SESSION_ID}` placeholder to verify the payment on your success page.

***

## Pre-fill Customer Email

Skip the email input step by pre-filling it:

```typescript theme={"system"}
const session = await client.checkout.createSession({
  productId: "prod_xxx",
  currency: "USD",
  buyerEmail: "user@example.com",
});
```

**Use case:** User is already logged into your app, so you know their email.

***

## Track with Metadata

Link checkout sessions to your internal systems:

```typescript theme={"system"}
const session = await client.checkout.createSession({
  productId: "prod_xxx",
  currency: "USD",
  metadata: {
    user_id: "usr_abc123",
    campaign: "black_friday_2024",
    referrer: "twitter",
  },
  successUrl: "https://yoursite.com/success",
});
```

Metadata is stored on the checkout session and can be retrieved via the `checkoutSession` GraphQL query. Webhook passthrough for metadata is coming soon.

***

## Subscription with Trial

Enable a trial period for subscription products:

```typescript theme={"system"}
const session = await client.checkout.createSession({
  productId: "prod_subscription",
  currency: "USD",
  withTrial: true,
  successUrl: "https://yoursite.com/welcome",
});
```

<Note>
  Discount codes and per-seat quantities are not yet supported via the API. Manage discounts through the Dashboard.
</Note>

***

## Subscription with Billing Detail

Pre-fill the consumer's billing information for tax calculation:

```typescript theme={"system"}
const session = await client.checkout.createSession({
  productId: "prod_xxx",
  currency: "USD",
  billingDetail: {
    country: "US",
    isBusiness: false,
    state: "CA",
    postcode: "94105",
  },
});
```

***

## Dynamic Success URL

Pass data to your success page:

```typescript theme={"system"}
const session = await client.checkout.createSession({
  productId: "prod_xxx",
  currency: "USD",
  successUrl: "https://yoursite.com/success?session_id={SESSION_ID}",
});
```

`{SESSION_ID}` will be replaced with the actual session ID, so you can verify the payment on your success page.

***

## Complete Example: SaaS Upgrade Flow

Here's a real-world example for upgrading a user's subscription using the SDK:

```typescript theme={"system"}
// app/api/upgrade/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from "next/server";
import { WaffoPancake } from "@waffo/pancake-ts";

const client = new WaffoPancake({
  merchantId: process.env.WAFFO_MERCHANT_ID!,
  privateKey: process.env.WAFFO_PRIVATE_KEY!,
});

export async function POST(req: NextRequest) {
  const { userId, planId, email } = await req.json();

  const session = await client.checkout.createSession({
    productId: planId,
    currency: "USD",
    buyerEmail: email,
    metadata: { user_id: userId, action: "upgrade" },
    successUrl: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?upgraded=true`,
  });

  // Server-side redirect — works in all browsers
  return NextResponse.redirect(session.checkoutUrl);
}
```

If you need to handle checkout on the client side (e.g., SPA):

```typescript theme={"system"}
async function handleUpgrade(planId: string) {
  const res = await fetch("/api/upgrade", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ userId: currentUser.id, planId, email: currentUser.email }),
  });
  const { checkoutUrl } = await res.json();

  // Redirect in same tab — Safari-safe
  window.location.href = checkoutUrl;
}
```

***

## Parameters Reference

| Parameter    | Type    | Required | Description                                                                   |
| ------------ | ------- | -------- | ----------------------------------------------------------------------------- |
| `productId`  | string  | Yes      | Product UUID from Dashboard                                                   |
| `currency`   | string  | Yes      | ISO 4217 currency code (e.g., `"USD"`)                                        |
| `buyerEmail` | string  | No       | Pre-fill consumer email                                                       |
| `successUrl` | string  | No       | Custom success redirect URL                                                   |
| `metadata`   | object  | No       | Custom key-value data stored on the session (webhook passthrough coming soon) |
| `withTrial`  | boolean | No       | Enable trial period for subscription products                                 |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Handle Webhooks" icon="webhook" href="/guides/webhooks">
    Get notified when payments complete
  </Card>

  <Card title="Verify Payments" icon="shield-check" href="/api-reference/webhooks">
    Securely verify webhook signatures
  </Card>
</CardGroup>
