Payments
How payments flow through Fooodo — Mollie under the hood, tip routing, donations, and webhook reconciliation.
Fooodo splits payment handling into a dedicated service. The customer-facing menu app proxies to it; the payment service owns Mollie integration, PCI scope, and webhook reconciliation.
Supported methods
In production today:
- Card (Visa, Mastercard, Maestro)
- Apple Pay
- Google Pay
- Trustly (bank-direct in EU markets)
Cash exists as a flow but is handled entirely by waiters outside the system. Any "cash" code paths in older versions of the menu app are legacy and not used in current operations.
The payment lifecycle
1. Menu app calls POST /api/payment/create with order details
2. Payment service builds the Mollie payload (with tip / donation routing)
3. Payment service creates a Mollie payment, returns a checkout URL
4. Guest is redirected to Mollie's hosted checkout
5. Guest completes (or cancels) on Mollie
6. Mollie sends a webhook to the payment service
7. Payment service updates internal state, fires PaymentStatusChangedEvent
8. PaymentStatusChangedListener (queued) calls back to the menu app
9. Menu app updates the order, marks it Paid in R-Keeper
10. Guest is redirected to the success or cancel pageThe whole pipeline is webhook-driven and idempotent. Mollie may retry webhooks on transient failures; the handler is safe to call repeatedly.
Payment states
The payment service tracks its own state machine, parallel to the order state machine in the menu app:
| State | What it means |
|---|---|
Open | Created, awaiting Mollie response |
Pending | Mollie has accepted, waiting on guest |
Authorized | Authorized but not yet captured |
Paid | Captured and final |
Failed | Terminal failure |
Cancelled | Guest cancelled |
Expired | Mollie payment timed out |
Cancelled and Expired are recoverable — a guest can retry on the same order, which creates a new payment attempt against the same order.
Tips
Tips run through the same payment in a single charge. Internally:
- The tip amount is added as a separate line on the Mollie payload, routed through the restaurant's tip menu item code in R-Keeper.
- After payment, the tip is split out and reflected in R-Keeper as a tip line on the order.
- No separate Mollie payment is created for tips.
The "tip menu item code" in the restaurant config is what makes this work — it's the R-Keeper SKU that represents tip revenue.
Donations
The same pattern applies to donations. Fooodo has a built-in pattern for routing a donation amount to a partner organisation (a Red Cross integration is the reference example) — this is configured at the company level.
Donations are charged through the same Mollie payment but routed to a different Mollie organisation ID. From the guest's perspective it is a single transaction; from the operator's perspective the donation does not appear in restaurant revenue.
Webhooks
Two webhooks are involved:
Mollie → payment serviceat/webhook/mollie— payment status changespayment service → menu appat the menu app's webhook URL — order status updates
Both are bearer-token authenticated with separate secrets. If you are operating a Fooodo deployment, both secrets need to be set; if you are integrating against Fooodo as a partner, you will not normally see these.
What partners see
Partner integrators do not integrate with the payment service directly. The contract is:
- You call the menu app's order-creation API.
- You receive an order and (when payment is required) a checkout URL.
- You do not handle card data, do not call Mollie, do not need PCI scope.
This is one of the load-bearing reasons the payment service exists as a separate boundary.
Refunds and reversals
Refunds are handled through the Mollie back office or, for operators on the Čili Pizza deployment, through the Nova admin on the payment service. There is no customer-facing refund flow today; refunds are an operator action.