Two independent answers to “whose data is this?”
Every merchant and every consumer belongs to a tenant, and no query is trusted to remember that on its own. PostgreSQL row-level security is bound to a per-transaction session variable, so the database refuses rows outside the caller's tenant regardless of what the SQL asked for — and every statement additionally carries an explicit tenant predicate, so a policy that is ever mis-scoped is not the only thing standing between two merchants.
- Row-level security is enabled and forced on every table holding tenant data.
- The tenant is set per transaction from an authenticated claim, never from a request parameter.
- Every statement names the tenant in its WHERE clause as well; the belt is tested independently of the braces.
- Credit and wallet adapters are constructed per tenant and reject a record belonging to another one outright.
A retried payment is one payment
A township cell connection routinely drops between a request and its response, and the app retries. Every endpoint that moves money takes a client-supplied idempotency key, and the second request with that key returns the first request's answer rather than moving money again. The distinction the platform is built around is between a rail that proved it wrote nothing and a rail that cannot say — only the first is ever retried on another rail.
- Idempotency keys are mandatory on every payment, advance, VAS and transfer endpoint.
- Money is integer minor units end to end. No floating-point number touches a balance anywhere in the platform.
- Every movement is double-entry; the ledger is proven to balance by test, not by inspection.
- A failed notification is never treated as a failed payment — a settled transaction with an undelivered SMS stays settled.
No card vault, because there is no card data
WynkPay does not store, process or transmit a primary account number, track data, CVV or PIN block in the clear. On tap-to-phone the card is read by the Halo.dot CPoC kernel and encrypted to the acquirer on the device; on a Sunmi terminal a certified EMV L1/L2 stack does the same. What returns to WynkPay is a token, a masked PAN and a scheme reference.
- The PCI CPoC certification belongs to the kernel provider; the solution-level assessment for the WynkPay integration is in progress and tap-to-phone is not live until it completes.
- Terminal keys are injected remotely into managed hardware and never exist inside the application.
- Offline tap is bounded by a per-transaction floor limit and a per-terminal daily exposure cap, enforced on the device and re-checked server-side.
- A PCI DSS Level 1 assessment path is defined; no Attestation of Compliance has been issued yet.
Authentication that survives a shared handset
Shop phones get handed around. Sessions are short, sensitive actions re-authenticate, and a device that has not checked in is treated as untrusted rather than as absent. PINs are never transmitted or stored in a recoverable form, and no screen in the estate ever shows a full identity number.
- Identity numbers are masked wherever they are stored, displayed or logged.
- Device binding and attestation gate the capabilities that can move money.
- Rate limiting and lockout apply to OTP, PIN and sign-in attempts on every surface.
- KYC tier limits are enforced server-side; the client's pre-check is a convenience, never the control.
Personal information does not reach the places that leak
Logs, metrics, analytics and error reports are the surfaces that get shipped to third parties, retained the longest and read by the most people. Cellphone numbers, identity numbers and card numbers do not go into any of them. Where a downstream system needs to know who a message is for, it is given an internal wallet or merchant id and resolves the destination itself, inside the service that also holds the consent record.
- No PII in application logs, analytics events or exception reports — enforced by test, not by review.
- Notification payloads carry an internal id, never a cellphone number.
- Data is processed under POPIA with an appointed Information Officer and a published section 18 notice.
- Retention periods are stated in the POPIA notice; FICA records are kept for the statutory period and no longer.
The platform is built to be inspected
The whole estate is one typed codebase under a strict compiler with no escape hatches: no implicit any, no suppressed type errors, indexed access checked, optional properties exact. That is a security property as much as a quality one — most real-world payment defects are a value being something other than what the caller assumed.
- Strict TypeScript across every package, app and service; type suppression is not permitted anywhere in the repo.
- Shared logic lives in one package and is consumed, never copied — one QR encoder, one catalogue, one money type.
- Money, credit and settlement paths carry adversarial tests, including deliberate over-collection and replay attempts.
- Dependencies are pinned by lockfile and the workspace is built from source at every commit.