# Next.js App Router

Server action to mint the payment code and build the QR, route handler to
receive the webhook, client component to display it.

## Files

```
app/
├── actions/payment.ts               server action — mints the code, builds the QR
├── api/webhooks/gpmpay/route.ts     webhook receiver
└── checkout/page.tsx                the checkout page
```

Copy them into a Next.js 14+ project (App Router).

## Setup

```bash
npm install @gpmpay/sdk
```

```bash
# .env.local
GPMPAY_API_TOKEN=gpm_xxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyy
GPMPAY_BANK_ACCOUNT_ID=<uuid>
GPMPAY_WEBHOOK_SECRET=<from createHmacEndpoint>
```

> None of these carry a `NEXT_PUBLIC_` prefix — on purpose. Adding one would
> ship your API token to every visitor's browser.

Register the endpoint once (e.g. from a script or `node --eval`):

```ts
const { secret } = await client.webhookSettings.createHmacEndpoint({
  url: 'https://your-app.vercel.app/api/webhooks/gpmpay',
});
// store `secret` as GPMPAY_WEBHOOK_SECRET
```

For local development, point it at an ngrok tunnel.

## What to copy

- **The webhook route needs no body-parser configuration.** In the App Router,
  `await request.text()` already gives the exact raw bytes — that's what
  `createNextWebhookHandler` uses.
- **The server action returns only display-safe fields.** The `GpmPay` client
  never crosses into a client component.
- **A payment code derived from the cart id**, so a double-submitted form
  produces the *same* code instead of a second pending payment. GPM Pay has no
  order entity to deduplicate for you.
- **Matching with a regex over `payload.content`** — banks normalize the transfer
  content, so an exact-string comparison will eventually miss.
- **The amount comparison in the webhook handler.** Nothing checks it for you.
- **Idempotency on `payload.id`** in the webhook handler.
- On Vercel, keep the webhook handler on the Node.js runtime
  (`export const runtime = 'nodejs'`) — the SDK's signature verification uses
  `node:crypto`.
