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

# Payments

> Beginner-friendly guide to checkout links, webhooks, and credits purchases

Think of the payment system as two simple parts: a **checkout link** that sends your user to pay, and a **webhook** that tells your app "hey, the payment went through!"

That's really it. Let's break it down.

## What payments do in your app

You'll use payments for two things:

* **Selling plan tiers** -- like Small, Medium, or Large one-time purchases
* **Selling credit top-ups** -- like `credits-small` and `credits-large`

Here's the full flow, start to finish:

<Steps>
  <Step title="User clicks checkout">
    They hit a payment button in your app.
  </Step>

  <Step title="They pay on the provider's page">
    Stripe, LemonSqueezy, or Polar handles the actual payment.
  </Step>

  <Step title="Provider sends a webhook">
    Your app gets a secure HTTP request saying "payment succeeded."
  </Step>

  <Step title="Your app verifies & stores">
    The webhook is verified, the purchase is saved, and credits or plan state are updated.
  </Step>
</Steps>

## Pick your payment provider

<Tabs>
  <Tab title="Stripe">
    The most popular choice for developers. You get full control over products, prices, and metadata. If you've used Stripe before, you'll feel right at home.

    **Best for:** Teams who want the most familiar developer payment flow.

    [Set up Stripe ->](/payments/stripe)
  </Tab>

  <Tab title="LemonSqueezy">
    Super simple hosted product flow. Everything is product/variant oriented out of the box -- great if you want minimal setup.

    **Best for:** Solo builders who want a quick, hosted checkout experience.

    [Set up LemonSqueezy ->](/payments/lemonsqueezy)
  </Tab>

  <Tab title="Polar">
    Clean product-based checkout with solid metadata support. A modern alternative that's easy to work with.

    **Best for:** Builders who want a clean, product-based checkout flow.

    [Set up Polar ->](/payments/polar)
  </Tab>
</Tabs>

<Warning>
  Don't try to set up all three providers at once. Pick one, get it fully working, then expand later if you need to.
</Warning>

## Key concepts

### Checkout URL

This is the payment link your user clicks. You store these as env vars so your UI can use them without hardcoding.

```text theme={null}
https://buy.stripe.com/...
https://yourstore.lemonsqueezy.com/buy/...
https://polar.sh/checkout/...
```

### Webhook

A webhook is a secure HTTP request your payment provider sends to your app after a successful payment. Each provider has its own endpoint:

* **Stripe:** `https://yourdomain.com/api/payments/stripe`
* **LemonSqueezy:** `https://yourdomain.com/api/payments/lemonsqueezy`
* **Polar:** `https://yourdomain.com/api/payments/polar`

### Purchase type

This is the internal label your app uses to know what was bought. The repo supports:

`plan-small` | `plan-medium` | `plan-large` | `credits-small` | `credits-large`

Your payment provider might call it a "product", "variant", or "price" -- but inside this app, it always maps to one of those purchase types.

## Before you start

Make sure you have these ready:

* Your app runs locally
* Better Auth and your database are set up
* You know your production domain
* You've picked which provider to use first

## Shared env vars

These env vars are used regardless of which provider you choose:

<CodeGroup>
  ```env Required theme={null}
  NEXT_PUBLIC_PAYMENT_PROVIDER=stripe
  NEXT_PUBLIC_DEFAULT_MARKETING_PURCHASE_TYPE=plan-medium
  NEXT_PUBLIC_CHECKOUT_URL_TEMPLATE=https://...
  NEXT_PUBLIC_CHECKOUT_URL_CREDITS_SMALL=https://...
  NEXT_PUBLIC_CHECKOUT_URL_CREDITS_LARGE=https://...
  ```

  ```env Optional theme={null}
  NEXT_PUBLIC_CHECKOUT_URL_PLAN_SMALL=https://...
  NEXT_PUBLIC_CHECKOUT_URL_PLAN_MEDIUM=https://...
  NEXT_PUBLIC_CHECKOUT_URL_PLAN_LARGE=https://...
  NEXT_PUBLIC_AFFILIATES_URL=https://...
  ```
</CodeGroup>

| Variable                                      | What it does                                 |
| --------------------------------------------- | -------------------------------------------- |
| `NEXT_PUBLIC_PAYMENT_PROVIDER`                | Which provider the UI should use             |
| `NEXT_PUBLIC_DEFAULT_MARKETING_PURCHASE_TYPE` | Which plan the main marketing CTA represents |
| `NEXT_PUBLIC_CHECKOUT_URL_TEMPLATE`           | Fallback main checkout URL                   |
| `NEXT_PUBLIC_CHECKOUT_URL_CREDITS_SMALL`      | Checkout link for the small credit pack      |
| `NEXT_PUBLIC_CHECKOUT_URL_CREDITS_LARGE`      | Checkout link for the large credit pack      |

<Tip>
  Setting the specific Small/Medium/Large plan URLs gives you cleaner pricing buttons on your marketing pages.
</Tip>

## How the repo handles payments

Here are the key files you should know about:

| File                                                    | Purpose                                                    |
| ------------------------------------------------------- | ---------------------------------------------------------- |
| `lib/payments/public-config.ts`                         | Shared checkout URLs, labels, prices, credit-pack metadata |
| `components/(ui-components)/payments/checkout-link.tsx` | Hosted checkout buttons in the UI                          |
| `app/api/payments/[provider]/route.ts`                  | Shared webhook entrypoint                                  |
| `lib/payments/providers/*`                              | Provider-specific verification logic                       |
| `lib/payments/processor.ts`                             | Shared "payment succeeded" business logic                  |

The UI and business logic are mostly shared. Only the webhook verification differs per provider.

<Info>
  Real checkout URLs should live in env vars. Shared purchase display metadata lives in `lib/payments/public-config.ts`. UI components should never hardcode raw checkout links.
</Info>

## Metadata vs product maps

There are two ways to tell your app what a payment means.

<Tabs>
  <Tab title="Option A: Metadata (recommended)">
    Store a value like `type=plan-medium` directly in the payment provider. This is the cleanest approach because the purchase explains itself -- no extra configuration needed.
  </Tab>

  <Tab title="Option B: Product map in env">
    Keep a JSON map in your env vars:

    ```env theme={null}
    STRIPE_PRODUCT_MAP='{"prod_123":"plan-medium"}'
    POLAR_PRODUCT_MAP='{"prod_123":"credits-small"}'
    LEMON_SQUEEZY_VARIANT_MAP='{"123456":"credits-large"}'
    ```

    This is useful if you want to change mappings without touching provider settings.
  </Tab>
</Tabs>

## Credits

If the purchase type is `credits-small` or `credits-large`, the repo adds credits automatically after the webhook succeeds.

If it's a plan like `plan-medium`, the repo stores the purchase and updates the user's profile state instead.

## Optional Meta Ads attribution

If you run Meta Ads, the repo can track `InitiateCheckout` and `Purchase` events. Only enable this if you actually need it:

```env theme={null}
NEXT_PUBLIC_ENABLE_META_ATTRIBUTION=true
NEXT_PUBLIC_META_PIXEL_ID=...
META_ACCESS_TOKEN=...
```

## Suggested setup order

<Steps>
  <Step title="Pick one provider">
    Start with Stripe, LemonSqueezy, or Polar -- just one.
  </Step>

  <Step title="Create a Medium plan product">
    Keep it simple. One product to start.
  </Step>

  <Step title="Create a Small credits product">
    This lets you test the credits flow too.
  </Step>

  <Step title="Add checkout URLs to .env.local">
    Paste your payment links into the right env vars.
  </Step>

  <Step title="Add the webhook endpoint">
    Configure it in your provider's dashboard.
  </Step>

  <Step title="Complete a test payment">
    Go through the full flow yourself.
  </Step>

  <Step title="Verify the purchase in your database">
    Check that a row appeared in `purchases`.
  </Step>

  <Step title="Confirm the user gets the right result">
    Credits added? Plan state updated? You're golden.
  </Step>
</Steps>

<AccordionGroup>
  <Accordion title="Using the wrong webhook URL">
    Double-check that the URL in your provider dashboard matches your actual domain, including the correct path (`/api/payments/stripe`, `/api/payments/lemonsqueezy`, or `/api/payments/polar`).
  </Accordion>

  <Accordion title="Forgot to set the provider-specific secret">
    Each provider needs its own webhook secret env var. Without it, webhook verification fails silently.
  </Accordion>

  <Accordion title="Checkout links work but nothing happens after payment">
    You probably created checkout links but never connected the webhook. The webhook is what tells your app the payment happened.
  </Accordion>

  <Accordion title="Product not mapping to a purchase type">
    Make sure your product is mapped -- either via metadata (`type=plan-medium`) or via the product/variant map in env.
  </Accordion>

  <Accordion title="Changed .env.local but nothing changed">
    You need to restart your dev server after changing env vars. Next.js doesn't hot-reload env changes.
  </Accordion>
</AccordionGroup>

## Provider guides

<CardGroup cols={3}>
  <Card title="Stripe" icon="credit-card" href="/payments/stripe">
    The most common developer payment flow.
  </Card>

  <Card title="LemonSqueezy" icon="lemon" href="/payments/lemonsqueezy">
    Simple hosted product + variant workflow.
  </Card>

  <Card title="Polar" icon="credit-card" href="/payments/polar">
    Clean product-based checkout with metadata support.
  </Card>
</CardGroup>

<CardGroup cols={2}>
  <Card title="Meta Ads" icon="bullhorn" href="/services/meta-ads">
    Optionally track checkout clicks and purchases for Meta Ads.
  </Card>

  <Card title="Credits And Billing" icon="wallet" href="/services/credits-billing">
    How credits work in the dashboard and across apps.
  </Card>
</CardGroup>
