|

Storefront ordering

The guest-facing menu, cart, checkout, and order-confirmation flow in current Restaurant source.

Openfront Restaurant includes a customer-facing menu and ordering application. Current routes cover the menu, cart checkout, order confirmation, sign-in, profile, addresses, and customer order history.

Current routes

  • / for the menu and cart entry;
  • /menu/[id] for an item page;
  • /checkout for contact, delivery, payment, and review steps;
  • /order/confirmed/[id] for confirmation;
  • /account, /account/profile, /account/addresses, and account order routes.

The storefront implementation lives under app/(storefront) and features/storefront. Keystone models and custom GraphQL operations remain the backend contract.

What drives the storefront

  • StoreSettings supplies branding, locale, currency, hours, delivery rules, tax, and pickup settings.
  • MenuCategory, MenuItem, and MenuItemModifier supply menu content and customization.
  • Cart and CartItem hold checkout state.
  • PaymentCollection and PaymentSession hold the selected provider session.
  • RestaurantOrder, OrderItem, and Payment are created when checkout completes.

Current checkout sequence

Create or resume a cart

The server stores a cart ID in the _restaurant_cart_id cookie. Cart reads and writes call custom operations that check the authenticated user or the matching cart cookie before using privileged Keystone access.

Add menu items and checkout details

The cart stores item quantities, modifiers, special instructions, contact data, pickup or delivery mode, and delivery fields. Guest contact submission currently creates a user record when no signed-in user exists and connects it to the cart.

Validate delivery and calculate totals

Checkout normalizes the address and checks the configured delivery mode, country, postal code, and minimum. completeActiveCart recalculates tax, tip, pickup discount, delivery fee, and total from current cart and StoreSettings data.

Select and confirm payment

The storefront initiates a PaymentSession. Stripe confirms card payment in the browser; PayPal uses its order approval flow; the manual provider records pending payment. Order completion checks or captures non-manual payment through the configured adapter before creating the order.

Create the order

completeActiveCart creates the RestaurantOrder, its OrderItem rows, a Payment, the cart-to-order link, and kitchen tickets for kitchen-active statuses. The confirmation page then reads the resulting order.

Checkout mutation

After a payment session has been selected and, where required, approved by the provider, the storefront completes the cart with this custom operation:

mutation CompleteRestaurantCart($cartId: ID!, $paymentSessionId: ID) {
  completeActiveCart(cartId: $cartId, paymentSessionId: $paymentSessionId) {
    id
    orderNumber
    status
    secretKey
  }
}

Use the generated schema.graphql from the same source revision. Do not replace this workflow with unrestricted generated CRUD from an untrusted client.

Order access

getCustomerOrder(orderId, secretKey) returns an order to its authenticated customer. It also accepts the exact secretKey for an order that has one. Treat that value as a credential: keep it out of analytics, logs, referrers, screenshots, and shared caches, and test that wrong-user and wrong-secret requests return no order details.

Current payment paths

The storefront renders Stripe, PayPal, and manual payment controls when their provider sessions are selected. Availability still depends on installed provider records, environment configuration, credentials, adapter responses, and the exact checkout path being tested.

Current limitations

Current completion performs several privileged writes and provider calls without one encompassing database transaction or a caller-supplied idempotency key. Test retries, duplicate submission, partial order/item/payment writes, provider reconciliation, and kitchen-ticket recovery before accepting live orders.

  • The application exposes no /api/categories, /api/menu-items, or /api/orders/* REST routes in current source. External clients should use reviewed GraphQL operations or add a separately authenticated API contract.
  • The customer storefront does not include a reservations flow.
  • A cart cookie contains the cart ID rather than a separate scoped cart token; cookie security and cross-owner negative tests remain important.
  • Provider support in the UI does not establish end-to-end readiness for refunds, webhooks, retries, or reconciliation.
  • Generated list CRUD remains distinct from the safer custom cart and order workflows and should not be exposed merely to simplify another client.

Where to go next

On this page