Skip to main content
Version: 4.0 preview

Latest features: payments and commerce

Version
4.0 preview
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