Add independent payment-service (port 8012, payment_db) with foundation licensing, BYO-PSP, merchant accounts, idempotent requests, callbacks, and immutable ledger. Co-authored-by: Cursor <cursoragent@cursor.com>
5.3 KiB
ADR-020: Independent Enterprise Payment Platform Service (Torbat Pay)
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-07-27 |
| Deciders | Platform Architecture |
| Supersedes | — |
| Superseded by | — |
Context
Every TorbatYar vertical — Accounting, CRM, Hospitality, Sports Center, Marketplace, Delivery, Experience, Healthcare, Beauty Business, Automation, and future services — needs payment capture without embedding PSP credentials, callback handling, or transaction ledgers inside each service.
Tenants require two commercial modes:
- Bring Your Own PSP (BYO-PSP) — connect tenant-owned gateways (ZarinPal, IDPay, NextPay, Pay.ir, Mellat, Parsian, Saman, …).
- Torbat Pay Merchant — Torbat Pay acts as payment facilitator; tenants onboard as sub-merchants under platform settlement rules.
TorbatYar already isolates shared platforms as independent services (Communication ADR-012, Loyalty ADR-011, Delivery ADR-015). Payment capture, PSP routing, verification, and payment-domain ledger must follow the same pattern. Accounting owns financial journals via Posting Engine ADR-010; Payment must never create journal entries locally.
International PSP expansion is not in current scope but the provider adapter architecture must allow future regions without vertical changes.
Commercial product name: Torbat Pay.
Decision
- Introduce
paymentas an independent Enterprise Payment Platform service with sole ownership ofpayment_db(ADR-001). - Permission prefix:
payment.*. Publish-only domain events:payment.*. - Row-level multi-tenancy via
tenant_id(ADR-003). - PSP implementations sit behind provider protocols/adapters owned by Payment; verticals never call PSP APIs directly.
- Consume Accounting, CRM, Loyalty, Communication, Core, and all vertical services only via REST API and Events — never shared tables or foreign model imports.
- Financial journals only through Accounting Posting Engine (ADR-010). Payment emits settlement/reconciliation intents; Accounting posts externally.
- Customer payment notifications (receipt SMS/email) only through Communication (ADR-012).
- Vertical services (Hospitality POS, Marketplace checkout, Sports Center membership, Experience widgets, etc.) hold commercial context refs only (
order_ref,invoice_ref,checkout_session_ref) and delegate capture to Payment APIs. - Implementation phases registered as
payment-14.0–payment-14.10in phase-manifest.yaml; roadmap in payment-roadmap.md. Phase numeric 14.x is the Payment track identifier; it is distinct from Beauty Business phases (beauty-business-14.x). - API port 8012 reserved; service path
backend/services/payment. - UI for checkout, merchant admin, and reconciliation dashboards lives in
frontend/only (ADR-002); this service exposes APIs and events. - No international PSP implementation in initial HIGH-priority phases; adapter registry and credential schema must be region-aware for future expansion.
Consequences
Positive
- One reusable payment platform for all current and future TorbatYar services
- Enforceable PSP credential isolation and callback verification in one place
- BYO-PSP and Torbat Pay Merchant modes without duplicating integration code per vertical
- Clear boundary with Accounting (money movement vs financial posting)
Negative
- Additional deployable service and database
- Eventual consistency between payment status and vertical order state
- PCI scope management for facilitator mode requires operational discipline (documented, not implemented in registration)
Neutral
- Registration (this ADR + manifests) precedes business code;
payment-14.0implements the scaffold - Loyalty wallet/gift-card money flows remain in Loyalty; Payment handles external PSP money-in only unless explicitly integrated later
Alternatives Considered
- Payment subsystem inside Core — rejected (violates business-module boundaries; couples platform billing with tenant commerce payments).
- Payment tables inside Accounting — rejected (Accounting owns posting semantics, not PSP routing/callbacks).
- Per-vertical PSP integrations — rejected (duplicates credentials, callbacks, idempotency, and audit across Hospitality/Marketplace/Sports Center).
- Third-party payment orchestration SaaS only — rejected for primary path (tenant BYO-PSP and facilitator control require owned platform).