Inkress ADRsarchitecture decisions
Overview / Fees / billing
ADR 0022

Per-transaction fees are collected as an internal ledger transfer to the platform merchant

AcceptedFees / billing

Context

Once a transaction is priced with its platform and provider fees (ADR 0021), the fees have to actually be collected from the merchant. There are three broad ways: net them out of the gateway's settlement to the merchant, invoice the merchant separately after the fact, or move them on our own books. Netting from settlement depends on each processor supporting fee deduction and on its payout timing; separate invoicing introduces accounts-receivable and dunning for money we could have taken at the source.

Decision

Collect per-transaction fees as an internal double-entry ledger movement to the platform merchant, not as a settlement deduction and not as a separate invoice.

  • Fees are ledger entries, taken at capture. When a transaction is captured, its platform_fee and provider_fee are recorded on the ledger transaction; background jobs then debit the merchant and credit the recipient/platform merchant for those amounts. The platform's own merchant account (the org's default/parent merchant, ultimately Inkress's platform merchant) is just another account in the same ledger, so fee revenue is captured in-system with full double-entry auditability.
  • Processor-independent. Because collection is a movement on our ledger rather than a gateway feature, it works uniformly across every processor (ADR 0008) regardless of whether that processor can net fees at settlement or when it pays out.
  • Recurring plan price is separate. The merchant's own subscription/plan price (what a merchant pays Inkress for their plan) is not collected this way — it is billed per period by a worker that raises a normal order and payment link. Per-transaction fees and the plan price are deliberately different mechanisms.

Consequences

  • Positive: fee revenue is tracked on our own double-entry ledger, decoupled from each acquirer's settlement timing and format, and auditable as debit/credit pairs; the platform is modelled as a merchant account like any other.
  • Positive: one collection mechanism spans all processors; onboarding a processor does not require it to support fee netting.
  • Neutral / to revisit: fees settle post-capture via background jobs, so there is a short window between the charge and the internal transfer; the ledger — not the gateway payout — is the source of truth for merchant balances, and the debit/credit jobs must run reliably. (Those fee debit/credit jobs currently run on the legacy Verk runner, not Oban — part of the same Verk→Oban migration debt as ADR 0014.)
  • Neutral / to revisit: correctness depends on the recipient/parent-merchant chain being configured (an org's default merchant resolving to the platform merchant); a mis-set recipient would credit the wrong account.

Alternatives considered

  • Net fees out of gateway settlement: rejected — it depends on each processor supporting fee deduction and couples our revenue to the acquirer's payout mechanics and timing; not all of WiPay/Lynk/Stripe/FAC behave the same, so an internal ledger transfer is uniform and under our control.
  • Invoice merchants separately for fees: rejected — it adds accounts-receivable, collections and dunning for money we can otherwise take at transaction time; a fee that is already a booked ledger debit cannot be declined later.
  • Skim fees inside the gateway charge (charge net-of-fee): rejected — it distorts the amount the customer sees versus what the merchant is owed, and does not generalise across processors; fees are computed in the calculator (ADR 0021) and moved on our ledger afterwards.
← 0021 Platform and provider fees are data-driven fee groups on per-merchant plans 0023 Authorization is DB-driven from a resolved Principal, with YAML as the fallback decider →