Fast-casual ordering is not e-commerce. That sentence sounds obvious until you're three months into building the payment and POS layer and you keep reaching for e-commerce abstractions that don't fit. Cart versioning, modifier-level pricing, tip handling at minor units, card-on-file behavior in a loyalty context, real-time menu state during a lunch service, none of these map cleanly onto the mental models that Shopify and Stripe documentation are built around. Here's what the three integrations we've shipped actually look like from the inside.
Foodics: Deep Two-Way Sync
The integration shape. Foodics is the closest thing in our stack to a full data partnership. We read menu items, modifier groups, branch configuration, and order events from Foodics, and we write loyalty state, redemption events, and in-app order submissions back to it. Two-way, live, with the ordering surface in the branded app acting as a front-end for the Foodics order management layer rather than a parallel order system.
Modifier groups required careful schema work. Foodics modifier groups have nested structure: a group has a name, a required flag, min/max selection counts, and a set of options each with a name, price delta, and availability state. Mapping this into a relational schema that supports fast reads at order time and clean writes when the menu updates required migration 0096, which added a unique constraint on modifier group composition to prevent duplicate modifier sets from being written on rapid sync cycles. The constraint sounds defensive; it existed because a fast menu-sync cycle on a branch with 80+ modifiers was producing duplicate rows on 3-4 modifiers per run.
Sort order exceeded integer bounds. Foodics uses large sort order integers on menu items, in some cases values above 2,147,483,647, which is the signed 32-bit integer ceiling. Our original schema used a standard integer column for sort order. Migration 0097 widened it to bigint. This is the kind of bug that sits dormant until you onboard an operator whose menu was built with bulk sort-order imports rather than sequential assignment. It's not subtle when it fires, sort order silently wraps and the menu renders in completely wrong order, but it's invisible until the right data shape arrives.
Cart versioning for concurrent sessions. The in-app ordering flow allows a guest to build a cart, navigate away, return, and resume. On high-traffic lunch sessions, we occasionally saw concurrent modifier updates to the same cart from the same session, a timing issue in the optimistic update path. Migration 0099 (orders_cart_version_unique) added a version column to cart rows and a unique constraint on cart_id + version, enforcing last-write-wins semantics with a detectable conflict error rather than a silent overwrite. The branded app handles the conflict by re-presenting the current cart state to the guest and letting them re-confirm.
Square: Card-on-File and the Digital Wallet Layer
The integration shape. Square's integration center of gravity is payments, not menu management. The Square integration we've shipped handles card-on-file storage for returning guests, Apple Pay, Google Pay, and a saved-card wallet that surfaces in the checkout flow. Square's loyalty layer exists but is not what we integrate with , Habitu replaces it with our own loyalty engine, using Square only for payment execution.
Card-on-file in a loyalty context has specific requirements. When a loyalty guest returns to the app, we want their saved card to surface immediately in checkout, not as a generic "use saved card" button, but as a named, last-four-digit-visible option that matches their mental model of the card they used last time. Square's customer card API supports this; the integration requires storing Square's customer_card_id in the Habitu guest profile and presenting it through a UI that matches Apple's Human Interface Guidelines for saved payment methods. The implementation is not complex, but getting the UX to feel like a native wallet rather than a "saved card" field in a web form required careful attention to the checkout sheet design.
Apple Pay and Google Pay are not optional for US fast-casual. In the US, tap-to-pay behavior from mobile users is high enough that an app without Apple Pay and Google Pay support will see measurably lower checkout conversion. Both are now live on the Square payment path. The integration follows Square's Web Payments SDK flow, the payment request object, the tokenization step, the charge call. Standard, but it required separate testing on real Apple Pay-enrolled devices; simulator testing does not cover the full tokenization path.
Stripe: Payment Intents, 3DS, and Tips at Minor Units
The integration shape. Stripe handles direct payment flows for the markets where Square isn't the POS of record, primarily for Habitu's own direct-order surface in MENA, and as a fallback payment layer for operators whose POS doesn't handle app-originated payments natively. The integration uses Stripe's Payment Intents API with automatic payment methods.
3DS handling is not optional in MENA. Strong Customer Authentication requirements in the UAE and Saudi Arabia mean that a non-trivial fraction of card transactions will trigger a 3DS challenge. The Stripe integration handles this via the payment intent confirmation flow: if the intent returns a requires_action status, the app presents the 3DS challenge iframe, waits for confirmation, and then re-polls the intent status before marking the order as paid. Skipping this flow produces silent payment failures on 3DS-enrolled cards, the charge appears to succeed on the client side, the intent never confirms on the server side, and the order sits in a limbo state. We caught this in staging with a test card configured for 3DS; it's not subtle once you look for it.
Tip handling at minor units. Tips in fast-casual are handled differently than in full-service. The app presents a tip prompt post-checkout (not pre-authorization), which means the tip amount is a separate charge or a capture adjustment against the original authorization. Stripe's capture flow allows adjusting the captured amount up to the authorized ceiling, which is the clean path. Migration 0100 (tip_minor_bounds) added a constraint ensuring tip amounts are stored and processed in minor currency units (fils for AED, halalas for SAR, cents for USD) with upper bounds validation, a tip that exceeds 50% of the order total is flagged for manual review rather than silently authorized. This constraint exists because tip-prompt UX bugs can produce malformed tip values, and a malformed tip that gets charged is a guest experience failure that's much harder to recover from than a declined payment.
The Lesson: Fast-Casual Ordering Is a Distinct Problem Shape
E-commerce checkout is: add to cart, enter address, enter card, confirm, done. The cart is static between sessions. The items have fixed prices. The payment is a single synchronous charge. Shipping handles the post-payment complexity.
Fast-casual ordering is: build a cart with nested modifiers in real-time, against a menu that changes during service, at a location that may have items sold out, for a guest whose loyalty balance affects pricing, with payment methods that include tap-to-pay flows requiring device-native handling, in a region that may require strong authentication, with a tip amount settled after the primary payment. Each of those clauses is a failure mode if you treat it as an e-commerce edge case rather than a first-class requirement.
The migration trail tells the story directly: 0096 through 0100 are all fast-casual-specific requirements that have no analog in a generic e-commerce integration. Modifier uniqueness, sort order bounds, cart versioning, tip minor units, none of these appear in the Stripe or Square quickstart guides. They appear when real operators use the system at real volume during a real lunch service.
The Foodics integration strategy post covers the market reasoning behind our POS priority order. The pricing page covers what operators pay for the full stack. If you're running a fast-casual chain and evaluating whether Habitu's payment and POS integrations cover your current setup, that's the right conversation to have before any technical commitment.