Start
Quick start with npx
One command sets Perly Pay up in your project: a checkout route, a webhook route that checks signatures, a Pay button and your test key.
npx perly-pay init reads your project, writes the code for its framework, and leaves you one press from a test payment. It sets up test mode only; you go live in the dashboard.
perly-pay package is on its way to npm. Until it is there, follow the Quick start: the same code, by hand.Run it
It finds the framework and the package manager, opens Dashboard → Developers and asks for a test key (sk_test_…). A live key is refused.
What it writes
| Framework | Found by | Files |
|---|---|---|
| Next.js (App Router) | next | lib/perly-pay.ts, app/api/perly-pay/checkout/route.ts, app/api/perly-pay/webhook/route.ts, components/PayButton.tsx, app/perly-pay/page.tsx |
| Express | express | lib/perly-pay.js, routes/perly-pay.js |
| Hono | hono | lib/perly-pay.ts, routes/perly-pay.ts |
| Laravel | artisan, laravel/framework | app/Http/Controllers/PerlyPayController.php, routes/perly-pay.php, config/perly-pay.php, resources/views/perly-pay/button.blade.php |
| Node.js | any other package.json | lib/perly-pay.js, perly-pay.js (node:http) |
TypeScript or JavaScript, ES modules or CommonJS, as your project is, and under src/ when your code lives there. npm, pnpm, yarn, bun and Composer.
- Checkout. The button posts to
/perly-pay/checkout(/api/perly-pay/checkoutin Next.js). It opens an invoice and sends the buyer to pay it. - Webhook.
/perly-pay/webhookchecksPerly-Signatureon the raw body. Oninvoice.fulfilledit callsgrantAccess(): the one TODO you fill in. - Plans. One-off sales and credit packs, in
PLANSat the top of the core module. The plan, its credits and your user id travel in the invoice's metadata and come back in the webhook. - Keys.
PERLY_PAY_SECRET_KEYandPERLY_PAY_WEBHOOK_SECRETin.env.local(Next.js) or.env, and that file in.gitignore.
--force does.Connect the webhook
Perly Pay has to reach the webhook URL. Add it in Dashboard → Developers → Webhooks for invoice.fulfilled and paste its whsec_… secret into PERLY_PAY_WEBHOOK_SECRET. Or let init do both:
npx perly-pay init --webhook-url https://your-app.example/perly-pay/webhookOn your laptop, a tunnel gives a public URL: cloudflared tunnel --url http://localhost:3000. More in Webhooks.
Pay once, in test mode
- 1
Start your app
Express and Node read
.envwithnode --env-file=.env, or with dotenv. - 2
Open /perly-pay
Press Pay with crypto. You land on the checkout.
- 3
Simulate payment
The invoice turns paid, then fulfilled. See Test mode.
- 4
Watch grantAccess run
Your webhook gets
invoice.fulfilled. Now write what it grants.
With an AI assistant
--yes asks nothing, so Claude Code, Cursor or Codex can run it for you. Give it the key and what you sell:
npx perly-pay init --yes --key sk_test_… --product "Logo review" --amount 4900--json prints the result for the assistant to read.
Options
| Flag | What it does |
|---|---|
--yes, -y | No questions. For AI assistants and scripts. |
--framework <name> | next, express, hono, laravel or node, when the guess is wrong. |
--key <sk_test_…> | Your test key. Default: PERLY_PAY_SECRET_KEY, or it asks. |
--webhook-url <url> | Registers the URL for invoice.fulfilled and saves its secret. |
--product <name>, --amount <cents>, --currency <code> | What you sell, in USD or EUR. --amount 4900 is $49.00. |
--install | Installs the SDK too. Otherwise the command is printed. |
--force | Replaces files and values that are already there. |
--plugin, --mcp | Prints how to add the Claude Code plugin and the perly-pay-mcp server. |
--dry-run | Says what would happen, writes nothing. |
--json | Prints the result as JSON. |
--api <origin>, --dir <path> | Another Perly Pay (a local one), another folder. |
Go live
- Connect your live wallet keys in Dashboard → Wallets.
- Make a live key (
sk_live_…) and a live webhook endpoint. - Set them where your app runs. The code stays as it is. SDKs has the rest of the API.