Zopping

Build a complete storefront

A worked, end-to-end walkthrough: boot → home → listing → product → cart → checkout → order → account. Every step shows the real request and the response fields you render. Base URL is the store domain: https://<store-domain>/api.

0. Boot — load store config

One call gives you currency, branding, enabled features and the default store.

curl https://<store-domain>/api/organization
{
  "data": {
    "name": "Tamimi",
    "logo": "https://cdn/.../logo.png",
    "currency": {
      "name": "Saudi Riyal",
      "symbol": "SR"
    },
    "theme": {
      "name": "Fashion",
      "slug": "theme-fashion"
    },
    "defaultStoreId": 7,
    "languages": [
      "en",
      "ar"
    ],
    "config": {
      "order": {
        "orderTypes": [
          "DELIVERY",
          "PICKUP"
        ],
        "paymentMethods": [
          "CASH",
          "ONLINE"
        ],
        "minimumOrderValue": 50
      },
      "website": {
        "primaryColour": "#0a7",
        "authProviders": [
          "OTP",
          "PASSWORD"
        ],
        "isCustomerWishlistEnabled": true
      }
    }
  }
}

Use currency.symbol for prices, config.website flags to toggle features (wishlist, ratings, slots…), and config.order.paymentMethods at checkout.

1. Home — render the layout

curl https://<store-domain>/api/layout/home

layouts is an ordered array of sections. Render each from its value (see the layout system):

{
  "data": {
    "layouts": [
      {
        "name": "ImageSlideShow",
        "status": "ENABLED",
        "value": {
          "images": [
            {
              "url": "https://…/banner.jpg",
              "link": "/category/fruits"
            }
          ]
        }
      },
      {
        "name": "ProductCollection",
        "status": "ENABLED",
        "data": {
          "title": "Best Sellers"
        },
        "value": {
          "collection": {
            "count": 240,
            "limit": 20,
            "product": []
          }
        }
      }
    ]
  }
}

Map section.name → a component; feed it section.value.

2. Listing (PLP) — products in a category

Either render the category page layout (/api/layout/category?url={slug}) or query products directly:

curl "https://<store-domain>/api/product?categoryId=1201&page=1&limit=20"
{
  "data": {
    "count": 240,
    "limit": 20,
    "offset": 0,
    "product": [
      {
        "id": 10231,
        "name": "Tata Salt 1kg",
        "slug": "tata-salt-1kg",
        "images": [
          "https://…/p1.jpg"
        ],
        "storeSpecificData": [
          {
            "storeId": "<store-id>",
            "mrp": "28.00",
            "discount": "10",
            "stock": 240
          }
        ],
        "averageRating": 4.4,
        "hasVariants": 0
      }
    ]
  }
}

Computing the price to show: mrp is a string, and discount is a percentage string — both on storeSpecificData for the store you're selling in:

sellingPrice = mrp − (mrp × discount / 100)
// 28.00 with discount "10" → 25.20

Out of stock when stock === 0 and unlimitedStock !== true.

3. Product detail (PDP)

curl "https://<store-domain>/api/layout/product?url=tata-salt-1kg"

The full product is on page.entity (with variants, each variant carrying its own storeSpecificData and images); the page's sections come in layouts. For a product with variants (hasVariants: 1), let the shopper pick a variant and price from that variant's storeSpecificData.

4. Add to cart

Items are { id: variantId, q: quantity, t: timestampMs }. q: "0" removes.

curl -X POST https://<store-domain>/api/cart \
  -H "Content-Type: application/json" \
  -d '{ "cart": { "items": [ { "id": "88120", "q": "2", "t": 1750000000000 } ] }, "orderType": "DELIVERY", "storeId": "<store-id>" }'

5. Render the cart

curl https://<store-domain>/api/cart
{
  "data": {
    "items": [
      {
        "id": "88120",
        "q": 2,
        "mrp": 28,
        "discount": 10,
        "product": {
          "name": "Tata Salt 1kg"
        }
      }
    ],
    "youPay": 50.4,
    "savings": 5.6,
    "amounts": {
      "orderAmount": 50.4,
      "totalMRP": 56,
      "totalDiscount": 5.6,
      "shippingAmount": 0,
      "totalCouponDiscount": 0
    },
    "isServiceable": true,
    "storeId": "<store-id>",
    "orderType": "DELIVERY"
  }
}

Render line items from items, the summary from amounts, and the headline total from youPay / savings.

6. Sign the shopper in (if required)

Guest checkout is allowed, but for saved addresses/orders, log in:

curl -X POST https://<store-domain>/api/login \
  -H "Content-Type: application/json" -d '{ "username": "asha@example.com", "password": "••••••" }'
# → { "data": { "accessToken": "eyJ…", "customer": { "id": 7781 } } }

Send Authorization: Bearer <accessToken> on the calls below. (OTP flow: POST /otp → POST /customer-verification.)

7. Checkout — totals, slots, payment options

curl -X POST https://<store-domain>/api/checkout \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "addressId": "addr-123", "preferredSlotId": 31, "paymentMethod": "ONLINE", "couponCodes": ["SAVE20"] }'
{
  "data": {
    "cart": {
      "youPay": 70.4,
      "amounts": {
        "shippingAmount": 20,
        "totalCouponDiscount": 0
      }
    },
    "slot": {
      "2026-06-23": [
        {
          "id": 31,
          "text": "9–10 AM",
          "available": true,
          "charge": 0
        }
      ]
    },
    "paymentMethods": [
      "CASH",
      "ONLINE"
    ],
    "couponDiscounts": [
      {
        "coupon": {
          "couponCode": "SAVE20"
        },
        "applied": false,
        "error": "Min order not met"
      }
    ]
  }
}

Use slot (keyed by date) to render the slot picker, paymentMethods for the payment choices, and re-read cart.amounts for the final figure.

8. Place the order & pay

curl -X POST https://<store-domain>/api/order -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{ "addressId": "addr-123", "preferredSlotId": 31, "paymentMethod": "ONLINE" }'

For online payment, initiate it and redirect the shopper using the returned redirectUrl / payment key (GET /api/payment-key):

curl -X POST https://<store-domain>/api/order/55012/payment -H "Authorization: Bearer $TOKEN"

9. Account & orders

curl https://<store-domain>/api/me      -H "Authorization: Bearer $TOKEN"   # profile
curl https://<store-domain>/api/order   -H "Authorization: Bearer $TOKEN"   # order history
curl "https://<store-domain>/api/track?orderId=ORD-55012"                    # delivery tracking

That's a complete store. From here add wishlist, ratings, blogs and notifications from the Storefront API reference.

Using the SDK instead

The @zopping/ecom SDK + theme path gives you these flows as hooks (useLayout, useCart, useCheckout) — see Build a storefront.