API Reference

Integration Examples

Copy-paste ready code for webhook handling and order creation in your stack.

server.js — Create order endpoint
const express = require("express");
const crypto  = require("crypto");
const fetch   = require("node-fetch"); // node 18+: built-in fetch works too

const app = express();

// IMPORTANT: use express.raw() for the webhook route to preserve the raw body
// for HMAC verification. Use express.json() for all other routes.
app.use("/webhooks", express.raw({ type: "application/json" }));
app.use(express.json());

const API_KEY        = process.env.CHAINPAY_API_KEY;        // sk_live_...
const WEBHOOK_SECRET = process.env.CHAINPAY_WEBHOOK_SECRET; // whsec_...

// POST /create-payment — called from your frontend
app.post("/create-payment", async (req, res) => {
  const { amount, userId, yourOrderId } = req.body;

  const response = await fetch("https://chainpay.pro/api/v1/orders", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      amount:     amount,
      currency:   "USDT",
      chain:      "trc20",
      externalId: yourOrderId,              // your DB order ID
      metadata:   { userId },              // returned in webhook
    }),
  });

  if (!response.ok) {
    const err = await response.json();
    return res.status(response.status).json({ error: err.message });
  }

  const order = await response.json();

  // Redirect user to the hosted checkout page
  res.json({
    orderId:     order.orderId,
    checkoutUrl: order.checkoutUrl,
    expiresAt:   order.expiresAt,
  });
});
server.js — Webhook handler
// POST /webhooks/chainpay
app.post("/webhooks/chainpay", (req, res) => {
  const signature = req.headers["x-chainpay-signature"];

  // req.body is a Buffer because we used express.raw() above
  const expected = crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");

  // Use timingSafeEqual to prevent timing attacks
  let isValid = false;
  try {
    isValid = crypto.timingSafeEqual(
      Buffer.from(signature ?? "", "hex"),
      Buffer.from(expected, "hex")
    );
  } catch {
    // timingSafeEqual throws if buffers differ in length
    isValid = false;
  }

  if (!isValid) {
    console.warn("Webhook signature mismatch");
    return res.status(401).send("Invalid signature");
  }

  const payload = JSON.parse(req.body.toString());

  switch (payload.event) {
    case "payment.completed":
      console.log(
        `[ChainPay] Order ${payload.orderId} completed.`,
        `External: ${payload.externalId}, Net: ${payload.netAmount} ${payload.currency}`
      );
      // TODO: mark order as paid in your database
      // TODO: trigger fulfilment (send email, unlock content, etc.)
      break;

    case "payment.expired":
      console.log(`[ChainPay] Order ${payload.orderId} expired.`);
      // TODO: mark order as expired, notify the user if needed
      break;

    default:
      console.log(`[ChainPay] Unknown event: ${payload.event}`);
  }

  // Always respond 200 quickly — heavy processing should be async (queue / worker)
  res.status(200).send("OK");
});

app.listen(3000, () => console.log("Server running on port 3000"));
Query an order
// GET /order-status/:id — proxy to ChainPay
app.get("/order-status/:id", async (req, res) => {
  const response = await fetch(
    `https://chainpay.pro/api/v1/orders/${req.params.id}`,
    { headers: { "Authorization": `Bearer ${API_KEY}` } }
  );
  const order = await response.json();
  // order.status: "pending" | "confirming" | "completed" | "expired" | "failed"
  res.json(order);
});

Key points to remember

  • Always verify the X-ChainPay-Signature header before processing any webhook.
  • Parse the webhook body from the raw request bytes — do not re-serialize the parsed JSON before hashing.
  • Respond 200 OK quickly; run heavy processing asynchronously to avoid timeouts.
  • Use sk_test_ keys + POST /api/v1/sandbox/simulate-payment for end-to-end testing without real funds.
  • Store API keys and webhook secrets in environment variables, never in source code.