Accept a payment in four steps
Create an intent from your server, send the customer to the hosted
checkout (or embed it), and confirm the result with a signed webhook. All amounts are
integer micro-USD — 1000000 = $1.00.
1 · Create a payment intent
From your backend, authenticate with your secret key
(n4npay_sk_…) and POST the amount plus where to send the customer afterwards.
The response includes a checkout_url and a one-time client_secret.
curl https://pay.n4n.io/v1/payment_intents \
-H "Authorization: Bearer n4npay_sk_3f9a2b7c8d1e4f5a6b0c9d8e7f2a1b3c" \
-H "Content-Type: application/json" \
-d '{
"amount_microusd": 4990000,
"description": "Pro plan",
"success_url": "https://shop.example/thanks",
"cancel_url": "https://shop.example/cart"
}'
# → 201 Created
# {
# "id": "9b2f6d4e-1c3a-4e8f-9a21-7c5d2e8b4f10",
# "status": "requires_payment",
# "amount_microusd": 4990000,
# "client_secret": "cs_…",
# "checkout_url": "https://pay.n4n.io/checkout/9b2f6d4e-1c3a-4e8f-9a21-7c5d2e8b4f10?cs=cs_…"
# }
2 · Send the customer to checkout
The simplest integration: redirect the browser to the
checkout_url from the response. We host the payment UI (PayPal + self-custody
crypto); after payment we redirect to your success_url.
// after creating the intent server-side: res.redirect(intent.checkout_url);
3 · Or embed the checkout inline
Drop embed.js onto your page and mount the
checkout in a container. The widget appends an iframe pointing at the checkout_url
and calls onSuccess when the payment settles. Your page's origin must be listed in
your merchant allowed origins.
<script src="https://pay.n4n.io/embed.js"></script>
<div id="pay-here"></div>
<script>
N4nPay.embed({
checkoutUrl: intent.checkout_url, // from your server
container: "#pay-here",
onSuccess: function (p) { window.location = "/thanks?pi=" + p.intentId; }
});
</script>
embed({checkoutUrl, container, onSuccess})
returns {destroy()} to remove the iframe and its listener. Completion is delivered
over postMessage as {type:"n4npay", event:"succeeded", intent_id} from
the pay.n4n.io origin — the widget validates the origin before invoking your callback.
4 · Verify webhooks
Payments are confirmed asynchronously. Each delivery
carries an N4nPay-Signature header — verify it with your endpoint's signing secret
(whsec_…) before trusting the body. The signed payload is
<t> + "." + rawBody; compare in constant time and reject stale timestamps.
N4nPay-Signature: t=1700000000,v1=hex(hmac_sha256(secret, t + "." + body))
func verify(secret, header, rawBody, now) bool {
// header: "t=<unix>,v1=<hex>"
t, v1 := parse(header) // split on "," then "="
if abs(now - t) > tolerance { return false } // reject replays (e.g. 5m)
expected := hex(hmac_sha256(secret, t + "." + rawBody))
return constant_time_equal(v1, expected)
}
Event catalogue
Every event body carries type,
created_at (RFC3339), and a data.payment_intent object (the same shape as the API).