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

# Next.js Integration

> Ship paid plans in your Next.js app — checkout, webhooks, and tax handled as Merchant of Record

Wire a Next.js App Router app to Macropay end to end: create something to sell, send buyers to checkout, and react to paid orders — all in about five minutes. Because Macropay acts as your **Merchant of Record**, you never touch the parts that usually slow a launch down: we are the seller on the customer's statement, we calculate and remit **sales tax and VAT worldwide**, and card data stays inside our **PCI DSS Level 1 compliant** vault. You write app logic; we own the tax and compliance liability.

<Info>
  Build against the [Sandbox environment](https://sandbox.macropay.ai/start) first. It mirrors production exactly but moves no real money, so you can replay checkouts and webhooks freely.
</Info>

## What you'll need

* A [Macropay account](https://macropay.ai/signup) with an organization
* Next.js 14 or newer, using the App Router
* An [organization access token](/integrate/authentication) (OAT)

## Install the SDK

Add the SDK, the React checkout component, and the webhook verifier:

<CodeGroup>
  ```bash npm theme={null}
  npm install @macropayments/sdk @macropayments/checkout standardwebhooks
  ```

  ```bash pnpm theme={null}
  pnpm add @macropayments/sdk @macropayments/checkout standardwebhooks
  ```

  ```bash yarn theme={null}
  yarn add @macropayments/sdk @macropayments/checkout standardwebhooks
  ```
</CodeGroup>

Drop your credentials into `.env.local`:

```bash theme={null}
MACROPAY_ACCESS_TOKEN="macropay_oat_your_token_here"
MACROPAY_WEBHOOK_SECRET="whsec_your_secret_here"
```

<Steps>
  <Step title="Create a product">
    In Macropay, everything you sell is a **product** — one-time or recurring share the same API and data model, only the pricing differs. Spin one up in the [dashboard](https://app.macropay.ai) or straight from code. Here's a monthly subscription priced at \$24:

    ```ts theme={null}
    import { Macropay } from "@macropayments/sdk";

    const macropay = new Macropay({
      accessToken: process.env.MACROPAY_ACCESS_TOKEN!,
      server: "sandbox",
    });

    const product = await macropay.products.create({
      name: "Analytics Pro",
      prices: [
        {
          type: "recurring",
          recurringInterval: "month",
          priceAmount: 2400, // amounts are in cents
          priceCurrency: "usd",
        },
      ],
    });

    console.log("Product ID:", product.id);
    ```

    <Tip>
      Need usage-based, seat-based, free, or pay-what-you-want pricing instead? Swap the `prices` array — the rest of this guide is identical. See [pricing models](/features/products) for the full set.
    </Tip>

    Keep the `product.id`; the next step needs it.
  </Step>

  <Step title="Send buyers to checkout">
    A route handler is the cleanest way to mint a checkout on demand. It takes a product ID, opens a hosted checkout session, and redirects the buyer:

    ```ts theme={null}
    // src/app/checkout/route.ts
    import { NextRequest, NextResponse } from "next/server";
    import { Macropay } from "@macropayments/sdk";

    const macropay = new Macropay({
      accessToken: process.env.MACROPAY_ACCESS_TOKEN!,
      server: "sandbox",
    });

    export async function GET(req: NextRequest) {
      const productId = req.nextUrl.searchParams.get("productId");
      if (!productId) {
        return NextResponse.json({ error: "productId required" }, { status: 400 });
      }

      const checkout = await macropay.checkouts.create({
        productId,
        successUrl: "https://your-app.com/welcome?checkout_id={CHECKOUT_ID}",
      });

      return NextResponse.redirect(checkout.url);
    }
    ```

    Point your pricing page at the route and pass the product ID through:

    ```tsx theme={null}
    <a href="/checkout?productId=prod_xxx">Start Analytics Pro</a>
    ```

    The hosted page handles currency localization, theming, and tax collection automatically — VAT and sales tax appear at the right rate for the buyer's country with no extra work on your side.
  </Step>

  <Step title="Embed checkout inline (optional)">
    To keep buyers on your own page, render the checkout component instead of redirecting:

    ```tsx theme={null}
    // src/app/pricing/page.tsx
    "use client";

    import { MacropayCheckout } from "@macropayments/checkout";

    export default function PricingPage() {
      return (
        <MacropayCheckout
          productId="prod_xxx"
          successUrl="/welcome"
        />
      );
    }
    ```

    For theming, prefilled fields, and other options, see the [embedded checkout](/features/checkout/embed) reference.
  </Step>

  <Step title="Listen for webhooks">
    Webhooks are how your app learns a payment succeeded so it can grant access. Every Macropay webhook is signed using the [Standard Webhooks](https://www.standardwebhooks.com/) spec — verify the signature with the `standardwebhooks` library before trusting the payload:

    ```ts theme={null}
    // src/app/api/webhooks/macropay/route.ts
    import { NextRequest, NextResponse } from "next/server";
    import { Webhook } from "standardwebhooks";

    const wh = new Webhook(process.env.MACROPAY_WEBHOOK_SECRET!);

    export async function POST(req: NextRequest) {
      const body = await req.text();
      const headers = {
        "webhook-id": req.headers.get("webhook-id")!,
        "webhook-timestamp": req.headers.get("webhook-timestamp")!,
        "webhook-signature": req.headers.get("webhook-signature")!,
      };

      let payload: any;
      try {
        payload = wh.verify(body, headers);
      } catch {
        return NextResponse.json({ error: "invalid signature" }, { status: 401 });
      }

      switch (payload.type) {
        case "order.paid":
          // Grant access, update your database, send a receipt
          console.log("Order paid:", payload.data.id);
          break;
        case "subscription.created":
          console.log("New subscription:", payload.data.id);
          break;
        case "subscription.canceled":
          // Revoke access at period end
          console.log("Subscription canceled:", payload.data.id);
          break;
      }

      return NextResponse.json({ received: true });
    }
    ```

    <Warning>
      Register the endpoint in the dashboard under **Settings** → **Webhooks**, then read the body as raw text (as above) — parsing JSON first will break signature verification. For local development, tunnel your dev server with [ngrok](https://ngrok.com): `ngrok http 3000`.
    </Warning>

    The full catalog of event types lives in the [webhook events](/integrate/webhooks/events) reference.
  </Step>

  <Step title="Test in the sandbox">
    1. Open your checkout page in the browser.
    2. Pay with the test card `4242 4242 4242 4242`, any future expiry, any CVC.
    3. Confirm you land on your `successUrl`.
    4. Watch the `order.paid` event arrive in your terminal.
  </Step>
</Steps>

## Going to production

Three changes flip you from test to live:

| Sandbox                          | Production                                                        |
| -------------------------------- | ----------------------------------------------------------------- |
| `macropay_oat_...` sandbox token | production OAT from the live dashboard                            |
| `server: "sandbox"`              | `server: "production"` (or omit the field)                        |
| Sandbox webhook endpoint         | Production endpoint URL registered in **Settings** → **Webhooks** |

<Check>
  Once live, every order automatically carries the correct tax, settles into your Macropay balance, and pays out via ACH or SEPA at no fee. Disputes and chargebacks are handled for you as part of the Merchant of Record service.
</Check>

## Where to go next

<CardGroup cols={2}>
  <Card title="All webhook events" icon="bell" href="/integrate/webhooks/events">
    Every event type your app can react to
  </Card>

  <Card title="Customer portal" icon="user" href="/guides/customer-portal">
    A self-serve page for managing subscriptions
  </Card>

  <Card title="Meter AI & agent usage" icon="microchip" href="/guides/ai-billing">
    Bill by tokens, activity, or outcome — and track agent ROI
  </Card>

  <Card title="Embedded checkout" icon="window-maximize" href="/features/checkout/embed">
    Theming, prefills, and advanced options
  </Card>
</CardGroup>
