Skip to main content
The Payment Service is the transaction-execution layer of PCX. It owns every payment record from creation through provider settlement, and provides the tooling needed to validate bank accounts before a payment is attempted.

What it does

Payment lifecycle — Payments are created via POST /payments. Internally the service calls the Transaction Lambda synchronously to obtain a transaction_id before persisting the payment record, so a transaction and payment always exist as a linked pair. Status progresses from initiated through optional intermediate states (reviewed, confirmed, processing) to a terminal state (completed, failed, refunded, rejected, or canceled). Admin updates go through PATCH /payments/{payment_id}; webhook-driven status transitions arrive at PATCH /payments/{payment_id}/webhook. Payment method routing — Every payment carries a payment_method (bank_transfer, card, mobile_money, crypto, or wallet) and a direction (inbound, outbound, external). The optional payment_path_method (direct, intermediary, third_party) records how the funds actually moved once the payment is processed. Provider integration — The provider field names the external payment provider (Paystack, Flutterwave, TrueLayer, YellowCard, Squad, etc.). A provider_config_id links to the active routing rule from the Provider Configs sub-system. Provider-specific data is stored in provider_data and webhook_metadata. Look-ups by external reference — Three secondary-index look-ups let internal services resolve a payment without a payment_id: by transaction_id (/payments/transaction/{transaction_id}), by provider_payment_id (/payments/provider/{provider_payment_id}), and by any reference string (/payments/reference/{reference}). Bank account validation — Before a payment is submitted, callers can validate the destination account. UK accounts can be checked by format (/bank-validation/validate-uk-account) or via full modulus checking (/bank-validation/validate-uk-account-checker). Identity verification of the account holder runs through a TrueLayer OAuth flow: initiate at /bank-validation/truelayer/auth/init, exchange the code at /bank-validation/truelayer/callback, then call /bank-validation/verify-uk-account-holder with the resulting token. Nigerian accounts are resolved against Paystack via /bank-validation/validate-nigerian-account. Org payment configsOrgPaymentConfig records let admins apply per-organisation rules: preferred payment method, crypto on/off flags, KYC tier required for crypto, country restrictions, min/max amounts, and supported currency lists. Managed via /payments/org-configs. Provider configsProviderConfig records define routing rules: which provider handles a given country + payment method + direction combination, at what priority, and with what webhook settings. Managed via /payments/provider-configs. A config can be scoped to a specific org_id or left null for global applicability. AnalyticsGET /payments/analytics returns aggregate payment metrics; GET /payments/analytics/providers breaks them down by provider. Both require internal-admin.

Key concepts

ConceptDescription
PaymentStatusinitiatedreviewed / confirmedprocessing / pendingcompleted / failed / refunded / rejected / canceled
PaymentDirectioninbound (funds arriving at PCX), outbound (funds leaving), external (third-party movement)
PaymentMethodbank_transfer, card, mobile_money, crypto, wallet
PaymentProviderOne of: paystack, flutterwave, truelayer, yellowcard, stripe, paypal, squad, sarepay, trustpayment, manual, crypto, fiat, hybrid
PaymentPathMethodHow funds were routed: direct, intermediary, or third_party
OrgPaymentConfigPer-org overrides for crypto restrictions, KYC levels, and supported currencies
ProviderConfigRouting rule mapping (country + method + direction + priority) to a provider
TrueLayer OAuthThree-step flow: /auth/init → user redirect → /callback token exchange → /verify-uk-account-holder

Authentication

All endpoints require a valid Bearer token. Admin-only endpoints (/payments/all, /payments/status/{status}, /payments/analytics, org/provider config CRUD) additionally enforce the internal-admin role via roles_required middleware. The X-User-Id header is used internally when one service calls another; end clients should rely on the Bearer token for identity.