Security
Read this if you're integrating in production. The short version: pin everything you can.
API keys
- Treat the secret like a password. Put it in a secret manager, never commit it, never log it.
- Use one key per integration role and scope it down to the minimum needed (read-only for reporting, write for the integration server).
- Add an IP allowlist on every production key.
- Set a tight per-key rate limit. A leaked key with rate-limit 60 is much less dangerous than 6000.
- Rotate keys on every staff change.
- 5 failed attempts → 1 hour lockout. The lockout is per-key, so an attacker can't lock out other keys by guessing.
Webhooks
- Verify the
Swap-Pay-Signatureheader — never trust an unsigned event. - Reject events older than 5 minutes (replay window).
- Always dedupe by
event_id. Retries reuse the same id. - Use HTTPS endpoints with strict TLS — we refuse to send to
http://, RFC1918, loopback, link-local. - Respond 2xx within 10 seconds. Slow replies trigger retries and eventual dead-letter.
- If your endpoint accumulates 20 consecutive failures, we auto-pause the webhook. Re-enable in the cabinet.
Hosted checkout
- The
payment_urlcontains an HMAC; tampered URLs return 404. Send your customer the full URL exactly as we return it. - The page sets a strict
Content-Security-Policy,X-Frame-Options: DENY, andframe-ancestors 'none'. Don't iframe it. - Customer data: we store the email hash for matching only; we don't expose your customer IDs publicly.
Money safety
- Use raw minor units in your storage. Never store amounts as floats.
- The exact amount on the hosted checkout includes the service fee. Wrong-amount transfers go to manual review.
- Every payout and refund waits for operator approval — the broadcast doesn't happen until a human signs off.
- Ledger is hash-chained per (merchant, asset). Reconciliation runs every 6 hours; drift fires a critical alert.
Customer redirect
success_url/fail_urlmust be HTTPS.- Don't include secrets in the redirect target — the customer can see and share the URL.
- Verify the payment status on your server side via webhook (or
GET /invoices/:id) before fulfilling the order. The redirect alone is not authoritative — it's a UX shortcut.
2FA
Enable TOTP on every cabinet account, especially anyone with admin role. Cabinet sessions are 30-day cookies; a leaked cookie without 2FA is a full takeover.