01 · Start here
The three rules that prevent most integration bugs
- Require an address before opening Floward. The shipping address city and country decide which HOI fulfillment center, catalog, prices, stock, and slots apply.
- Use the server-provided cart and price. Never invent a Floward cart ID, send a catalog price, or reuse a price from another address.
- Prepare immediately before payment. HOI availability, price, fulfillment center, and delivery slot can change; the prepare result is the mobile UI’s final checkout gate.
02 · Mental model
Address → fulfillment center → sellable catalog
Floward is not a conventional fixed-price Medusa catalog. Barq stores the product identity and content; HOI supplies the location-specific sellability and binding price.
city + country
resolved by name
current policy
stock + price
When the address changes
Reload the Floward home/cart state and treat the previous product prices and selected delivery slot as stale. The backend re-resolves the city and center; products may disappear, prices may change, and a slot from the old center becomes invalid.
03 · Home & browsing
Bootstrap Floward with one request
Creates or retrieves the customer’s persistent Floward cart, resolves its address, warms the fulfillment-center catalog, and returns page-builder sections.
GET {{BASE_URL}}/store/floward/home{
"storefront": {
"cart": {
"id": "cart_01FLOWARD",
"type": "floward",
"item_count": 2,
"requires_shipping_address": true,
"shipping_address": {
"id": "addr_01",
"city": "Dubai",
"country_code": "ae",
"latitude": 25.276987,
"longitude": 55.296233
}
},
"wish_list_id": "wish_01"
},
"page_sections": [
{
"id": "floward_category_collection",
"type": "scrollable_subcategory_section",
"title": "Categories",
"content": []
},
{
"id": "popularFloward",
"type": "product_listing_section",
"title": "Popular",
"config": {
"api_url": "/store/floward/products?..."
},
"styles": { "axis": "horizontal" }
}
],
"config": {
"dependency": { "requires_address": true }
}
}Mobile behavior
- Render page sections using the existing Barq/Nana page-builder contracts.
- Use every action’s supplied
api_url; do not reconstruct category queries. - Keep the returned Floward cart ID in storefront state for add/update/remove operations.
- If the API reports a missing or unsupported address, return the customer to address selection.
04 · Products & pricing
Only show what the current center can fulfill
Supports the standard product-list query parameters used in the page-builder URLs: pagination, category, tags, search, fields, and expansions.
GET {{BASE_URL}}/store/floward/products?category_id[0]=pcat_01&expand=price_range&limit=20&offset=0{
"products": [
{
"id": "prod_01",
"title": "Pink Rose Arrangement",
"thumbnail": "https://...",
"price_range": { "min": 22885, "max": 22885 },
"variants": [
{
"id": "variant_01",
"inventory_quantity": 1,
"calculated_price": 22885,
"original_price": 22885,
"prices": [{ "currency_code": "sar", "amount": 22885 }]
}
]
}
],
"count": 1,
"offset": 0,
"limit": 20
}22885 means SAR 228.85. Use the returned currency and the application’s money formatter. Never use an imported variant price as a Floward fallback.| Situation | Expected UI behavior |
|---|---|
| Product is absent from the response | Do not display it. It is not sellable from the selected center. |
| Address changes | Discard the current result set and reload from page 1. |
| Empty result | Show an address-specific empty state, not a global “out of stock” claim. |
| Price changes later | Use the prepare error to refresh the cart and ask the customer to review. |
05 · Cart lifecycle
The Floward cart is persistent
The customer has one persistent Floward cart. Reopening Floward retrieves it; mobile should not create a replacement cart for normal shopping.
Add a single Medusa variant. The backend confirms that it is an HOI dynamically priced product, resolves the current center, applies the trusted price, and records the pricing snapshot.
POST {{BASE_URL}}/store/carts/cart_01FLOWARD/line-items
Idempotency-Key: {{UUID}}
Content-Type: application/json
{
"variant_id": "variant_01",
"quantity": 1
}Quantity and removal
Use the existing Medusa cart line-item endpoints. Dynamic Floward pricing is preserved by the backend when quantity changes.
POST /store/carts/:cart_id/line-items/:line_item_id
{ "quantity": 2 }
DELETE /store/carts/:cart_id/line-items/:line_item_id06 · Detailed carts
One cart summary API, two selectors
Use type=floward for the persistent cart. During checkout, use id=cart_... to request the exact ephemeral checkout cart. If both are supplied, the type must match the ID.
{
"default_address": { "id": "addr_01", "city": "Dubai" },
"total_item_count": 5,
"carts": [
{ "id": "cart_default", "type": "default", "title": "Digital Vouchers", "item_count": 1 },
{ "id": "cart_floward", "type": "floward", "title": "Floward", "item_count": 4 }
],
"detailed": {
"id": "cart_floward",
"type": "floward",
"title": "Floward",
"cart_sections": ["itemListing", "giftCardMessaging", "deliveryDetails"],
"items": [],
"subtotal": 5000,
"shipping_total": 0,
"discount_total": 0,
"total": 5000,
"allow_discounts": false,
"requires_shipping": true,
"required_minimum_spend": null,
"free_delivery": null,
"shipping_address": null,
"gift_card_messaging": {},
"delivery_details": {},
"dependencies": {
"requires_address": true,
"can_checkout": true,
"blocked_reason": ""
}
}
}| Selector | Use |
|---|---|
?type=floward | Shopping cart screen and persistent-cart badge. |
?id=cart_... | Exact checkout cart after the customer chooses a subset of items. |
?id=...&type=floward | Optional defensive assertion; mismatch is rejected. |
07 · Gift messaging
Write flat; read structured
The generic API name can support another gift-capable cart later. Today, only owned, incomplete Floward carts accept it.
{
"recipient_name": "Alex",
"recipient_phone": "+966500000000",
"sender_name": "Derek",
"message": "Congratulations!",
"feeling_url": "https://example.com/feeling"
}Every field is optional for partial updates. Send null to clear a field. Limits: recipient and sender names 100 characters, phone 100 characters, message 250 characters.
{
"id": "cart_floward",
"type": "floward",
"gift_card_messaging": {
"recipient_name": { "text": "Alex", "max_length": 100, "required": false },
"sender_name": { "text": "Derek", "max_length": 100, "required": false },
"message": { "text": "Congratulations!", "max_length": 250, "required": false },
"feeling_url": { "text": "https://example.com/feeling", "required": false }
},
"delivery_details": {
"recipient_name": { "text": "Alex", "max_length": 100, "required": false },
"recipient_phone": { "text": "+966500000000", "max_length": 100, "required": false },
"slot": { "supports_express": false, "supports_same_day": false, "selected_slot": null }
}
}08 · Delivery slots
Discover by date, then select by ID
{
"delivery_date": "2026-08-20",
"supports_express": false,
"supports_same_day": false,
"slots": [
{ "id": "slot_morning", "name": "09:00 - 12:00" },
{ "id": "slot_afternoon", "name": "12:00 - 15:00" }
]
}- Dates must use
YYYY-MM-DDand cannot be in the past. - An available date can return an empty
slotsarray. supports_same_dayis true only for today with at least one returned slot.supports_expressis currently always false because HOI exposes no express flag.
{
"delivery_date": "2026-08-20",
"slot_id": "slot_morning"
}The backend refetches HOI slots, rejects unknown IDs, stores the trusted name, and binds the selection to the current fulfillment center.
"delivery_details": {
"slot": {
"supports_express": false,
"supports_same_day": false,
"selected_slot": {
"delivery_date": "2026-08-20",
"id": "slot_morning",
"name": "09:00 - 12:00"
}
}
}09 · Prepare & errors
The final mobile checkout gate
Call immediately before proceeding to payment. Do not infer readiness only from the detailed cart’s can_checkout field.
{ "valid": true, "errors": [] }{
"valid": false,
"errors": [
{
"item_ids": ["item_01"],
"error": {
"code": "PRICE_CHANGED",
"message": "The price of this Floward product has changed",
"details": { "cart_price": 22885, "provider_price": 23500 }
}
}
]
}| Error code | Meaning | Recommended mobile action |
|---|---|---|
| FLOWARD_ADDRESS_REQUIRED | City or country is missing. | Open address selection. |
| FLOWARD_LOCATION_UNSUPPORTED | No HOI city/center supports the address. | Explain that Floward is unavailable at this address. |
| FLOWARD_FULFILLMENT_UNAVAILABLE | No single selected center can fulfill the cart. | Refresh cart; identify affected items. |
| INSUFFICIENT_INVENTORY | An item or requested quantity is unavailable. | Mark the item and offer remove/change quantity. |
| PRICE_CHANGED | HOI’s current price differs from the cart snapshot. | Refresh details and ask the customer to confirm. |
| FLOWARD_DELIVERY_SLOT_REQUIRED | No selected slot is stored. | Open the delivery-slot picker. |
| FLOWARD_DELIVERY_SLOT_UNAVAILABLE | The center changed, date passed, or slot disappeared. | Clear the selection, reload slots, and reselect. |
| FLOWARD_DELIVERY_SLOT_VALIDATION_FAILED | HOI could not confirm the selected slot. | Show retry UI; do not continue to payment. |
| FLOWARD_AVAILABILITY_FAILED | HOI availability could not be confirmed. | Show retry UI; do not continue to payment. |
10 · Checkout sequence
Recommended screen-to-screen orchestration
- Confirm the customer has a selected address with city and country.
- Load
/store/floward/homeand retain its persistent cart ID. - Browse only through
/store/floward/productsURLs supplied by the page builder. - Add products to the returned Floward cart. Render prices from the server response.
- Load
/store/carts?type=flowardfor the cart screen. - Submit gift-card messaging and recipient details.
- Request slots for a chosen date and submit the selected slot ID.
- Create or select the exact checkout cart according to the existing Barq partial-checkout flow.
- Call
/prepareon that exact cart. Resolve every error before payment. - Continue through the existing Barq payment and completion experience only after
valid: true.
id, not type=floward.11 · Mobile test plan
Scenarios worth automating
- Customer without an address is routed to address selection before Floward loads.
- Supported address returns categories, Popular products, and a persistent Floward cart.
- Changing city reloads products and can change price and availability.
- Only products returned by the Floward product API are displayed.
- Add, quantity update, and removal always use server-returned totals.
- Gift fields support partial update and explicit clearing.
- Past dates are rejected and dates with no slots show a useful empty state.
- Changing address after selecting a slot requires reselection.
- Prepare price change refreshes the cart and asks for confirmation.
- Prepare inventory failure identifies the affected line item.
- Prepare slot failure returns the customer to slot selection.
- Unselected persistent-cart items survive partial checkout.
12 · Rollout boundaries
What mobile owns—and what remains backend work
| Capability | Status | Owner |
|---|---|---|
| Location-aware home and product browsing | Implemented | Mobile consumes |
| Dynamic cart pricing and prepare validation | Implemented | Backend enforces |
| Gift messaging and recipient details | Implemented | Mobile captures |
| Delivery slot discovery, selection, and prepare validation | Implemented | Mobile captures; backend validates |
| Express-slot capability | Unavailable from HOI; always false | Backend |
HOI order submission from order.process | Upcoming | Backend |
| HOI status synchronization, cancellation, returns, and refunds | Upcoming | Backend |