Skip to content

Quickstart

Estimated time: 5 minutes. Prerequisites: a B4bit Pay account and your device API key.

Ventana de terminal
export B4BIT_API_KEY=<your-api-key> # from pay.b4bit.com/settings/commerce/devices
export B4BIT_SECRET=<your-secret-key-hex> # only used to verify webhook signatures
Ventana de terminal
curl -H "X-Device-Id: $B4BIT_API_KEY" \
https://pos.b4bit.com/api/v1/currencies
Ventana de terminal
curl -X POST \
-H "X-Device-Id: $B4BIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"expected_output_amount": 50.00,
"fiat": "MXN",
"notes": "Test order",
"reference": "quickstart-'"$(date +%s)"'",
"merchant_urlok": "https://example.com/pay/ok",
"merchant_urlko": "https://example.com/pay/error"
}' \
https://pos.b4bit.com/api/v1/orders/
import express from 'express';
import crypto from 'node:crypto';
const app = express();
app.use('/webhook', express.raw({ type: '*/*' }));
app.post('/webhook', (req, res) => {
const nonce = req.header('x-nonce');
const signature = req.header('x-signature');
const body = req.body.toString('utf-8');
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - Number(nonce)) > 20) return res.sendStatus(401);
const key = Buffer.from(process.env.B4BIT_SECRET, 'hex');
const mac = crypto.createHmac('sha256', key).update(nonce + body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(signature))) {
return res.sendStatus(401);
}
res.sendStatus(200);
});
app.listen(3000);
  • Timing-safe HMAC — uses crypto.timingSafeEqual / hmac.compare_digest / hash_equals. Never ==.
  • Nonce time window — rejects requests older than 20 s.
  • Raw body — does not parse and reserialize JSON before signing/verifying.
  • Fast HTTP 200 response — less than 5 seconds. Heavy logic in async queue.
  • Idempotency — tolerates the same webhook arriving N times without duplicated side effects.
  • All 11 statuses — each has a branch in your handler, even if only logging.
  • safe field — does not release product while safe=false.
  • TAG/MEMO for XRP/XLM/ALGO — displayed prominently to the customer.
  • reference — unique per order, persisted alongside the B4bit Pay identifier.
  • Logs without secrets — never logs X-SIGNATURE, B4BIT_SECRET, or full webhook body.
  • Rate limits — implements exponential backoff on HTTP 429.
  • Reconciliation — a periodic job calls GET /orders/ and crosses with your base in case a webhook was lost.