Blog7 min read

One checkout cannot pay two parties

A payment architecture was designed, reviewed and scheduled before a paragraph in our own terms of service made it unbuildable. The constraint was legal, not technical — and the append-only ledger underneath it did not care.

  • Architecture
  • Payments
  • Compliance

I had the integration designed. Tenant is the Mollie merchant, property owners are connected organizations, one hosted checkout, Mollie’s Split Payments routing array divides the money at capture. We take no applicationFee — we’re a SaaS vendor, not a marketplace. We hold only the OAuth app.

It was written down, reviewed, scheduled. It cannot be built. Not because Mollie’s API is missing a field, but because of a sentence in our own AGB — one we had tightened three weeks earlier for unrelated reasons.

This is the story of that collision, and of why unwinding it cost almost nothing.

The shape of the problem

Reynt is a booking platform for vacation rentals. It runs in two modes. In direct mode a tenant rents out their own properties and there is one party to pay. In marketplace mode the tenant is an agency: the properties belong to individual owners, the agency books them, takes a commission, and passes the rest on.

Marketplace mode has three parties in every transaction — guest, agency, owner — and exactly one of them is holding a credit card. The interesting question in any booking platform is not how do we take the money. It is whose balance does it land on, and who is liable when it goes wrong.

We already had a working answer for bank transfers: send the guest two invoices, weeks apart. The down payment goes to the agency, sized to exactly the commission. The balance goes to the owner. No money ever needs splitting, because it was never pooled. The guest pays two parties directly, which is unglamorous and completely correct.

Card payments were supposed to be better than this. One checkout, one redirect, money divided automatically. That was the plan.

The two products that ended it

Mollie offers two Connect products. Reading their documentation carefully, in sequence, the plan dies twice.

Connect for Marketplaces. The docs are unambiguous about whose money it is: “your platform is the owner of the payment”, “your rates are charged on your account directly”, and “The chargeback liability also remains on your account.” The unrouted remainder settles on the platform’s own balance — their worked example ends with “we will receive the remainder of €2,50 minus any payment fees charged by Mollie.” And routing is a capability of whoever owns the OAuth app, not of the merchant you are routing on behalf of.

The detail cuts the wrong way for us. A full chargeback reverses the routing and debits the seller automatically. A partial one does not: the amount and its fees come off the marketplace’s balance, and recovery from the seller is a separate conversation. Even in the good case, the platform is fronting other people’s money.

Connect for Platforms. Here the comparison inverts: payment ownership is “The merchant” and so is chargeback liability, and the platform earns through application fees or resell pricing — fees that pay out to whoever owns the Connect app. But the same table answers the question that actually matters in four words. Can more than one party be in a single checkout? For Platforms: “No, one merchant per payment.”

Put those together and the conclusion is unavoidable. Splitting one checkout across two parties requires the OAuth app owner to be the money owner. There is no configuration in which a third party orchestrates a split between two others while staying outside the flow of funds. The entity holding the app is the entity holding the money and the chargeback risk. Those are the same entity by construction.

Our OAuth app. Our balance. Every tenant’s chargeback liability.

Our terms carry a paragraph on the flow of funds, in substance: end-customer payments flow exclusively to accounts of the customer or the property owners; the provider maintains no account for those payment flows and receives no funds belonging to either.

We had made that paragraph vendor-neutral shortly before, to state plainly that reynt is not economically involved in guest money. It’s a commercial position — we charge a flat per-property subscription, not a per-booking commission — but it is also a regulatory one. A German SaaS vendor that starts holding third-party funds has walked into a payment-services analysis, a subprocessor entry in the AVV, and a conversation with BaFin’s definition of a payment service that I have no interest in having.

So the technically cleanest option — reynt as the marketplace, one checkout, automatic splits, settlement disappearing entirely — is rejected. Not because it’s hard. Because it would require rewriting what we promise customers about their money, and the promise is worth more than the feature.

That is the whole finding, and it took reading two product pages to reach. It would have taken considerably longer to reach it during a Mollie compliance review, six weeks into an implementation.

What we built instead

If one checkout can’t pay two parties, use two checkouts. mollie_split mirrors bank_transfer_split exactly, with each leg’s method flipped:

Leg Target Amount Mollie org
down_payment tenant total × platformFeeFactor tenant’s org
balance owner remainder owner’s org

Two hosted checkouts, arriving weeks apart — which is already what the guest experiences on the bank-transfer rail. We are not degrading the experience relative to a shipped product; we are declining to improve it in a way that would have cost us our position on funds. A cash variant keeps the same split and lets the guest settle the owner’s leg on arrival.

This is now built. Each leg is created against its own party’s Mollie organization, and because nothing in the type system stops a leg being created against the wrong one, the guarantee is pinned by an integration test that runs against a real database and asserts the two legs land on two different organizations. Owners authorize their own organization through a signed, expiring invite the agency sends them; reynt holds the OAuth application, mints short-lived tokens that act on the tenant’s or the owner’s organization, and at no point holds funds or a balance of its own.

There’s a free consolation prize. The single-party type mollie_tenant resolves to { downTarget: 'tenant', balTarget: 'tenant' } and is deliberately absent from MARKETPLACE_ONLY_PAYMENT_TYPES, so it is already legal in marketplace mode. Agencies that are happy to collect everything and settle with owners by SEPA get a card rail with zero additional work — no owner KYC, no Connect onboarding, no owner-side anything. For most agencies that is the right answer anyway.

The part that made this cheap

Discovering a constraint late is usually expensive. This one wasn’t, and the reason is worth generalizing.

Payments in reynt are an append-only event ledger. Every event records which leg it belongs to, which party it is owed to, and the provider’s own reference for it. Crucially, the party is part of what makes an event unique — not just the leg and the provider reference.

That decision was forced years ago by a different provider, one whose per-party model emitted two events under a single notification: same provider reference, different party. To record both without one overwriting the other, the party had to enter the key.

That provider is long gone from this codebase. The key it forced is exactly what now lets two independent payments coexist on one booking. The model was never about that provider — it was the first thing that forced us to admit a booking can owe money to more than one party. Once that is in the schema, the provider is a detail, which is the whole point.

The settlement arithmetic needed no changes either. calculateOwnerSettlement already handled compensation on deposit − effectiveClaim, including the awkward case where a later booking modification moves the commission away from the down payment that was already collected. Refunds run against whichever organization holds that leg, so there is no cross-organization clawback — the single largest simplification versus a real split.

The rule that follows

The record of which provider handled a payment is a discriminator and nothing else. Amounts, due dates and payment statuses are sourced from the event ledger, never from the provider’s blob.

Mutable PSP session state — the provider’s payment id, the hosted-checkout URL — lives in its own table, keyed per leg and party. It is deliberately not on the ledger, because the ledger is append-only and a checkout URL expires. Rewriting money rows to refresh a link would corrupt the one structure in the system whose immutability is load-bearing.

So: model the money, not the provider. The ledger records what is owed, to whom, by which leg. The provider records nothing that matters. When the provider changes — and it changed twice in one summer, from Stripe to nothing to Mollie — the change is confined to a strategy class and a session table.

What’s still open

One shape remains genuinely attractive and genuinely unknown: the tenant as their own marketplace, owning their own Mollie OAuth app with their owners as connected sellers, reynt integrating with tenant-supplied credentials. Money never touches us, § 9(2) survives, and per-route releaseDate could hold the owner’s share until check-in.

Whether Mollie will approve a small accommodation agency as a marketplace — and whether a third party may drive that app’s OAuth flow on their behalf — is not answerable by reading more documentation. It needs a conversation with a human at Mollie. The data model leaves room for it and commits to nothing.

Owner authorization turned out to be the smaller problem than expected. Owners are records, not users — there is no owner portal and no owner login — so the flow runs on a signed, expiring link the agency sends, where the verified token rather than any URL parameter is what confines the lookup. That is built. Building a full account system for people who have never had one remains unnecessary, which is a better outcome than the one I predicted when I first wrote this down.


Reynt is a multi-tenant vacation-rental platform. The two-checkout split described here is built and covered by integration tests; what stays unbuilt — deliberately — is the single-checkout marketplace split this post argues against. Mollie’s product documentation was re-checked in August 2026 and still says what is quoted above. If any of this is wrong about their products, I’d like to know.