Skip to main content
Version: 3.x

Latest features: payments and commerce

Version
3.x
Updated
View markdown

Taking money is a subsystem now, not a folder of connectors. The payment layer was designed from requirements rather than grown from the old connector code: a payment is a record, every fact about it is an event, exactly one class may change its state, and providers declare what they need rather than carrying their own admin plumbing.

Back to Latest features


The five ideas

  1. A payment is a first-class record. One row per attempt against an order — provider, flow, state, amount in minor units, currency, the provider's reference. An order can have several attempts and the ledger shows them all.
  2. Every fact is an event. An append-only log of every state change, API call and response, webhook (verified or refused) and admin action, with redacted payloads. Nothing about a payment is ever knowable only from a file on disk.
  3. One state-writer. A single service enforces the legal transitions, verifies amounts against the order, and deduplicates webhooks by a unique key — replay-safe by schema, not by discipline. Providers never touch the database.
  4. Providers declare, the framework renders. A provider is one self-contained directory: identity, declarative config fields (the admin form renders itself), the flow it runs and the settlement channels it supports.
  5. The customer's money state is never taken from the request. A return URL carries ids, never outcomes. A forged ?state=paid does nothing.

Three flows, and a taxonomy behind them

What the customer experiences comes in exactly three shapes — redirect (we send the browser to the PSP and it comes back), onsite (the PSP's own JS renders the payment UI into our page; the card number never touches CBX) and instructions (no interactive step — payment instructions with a structured reference, settlement arrives later).

Every PSP form factor maps onto one of those three. New form factors get a mapping, not a fourth flow. And on-site card entry is always the PSP's JS rendering into our page — nothing that would put a card number through CBX is ever in scope.

Settlement arrives three ways

Return-verify, signed webhook, and polling — because in practice a provider gives you some subset of those and never quite the one you planned on. configbox:payments:poll is the scheduled half, for providers whose truth only becomes available by asking.

Fourteen providers — and their real status

The roster is deliberately geographic: one integration per market where a local rail is what customers actually use, rather than one global card processor and a shrug. It covers Stripe, PayPal, Mollie, Klarna, Adyen, Authorize.Net, Saferpay, Nexi, PayU, Square, Trustly, PostFinance, the Swiss QR-bill, and DemoPay — the fake, for development, demos and end-to-end tests.

The documentation states each provider's verification status rather than implying it. Some are shipped and live-verified; several are wired against the provider's documented API but have never seen merchant credentials, and say so. Read the roster before promising a client a rail.

Provider secrets are handled properly

Settings a provider declares as secret are encrypted at rest, with the key derived from the host platform's own secret — so a database dump alone cannot decrypt them; it takes the config file too. The admin form never echoes a stored secret: the field renders empty with a value is stored placeholder, the serialized record is redacted, and a blank submission means unchanged while a typed one replaces. Those two halves are one design — shipping only the no-echo field would make every save wipe the API key.

The admin side

A filterable payment ledger with a full event timeline per payment, the provider's references, and the actions an operator needs — mark settled, mark failed, add a note, check with the provider. Plus a recent-events view for when a webhook misbehaves, and a payment-methods form that leads with the provider and swaps its fields live.

Adding a provider

There is a conformance checklist, an explicit server-side contract, and a client-side contract for on-site flows — and the invariants the framework guarantees are each stated as something tested or testable rather than asserted.

The rest of commerce

Not new in this period, but the ground the above stands on, and worth the link from here:


Next: catalog operations · platform & extensibility · back to Latest features