Compliance & security
What the platform enforces in code, and what needs a contract.
Two questions get asked together and have very different answers. What the platform does on every request is on this page, verifiable against the API you are already calling. Certifications, licences and data residency are contractual, they change per market and per contract, and they come from enterprise@wajub.com rather than from a documentation page.
What runs on every request
| Guarantee | Mechanism |
|---|---|
| A key is never compared in plaintext | Lookup by SHA-256 of the value presented |
| A revoked or expired key stops working immediately | 401, checked before anything else |
| A private key cannot be used from a browser | Origin or Referer on an sk. key is refused with 403 |
| A leaked key can be bound to your servers | Per-key IP allow-list, exact address or CIDR |
| A key can be narrowed to what it needs | Restricted keys, fifteen resource scopes, read and write |
| A webhook proves it came from Wajub | HMAC-SHA256 over timestamp and body, 300 second tolerance |
| Sandbox cannot reach live data | Separate databases, selected by the key that authenticated |
| A retry cannot charge twice | Idempotency-Key, 24 hour cache and a permanent unique index |
Security best practices walks through each of those from the integrator's side, including rotation without downtime.
A key in a browser is reported, not just refused
A private key presented with a browser header is refused and the key owner is emailed, throttled to one alert an hour. Treat that email as a leak notification and rotate, because the request reached us with a valid key.
Card data and your PCI scope
Card numbers never reach Wajub. The payer types the card into the card provider's own fields or window, and Wajub only receives what the provider hands back: a token, encrypted fields or a session reference.
| Card provider | Where the payer types the card |
|---|---|
| Stripe | Stripe's card fields, inside Hosted Checkout or Wajub Components |
| Adyen | Adyen's encrypted card fields, inside Hosted Checkout or Wajub Components |
| Mollie | Mollie's card fields, inside Hosted Checkout or Wajub Components |
| PayPal | PayPal's card button and form, inside Hosted Checkout or Wajub Components |
| Paystack, Flutterwave, FedaPay, Paddle, PayDunya, CinetPay, Kkiapay | The provider's payment window, opened on top of Hosted Checkout or Wajub Components |
The mobile SDKs use Stripe's fields where Stripe handles the card. For the other providers they open the provider's own payment page. Your servers never handle card data either, since they only create the payment and hand the payer a checkout.
Raw card data is refused
A card number, security code or card PIN sent to POST /payments/{id} or to the payment session
API is refused with 422 and error_code: raw_card_data_not_accepted, before anything is
processed. Sandbox accepts only the cards listed in
Test scenarios, and refuses any other number with
error_code: sandbox_test_card_required.
Of a card that has been charged, two fragments survive on our side.
| Fragment | What it is | What it is for |
|---|---|---|
| The issuing BIN | Risk scoring, issuer and country signals | |
Shown as **** 4242 | Letting a returning payer recognise their card |
The digits in between are never written down. A saved payment method is a token plus those two fragments, never a number you could charge elsewhere.
Deleting a payer
DELETE /customers/{id} is a real erasure request, not a status change. In one transaction it
archives the row, then anonymises it.
| Step | What happens |
|---|---|
| The customer record | name becomes Deleted User {pseudonym}, every other identifying field is nulled |
| Addresses | Deleted |
| Saved payment methods | Deleted, vault entries included |
| Transaction snapshots | Pseudonymised, so the accounting still balances |
| Search index | The customer is removed |
| Audit trail | The anonymisation itself is recorded |
Tax IDs are the one exception: they are kept when an approved compliance record references that customer, because deleting them would break a filing obligation that outlives the request.
The operation is idempotent
A customer whose email and phone are already null and whose name already starts with
Deleted User is recognised as anonymised and skipped. Repeating the call is safe, and it never
double-archives.
Transactions themselves are not deleted. They keep the pseudonym, which is what makes the payer unidentifiable while leaving your books intact.
Sandbox and live are separate systems
The key you authenticate with selects the database, before your request reaches a controller. A sandbox key and a live key on the same team read different tables entirely.
The practical consequences show up as 404, not as a permission error. A live payment id looked up
with a test key does not exist, and the reverse is equally true. Nothing crosses: not customers,
not webhook endpoints, not events.
Verification gates live money
A team that has not passed compliance verification has its six account ceilings written as zero,
and zero is enforced as a refusal. Live payments answer 402 with
error_code: merchant_not_verified and payouts answer 422, while sandbox keeps working
unchanged. Limits and quotas has the ceilings and the multipliers that lift
them.
Reporting a vulnerability
Send it to security@wajub.com. The API, Konsole, the SDKs and this documentation site are all in scope. There is no public bounty programme, and disclosures are credited in the changelog when the reporter wants to be named.
Questionnaires, certifications and residency
Anything that belongs in a contract is answered by a person, not by this page: certification status and certificates, penetration test reports, data processing agreements, residency commitments, per-market licensing and the regulated partners behind each corridor.
Write to enterprise@wajub.com with the questionnaire you need completed.