Fooodo / Docs

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 page

The 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:

StateWhat it means
OpenCreated, awaiting Mollie response
PendingMollie has accepted, waiting on guest
AuthorizedAuthorized but not yet captured
PaidCaptured and final
FailedTerminal failure
CancelledGuest cancelled
ExpiredMollie 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 service at /webhook/mollie — payment status changes
  • payment service → menu app at 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.

On this page