Palm Vein Payment Terminal — v3
Business Proposal & Solution Design
Version 3 (2026-04-14). See v1 and v2 for the prior versions. v3 is an architectural pivot: the terminal becomes a thin client, all payment logic moves behind an API gateway, and payment rails are pluggable via a rail registry. Changes from v2 are summarised in Changes from v2 immediately below, and the older Changes from v1 summary is kept for history.
Changes from v2
v3 changes the deployment shape of the system. v2 still described a fat terminal that integrated scanner, card reader, EMV kernel, payment routing, fraud, audit, and rail SDKs directly. v3 collapses the terminal to its minimum viable surface and pushes everything else behind a single payments gateway API.
| # | Area | What changed in v3 | Why |
|---|---|---|---|
| 1 | Terminal scope narrowed | Device contains only: base UI, palm-vein hardware integration (SDK + 1:N match + on-device template store), card-reader hardware passthrough (PCI-SPoC, no EMV kernel), secure gateway-API client. Everything else is an API call. | POPIA requires palm templates to stay on-device, but nothing else belongs there. Fat terminals are slow to certify, slow to update, and couple hardware vendors to payment logic. |
| 2 | Single Payments Gateway API (PGW) | All payment orchestration is behind one gateway: POST /v1/payments, POST /v1/refunds, POST /v1/disputes, POST /v1/palm-sessions/resolve, etc. The terminal never calls PayShap, card networks, or fraud services directly. | One integration point = one certification, one audit boundary, one place to rotate secrets, one authoritative ledger. Fewer device firmware pushes for backend changes. |
| 3 | Rail Registry — pluggable rails | PayShap, card networks, RTGS, SWIFT, future rails are all RailAdapter plugins behind the gateway. The client can add or swap rails via an admin API without touching the device. Routing policy (which rail for which amount/region/merchant) is server-side configuration. | v1/v2 hard-wired PayShap + card as the only options. v3 makes “payment rail” a first-class pluggable concept so future rails drop in as adapters. |
| 4 | EMV kernel cloud-resident | The card reader on the device is passthrough-only (PCI-SPoC). The EMV kernel runs in the gateway. | Cloud EMV lets the client add card-brand updates, change acquirer, or support new EMV flows without re-certifying hardware. |
| 5 | Single /fdl-build command | v2 listed 17 separate /fdl-generate commands across five phases. v3 collapses them into one /fdl-build invocation that reads this plan, resolves all referenced blueprints, and generates the full stack end-to-end under the 3-gate pipeline. | /fdl-build is the documented way to generate a complete app from a plan. Per-blueprint calls were a manual-orchestration workaround. |
| 6 | Admin Dashboard (transactions + vendor management) | New first-class web app (built alongside gateway + terminal) covering: live transaction explorer with drill-down, refund/dispute initiation, reconciliation state, rail registry management, vendor management (acquirers, SMS/email providers, KYC providers, PayShap participant bank config) with credential rotation, per-merchant overrides, and audit trail for every admin action. | Rail management alone (v3 initial) isn’t an admin console. Ops teams need a UI to investigate transactions, rotate vendor credentials, and enable/disable rails without touching code. |
| 7 | Full Sandbox capability with a Mock Rail | New sandbox environment (parallel to prod PGW) plus a sandbox rail adapter that satisfies the full RailAdapter contract — realistic latency, scripted success/decline/timeout/rate-limit scenarios, webhook callbacks that mirror real rails (pacs.002, PaymentStatusReport, etc.). Tenant-level sandbox mode flag so a merchant can flip a terminal to sandbox for demo or integration testing without code changes. | The client needs to demonstrate the full payment experience end-to-end — palm, card, refund, dispute, receipt — without moving real money. A Mock Rail that is contract-compatible with real rails means test coverage runs the same code path as production. |
What v3 keeps from v2: POPIA reference blueprint pinning, capability-layer pinning (uses: [code-quality-baseline, security-baseline, ai-pr-review]), and the 3-gate post-gen pipeline. All three still apply; the build command just runs them once against the full generated stack instead of per-feature.
Open architectural question (flag): v3 assumes the card reader hardware stays physically on the terminal (PCI-SPoC passthrough). If the preference is a palm-only terminal with card payments handled by a detached PED talking to the gateway directly, say so and §3.2 + §Appendix A collapse further. This plan proceeds with the all-in-one hardware from v1.
Changes from v1
v1 was written before three April 2026 framework changes. v2 reconciled the plan with them (and v3 keeps them all):
| # | Area | What changed in the framework | What v2 did about it (still applies in v3) |
|---|---|---|---|
| 1 | POPIA reference blueprint | blueprints/data/popia-compliance.blueprint.yaml was added as the canonical FDL representation of Act 4 of 2013. Per CLAUDE.md, every blueprint that handles SA personal information must list popia-compliance in related[] as required. | §8.3 cites the blueprint; §12.4 adds a row for data/popia-compliance; §12.5 marks POPIA as an explicit coverage category; every blueprint handling SA PII lists popia-compliance in related[]. |
| 2 | 3-gate post-gen pipeline | docs/gates.md documents Gate 1 (fake/placeholder scan), Gate 2 (compile/type-check), Gate 3 (cold-context AI PR review). /fdl-generate and /fdl-build run all three automatically and emit BLOCKED instead of FILES on a critical finding. | §Build documents the automatic gating; a “Gate response” sub-section tells operators how to react to a BLOCKED emit. |
| 3 | Capability-layer pinning | Blueprints declare uses: [code-quality-baseline, security-baseline, ai-pr-review] to pin per-feature gate contracts. Without uses:, only implicit baselines run and no per-blueprint api-endpoint / security anti-patterns are enforced. | All payment-terminal blueprints declare these three capability imports before build. §Appendix B lists them. |
Original Request
“I want to build a payment terminal app that uses a palm vein scanner. It will give the user the option to pay with a card or hand, and the payment system it will use to do the payment will be PayShap rail.”
Requirements Elicitation
The following requirements were gathered through structured questioning:
| Question | Answer |
|---|---|
| What is the terminal platform? | Android payment terminal with built-in palm scanner and card reader (all-in-one hardware) |
| How should card payments work? | Full EMV support — chip insert, NFC tap, and magnetic stripe swipe |
| How does palm vein link to payment? | Palm IS the payment method — scanning palm triggers PayShap payment directly, no card or PIN needed |
| Does the terminal need to work offline? | Limited offline — small transactions queued (R500 cap), larger ones blocked until connectivity restores |
| Where does palm enrolment happen? | Both at the terminal (walk-up) and via a separate mobile app or web portal |
| Single merchant or multi-merchant? | Fleet/chain — one merchant operating many terminals across multiple locations |
| What receipts does the terminal produce? | Digital only — SMS or email, no built-in printer |
| Does the terminal support refunds? | Yes, but manager PIN/authorisation required at the terminal |
1. Executive Summary
This proposal outlines a next-generation payment terminal solution that combines traditional card payments with palm vein biometric payments — allowing customers to pay by simply placing their hand over a scanner.
The solution runs on an Android-based payment terminal with an integrated palm vein scanner and card reader. Payments are settled in real time through PayShap, South Africa’s instant payment rail, delivering sub-10-second settlement for every transaction.
Value Proposition
| For Customers | For Merchants |
|---|---|
| Pay without a card, phone, or wallet | Faster checkout — under 5 seconds per palm transaction |
| No PINs to remember for palm payments | Reduced card fraud exposure |
| Works even if they forgot their card | Lower interchange fees via PayShap vs card networks |
| Secure — palm veins cannot be copied or stolen | Fleet-wide management from a single dashboard |
| Enrol once, pay at any terminal in the chain | Offline resilience — never lose a sale |
How It Works (30-Second Overview)
- Customer enrols once — scans their palm at a terminal or via a mobile app, links it to their bank account
- At checkout — merchant enters the amount, customer chooses palm or card
- Palm payment — customer places hand on scanner, funds transfer instantly via PayShap
- Card payment — customer taps, inserts, or swipes their card as usual
- Receipt — digital receipt sent via SMS or email
2. The Problem
Traditional payment terminals offer only card-based payments. This creates several challenges:
- Friction at checkout — customers fumble for cards, enter PINs, wait for authorisation
- Card dependency — no card means no sale (forgotten wallet, lost card, declined card)
- Fraud risk — card skimming, stolen card numbers, and counterfeit cards remain persistent threats
- High fees — card network interchange fees eat into merchant margins
- No differentiation — every competitor offers the same card terminal experience
3. The Solution
v3 architectural pivot. The terminal is now a thin client. The only payment-domain logic it contains is the palm-vein capture and local 1:N match (required by POPIA because templates must not leave the device). Everything else — rail selection, EMV authorisation, fraud scoring, refunds, disputes, receipt delivery, audit — is an API call to the central Payments Gateway. New rails are added on the server side through the Rail Registry; the device does not change when rails change.
3.0 Deployment shape
| Layer | What lives here in v3 | What used to live here in v2 |
|---|---|---|
| Terminal (Android, Kotlin) | Base UI (amount entry, method selection, receipt display). Palm-vein SDK + on-device template store + 1:N match. Card-reader hardware passthrough (PCI-SPoC; no EMV kernel). Signed PGW API client (mTLS + device attestation). Small offline UI buffer (display only — no payment decisioning offline). | All of the above plus EMV kernel, PayShap SDK calls, card gateway calls, fraud scoring, audit, receipt delivery, offline payment queue with risk limits. |
| Payments Gateway (PGW) | Uniform API. EMV kernel (cloud). Rail Registry. Routing policy engine. Fraud engine. Refund + dispute lifecycle. Receipt service. Audit logger. Offline reconciliation (replays device-buffered intents). Fleet management. | Minimal — was essentially just routing and config. |
| Rail adapters (plugins behind PGW) | payshap (ZA_RPP), card (Visa/Mastercard), future: rtgs, swift, stablecoin, any client-registered rail. Each adapter implements the RailAdapter contract. | PayShap and card were hard-wired integrations, not pluggable adapters. |
3.1 Palm Vein Payment (Primary Innovation)
Palm vein recognition uses near-infrared light to map the unique vein pattern inside a person’s hand. The integrated scanner (SDPVD310API SDK) captures palm images at 15-30cm distance. 1:N match runs on the device against the locally-stored template set — templates never leave the terminal, satisfying POPIA s.26 (special personal information).
Unlike fingerprints or facial recognition:
- Cannot be forged — vein patterns are internal and invisible to the naked eye
- Cannot be stolen — no physical token to lose or copy
- Cannot be replicated — each person’s vein pattern is unique, even between identical twins
- Contactless — hand hovers 15-30cm above the scanner (hygienic)
- Self-improving — templates auto-update on each successful match (
SD_API_Match1VNEx) as vein patterns change over time
Technical flow when a customer scans their palm (v3):
| Step | What happens | Where | Time |
|---|---|---|---|
| 1 | Scanner captures palm image and extracts vein features (SD_API_ExtractFeature) | Terminal | < 1s |
| 2 | Feature compared against all enrolled templates (SD_API_Match1VN — 1:N match) | Terminal | < 0.5s |
| 3 | Matched template_id + device attestation sent to PGW: POST /v1/palm-sessions/resolve | Terminal → PGW | < 0.3s |
| 4 | PGW resolves template_id → linked payment proxy (server-side linkage table, not biometric data) | PGW | < 0.2s |
| 5 | Terminal calls POST /v1/payments with amount + palm-session-token | Terminal → PGW | — |
| 6 | PGW selects rail via Rail Registry policy, invokes adapter, returns settlement status via webhook or long-poll | PGW → rail | < 10s end-to-end |
Total end-to-end: under 10 seconds (SLA enforced by the gateway, not by any specific rail).
Privacy note. Only
template_id(an opaque reference) crosses the network boundary. The biometric template itself never leaves the device.
3.2 Card Payment (Full Compatibility, Cloud-EMV)
The terminal’s built-in card reader is a PCI-SPoC passthrough. It captures card data and forwards it to the PGW over a secure channel. The EMV kernel, authorisation logic, tokenisation, and card-network integration all run in the gateway.
| Method | Terminal role | Gateway role |
|---|---|---|
| EMV chip | Read chip data, capture PIN on secure keypad (SPoC) | Run EMV kernel, authorise with issuer, tokenise |
| Contactless/NFC | Read NFC data (Visa payWave, Mastercard Contactless, Apple Pay, Google Pay) | EMV kernel, authorisation, tokenisation |
| Magnetic stripe | Read stripe data | Authorisation (legacy fallback) |
Raw card data is never persisted on the terminal; card data is in transit only and is tokenised the moment it reaches the gateway. Full PCI DSS Level 1 compliance is centralised at the PGW, not the device.
3.3 Payments Gateway API (PGW) — Single Integration Point
Every payment action the terminal performs is one of these API calls:
| Endpoint | Purpose | Called by terminal |
|---|---|---|
POST /v1/palm-sessions/resolve | Turn a local template_id into a server-side palm_session_token | Before palm payment |
POST /v1/payments | Submit a payment intent — rail chosen by server-side policy | All payments |
GET /v1/payments/{id} | Poll payment status (fallback to webhook) | After submit |
POST /v1/refunds | Initiate refund (manager PIN attached) | Refund flow |
POST /v1/disputes | Log a dispute on behalf of a customer | Dispute flow |
POST /v1/receipts | Request SMS/email receipt delivery | End of flow |
POST /v1/enrolments | Register a new palm template linkage (template_id + proxy) | Enrolment flow |
POST /v1/device/heartbeat | Fleet heartbeat + health metrics | Every 60s |
GET /v1/device/config | Pull merchant config, rail display rules, limits | On boot + push-reload |
All endpoints are:
- HTTPS + mTLS — client cert per device
- Device-attested — signed device-attestation header bound to a TPM-backed key
- Idempotent — client-generated
Idempotency-Keyper intent - Versioned —
/v1/prefix; breaking changes gated by version
3.4 Rail Registry — Pluggable Payment Rails
v3 treats “payment rail” as a first-class pluggable concept. The gateway loads a registry of RailAdapter implementations, each satisfying a uniform contract:
interface RailAdapter {
id: string // e.g. "payshap", "card", "rtgs"
display_name: string
supports(intent: PaymentIntent): bool
quote(intent): FeeQuote // optional pre-trade fee preview
execute(intent): PaymentResult
refund(original: PaymentResult, amount: Money): PaymentResult
on_webhook(event: RailEvent): void // async settlement callbacks
}
Built-in rails at launch:
| Rail | Adapter ID | Use case |
|---|---|---|
| PayShap | payshap | Real-time ZAR settlement via BankservAfrica (primary for palm payments) |
| Card | card | EMV chip / NFC / stripe via acquirer |
| RTGS | rtgs (optional) | High-value settlements (SAMOS) |
Client-registrable rails (via admin API):
POST /v1/admin/rails # register a new rail adapter
PUT /v1/admin/rails/{id}/enable # turn on for this merchant
PUT /v1/admin/rails/{id}/disable # turn off
PUT /v1/admin/routing-policy # rules: amount/region/BIN → rail choice
GET /v1/admin/rails # list registered rails
Routing policy examples:
amount < 1000 AND rail_available(payshap) → payshapcard_brand == AMEX → card (amex-acquirer)region == 'EU' AND amount > 15000 → rtgs
Adding a new rail in v3: implement the adapter, drop it in the registry via POST /v1/admin/rails, update routing policy. No device firmware change, no recertification, no new /fdl-generate call on the terminal app.
3.5 Automatic Fallback
If a palm scan fails (unregistered customer, match failure, scanner issue), the terminal automatically offers card payment as a fallback — the merchant never needs to re-enter the amount. If a chosen rail fails mid-transaction (e.g. PayShap timeout), the PGW’s routing policy can re-route to the next eligible rail without the device knowing — the device just sees a single POST /v1/payments returning success or a terminal-displayable error.
3.6 Admin Dashboard
A web application for operators, deployed alongside the PGW. It is the only UI through which humans manage the payment stack in production.
Two primary modules:
3.6.1 Transactions Console
| Capability | Description |
|---|---|
| Live transaction explorer | Searchable, filterable list of every POST /v1/payments intent — filter by merchant, terminal, rail, amount band, status, time window, card BIN, palm session, correlation ID |
| Drill-down | Per-transaction view showing the full audit chain: intent → routing decision (which rail, why) → rail adapter calls → webhook callbacks → settlement confirmation → receipt delivery |
| Refund initiation | Initiate a refund from the console (with operator auth + reason code); the refund runs through the same POST /v1/refunds path the terminal uses, so admin refunds share code with terminal refunds |
| Dispute initiation | Log a dispute on behalf of a customer (when a customer phones in, not walk-up) — calls POST /v1/disputes |
| Reconciliation state | Per-rail reconciliation view — shows matched / unmatched settlements against the audit ledger, highlights breaks for investigation |
| Export | CSV / JSON export of any filtered view for regulator or auditor requests — inherits the observability/compliance-exports contract |
3.6.2 Vendor Management
Every real-world vendor the PGW talks to — acquirers, PayShap participant banks, SMS providers, email providers, KYC/FICA providers, fraud-data providers — is a Vendor record with config and credentials managed here.
| Capability | Description |
|---|---|
| Vendor registry | List / add / edit / disable vendors. Each vendor has a type (rail / sms / email / kyc / fraud-signal), endpoint URLs, environment (prod / sandbox), and credential handle |
| Credential rotation | Credentials never appear in the UI as plaintext — only masked. Rotation is a guided flow: new credential entered, validated by calling the vendor’s health endpoint, promoted atomically, old credential revoked. Every rotation is audit-logged |
| Per-merchant override | A merchant can override a default vendor (e.g. use a different SMS provider for a specific region) without affecting other merchants |
| Health + cost dashboard | Live health ping per vendor, running cost-per-transaction, SLA compliance, last-24h error rate |
| Rail registry integration | Rail vendors (PayShap participant banks, card acquirers) plug into the rail-registry admin API already in §3.4 — this module unifies the UX for all vendor types |
Auth model: Admin Dashboard users authenticate via the existing auth/login blueprint and are authorised via access/role-based-access (roles: ops-viewer, ops-operator, vendor-admin, rail-admin, super-admin). Every mutating action is audit-logged with actor + before/after diff.
3.7 Sandbox Environment & Mock Rail
A full sandbox that mirrors production PGW, with a sandbox rail adapter that implements the full RailAdapter contract — the terminal cannot tell the difference, which is the whole point.
Why it’s a separate sandbox environment (not just a test flag):
| Concern | Approach |
|---|---|
| No real money, ever | Sandbox PGW has zero routes to any real rail — payshap / card adapters are not loaded; only sandbox is. Impossible to accidentally settle a real payment from sandbox even if misconfigured |
| Realistic contract | Sandbox PGW runs the same /v1/* API surface as prod, the same 3-gate build output, the same mTLS / device-attestation requirements. Integration testing is against a surface that matches prod byte-for-byte |
| Data isolation | Sandbox has its own audit ledger, vendor registry, merchant config, palm template store — no leakage between prod and sandbox |
| Demonstrable | Merchants, auditors, and prospective customers can be given sandbox terminals to try the full flow without any compliance exposure |
3.7.1 The sandbox Rail Adapter
Satisfies the RailAdapter contract from §3.4 but routes to an in-memory scenario engine instead of a real rail.
| Feature | Behaviour |
|---|---|
| Scenario library | Built-in scenarios: approve-fast, approve-slow, decline-insufficient-funds, decline-fraud, timeout, rate-limit, partial-success-then-webhook-reversal, duplicate-intent-409, settlement-callback-at-t+3s, settlement-callback-at-t+60s |
| Trigger selection | Scenario chosen by: (a) amount-based magic numbers (e.g. R13.37 always declines), (b) per-merchant default, (c) explicit X-Sandbox-Scenario header on the request — useful for automated tests |
| Webhook fidelity | Emits the same webhook message shape a real rail would — PayShap-style pacs.002 PaymentStatusReport for palm, EMV-style authorisation response for card — so webhook handlers are tested end-to-end |
| Configurable latency | Each scenario specifies a realistic latency distribution; sandbox can also be run in “fast” mode (sub-100ms) for CI or “realistic” mode (matches real rail percentiles) for demos |
| State persistence | A sandbox payment can be refunded, disputed, and reconciled like any other — the Mock Rail maintains full lifecycle state so the console and reconciliation flows are exercised |
| Deterministic replay | A scenario run with the same inputs produces byte-identical output (excluding timestamps) — useful for golden-file regression tests |
3.7.2 Switching a Terminal to Sandbox
Sandbox is selected at device enrolment (the terminal is provisioned with either prod or sandbox PGW endpoint + cert bundle) — it is not a runtime flag the cashier can flip, because that would be a trivial attack vector. A terminal can be re-provisioned from sandbox to prod (or vice versa) only via the Admin Dashboard’s fleet module with super-admin authorisation.
3.7.3 Demo Mode
For sales demos, the Admin Dashboard can drive a sandbox terminal through a scripted scenario timeline — e.g. “enrol palm → approve R250 palm payment → approve R1,200 card payment → refund card payment → dispute palm payment” — with UI annotations explaining each step. Demo mode is a read-only overlay on sandbox; it never mutates prod.
3.1 Palm Vein Payment (Primary Innovation)
Palm vein recognition uses near-infrared light to map the unique vein pattern inside a person’s hand. The integrated scanner (SDPVD310API SDK) captures palm images at 15-30cm distance and extracts biometric features for 1:N template matching.
Unlike fingerprints or facial recognition:
- Cannot be forged — vein patterns are internal and invisible to the naked eye
- Cannot be stolen — no physical token to lose or copy
- Cannot be replicated — each person’s vein pattern is unique, even between identical twins
- Contactless — hand hovers 15-30cm above the scanner (hygienic)
- Self-improving — templates auto-update on each successful match (
SD_API_Match1VNEx) as vein patterns change over time
Technical flow when a customer scans their palm:
| Step | What happens | Time |
|---|---|---|
| 1 | Scanner captures palm image and extracts vein features (SD_API_ExtractFeature) | < 1s |
| 2 | Feature compared against all enrolled templates (SD_API_Match1VN — 1:N match) | < 0.5s |
| 3 | Matched template resolves to linked PayShap proxy (ShapID, mobile number, or account) | < 0.5s |
| 4 | Proxy resolved to bank account via BankservAfrica identifier determination | < 3s |
| 5 | Credit push submitted via POST /transactions/outbound/credit-transfer (ISO 20022 pacs.008) | — |
| 6 | Settlement confirmed via callback (pacs.002 PaymentStatusReport) | < 10s total |
Total end-to-end: under 10 seconds (PayShap SLA mandated by scheme rules)
3.2 Card Payment (Full Compatibility)
The terminal’s built-in card reader supports all standard payment methods:
| Method | How it works | Use case |
|---|---|---|
| EMV chip | Insert card, PIN entry on terminal keypad | Primary card method — most secure |
| Contactless/NFC | Tap card, phone, or wearable (Visa payWave, Mastercard Contactless, Apple Pay, Google Pay) | Fast for low-value transactions |
| Magnetic stripe | Swipe card | Legacy fallback |
Card payments are routed through a provider-agnostic payment gateway (POST authorize → POST capture → webhook confirmation) with full PCI DSS Level 1 compliance. Card data is tokenised immediately — raw card numbers never stored on the terminal.
3.3 PayShap Payment Details
PayShap (ZA_RPP — Rapid Payments Programme) is South Africa’s real-time payment rail operated by BankservAfrica. Key parameters:
| Parameter | Value |
|---|---|
| Scheme maximum | R50,000 per transaction (raised from R3,000 in August 2024) |
| Settlement | Real-time via Reserve Bank accounts |
| Availability | 24/7 |
| API | Asynchronous — all operations return HTTP 202, results via webhook callbacks |
| Standard | ISO 20022 (pacs.008 credit transfer, pacs.002 status, pain.013 request-to-pay) |
| Proxy types | ShapID (bank-generated ID), mobile phone number, account number, Shap Name (business) |
| Participating banks | Absa, FNB, Nedbank, Standard Bank, African Bank, Capitec, Discovery, Investec, TymeBank |
Fee structure (per transaction):
| Amount | Fee |
|---|---|
| Under R100 | R1 (many banks offer free) |
| R100 – R1,000 | R5 |
| R1,000 – R50,000 | Lesser of 0.05% or R35 |
3.4 Automatic Fallback
If a palm scan fails (unregistered customer, match failure, scanner issue), the terminal automatically offers card payment as a fallback — the merchant never needs to re-enter the amount.
4. User Journeys
4.1 Customer Payment Journey
flowchart TD
A["Merchant enters amount"] --> B{"Pay with Palm or Card?"}
B -->|"Palm"| C["Customer places hand on scanner"]
B -->|"Card"| D["Customer taps, inserts, or swipes"]
C --> E["Match vein pattern & resolve PayShap proxy"]
D --> F["Card network authorisation"]
E --> G{"PayShap settlement"}
F --> H{"Approved?"}
G -->|"Settled"| I["Transaction complete"]
G -->|"Failed"| J["Offer card fallback"]
J --> D
H -->|"Yes"| I
H -->|"No"| K["Card declined"]
I --> L{"Customer wants receipt?"}
L -->|"Yes"| M["Send SMS or email receipt"]
L -->|"No"| N["Return to idle"]
M --> N
style A fill:#4a90d9,color:#fff
style B fill:#f5a623,color:#fff
style I fill:#7ed321,color:#fff
style K fill:#d0021b,color:#fff
style N fill:#9b9b9b,color:#fff
Palm payment time: ~3-5 seconds (scan + match + settle) Card payment time: ~5-15 seconds (depending on card type and network)
4.2 Customer Enrolment Journey
Customers can enrol their palm at any terminal in the fleet or via a separate mobile app:
| Step | At Terminal | Via Mobile App |
|---|---|---|
| 1 | Merchant activates enrolment mode | Customer opens app |
| 2 | Customer places hand on scanner (4 scans taken) | Customer scans palm using phone camera + attachment |
| 3 | Optionally enrol second hand | Optionally enrol second hand |
| 4 | Enter phone number on terminal keypad | Phone number pre-filled from app profile |
| 5 | Enter 6-digit OTP received via SMS | Enter OTP within app |
| 6 | Palm linked to PayShap proxy | Palm linked to PayShap proxy |
| 7 | Ready to pay at any terminal | Ready to pay at any terminal |
Enrolment time: ~2 minutes (one-time)
4.3 Refund Journey
Refunds require manager authorisation for fraud prevention:
- Manager enters their PIN on the terminal
- Look up original transaction by reference number
- Refund processed via the same method as the original payment
- Palm payment refund: reversed via PayShap
- Card payment refund: reversed via card network
- Digital confirmation sent to customer
5. System Architecture (v3 — Thin Client + Gateway + Rail Registry)
5.1 High-Level Overview
graph TD
subgraph LOCATIONS["MERCHANT LOCATIONS (Thin-Client Terminals)"]
T1["Terminal 1\nUI + Palm SDK + SPoC card reader"]
T2["Terminal 2\nUI + Palm SDK + SPoC card reader"]
TN["Terminal N\nUI + Palm SDK + SPoC card reader"]
end
T1 & T2 & TN -->|"mTLS + device attestation\nSingle PGW API"| PGW
subgraph PGW["PAYMENTS GATEWAY (PGW)"]
API["Uniform Payments API\n/v1/payments /v1/refunds\n/v1/disputes /v1/palm-sessions"]
ROUTE["Routing Policy Engine\n(amount / region / BIN → rail)"]
EMV["Cloud EMV Kernel\n(authorisation, tokenisation)"]
FRAUD["Fraud & Risk Engine"]
AUDIT["Audit Ledger\n(hash-chained)"]
RCPT["Receipt Service\n(SMS / Email)"]
FLEET["Fleet Manager\n(config, OTA, heartbeats)"]
ADMIN["Admin API\n/v1/admin/rails\n/v1/admin/routing-policy"]
API --> ROUTE --> REGISTRY
API --> FRAUD
API --> AUDIT
API --> RCPT
end
subgraph REGISTRY["RAIL REGISTRY (Pluggable Adapters)"]
PS_A["payshap adapter"]
CARD_A["card adapter\n(uses Cloud EMV)"]
RTGS_A["rtgs adapter (optional)"]
CUSTOM_A["client-registered rails…"]
end
PS_A --> PS["PayShap (ZA_RPP)\nBankservAfrica / PayInc"]
CARD_A --> CN["Card Networks\nVisa, Mastercard, AMEX"]
RTGS_A --> RTGS["SAMOS / RTGS"]
CUSTOM_A --> OTHER["future rails\n(SWIFT, stablecoin, etc.)"]
FLEET -.->|"Heartbeat + OTA config"| LOCATIONS
ADMIN -.->|"register/enable/disable rails"| REGISTRY
style LOCATIONS fill:#e8f4fd,stroke:#4a90d9
style PGW fill:#f5f5f5,stroke:#666
style REGISTRY fill:#fff4e0,stroke:#d48806
style PS fill:#7ed321,color:#fff
style CN fill:#4a90d9,color:#fff
style RTGS fill:#9254de,color:#fff
style OTHER fill:#9b9b9b,color:#fff
5.2 Component Summary
| Component | Layer | Purpose | Technology | Key Integration |
|---|---|---|---|---|
| Terminal App | Device | Base UI, palm capture + local 1:N match + on-device template store, SPoC card reader passthrough, PGW API client | Android (Kotlin), SDPVD310API (JNI) | PGW API only — no direct rail calls |
| PGW — Uniform API | Gateway | One public surface for every payment action | REST / mTLS | Versioned /v1/* endpoints, idempotency, OpenAPI |
| PGW — Routing Policy Engine | Gateway | Chooses rail per intent (amount / region / BIN / availability / merchant pref) | Rules engine | Server-side configuration via admin API |
| PGW — Cloud EMV Kernel | Gateway | EMV L2 kernel, PIN verification (SPoC-cloud), tokenisation | Certified EMV library | Acquirer APIs, card networks |
| PGW — Fraud & Risk Engine | Gateway | Velocity, risk scoring, auto-block | ML service + rules | Upstream of execute() in every rail adapter |
| PGW — Audit Ledger | Gateway | Immutable hash-chained record of every intent + state change | Append-only store | 7-year retention, SHA256 chain |
| PGW — Receipt Service | Gateway | SMS / Email delivery | SMS gateway + email API | E.164, DKIM |
| PGW — Fleet Manager | Gateway | Device config push, OTA, heartbeats, alerting | MDM + backend | Signed config, staged rollouts |
| PGW — Admin API | Gateway | Register / enable / disable rails; update routing policy | REST / mTLS | Operator console, audit trail |
| Rail Registry | Gateway plugin layer | Loads and manages RailAdapter implementations | Plugin loader | Built-in: payshap, card; client-registrable adapters |
payshap adapter | Rail plugin | ZA_RPP real-time credit push | Electrum Regulated Payments API v23.0.1 | ISO 20022 (pacs.008, pacs.002) |
card adapter | Rail plugin | Card authorisation via PGW’s Cloud EMV + acquirer | Acquirer API | PCI DSS L1 tokenisation |
rtgs adapter (optional) | Rail plugin | High-value settlement | SAMOS / BankservAfrica | ISO 20022 |
| Client-registered adapters | Rail plugin | Any future rail the merchant operator needs | RailAdapter contract | Added via POST /v1/admin/rails |
sandbox rail adapter | Rail plugin (sandbox only) | Contract-compatible mock rail with scenario engine, realistic latency, full webhook fidelity | In-memory state machine | Loaded only in sandbox PGW; never in prod |
| Admin Dashboard — Transactions Console | Web app | Live transaction explorer, drill-down, refund / dispute initiation, reconciliation, export | TypeScript / React | Calls PGW /v1/* and admin APIs |
| Admin Dashboard — Vendor Management | Web app | Registry of acquirers, SMS/email/KYC/fraud-signal providers, credential rotation, per-merchant overrides, health + cost dashboard | TypeScript / React | Calls /v1/admin/vendors/* |
| Sandbox PGW (parallel environment) | Gateway (sandbox) | Mirror of prod PGW with only the sandbox rail loaded; isolated data plane | Same gateway code, different config | Cannot reach any real rail |
| Terminal Display Buffer | Device | Stores receipt and status data for display only; not a payment queue | SQLite on-device | Flushed on display |
6. Offline Behaviour (v3 — Gateway-Mediated)
v3 change. Because the terminal has no direct rail access, it cannot settle payments offline. Offline resilience now lives at the gateway, not on the device. The device’s behaviour when the PGW is unreachable is to fail-closed — the merchant is shown a clear “payments unavailable” message. This is the safer posture for a thin client: no on-device risk limits to tune, no provisional receipts to reconcile, no offline template linkage drift.
| Scenario | Terminal behaviour | Gateway behaviour |
|---|---|---|
| PGW reachable, rail healthy | Normal flow | Normal execution |
| PGW reachable, rail degraded | Normal flow | Routing policy re-routes to alternate rail (if one qualifies) |
| PGW reachable, no rail qualifies | Show “payment method unavailable — try another” | Rejects intent with NO_ELIGIBLE_RAIL |
| PGW unreachable (device is offline) | Show “offline — cannot accept payments” | — |
| Palm match fails | Offer card fallback | — |
If a merchant requires offline capture (e.g. low-connectivity locations), the approach in v3 is to deploy an edge PGW node close to the terminals rather than re-adding offline logic to the device. Edge nodes still speak the same /v1/* API to the terminal and reconcile with the central PGW when connectivity returns. This keeps the device contract unchanged.
7. Fleet Management
For merchants with multiple locations, the solution includes centralised fleet management:
| Capability | Description |
|---|---|
| Device Registration | Register new terminals with serial number and location |
| Health Monitoring | Real-time heartbeats (60s interval), battery, scanner and card reader status |
| Remote Configuration | Push payment limits, UI settings, and feature flags to individual terminals or the entire fleet |
| OTA Updates | Staged app rollouts — test group first, then wider fleet, with automatic rollback on failure |
| Alerting | Instant alerts when terminals go offline (3 missed heartbeats) or hardware degrades |
| Decommissioning | Remote wipe of all local data when a terminal is retired |
8. Security & Compliance
8.1 Biometric Data Protection
| Measure | Implementation |
|---|---|
| Data locality | Palm vein templates stored on-device only — never transmitted to external systems |
| Encryption | All biometric data encrypted at rest using hardware-backed keystore |
| POPIA compliance | Full compliance with the Protection of Personal Information Act |
| Liveness detection | Anti-spoofing checks prevent use of fake hands or images |
| Fraud detection | 3 failed palm matches in 5 minutes triggers automatic suspension review |
| Consent | Explicit opt-in during enrolment, right to deletion at any time |
8.2 Payment Security
| Measure | Implementation |
|---|---|
| PCI DSS | Card data never stored on terminal — tokenised immediately |
| TLS 1.2+ | All backend communication encrypted in transit |
| Tamper detection | Hardware tamper triggers terminal lockdown and alert |
| Manager auth | Refunds require manager PIN — no unattended reversals |
| Idempotency | Duplicate transaction prevention on all payment rails |
| Audit trail | Every transaction state change logged with timestamp and actor |
8.3 Regulatory Considerations
| Regulation | Relevance | Approach |
|---|---|---|
| POPIA | Biometric data is personal information | On-device storage, consent-based enrolment, right to deletion. Canonical rules encoded in data/popia-compliance (Act 4 of 2013 — eight conditions, breach-notification s.22, transborder s.72, special-PI s.26-33, children’s PI s.34-35, direct-marketing s.69, automated-decision s.71). Every blueprint handling SA PII lists it in related[] as required. |
| PCI DSS | Card payment processing | Tokenisation, no card data storage, certified terminal hardware |
| SARB | PayShap participation | Integration via licensed clearing house participant |
| FICA | Customer identification | Phone number verification via OTP during enrolment |
9. Risk Assessment
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Palm scanner hardware failure | Low | Medium | Card fallback always available; fleet monitoring alerts on degradation |
| Network outage during payment | Medium | Low | Offline queue with configurable risk limits |
| Fraudulent enrolment | Low | High | OTP verification, duplicate palm detection, phone number uniqueness |
| PayShap service downtime | Low | High | Card payment fallback; retry with exponential backoff |
| Data breach (biometric) | Very Low | Very High | Templates never leave device; hardware encryption; no central biometric database |
| Customer adoption resistance | Medium | Medium | Card always available as alternative; enrolment is optional and quick |
| Regulatory changes | Low | Medium | Modular architecture allows rapid compliance updates |
10. Implementation Roadmap
Phase 1: Gateway + Rail Registry (Weeks 1-5)
- Payments Gateway uniform API (
/v1/payments,/v1/refunds,/v1/disputes,/v1/palm-sessions,/v1/enrolments,/v1/receipts,/v1/device/*) - Rail Registry (plugin loader +
RailAdaptercontract) - Built-in adapters:
payshap,card - Cloud EMV kernel integration
- Admin API: register/enable/disable rails, routing policy
- Audit ledger, fraud engine, receipt service
Phase 2: Thin-Client Terminal (Weeks 6-9)
- Android terminal UI (amount entry, method selection, receipt display)
- Palm-vein SDK + on-device template store + local 1:N match
- PGW API client (mTLS, device attestation, idempotency)
- PCI-SPoC card-reader passthrough (no EMV on device)
- Manager-authorised refund UI (calls
/v1/refunds)
Phase 3: Enrolment, Fleet, Admin Dashboard & Sandbox (Weeks 10-13)
- At-terminal palm enrolment — local template +
/v1/enrolmentslink to proxy via OTP - Fleet management dashboard (registration, monitoring, config push, sandbox/prod re-provisioning)
- OTA update pipeline with staged rollout
- Admin Dashboard — Transactions Console (live explorer, drill-down, refund / dispute initiation, reconciliation, export)
- Admin Dashboard — Vendor Management (vendor registry, credential rotation, per-merchant overrides, health + cost dashboard)
- Admin Dashboard — Rail Registry management (add / enable / disable / route rails — see §3.4)
- Sandbox PGW deployment (parallel environment, only
sandboxrail loaded, isolated data plane) sandboxrail adapter (scenario library, trigger selection, webhook fidelity, deterministic replay)- Demo Mode (scripted scenario timelines for sales demos, driven from Admin Dashboard)
Phase 4: Hardening & Launch (Weeks 13-16)
- Security audit and penetration testing (device + PGW)
- PCI DSS Level 1 certification at the gateway (device scope reduced to PCI-SPoC)
- POPIA compliance review — templates never leave device verified
- Pilot deployment at selected locations
- Performance testing under load
11. Key Metrics & Success Criteria
| Metric | Target | How Measured |
|---|---|---|
| Palm payment settlement time | < 10 seconds | End-to-end from scan to settlement confirmation |
| Palm match accuracy | > 99.5% | Successful matches / total scan attempts |
| Terminal uptime | > 99.9% | Heartbeat monitoring across fleet |
| Customer enrolment completion rate | > 80% | Completed enrolments / initiated enrolments |
| Offline queue success rate | > 95% | Successfully settled / total queued transactions |
| Transaction throughput | > 20 per minute per terminal | Load testing under peak conditions |
12. Production Readiness Assessment
12.1 Initial Coverage (Before Gap Resolution)
| Category | Status | Covered By | Notes |
|---|---|---|---|
| Authentication | Covered | auth/login | Terminal operator login |
| Authorisation | Covered | access/role-based-access | Manager vs cashier roles for refund auth |
| Transaction records | Covered | payment/pos-core | Session-based order and payment tracking |
| Reconciliation | Covered | data/bank-reconciliation | End-of-day settlement matching |
| SMS notifications | Covered | notification/sms-notifications | OTP delivery and receipt sending |
| Email notifications | Covered | notification/email-notifications | Digital receipt delivery |
| Push notifications | Covered | notification/mobile-push-notifications | Fleet alerts to administrators |
| Audit trail | Covered | observability/audit-logging | Immutable, hash-chained audit log |
| Fraud & risk | Gap | — | No fraud detection or risk scoring engine |
| Disputes | Gap | — | No chargeback or dispute lifecycle management |
| Compliance reporting | Covered | observability/compliance-exports | Regulatory-grade export for eDiscovery |
| Observability | Partial | infrastructure/terminal-fleet | Device health only; no application-level metrics |
| Encryption & keys | Partial | auth/e2e-key-exchange | Key exchange exists; no dedicated HSM blueprint |
| Customer data | Covered | data/customer-supplier-management | Customer master data with consent controls |
| Hardware: Palm scanner | Covered | integration/palm-vein | Full SDK integration |
| Hardware: Card reader | Gap | — | No standalone EMV card reader SDK blueprint |
| Resilience | Covered | payment/terminal-offline-queue | Risk-limited offline queuing |
| Operations | Covered | infrastructure/terminal-fleet | Fleet management, OTA updates, remote config |
Initial Score: 14 / 18 — 78%
12.2 Gaps Identified
| # | Gap | Why It’s Needed for Production |
|---|---|---|
| 1 | Fraud detection & risk scoring | Payment systems must detect velocity abuse, unusual patterns, and compromised accounts in real time to prevent financial loss |
| 2 | Dispute & chargeback management | Merchants must handle payment disputes, submit evidence, and track resolution within regulatory timeframes (PayShap and card network rules) |
| 3 | EMV card reader SDK | Card reader hardware integration needs the same level of specification as the palm vein scanner SDK to ensure correct EMV kernel handling |
| 4 | Application observability | Fleet monitoring covers device health but not transaction-level metrics, error rates, latency percentiles, or business dashboards |
12.3 Steps Taken to Resolve Gaps
| # | Gap | Action Taken | Result |
|---|---|---|---|
| 1 | Fraud detection | Created from scratch via /fdl-create — no upstream repo covers payment-specific risk scoring for PayShap + palm vein | Created payment/fraud-detection — velocity checks, risk scoring 0-100, auto-block at 85+, analyst review queue, blacklists |
| 2 | Dispute management | Created from scratch via /fdl-create — domain-specific to SA PayShap refund rules (UETR reference, amount constraints) and card chargeback lifecycle | Created payment/dispute-management — full dispute lifecycle with SLA (48h response, 30-day resolution), evidence deadlines, auto-resolve |
| 3 | EMV card reader SDK | Created from scratch via /fdl-create modelled on integration/palm-vein blueprint structure | Created integration/emv-card-reader — chip/NFC/stripe, EMV kernel, PIN entry (DUKPT), PCI PTS compliance, card brand detection |
| 4 | Application observability | Created from scratch via /fdl-create — general observability blueprints exist but none cover payment-specific KPIs | Created observability/payment-observability — transaction metrics, latency p50/p95/p99, alerting rules, 3 dashboard definitions, health checks |
12.4 Existing Blueprints Integrated
| Blueprint | Category | How It Fits | Link Added To |
|---|---|---|---|
login | auth | Terminal operator authentication | terminal-payment-flow |
role-based-access | access | Manager PIN auth for refunds | terminal-payment-flow |
sms-notifications | notification | OTP delivery, SMS receipts | terminal-enrollment, terminal-payment-flow |
email-notifications | notification | Email receipt delivery | terminal-payment-flow |
audit-logging | observability | Immutable transaction audit trail | terminal-payment-flow, palm-pay |
bank-reconciliation | data | End-of-day settlement matching | payshap-rail |
customer-supplier-management | data | Enrolled customer profiles | palm-pay, terminal-enrollment |
mobile-push-notifications | notification | Fleet alerts to administrators | terminal-fleet |
compliance-exports | observability | Regulatory transaction export | payshap-rail |
multi-factor-auth | auth | Manager PIN as second factor | terminal-payment-flow |
session-management-revocation | auth | Terminal session lifecycle | terminal-payment-flow |
popia-compliance | data | Canonical SA Act 4/2013 encoding — eight conditions, breach-notification, transborder, special-PI, children’s PI, direct-marketing, automated-decision | palm-pay, palm-vein, biometric-auth, terminal-enrollment, terminal-payment-flow, payshap-rail, fraud-detection, dispute-management |
12.5 Final Coverage (After Gap Resolution)
| Category | Status | Covered By | Notes |
|---|---|---|---|
| Authentication | Covered | auth/login | Terminal operator login |
| Authorisation | Covered | access/role-based-access | Manager vs cashier roles |
| Transaction records | Covered | payment/pos-core | Session-based order tracking |
| Reconciliation | Covered | data/bank-reconciliation | End-of-day settlement matching |
| SMS notifications | Covered | notification/sms-notifications | OTP and receipt delivery |
| Email notifications | Covered | notification/email-notifications | Email receipt delivery |
| Push notifications | Covered | notification/mobile-push-notifications | Fleet alerts |
| Audit trail | Covered | observability/audit-logging | Immutable hash-chained log |
| Fraud & risk | Covered | payment/fraud-detection | Risk scoring, velocity, auto-block |
| Disputes | Covered | payment/dispute-management | Chargeback lifecycle, SLA enforcement |
| Compliance reporting | Covered | observability/compliance-exports | Regulatory-grade export |
| Observability | Covered | observability/payment-observability | Transaction metrics, alerting, dashboards |
| Encryption & keys | Covered | auth/e2e-key-exchange + integration/emv-card-reader | DUKPT key management for card, hardware keystore for palm |
| Customer data | Covered | data/customer-supplier-management | Customer profiles with consent |
| Hardware: Palm scanner | Covered | integration/palm-vein | Full SDK integration |
| Hardware: Card reader | Covered | integration/emv-card-reader | EMV chip, NFC, stripe, PIN, PCI PTS |
| Resilience | Covered | payment/terminal-offline-queue | Risk-limited offline queuing |
| Operations | Covered | infrastructure/terminal-fleet | Fleet management, OTA, monitoring |
| SA PII / POPIA | Covered | data/popia-compliance | Canonical Act 4/2013 rules — pinned as required on every blueprint handling SA personal information |
| Gateway API (single integration point) | Covered (v3) | integration/payments-gateway-api | Uniform /v1/* surface; mTLS + device attestation |
| Rail Registry (pluggable rails) | Covered (v3) | payment/rail-registry | Add/swap rails without touching devices |
| Cloud EMV Kernel | Covered (v3) | payment/cloud-emv-kernel | EMV L2 kernel moved off-device to PGW |
| Thin-Client Terminal | Covered (v3) | payment/terminal-thin-client | Supersedes v1/v2 fat-terminal flow |
| Device Attestation | Covered (v3) | auth/device-attestation | TPM-backed identity, required by PGW on every call |
| Admin Dashboard — Transactions | Covered (v3) | admin/transactions-console | Live explorer, drill-down, refund / dispute initiation, reconciliation, export |
| Admin Dashboard — Vendor Management | Covered (v3) | admin/vendor-management | Vendor registry, credential rotation, per-merchant overrides, health + cost |
| Sandbox Environment | Covered (v3) | testing/sandbox-environment | Parallel PGW with data isolation, provisioned at device enrolment, no prod rail access |
| Mock Rail Adapter | Covered (v3) | testing/sandbox-rail-adapter | Full RailAdapter contract — scenarios, webhooks, latency, deterministic replay |
Final Score (v3): 28 / 28 — 100%
Note: the 9 new categories in v3 (5 architectural + 4 admin/sandbox) reflect the architectural pivot and ops surface, not newly-discovered gaps. They formalise the gateway boundary and operator tooling that v1/v2 implicitly assumed would be built ad-hoc.
This system meets production readiness requirements. All categories are covered by validated blueprints.
Appendix A: Technical Specifications
Terminal Hardware
| Component | Specification |
|---|---|
| Operating System | Android |
| Palm Scanner | Integrated near-infrared palm vein scanner |
| Card Reader | EMV chip + NFC contactless + magnetic stripe |
| Connectivity | WiFi + Cellular (4G/LTE) + Ethernet |
| Display | Touchscreen for merchant and customer interaction |
PayShap Integration (ZA_RPP — Rapid Payments Programme)
| Parameter | Value |
|---|---|
| Scheme operator | BankservAfrica (PayInc clearing house) |
| Standard | ISO 20022 compliant |
| Settlement | Real-time via Reserve Bank accounts (< 10 seconds end-to-end SLA) |
| Availability | 24/7 — always on |
| Scheme transaction limit | R50,000 per transaction (raised from R3,000 in August 2024) |
| Bank-determined limit | Actual limit set by payer’s bank — may be lower than scheme maximum |
| Proxy types | ShapID (bank-generated), mobile phone number, account number, Shap Name (business) |
| Authentication | OAuth 2.0 bearer tokens |
| API style | Asynchronous — HTTP 202 for all operations, responses via webhook callbacks |
| API format | RESTful JSON |
| Tracing | Optional traceparent and tracestate headers |
| Idempotency | Duplicate requests return HTTP 409 with original error echoed |
| Certification | Comprehensive certification and market acceptance testing required before production |
Fee structure:
| Amount range | Fee |
|---|---|
| Under R100 | R1 per transaction |
| R100 – R1,000 | R5 per transaction |
| R1,000 – R50,000 | Lesser of 0.05% or R35 |
Many banks offer free PayShap for amounts under R100 to drive adoption.
Participating banks: Absa, FNB, Nedbank, Standard Bank, African Bank, Capitec, Discovery, Investec, TymeBank, and others.
API operations (via Electrum Regulated Payments API v23.0.1):
| Operation | Endpoint | Direction |
|---|---|---|
| Credit transfer | POST /transactions/outbound/credit-transfer | Outbound |
| Bulk credit transfer | POST /transactions/outbound/bulk/credit-transfer | Outbound |
| Request-to-pay | POST /transactions/outbound/request-to-pay | Outbound |
| RTP cancellation | POST /transactions/outbound/request-to-pay/cancellation-request | Outbound |
| Refund initiation | POST /transactions/outbound/refund-initiation | Outbound |
| Status query | POST /transactions/outbound/credit-transfer/status-request | Outbound |
| Credit transfer auth response | POST /transactions/inbound/credit-transfer-authorisation-response | Inbound (callback) |
| RTP response | POST /transactions/inbound/request-to-pay-response | Inbound (callback) |
| Identifier determination | POST /identifiers/outbound/identifier-determination-report | Inbound (callback) |
ISO 20022 message types: pacs.008 (credit transfer), pacs.002 (payment status), pain.013 (request-to-pay), camt.056 (cancellation)
| Request-to-Pay lifecycle states: PRESENTED → CANCELLED | REJECTED | EXPIRED | PAID |
OpenAPI spec: https://docs.electrumsoftware.com/_spec/openapi/elpapi/elpapi.json
Palm Vein Scanner (Biometric Hardware SDK)
| Parameter | Value |
|---|---|
| Technology | Near-infrared palm vein pattern recognition |
| Scan distance | 15-30cm from device centre |
| Hand position | Centred, fingers spread naturally |
| Registration captures | 4 palm images fused into one template |
| Match method | 1:N template comparison (SD_API_Match1VN) |
| Palms per user | Up to 2 (left and right) |
| Template auto-update | On successful match via SD_API_Match1VNEx (vein patterns change over time) |
| Operation timeout | Configurable, -1 to 1000 seconds (default 30s) |
| LED indicators | Off, Red, Green, Blue (duration: 0ms permanent or 1000ms flash) |
| SDK library | SDPVD310API (native C/C++, JNI on Android) |
| Supported platforms | Windows (x86, x86_64), Linux (x86, x86_64, mips64el, aarch64), Android via JNI |
| Licence | Valid licence file required for SDK initialisation |
| Image size | ~257,078 bytes per palm vein image (proprietary binary format) |
| Initialisation | SD_API_GetBufferSize then SD_API_Init — each called exactly once at program start |
SDK API operations:
| Operation | Function | Description |
|---|---|---|
| Initialise | SD_API_Init | Set up SDK with licence, auto-update, and logging |
| Buffer sizes | SD_API_GetBufferSize | Get feature, template, and image buffer sizes |
| Open device | SD_API_OpenDev | Connect to scanner, get firmware and serial |
| Extract feature | SD_API_ExtractFeature | Capture single palm image and extract vein features |
| Register template | SD_API_Register | Capture 4 images and fuse into template |
| Match 1:N | SD_API_Match1VN | Compare one feature against N stored templates |
| Match with auto-update | SD_API_Match1VNEx | Match and automatically update template |
| Cancel | SD_API_Cancel | Cancel current extraction or registration |
| LED control | SD_API_SetLed | Control LED colour and duration |
| Close device | SD_API_CloseDev | Disconnect from scanner |
| Uninitialise | SD_API_Uninit | Release all SDK resources |
Offline Queue Defaults
| Parameter | Value |
|---|---|
| Max per transaction | R500 |
| Max queue depth | 10 transactions |
| Max total queued value | R2,000 |
| Queue expiry | 24 hours |
| Processing order | FIFO (first in, first out) |
| Retry policy | Exponential backoff, max 3 retries |
Appendix B: Feature Blueprint Reference
The complete technical specifications for each system component are defined as FDL (Feature Definition Language) blueprints. These blueprints serve as the authoritative source for implementation:
| Feature | Blueprint | Description |
|---|---|---|
| PayShap Rail | integration/payshap-rail | Real-time credit push payments with proxy resolution |
| Palm Pay | payment/palm-pay | Palm template to payment proxy linking |
| Terminal Flow | payment/terminal-payment-flow | End-to-end transaction orchestration |
| Enrolment | payment/terminal-enrollment | At-terminal palm registration |
| Fleet Management | infrastructure/terminal-fleet | Device lifecycle and monitoring |
| Offline Queue | payment/terminal-offline-queue | Risk-limited offline resilience |
| Palm Scanner SDK | integration/palm-vein | Hardware integration and template matching |
| Biometric Auth | auth/biometric-auth | Palm enrolment and authentication |
| POS Core | payment/pos-core | Session and register management |
| Payment Processing | payment/payment-processing | Multi-method transaction handling |
| Card Methods | payment/payment-methods | Card tokenisation and management |
| Payment Gateway | integration/payment-gateway | Card authorisation and capture |
| Refunds | payment/refunds-returns | Refund processing with approval workflow |
| Fraud Detection | payment/fraud-detection | Real-time risk scoring and transaction blocking |
| Dispute Management | payment/dispute-management | Chargeback and dispute lifecycle |
| EMV Card Reader | integration/emv-card-reader | Card reader hardware SDK integration |
| Payment Observability | observability/payment-observability | Transaction metrics, alerting, dashboards |
| POPIA Compliance | data/popia-compliance | Canonical SA Act 4/2013 encoding — must appear in related[] of every blueprint handling SA personal information |
| Payments Gateway API (PGW) | integration/payments-gateway-api (new for v3) | Uniform /v1/* surface — the only API the terminal calls |
| Rail Registry | payment/rail-registry (new for v3) | Pluggable RailAdapter contract, admin API, routing policy engine |
| Cloud EMV Kernel | payment/cloud-emv-kernel (new for v3) | Server-side EMV L2 kernel, PIN verification, tokenisation — card reader on device is passthrough-only |
| Thin-Client Terminal | payment/terminal-thin-client (new for v3, supersedes terminal-payment-flow) | Base UI + palm capture + SPoC passthrough + PGW API client — no direct rail calls |
| Device Attestation | auth/device-attestation (new for v3) | TPM-backed device identity, mTLS client cert, signed attestation on every call |
| Transactions Console | admin/transactions-console (new for v3) | Live explorer, drill-down, refund / dispute initiation, reconciliation, export — admin web UI |
| Vendor Management | admin/vendor-management (new for v3) | Vendor registry (acquirers, SMS/email/KYC/fraud-signal), credential rotation, per-merchant overrides, health + cost |
| Sandbox Environment | testing/sandbox-environment (new for v3) | Parallel PGW with data isolation, device-enrolment-time provisioning, no real rail access |
| Mock Rail Adapter | testing/sandbox-rail-adapter (new for v3) | RailAdapter contract — scenarios, webhooks, latency profiles, deterministic replay |
Capability imports expected on every feature blueprint (v2)
Every blueprint listed above is expected to declare the following capability imports for the 3-gate pipeline to enforce per-feature contracts:
| Capability | Path | Purpose |
|---|---|---|
code-quality-baseline | blueprints/capabilities/quality/code-quality-baseline.capability.yaml | TODO/mock/stub/placeholder anti-patterns consumed by Gate 1 |
security-baseline | blueprints/capabilities/security/security-baseline.capability.yaml | Secret patterns and insecure-default anti-patterns consumed by Gate 1 |
ai-pr-review | blueprints/capabilities/quality/ai-pr-review.capability.yaml | Cold-context reviewer contract consumed by Gate 3 |
Appendix C: Build Commands
Production Readiness Status
| Metric | Value |
|---|---|
| Initial assessment | 14 / 18 categories — 78% |
| After gap resolution | 18 / 18 categories — 100% |
| v2 (POPIA pin added) | 19 / 19 categories — 100% |
| v3 (gateway + rail registry + thin client) | 24 / 24 categories — 100% |
| v3 (with admin dashboard + sandbox + mock rail) | 28 / 28 categories — 100% |
| Total blueprints | 26 (17 v2 feature blueprints + 5 v3 architecture blueprints + 4 v3 admin/sandbox blueprints) + 1 regulatory reference (data/popia-compliance) + 3 capability imports (code-quality-baseline, security-baseline, ai-pr-review) |
| Build command | Single /fdl-build --plan docs/plans/payment-terminal-app-v3.md --targets "gateway:typescript-node,terminal:kotlin-android,admin:typescript-react,sandbox-gateway:typescript-node" — the sandbox-gateway target reuses the gateway codebase with a sandbox-only config (only the sandbox rail is loaded) |
| Post-gen enforcement | The single /fdl-build call runs Gate 1 (fake/placeholder scan) → Gate 2 (compile/type-check, per target) → Gate 3 (cold-context AI PR review) once against the full generated stack |
| Verdict | Production-ready — all categories covered, rails pluggable, all generations gated |
Remaining Gaps
None — all 18 production categories are covered. See Section 12.5 for the full coverage table.
All 4 gaps identified in the initial assessment have been resolved:
| Gap | Resolution | Blueprint Created |
|---|---|---|
| Fraud detection | Created from scratch — no upstream repo covers PayShap + palm vein risk scoring | payment/fraud-detection |
| Dispute management | Created from scratch — domain-specific to SA PayShap refund rules and card chargebacks | payment/dispute-management |
| EMV card reader | Created from scratch — modelled on integration/palm-vein SDK structure | integration/emv-card-reader |
| Application observability | Created from scratch — payment-specific KPIs not covered by generic observability | observability/payment-observability |
Pre-build validation (run before generating code):
# Verify all blueprints are valid
node scripts/validate.js
# Verify no incomplete blueprints
node scripts/completeness-check.js
If either check fails, fix the flagged blueprints before generating code.
Build Command (v3 — single /fdl-build invocation, fully automated)
v3 change. v2 ran 17 separate
/fdl-generatecalls across five phases. v3 collapses the entire build into one/fdl-buildinvocation that reads this plan, resolves every referenced blueprint, generates the full stack, and runs the 3-gate pipeline — end-to-end.
Run it (assumes a brand-new target project — no prior FDL setup required)
The target directory /home/theuns/Projects/VenaPalm is assumed to be a fresh project (may not even exist yet). The build handles its own bootstrap every invocation — idempotent, safe to re-run.
There are two entry points; pick whichever matches where Claude Code is currently open.
Entry A — Claude Code open in the FDL kit repo (most common for plan-driven builds):
/fdl-build --plan docs/plans/payment-terminal-app-v3.md \
--path /home/theuns/Projects/VenaPalm \
--targets "gateway:typescript-node,terminal:kotlin-android,admin:typescript-react,sandbox-gateway:typescript-node"
--path is the destination the generated apps are written into (created if missing). Each --targets entry becomes a sub-directory under that path: gateway/, terminal/, admin/, sandbox-gateway/.
Entry B — Claude Code open in a brand-new target project that has never seen FDL:
# one-time in a terminal, from the empty target dir
cd <your-target-project-dir>
npx ai-fdl-kit init --tool claude # installs FDL + CLAUDE.md + copies all FDL skills
# then open Claude Code in that directory and run
/fdl-build --plan <path-or-url-to-this-plan> \
--targets "gateway:typescript-node,terminal:kotlin-android,admin:typescript-react,sandbox-gateway:typescript-node"
No --path needed in Entry B — the current working directory is the target.
Either entry point kicks off the same single-invocation pipeline. The skill runs everything the plan used to list as separate manual steps:
- Auto-prep (Phase 0). Runs
node scripts/validate.jsandnode scripts/completeness-check.js. For every blueprint referenced in Appendix B, auto-patches missinguses: [code-quality-baseline, security-baseline, ai-pr-review]. For every blueprint that handles SA personal information (see §12.4), auto-patchesrelated: [{ feature: popia-compliance, type: required }]. Re-validates after patching. No manual prep, no “please run X then re-invoke” loop. - Target setup (Phase 0.6) — assumes a fresh project every time. Ensures the target directory exists (creates it if missing), initializes
package.jsonif missing, runsnpm install ai-fdl-kitinside it (same surface asnpx ai-fdl-kit— skips if already at current version), copies all.claude/skills/*into<target>/.claude/skills/so every FDL slash-command is available natively in the new project (skips skills that already exist), and writes<target>/CLAUDE.mdwith the FDL compact instructions (three-tier blueprint lookup, api-pinning contract, anti-patterns, remote blueprint API pointer). Every step is idempotent: check, then act, never clobber. The FDL kit repo itself is not copied — only the published npm package plus the skill files land in the target. - Build. Parses the plan, resolves every referenced blueprint, assembles one combined build graph across terminal / gateway / admin / sandbox-gateway, and writes code to
<target>/<target-key>/per--targets. - 3-gate pipeline. Runs Gate 1 (static fake/placeholder scan), Gate 2 (compile/type-check, per target), and Gate 3 (cold-context AI PR review) once against the full generated stack.
sandbox-gatewayandgatewayshare the same blueprints and code (differing only in which rails load), so both are gated — if the sandbox diverges from prod, the build blocks. - Auto-evolve (Phase 9). Runs
/fdl-auto-evolveautomatically in whichever context the skill is executing: in Entry A, it evolves the FDL kit repo (regenerates docs + JSON API, updates README/llms.txt metadata, commits the FDL tree). In Entry B, no FDL kit commit happens — the target project is a consumer, not the kit. In both entries, the generated app under the target is committed separately if it is a git repo; otherwise it is left as a clean working tree for the user togit initas they see fit.
Re-runs. Every invocation re-runs Phase 0.6 bootstrap. Because every step is idempotent (detect-then-act), a second /fdl-build call against the same --path is safe and cheap — it skips anything already in place and only re-does the prep + build + gate + evolve work.
On success: single consolidated FILES manifest plus the auto-evolve commit hash.
If the build returns BLOCKED
The skill handles one auto-retry. If Gate 1/2/3 trips on a blueprint defect (missing api:, fuzzy rule, missing error code) the skill patches the YAML where safe, re-validates, and re-runs the build once. If it still blocks, the findings are surfaced for human review — each finding cites the blueprint rule or capability guarantee it violated. Generator-output defects (hallucinated endpoint, mocked dependency, invented field) are fixed by re-running generation with the finding attached as context, never by hand-patching the output. tool-unavailable on Gate 2 is the only legitimate warn — install the missing toolchain rather than suppressing.
Total: one /fdl-build call → three generated apps (gateway / terminal / admin console) + sandbox-gateway parity → 3-gate pipeline → atomic commit. No manual prep, no manual post-build.