Search pages in the SMS Pay documentation.
Payment security depends on protecting API keys, verifying webhook signatures, preventing replay attacks, isolating sandbox/live environments, and keeping fulfillment idempotent.
Good:
SMS_PAY_LIVE_KEY=sk_live_xxxxxxxxxxxxxxxxx
Bad:
<script>
window.SMS_PAY_KEY = "sk_live_xxxxxxxxxxxxxxxxx";
</script>
import crypto from "node:crypto";
export function verifyWebhook(rawBody: Buffer, signature: string, secret: string) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const left = Buffer.from(signature);
const right = Buffer.from(expected);
return left.length === right.length && crypto.timingSafeEqual(left, right);
}
Signed webhook request:
POST /webhooks/sms-pay
X-Webhook-Id: delivery_123
X-Webhook-Event: payment.paid
X-Webhook-Signature: sha256=<hex>
X-Webhook-Timestamp: 2026-05-05T10:01:00.000Z
Content-Type: application/json
{
"event": "payment.paid",
"environment": "LIVE",
"timestamp": "2026-05-05T10:01:00.000Z",
"data": {
"payment_intent_id": "pi_live_123",
"amount": "500",
"currency": "BDT",
"customer_reference": "ORDER-10045"
}
}
Expected response:
HTTP/1.1 200 OK
Recommended controls:
X-Webhook-Id you process.X-Webhook-Timestamp and reject very old requests when your infrastructure allows.payment.paid cannot ship twice.The Android SMS Agent sends:
X-Api-Key: sk_live_xxxxxxxxxxxxxxxxx
X-Signature: sha256=<hmac-of-raw-body>
The server validates both the API key and signature before storing the SMS event.