Card vault: suggested architecture¶
Status: suggested, for discussion. An architecture review of the card payment stack (be-pspfunding, be-psp-payment, be-psp-checkout, be-psp-fiserv, be-cde-carddata, be-cde-externalcarddata) done 15 October 2026, with a target architecture and a plan. The goal is a provider-agnostic, Braintree-like card vault that be-pspfunding and sister company Rebtel both use, with provider routing behind it. The CDE is out of scope for change.
One interactive view is attached to this page:
- Card vault architecture review: verdict, current and target diagrams, findings with code references, the fixed CDE scope and why, how it reconciles with the PSP refactor projects, the database separation rule, the Braintree-shaped interface, provider capabilities, the plan as named work packages, the TabaPay steps to take now, and open questions.
Verdict in short¶
- The box already exists in embryo: be-psp-payment (MoneyFlow.Payment) has a provider-agnostic contract, config-driven routing and per-provider adapters, and the CDE gives a PAN vault with a hosted card iframe.
- What is missing is discipline at the edges: be-pspfunding calls providers directly (Astra), routing is hard-coded in two places, 3DS is Checkout-specific and defined in be-pspfunding, PII beyond name and address flows through the box, and there is no tenant concept beyond a licence string.
- Provider state is mixed into the product database: Astra tables and an AstraVerified column live in MinorityPspFunding, and provider verification facts are duplicated on the product's ExternalCard table.
Decisions taken in the review¶
- The box is be-psp-payment plus the two CDE services exactly as they are. be-pspfunding and Rebtel are both clients of it.
- CDE stays as is. The storage/processing split (be-cde-carddata stores; be-cde-externalcarddata is the only decryptor and the only service with provider egress) is a PCI control. Any change means re-assessment. Only two exceptions: one TabaPay proxy in be-cde-externalcarddata, and a config entry for Rebtel's Checkout processing channel.
- Repos do not matter, the contract does. One repo or two is fine as long as the box has its own contract package with no Majority types, its own database, its own deployable, and a one-way dependency enforced by a build-time test.
- Databases. Box state in MoneyflowPayment, product state in MinorityPspFunding, adapters keep their own, CDE untouched. No provider table, column or enum in the product database.
- KYC stays outside the box. The box holds account-holder name and billing address plus an opaque customer reference.
- Rebtel is a separate tenant with its own merchant accounts. First version: card pull, US and EU/UK cards with 3DS via Checkout, REST plus signed webhooks plus the existing hosted card iframe.
- TabaPay is built as the first clean adapter inside the box and replaces Astra flow by flow: payment-link pull, then MPay2Anyone push, then me2me.
Relation to the PSP refactor projects¶
Linear projects PSP refactor (P-PAY-1392) and PSP Refactor (P-BCP-1159) proposed consolidating payments into be-pspfunding and merging the two CDE services. After discussion: the CDE merge step is dropped for PCI reasons; "one repo" is accepted as two apps with a hard contract; "single point for payments" is the box, not the product app. The review page has the step-by-step reconciliation.
Next steps¶
- Write the decisions above as an ADR in the Decision Log and re-scope the two Linear projects.
- Point the TabaPay tickets (GROBE-406, GROBE-407, GROBE-411 and successors) at be-psp-payment and be-cde-externalcarddata only, following the "do now with TabaPay" list in the review.
- Settle the open questions listed at the end of the review: repo choice, Rebtel's Checkout entity, recurring charges in Rebtel v1, where the public façade sits, and who owns the ADR.