For the complete documentation index, see llms.txt. This page is also available as Markdown.

Signature Verification

Every webhook request is signed using your webhook secret. Always verify the signature before processing the payload — this confirms the request came from AstraPay and has not been tampered with.


Signature format

The X-AstraPay-Signature header contains a timestamp and HMAC-SHA256 signature:

X-AstraPay-Signature: t=1711900000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Verification steps

  1. Extract the timestamp (t) and signature (v1) from the header

  2. Construct the signed string: {timestamp}.{raw JSON body}

  3. Compute HMAC-SHA256 of that string using your webhook secret

  4. Compare your computed value to v1 using a constant-time comparison


Code examples

const crypto = require("crypto");

function verifyWebhookSignature(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((p) => p.split("="))
  );

  const timestamp = parts["t"];
  const signature = parts["v1"];

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(expected, "hex")
  );
}

// Express route example
app.post("/webhooks/astrapay", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-astrapay-signature"];

  if (!verifyWebhookSignature(req.body.toString(), sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }

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

  if (event.event === "payment.completed") {
    // Fulfill order using event.data
  }

  res.sendStatus(200);
});

Last updated