Link a payment to a customer account
For subscriptions and memberships: confirmation codes, the signed Continue link, POST /api/v1/payments/claim and the SDK claimPayment helper.
Use this when a payment must land on one specific signed-in user, even if they paid from a phone or a different browser. The customer gets a confirmation code by email and your backend claims it once.
Step 1: Turn on "Requires account link"
Tick Requires account link on the payment link or embed (or send
"requiresCustomerLink": trueto the API). The checkout then asks for the customer's email before showing the payment.Step 2: The customer pays and receives a code
After the payment is final, the receipt email contains an eight-character confirmation code (no 0, O, 1 or I) and, when the checkout was opened from your website, a Continue button back to it:
https://<your site>/?nervixpay_code=<code>&nervixpay_sig=<signature>.Step 3: Claim the code from your backend
Call
POST /api/v1/payments/claimwith a key that haspayments:read. Send thesignaturetoo when the customer arrived through the Continue link: it proves the link was not edited.claim-nervixpay.mjs const NERVIXPAY_ORIGIN = 'https://nervixpay.live' /** Call from your backend after the customer lands on ?nervixpay_code=...&nervixpay_sig=... or types a code. */ export async function claimNervixPayCode(code, signature, token = process.env.NERVIXPAY_TOKEN) { const response = await fetch(NERVIXPAY_ORIGIN + '/api/v1/payments/claim', { method: 'POST', headers: { authorization: 'Bearer ' + token, 'content-type': 'application/json' }, body: JSON.stringify(signature ? { code, signature } : { code }), }) const body = await response.json().catch(() => null) if (response.ok) return { ok: true, payment: body.payment, claimedAt: body.claimedAt, linkVerified: body.linkVerified } return { ok: false, status: response.status, code: body?.error?.code ?? 'request_failed', alreadyClaimedAt: body?.alreadyClaimedAt } }Continue link route (Express) import { claimNervixPayCode } from './claim-nervixpay.mjs' // The receipt's "Continue" button opens https://<your site>/?nervixpay_code=ABCD2345&nervixpay_sig=<hex> app.get('/', requireSignedInUser, async (req, res, next) => { const code = req.query.nervixpay_code if (!code) return next() const result = await claimNervixPayCode(String(code), req.query.nervixpay_sig ? String(req.query.nervixpay_sig) : undefined) if (result.ok) { await grantAccess(req.user.id, result.payment) // your logic: amountReceived, itemName, paidAt... return res.redirect('/account?upgraded=1') } if (result.code === 'already_claimed') return res.redirect('/account?already-linked=1') res.status(result.status).send('This confirmation code could not be linked.') })Successful claim { "payment": { "id": "22222222-2222-4222-8222-222222222222", "itemName": "Pro plan", "network": "polygon", "currencyToken": "USDC", "amountReceived": "25.000123", "customerEmail": "buyer@example.com", "customerReference": null, "originSite": "shop.example.com", "paidAt": "2026-09-26T10:02:10.000Z", "transactionId": "0xabc0000000000000000000000000000000000000000000000000000000000001", "transactionUrl": "https://polygonscan.com/tx/0xabc0000000000000000000000000000000000000000000000000000000000001" }, "claimedAt": "2026-09-26T12:00:00.000Z", "linkVerified": true }Step 4: Offer a "Have a code?" field
Customers who open the email on another device can type the code into your account page. Claim it the same way, without a signature.
Responses
| Status | Code | Meaning |
|---|---|---|
| 200 | Claimed now. Credit payment to the signed-in user. | |
| 404 | not_found | No paid order of yours carries this code. |
| 409 | already_claimed | Claimed before, at alreadyClaimedAt. Treat it as a replay unless it was your own retry. |
| 400 | invalid_signature | A signature was sent and does not match the code and site. |
linkVerified is true or false when a signature was checked and null when none was sent. The claim itself is always bound to your key and consumed once.
Coming soon: the integration kit (@nervixpay/integration) is not on npm yet. Once published, claimPayment and continueLinkParams do the same as the code above:
import { claimPayment, continueLinkParams } from '@nervixpay/integration'
const link = continueLinkParams(request.url) // { code, signature } or null
if (link) {
const result = await claimPayment(link.code, {
baseUrl: 'https://nervixpay.live',
accessToken: process.env.NERVIXPAY_TOKEN,
signature: link.signature ?? undefined,
})
if (result.ok) await grantAccess(currentUser.id, result.payment)
}