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 Name | Type | Description |
|---|---|---|
X-Merchant-Address | string (address) | Your EVM merchant wallet address where checkout assets are sent. |
Content-Type | string | Must 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");
});