BBarq Marketplace · Mobile Integration
Floward v1 · August 2026

Implementation guide · iOS & Android

Floward, from address to prepared cart.

A practical guide to location-aware browsing, dynamic prices, the persistent Floward cart, gift messaging, delivery slots, and checkout preparation in Barq Marketplace.

AuthenticatedEvery Floward route
Address-firstCity selects fulfillment
DynamicPrice and stock from HOI
One centerPer cart and order

01 · Start here

The three rules that prevent most integration bugs

  1. Require an address before opening Floward. The shipping address city and country decide which HOI fulfillment center, catalog, prices, stock, and slots apply.
  2. Use the server-provided cart and price. Never invent a Floward cart ID, send a catalog price, or reuse a price from another address.
  3. 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.
Currency values are minor unitsDates use YYYY-MM-DDCart type is floward
AuthenticationAll examples assume the normal authenticated Barq customer session and the application’s standard headers. A cart is accessible only to its owning customer.

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.

Customer address
city + country
HOI city
resolved by name
First center
current policy
Catalog snapshot
stock + price
Current center policyWhen a city has multiple active fulfillment centers, the backend selects the first returned center. Mobile must not select or display fulfillment centers.

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

GET/store/floward/home

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

GET/store/floward/products

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
}
Price display22885 means SAR 228.85. Use the returned currency and the application’s money formatter. Never use an imported variant price as a Floward fallback.
SituationExpected UI behavior
Product is absent from the responseDo not display it. It is not sellable from the selected center.
Address changesDiscard the current result set and reload from page 1.
Empty resultShow an address-specific empty state, not a global “out of stock” claim.
Price changes laterUse 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.

POST/store/carts/:cart_id/line-items

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
}
Do not send price metadataMobile sends only the variant and quantity. The backend reads HOI’s trusted center-specific price.

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_id
Cart isolationNever add Floward and non-Floward products to the same cart. The product details and Floward home responses provide the correct cart.

06 · Detailed carts

One cart summary API, two selectors

GET/store/carts?type=floward

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": ""
    }
  }
}
SelectorUse
?type=flowardShopping cart screen and persistent-cart badge.
?id=cart_...Exact checkout cart after the customer chooses a subset of items.
?id=...&type=flowardOptional defensive assertion; mismatch is rejected.

07 · Gift messaging

Write flat; read structured

POST/store/carts/:id/gift-card-messaging

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

GET/store/carts/:id/delivery-slots?delivery_date=2026-08-20
{
  "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-DD and cannot be in the past.
  • An available date can return an empty slots array.
  • supports_same_day is true only for today with at least one returned slot.
  • supports_express is currently always false because HOI exposes no express flag.
POST/store/carts/:id/delivery-slot
{
  "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"
    }
  }
}
Address changes invalidate slotsAfter an address update, reload slots and require the customer to select again. A slot is valid only for the fulfillment center that returned it.

09 · Prepare & errors

The final mobile checkout gate

POST/store/carts/:id/prepare

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 codeMeaningRecommended mobile action
FLOWARD_ADDRESS_REQUIREDCity or country is missing.Open address selection.
FLOWARD_LOCATION_UNSUPPORTEDNo HOI city/center supports the address.Explain that Floward is unavailable at this address.
FLOWARD_FULFILLMENT_UNAVAILABLENo single selected center can fulfill the cart.Refresh cart; identify affected items.
INSUFFICIENT_INVENTORYAn item or requested quantity is unavailable.Mark the item and offer remove/change quantity.
PRICE_CHANGEDHOI’s current price differs from the cart snapshot.Refresh details and ask the customer to confirm.
FLOWARD_DELIVERY_SLOT_REQUIREDNo selected slot is stored.Open the delivery-slot picker.
FLOWARD_DELIVERY_SLOT_UNAVAILABLEThe center changed, date passed, or slot disappeared.Clear the selection, reload slots, and reselect.
FLOWARD_DELIVERY_SLOT_VALIDATION_FAILEDHOI could not confirm the selected slot.Show retry UI; do not continue to payment.
FLOWARD_AVAILABILITY_FAILEDHOI availability could not be confirmed.Show retry UI; do not continue to payment.

10 · Checkout sequence

Recommended screen-to-screen orchestration

  1. Confirm the customer has a selected address with city and country.
  2. Load /store/floward/home and retain its persistent cart ID.
  3. Browse only through /store/floward/products URLs supplied by the page builder.
  4. Add products to the returned Floward cart. Render prices from the server response.
  5. Load /store/carts?type=floward for the cart screen.
  6. Submit gift-card messaging and recipient details.
  7. Request slots for a chosen date and submit the selected slot ID.
  8. Create or select the exact checkout cart according to the existing Barq partial-checkout flow.
  9. Call /prepare on that exact cart. Resolve every error before payment.
  10. Continue through the existing Barq payment and completion experience only after valid: true.
Partial checkoutThe persistent Floward cart protects unselected items for later. When checkout uses an ephemeral cart, request details and prepare using its exact 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

CapabilityStatusOwner
Location-aware home and product browsingImplementedMobile consumes
Dynamic cart pricing and prepare validationImplementedBackend enforces
Gift messaging and recipient detailsImplementedMobile captures
Delivery slot discovery, selection, and prepare validationImplementedMobile captures; backend validates
Express-slot capabilityUnavailable from HOI; always falseBackend
HOI order submission from order.processUpcomingBackend
HOI status synchronization, cancellation, returns, and refundsUpcomingBackend
Release coordinationDo not enable production Floward payment completion until HOI order submission and post-order status/refund handling are deployed and verified. Mobile does not call HOI directly.