TorbatYar/docs/architecture/adr/ADR-020.md
Mortezakoohjani 9fac160258 feat(payment): ship Torbat Pay MVP phases 14.0-14.5
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>
2026-07-27 17:57:04 +03:30

5.3 KiB
Raw Permalink Blame History

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:

  1. Bring Your Own PSP (BYO-PSP) — connect tenant-owned gateways (ZarinPal, IDPay, NextPay, Pay.ir, Mellat, Parsian, Saman, …).
  2. 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

  1. Introduce payment as an independent Enterprise Payment Platform service with sole ownership of payment_db (ADR-001).
  2. Permission prefix: payment.*. Publish-only domain events: payment.*.
  3. Row-level multi-tenancy via tenant_id (ADR-003).
  4. PSP implementations sit behind provider protocols/adapters owned by Payment; verticals never call PSP APIs directly.
  5. Consume Accounting, CRM, Loyalty, Communication, Core, and all vertical services only via REST API and Events — never shared tables or foreign model imports.
  6. Financial journals only through Accounting Posting Engine (ADR-010). Payment emits settlement/reconciliation intents; Accounting posts externally.
  7. Customer payment notifications (receipt SMS/email) only through Communication (ADR-012).
  8. 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.
  9. Implementation phases registered as payment-14.0payment-14.10 in 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).
  10. API port 8012 reserved; service path backend/services/payment.
  11. UI for checkout, merchant admin, and reconciliation dashboards lives in frontend/ only (ADR-002); this service exposes APIs and events.
  12. 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.0 implements the scaffold
  • Loyalty wallet/gift-card money flows remain in Loyalty; Payment handles external PSP money-in only unless explicitly integrated later

Alternatives Considered

  1. Payment subsystem inside Core — rejected (violates business-module boundaries; couples platform billing with tenant commerce payments).
  2. Payment tables inside Accounting — rejected (Accounting owns posting semantics, not PSP routing/callbacks).
  3. Per-vertical PSP integrations — rejected (duplicates credentials, callbacks, idempotency, and audit across Hospitality/Marketplace/Sports Center).
  4. Third-party payment orchestration SaaS only — rejected for primary path (tenant BYO-PSP and facilitator control require owned platform).