n4n.pay
Sign in
Integration

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 · create intent
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.

server · redirect
// 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.

html · embed snippet
<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.

header · N4nPay-Signature
N4nPay-Signature: t=1700000000,v1=hex(hmac_sha256(secret, t + "." + body))
pseudo-code · verify
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).

payment_intent.succeeded Payment captured (PayPal) or confirmed on-chain — fulfil the order.
payment_intent.expired The intent's TTL elapsed with no settled payment.
payment_intent.canceled You canceled the intent via the API before it settled.