Skip to content

Payments

Purpose

The bridge to external payment providers. Two gateways are supported, LiqPay and Mono. For each company, one config row in Payment settings picks the platform and binds credentials; the gateway sibling table holds the platform-specific configuration.

Every money movement that touches a gateway gets a Payment row — booking pay, pass purchase, wallet top-up, subscription payment. The polymorphic source_id + source_type columns identify what was being paid for.

Boundaries

  • In: gateway settings, gateway-level payment records, webhook idempotency.
  • Out:
    • In-app money flow (balance changes, refund workflow) — Wallet. Wallet transaction and gateway Payment are two perspectives on the same movement.
    • What is being paid for — referenced polymorphically. The Payment row does not own its source.
    • SaaS subscription billing state — Billing. A subscription payment may produce a gateway Payment row here.

Entities

IDEntityRole
ENT-036Payment settingsPer-company config row — picks platform, binds credentials
ENT-033LiqPay paymentLiqPay-specific config tied to a payment_settings row
ENT-034Mono settingsMono-specific config tied to a payment_settings row
ENT-035PaymentGateway-level money movement — polymorphic source

Backend implementation

  • Module: libs/features/payments/
  • Surface routing: payments-client.module.ts (user-facing checkout + status), payments-admin.module.ts (admin reads + config).
  • Drizzle schema: libs/shared/data-access-db/src/lib/schema/payments.schema.ts (pgSchema('payments')).

Cross-context relationships

  • This context owns the payments.* Postgres schema.
  • This context references:
    • Companies via payment_settings.company_id (no DB FK — cross-schema, see ADR).
    • Everywhere via payment.source_id + payment.source_type — polymorphic, no FK (ADR applies). Possible source types include booking, pass purchase, wallet top-up, subscription payment.
  • This context is referenced by:
    • Wallet via wallet_transaction.source_type='gateway' indirection (no FK to payment.id; in-app and gateway ledgers are independent).
    • Billing indirectly through the polymorphic source mechanism.

Open questions

  • OPEN: paymentSettings (company_id, platform) lacks UNIQUE — see Payment settings.
  • OPEN: payment.external_id lacks a partial UNIQUE — webhook idempotency relies on the handler; see Payment.
  • The gateway-level Payment row and the in-app Wallet transaction are two records of the same money movement and may drift if a webhook is missed — there is no DB-level cross-check.