Skip to content

Security best practices

Your integration moves money, so it deserves the same care as a payment card integration. These practices protect you and your customers.

Protect your secret keys

  • Keep keys in server-side configuration or a secrets manager. Never put them in front-end code, mobile apps, logs or version control.
  • Use separate keys per system, with only the scopes each one needs.
  • Add an IP allowlist to live keys, especially keys with payouts:write.
  • Rotate keys when someone with access leaves, and immediately if you suspect a leak. Revoke keys you no longer use.
  • Turn on two-factor authentication for every dashboard user. It is required to create live keys.

Trust only verified status

  • Fulfil orders only from a verified payment.completed webhook or from GET /payments/{id}, never from the customer reaching your success_url.
  • Compare received_amount and currency with what you expected. Treat underpaid as not fully paid.
  • Look up orders by your own reference and our id. Do not trust identifiers supplied by the browser.

Verify every webhook

  • Check the HMAC signature over the raw body with a constant-time comparison.
  • Reject timestamps older than 5 minutes to block replayed requests.
  • Make handlers idempotent: remember processed event IDs.
  • Serve your webhook endpoint over HTTPS. This is required in live mode.

Safe requests

  • Send an Idempotency-Key with every POST and reuse it on retries.
  • Handle amounts as decimal strings or integer minor units, never floating-point.
  • Never let customers set fees or amounts that your server has not checked. The API rejects any request that tries to set a fee.

Payouts

  • Build payout requests only from destinations your users registered and confirmed, for example by email confirmation.
  • Check the address format and network before sending. Crypto payouts to a wrong address cannot be reversed.
  • Watch payout.failed and payout.rejected events and alert your team.

What we do on our side

  • API secrets are stored only as hashes; webhook secrets are encrypted at rest.
  • Provider credentials are kept server-side only: in environment variables or encrypted in our database. They are never sent to browsers.
  • Provider callbacks are verified by signature, and status is confirmed with the provider before any balance change.
  • Every balance change is a balanced double-entry ledger record, protected against double spending with database transactions and locks.
  • Sensitive admin actions are audit-logged, and live payouts can require manual approval.

Reporting a vulnerability

If you find a security issue, please contact us privately before disclosing it.