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

78 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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](ADR-012.md), Loyalty [ADR-011](ADR-011.md), Delivery [ADR-015](ADR-015.md)). Payment capture, PSP routing, verification, and payment-domain ledger must follow the same pattern. Accounting owns financial journals via Posting Engine [ADR-010](ADR-010.md); 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](ADR-001.md)).
2. Permission prefix: `payment.*`. Publish-only domain events: `payment.*`.
3. Row-level multi-tenancy via `tenant_id` ([ADR-003](ADR-003.md)).
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](ADR-010.md)). Payment emits settlement/reconciliation **intents**; Accounting posts externally.
7. Customer payment notifications (receipt SMS/email) only through Communication ([ADR-012](ADR-012.md)).
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.0``payment-14.10`** in [phase-manifest.yaml](../../ai-framework/phase-manifest.yaml); roadmap in [payment-roadmap.md](../../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](ADR-002.md)); 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).
## Related Documents
- [Payment Roadmap](../../payment-roadmap.md)
- [Phase Area — Payment](../../phases/Payment/README.md)
- [Module Boundaries](../module-boundaries.md)
- [Module Registry — payment](../../module-registry.md#payment)
- [Payment Contracts Reference](../../reference/payment-contracts.md)
- [Phase Manifest](../../ai-framework/phase-manifest.yaml)
- [Service Manifest](../../ai-framework/service-manifest.yaml)
- [ADR-001](ADR-001.md) · [ADR-002](ADR-002.md) · [ADR-003](ADR-003.md) · [ADR-006](ADR-006.md) · [ADR-010](ADR-010.md) · [ADR-011](ADR-011.md) · [ADR-012](ADR-012.md) · [ADR-021](ADR-021.md)