# Examples

Runnable references for the two integration shapes.

There is only one payment model: **you mint the code, you build the QR, you
reconcile.** GPM Pay reports transactions; it does not hold order state. See
[the payments guide](../docs/en/02-payments.md) for the full picture.

| Example | Use when |
|---|---|
| [`self-reconcile/`](./self-reconcile/) | **Start here.** Plain Node + Express: checkout builds the QR, a webhook route matches transfers against your own database. |
| [`next-app-router/`](./next-app-router/) | Next.js 14+ App Router — server action + webhook route handler + checkout page. |

Each directory has its own README with run instructions and a "what to copy"
section calling out the parts that matter.

## Common to all of them

```bash
export GPMPAY_API_TOKEN=gpm_xxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyy
export GPMPAY_WEBHOOK_SECRET=<secret>      # from createHmacEndpoint()
export GPMPAY_BANK_ACCOUNT_ID=<uuid>       # next-app-router only
```

Scopes needed: `bank-accounts:read` and `webhooks:manage`.

Verify the token before anything else:

```bash
npx gpmpay ping
```

It probes each scope individually, so an under-scoped token is obvious
immediately rather than at the first webhook.

## Testing without real money

Point at the sandbox and simulate a transfer carrying your own code:

```ts
const client = new GpmPay({ apiToken, sandbox: true });
await client.simulator.createTransaction({
  bankAccountId,
  amount: 50_000,
  transferContent: 'ORD123',     // the code your checkout handed out
});
```

Or from the CLI:

```bash
npx gpmpay simulate tx --sandbox --account <uuid> --amount 50000 --content ORD123
```

`simulator` refuses to run against production unless passed
`{ allowOnProduction: true }` (`--allow-production` on the CLI), and webhooks
only fire for endpoints with `fireOnSimulated` enabled.

## Further reading

- [Full documentation](../docs/en/README.md) ([Tiếng Việt](../docs/vi/README.md))
- [Instructions for AI agents](../AGENTS.md)
