Payaider Docs Non-custodial crypto payments API 2026-08-27

Quick Start

Take your first crypto payment in about fifteen minutes: create an intent, redirect the customer, fulfil on a signed webhook. The money goes straight to your wallet.

What Payaider does

Your customer pays from their own wallet. The funds land directly in your wallet — they never pass through Payaider, and we hold no key that could move them. What we do is watch the chain, match the transfer to your invoice, verify it, and tell you.

That non-custodial design has one consequence you need to know up front: because funds go to your own shared address, the amount is the only discriminator available on-chain. So every concurrently-payable invoice gets a unique amount at high decimal precision. That is why a $100.00 order asks for 100.0431 USDT rather than a round number.

Before you start

  • A test secret key (sk_test_…) from the dashboard. Test mode is a full parallel universe backed by testnets — the same API, separate data.
  • A verified wallet on at least one network, and a route pointing an asset at it. The sandbox merchant already has three.
  • A testnet wallet with some test USDT or USDC, so you can actually pay the invoice you create.

Every example below uses the sandbox at https://api.sandbox.payaider.com. Swap the origin and the key prefix for live traffic; nothing else changes.

Step 1 — create a payment intent

Price the order in fiat. Any ISO currency works; settlement is crypto, which is exactly why going global is cheap here.

bash
curl -X POST https://api.sandbox.payaider.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0f2b7c34-9a1e-4d6f-8b02-5c7e1a9d3f44" \
  -d '{
    "amount": 10000,
    "currency": "USD",
    "metadata": { "order_id": "1234" },
    "success_url": "https://shop.example/thanks"
  }'
json — 201 Created
{
  "id": "pi_7Fk2Qd9RmTs4Vb",
  "object": "payment_intent",
  "mode": "test",
  "status": "created",
  "amount": "10000",
  "currency": "USD",
  "currency_decimals": 2,
  "received_amount": "0",
  "metadata": { "order_id": "1234" },
  "created": "2026-08-27T10:15:00.000Z",
  "expires_at": null,
  "checkout_url": "https://checkout.payaider.com/c/pi_7Fk2Qd9RmTs4Vb",
  "payment_method": null
}

Step 2 — redirect the customer

Send them to checkout_url. Hosted checkout does the hard parts: the asset and network picker with wrong-network warnings, the QR code and the plain-address copy button, the 15-minute countdown, live status, the underpayment "remaining" flow, and the "I have paid" transaction-hash fallback.

javascript
// 1. Create the intent server-side. Amounts are INTEGER MINOR UNITS:
//    10000 with currency "USD" is $100.00, not $10,000.
const response = await fetch("https://api.sandbox.payaider.com/v1/payment_intents", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.PAYAIDER_SECRET_KEY,
    "Content-Type": "application/json",
    // A retry without this header can mint two intents for one order, and a customer
    // who pays both cannot be refunded by anyone, including us.
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    amount: 10000,
    currency: "USD",
    metadata: { order_id: order.id },
    success_url: "https://shop.example/thanks",
  }),
});
const payment = await response.json();

// 2. Redirect the customer. Checkout handles asset choice, the QR, the countdown,
//    the "I've paid" fallback and the underpayment flow.
res.redirect(payment.checkout_url);

// 3. Fulfil on the webhook. NEVER on the success_url redirect: a customer can open
//    that URL without paying, and a payment can confirm after they close the tab.

Step 3 — fulfil on the webhook

Register an endpoint once, store the whsec_… secret it returns (you see it exactly once), and verify every delivery.

bash
curl -X POST https://api.sandbox.payaider.com/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c1e4d6f-8b02-4a19-9f3d-2e5c7a9d3f01" \
  -d '{ "url": "https://shop.example/webhooks/payaider", "enabled_events": ["*"] }'
javascript
import express from "express";
import crypto from "node:crypto";

const app = express();

// express.raw is mandatory: the signature is over the RAW bytes, and
// JSON.stringify(JSON.parse(body)) produces different bytes.
app.post("/webhooks/payaider", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyPayaiderSignature(req.get("Payaider-Signature"), req.body, [process.env.PAYAIDER_WEBHOOK_SECRET])) {
    return res.status(400).send("bad signature");
  }

  const event = JSON.parse(req.body.toString("utf8"));

  // Delivery is at-least-once and unordered. Dedupe on event.id before doing any work.
  if (alreadyProcessed(event.id)) return res.sendStatus(200);

  // confirmed and overpaid both count as paid. detected does NOT.
  if (event.type === "payment.confirmed" || event.type === "payment.overpaid") {
    fulfillOrder(event.data.metadata.order_id);
  }

  // ACK fast, then work asynchronously: the delivery timeout is 10 seconds.
  res.sendStatus(200);
});

The verifyPayaiderSignature function is on the Webhooks page, in Node, PHP and Python. Copy it verbatim — it is executed against the real signer in our test suite, so it cannot silently drift from what we send.

Step 4 — pay it on testnet

  1. Open the checkout_url you were given.
  2. Pick USDT on TRON (or USDC on Base). The page now shows an address, an exact amount and a countdown.
  3. Send exactly that amount from your testnet wallet. Do not round it — the amount is how we know which invoice you paid.
  4. Watch the page: awaiting_payment becomes detected within seconds, then confirming, then confirmed.
  5. Your webhook endpoint receives payment.detected, payment.confirming and payment.confirmed, in whatever order the network delivers them.

That is the whole integration. Everything below is detail you can read later.

The five mistakes everyone makes once

MistakeWhat happensFix
Fulfilling on payment.detectedYou ship against a 0-conf transfer that can still be reorged out.Fulfil on payment.confirmed or payment.overpaid only.
Trusting success_urlAnyone can open that URL without paying.The webhook is the only proof of payment.
Re-serializing the webhook body before verifyingThe signature never matches, because the bytes changed.Read the RAW body. In Express, express.raw({ type: "application/json" }).
Sending amount: 100.00Rejected as an invalid amount — or worse, accepted as one cent.Integer minor units: 10000.
Parsing amounts as JSON numbersAn 18-decimal token amount silently rounds in your runtime.They arrive as strings. Parse with a big-integer or decimal type.

Where to go next

  • Payments — the full lifecycle, every state, and what to do in each.
  • Webhooks — signature verification in three languages, the retry schedule, the delivery log.
  • Wallets — adding and verifying your receiving addresses, and routing assets to them.
  • Errors — every error code, what it means and how to fix it.
  • Security — what Payaider can and cannot do with your money, and what we ask of you.