Payment Methods
System-level catalog of payment methods (cash, card, digital wallets, etc.) that businesses can enable for their operations.
Architecture
Hexagonal architecture with strict layer boundaries:
payment-methods/
├── domain/
│ └── payment-methods-repository.domain.ts # IPaymentMethodsRepository port + DI token
├── application/
│ └── payment-methods.service.ts # Use cases (depends on domain port only)
├── infrastructure/
│ └── payment-methods.repository.ts # Kysely implementation of the port
└── interfaces/
├── payment-methods.controller.ts # HTTP routes + Swagger + RBAC guards
├── dtos/
│ ├── create-payment-method.dto.ts
│ ├── update-payment-method.dto.ts
│ └── payment-method-with-relation-response.dto.ts
└── query/
└── paginate-payment-methods.query.ts
Domain Concepts
Payment Method
A named payment type (e.g. "Credit Card", "Cash", "PayPal") with:
paymentGroup— classification bucket (see groups below)businessId— if set, the method is business-specific; if null, it's a global/system methodisActive— soft-delete flaggeneratesAccountsReceivable/generatesAccountsPayable— financial ledger flags
Payment Groups
| Group | Description |
|---|---|
electronicAndCardPayments | Credit/debit cards, POS terminals |
digitalWalletsAndMobilePayments | Apple Pay, Google Pay, mobile wallets |
cashAndManual | Cash, checks, manual entries |
bankRelated | Wire transfers, ACH, bank deposits |
cryptocurrency | Bitcoin, stablecoins |
buyNowPayLater | Afterpay, Klarna, installment plans |
otherSpecialized | Gift cards, store credit, vouchers |
Business Payment Method (related module)
When a payment method is created with a businessId, a businessPaymentMethod junction record is auto-created to link it. Businesses can also independently enable/disable global payment methods via the business-payment-methods module.
API Endpoints
| Method | Path | Description | Auth |
|---|---|---|---|
POST | /payment-methods | Create a payment method | Create |
GET | /payment-methods | List all (paginated, searchable) | Read |
GET | /payment-methods/:id | Get by ID | Read |
PATCH | /payment-methods/:id | Partial update | Update |
DELETE | /payment-methods/:id | Delete | Delete |
GET | /payment-methods/business/:businessId | List active methods for a business | Read |
GET | /payment-methods/with-relation/:businessId | All methods + business link status | Read |
With-Relation Endpoint
The with-relation endpoint returns all active payment methods (global + business-specific) with a LEFT JOIN to businessPaymentMethod. This tells the caller which methods the business has enabled. Results are sorted: linked methods first (bpmOrder: 1), then unlinked (bpmOrder: 2), alphabetically by name within each group.
Design Decisions
-
Global vs. business-specific methods: Methods with
businessId = nullare global (available to all businesses). Methods with abusinessIdare custom to that business. -
Auto-link on creation: When creating a method with a
businessId, the repository automatically inserts abusinessPaymentMethodrecord. This keeps the two tables consistent without requiring two API calls. -
DI via Symbol token: The service depends on the
IPaymentMethodsRepositorydomain interface viaPAYMENT_METHODS_REPOSITORYSymbol token, not the concrete Kysely implementation. This enables testing with mock repositories. -
RBAC: All endpoints are protected by
RolesGuardwithPolicyResource.PaymentMethod+ per-endpointPolicyAction.