API Reference

The ChainPay REST API acts as the gateway for programmatically creating payments, tracking invoices, and registering webhooks.

Headers

All merchant API requests require your merchant address passed in the header payload:

Header NameTypeDescription
X-Merchant-Addressstring (address)Your EVM merchant wallet address where checkout assets are sent.
Content-TypestringMust be application/json.

1. Create Payment Request

Endpoint: POST /payments

Request Body

{
  "amount": "0.05",
  "currency": "ETH",
  "description": "Invoice #82910",
  "network": "localhost"
}

Response Payload

{
  "id": "pay_5y8x9z...",
  "merchantAddress": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
  "amount": "0.05",
  "currency": "ETH",
  "network": "localhost",
  "status": "pending",
  "checkoutUrl": "http://localhost:3000/pay/pay_5y8x9z...",
  "createdAt": "2026-06-07T21:40:00.000Z"
}

2. Get Payment Status

Endpoint: GET /payments/:id

Queries database records and historical workflow logs. Returns full payment details, confirmations counted, and webhook attempts status history.

3. Webhooks Security

ChainPay signs webhook events using HMAC-SHA256 signatures, passing the hash signature inside the x-chainpay-signature header. You should verify webhook payloads on your server to prevent replay or spoofing attacks.

import crypto from "crypto";

// Webhook validation handler inside your Express/Node app
app.post("/webhooks/chainpay", (req, res) => {
  const signature = req.headers["x-chainpay-signature"];
  const clientSecret = process.env.CHAINPAY_WEBHOOK_SECRET;

  if (!signature) {
    return res.status(401).send("Missing signature header");
  }

  // Generate the HMAC signature using SHA-256
  const hmac = crypto.createHmac("sha256", clientSecret);
  const digest = hmac.update(JSON.stringify(req.body)).digest("hex");

  // Constant-time comparison to prevent timing attacks
  const isValid = crypto.timingSafeEqual(
    Buffer.from(signature, "utf-8"),
    Buffer.from(digest, "utf-8")
  );

  if (!isValid) {
    return res.status(401).send("Invalid webhook signature");
  }

  console.log("Verified payment status:", req.body.status);
  res.status(200).send("Webhook received");
});