Order flows
Pay-First versus Pay-Later — when to use each, and how each behaves end-to-end.
Fooodo supports two distinct order flows. They differ in when the kitchen sees the order and when payment happens. The choice depends on the service style, not the restaurant — a single restaurant can run different flows on different tables.
Pay-First (the older flow)
Best for: takeaway, quick service, food courts, anywhere the guest pays before eating.
1. Guest scans QR, builds cart
2. Guest pays
3. (only after successful payment) Order exported to R-Keeper
4. Kitchen prepares
5. Order delivered or collectedIn this flow, the kitchen never sees an unpaid order. R-Keeper sees a single order export per cart. If payment fails or is abandoned, nothing reaches the kitchen.
Pay-Later (the newer flow)
Best for: dine-in, table service, multi-round service where guests order over time.
1. Guest scans QR, builds cart
2. Items sent to R-Keeper kitchen immediately
3. Guest can add more rounds (each round sent incrementally)
4. When ready, guest pays
5. Order locked during payment, marked paid in R-Keeper after successEach new round is added to the same R-Keeper order — synced_to_rkeeper_at on each order item tracks which rounds have already been exported. The order stays open in R-Keeper from the first round until payment.
Why the flows differ
The mechanical difference is straightforward — Pay-First has one R-Keeper export; Pay-Later has many. The operational difference matters more:
- In Pay-First, the kitchen does no extra work if a guest abandons the cart. But the guest experience is wrong for dine-in: people don't pay before they eat, and asking them to do so feels like a takeaway counter.
- In Pay-Later, the guest experience matches a normal restaurant. But the operator has to accept that some orders may not get paid (a guest walks out, a card fails). The same risk exists with paper bills, so operators rarely flag this as new.
Configuring the flow
Flow is set per-table or as a restaurant default in the admin panel. A restaurant can mix flows — for example, dine-in tables on Pay-Later, takeaway counter on Pay-First, both running through the same Fooodo install.
States, in practice
Orders move through a state machine. Most operators only see four of these:
| State | What it means |
|---|---|
Created | Cart exists, no items submitted yet |
InProgress | Items submitted to R-Keeper, awaiting more or payment |
ReadyToBePaid | Guest finished ordering, payment pending |
Paid | Payment succeeded, R-Keeper marked paid |
Finished | Order closed, terminal success state |
There are three failure-adjacent states worth knowing:
| State | What it means |
|---|---|
Locked | R-Keeper has the order open (waiter editing). Auto-retries with backoff. |
Error | Export to R-Keeper failed after all retries. Needs manual intervention. |
Cancelled | Order was cancelled or timed out unpaid |
Most Locked states resolve in seconds. Persistent Locked usually means a waiter has the order open in R-Keeper and forgot to close it.
Error is the only state that needs human intervention. The rkeeper-errors log channel records what failed; the waiter typically resolves it by recreating the order in R-Keeper directly.
What runs in the background
A few scheduled jobs keep the order layer in sync. Operators do not need to know these by name, but integrators do:
| Cadence | Job |
|---|---|
| Every minute | Check restaurant availability in R-Keeper |
| Every minute | Cancel orders that timed out without payment |
| Every 2 min | Pull order updates from R-Keeper |
| Every 4 min | Pull table status from R-Keeper |
| Every 5 min | Sync item availability |
| Daily 09:00 | Full menu sync |
If R-Keeper is unreachable, jobs retry with exponential backoff; orders queue in their current state and reconcile when R-Keeper returns.