Skip to content

Accept a payment

Create a payment on your server, send the customer to checkout, and fulfill on the webhook.

Your server and hosted checkout

Recommended
Code needed
One API call and a webhook handler
Time
An afternoon
Customization
Amount, reference, metadata, redirects and expiry on every payment
Best for
Stores and apps with their own orders

Create the payment from your server when the customer is ready to pay, then send them to its url. Checkout takes it from there, and a webhook tells you when it's paid.

  1. Your server (POST /payments) → Hosted checkout (the payment's url)
  2. Hosted checkout (the payment's url) → Customer's wallet (sends the coin)
  3. Customer's wallet (sends the coin) → Your wallet (funds land here)
  4. Your wallet (funds land here) → 402pay (sees it on chain)
  5. 402pay (sees it on chain) → Your server (POST /payments): payment.succeeded
The customer's coins go straight to your wallet. The webhook is how your server hears about it.

Create the payment

server.jsNode.js
// When the customer checks out, create the payment and send them to it.app.post("/checkout", async (req, res) => {  const order = await orders.create(req.body);   const response = await fetch("https://dash.402pay.co/api/v1/payments", {    method: "POST",    headers: {      Authorization: `Bearer ${process.env.PAY402_SECRET_KEY}`,      "Content-Type": "application/json",    },    body: JSON.stringify({      amount: order.totalCents,      currency: "USD",      reference: order.id,      description: order.summary,      success_url: `https://example.com/orders/${order.id}`,      metadata: { order_id: order.id },    }),  });   const { data: payment } = await response.json();  res.redirect(303, payment.url);});
  • amount is in minor units of currency: 4900 with USD is $49.00.
  • reference is your own ID. It names one live payment, so a retried create with the same reference, amount and currency returns the payment that already exists, with 200. The same reference with a different amount or currency returns 409 reference_in_use. Without a reference, every create makes a new payment.
  • expires_at sets how long the payment can be paid, from 15 minutes to 30 days. It defaults to 24 hours.
  • success_url is where checkout sends the customer after paying. Without it, they go to the redirect in your checkout settings, or see their receipt when there's none. Both it and cancel_url must use https://, except on localhost and 127.0.0.1.

Fulfill the order

Fulfill on the payment.succeeded webhook, not the redirect: a customer can close the tab before your success page loads. Answer quickly, then do the work.

webhooks.jsNode.js
// Fulfill once, when the payment succeeds.app.post("/webhooks/402pay", express.raw({ type: "application/json" }), async (req, res) => {  if (!verifyWebhook(process.env.PAY402_WEBHOOK_SECRET, req.headers, req.body)) {    return res.sendStatus(400);  }  res.sendStatus(200);   const event = JSON.parse(req.body);  // A test delivery names a made-up payment, and its data holds only that id.  if (event.test) return;  if (event.type === "payment.succeeded") {    await orders.markPaid(event.data.reference, event.data.id);  }});

Payment statuses

StatusMeaning
pendingWaiting for the customer, or for funds to arrive.
succeededPaid in full, or accepted: the funds landed in your wallet, or paid fees you owed. Fulfill the order.
underpaidLess than the amount due arrived. Checkout asks for the rest, or you can accept it.
needs_reviewFunds arrived in a way that needs your decision, such as after the checkout expired.
failedA payment was canceled, by the business or by the customer at a link's checkout, or a declined card had no route left or wasn't retried within 30 minutes. No funds landed in the wallet. It isn't always final: an API payment you didn't cancel can still be paid, since the customer's next attempt makes it pending again. It's over once canceled_at is set.
expiredNothing arrived before the payment, or a link's quote, expired.

Cancel or accept

Cancel a payment that hasn't received funds with POST /payments/{id}/cancel. Count an underpaid payment, or one that needs review, as paid with POST /payments/{id}/accept.

Next steps