Developer Setup

Zelly Checkout

Zelly Checkout is the customer-facing checkout UI. It is a React SPA that runs as a full-screen popup or desktop page embedded in merchant Shopify stores via an iframe. It is deployed on Cloudflare Pages and has no server-side compute — no ECS, no Terraform, no AWS.

PropertyValue
Repozelly-in/checkout-ui-build — cloned locally at zelly-checkout/app/
PlatformCloudflare Pages (static; no Workers runtime)
Live URLhttps://checkout.zelly.in/popup.html
CF project namecheckout-ui-build-worker-new-test-debug
Build toolVite 5 · React 18 · vite-plugin-singlefile
Build outputapp/dist/ — fully self-contained HTML files
i
Clone the repo into zelly-checkout/app/. The outer zelly-checkout folder is a separate legacy repo — the active checkout source lives in zelly-in/checkout-ui-build, cloned inside it. Run cd zelly-checkout/app && npm install once; everything else in this guide runs from that directory.

How it works

A merchant installs the Zelly loader script into their Shopify theme. When a customer clicks a data-zelly-checkout button, the loader fetches the Shopify cart, creates an <iframe> pointing at checkout.zelly.in/popup.html, and passes the cart data in via postMessage. The iframe renders the full checkout flow, then signals back to the parent on success or close.

Build targetFileUse case
popupdist/popup.htmlMobile-first full-screen iframe embedded by the loader script
desktopdist/desktop.htmlDesktop variant shown in a side-panel layout
theme-logindist/theme-login.htmlLogin widget injected into the merchant's own theme pages

Local development

Zelly Checkout does not run in the shared docker-compose stack. Run it standalone from the app/ directory with a mock API that ships in the repo — no backend required.

  1. Install dependencies
    bash / PowerShell — run from app/
    cd d:\zelly\zelly-checkout\app     # Windows
    cd /path/to/zelly-checkout/app    # Mac/Linux
    npm install
  2. Set up the local environment file (copy the example and leave defaults for mock mode)
    bash
    cp .env.example .env
    # Default VITE_USE_MOCK=true — no backend needed
    # To hit staging API: set VITE_API_BASE=https://api-staging.zelly.in/v1
    PowerShell
    Copy-Item .env.example .env
  3. Start the dev server for the target you're working on
    bash / PowerShell
    npm run dev:popup        # http://localhost:5173 — mobile popup UI
    npm run dev:desktop      # http://localhost:5174 — desktop side-panel
    npm run dev:theme-login  # http://localhost:5175 — theme login widget

    Vite HMR is active — edits to any src/ file reflect instantly without a page reload.

i
Mock mode (VITE_USE_MOCK=true) is the default. The app ships a built-in mock data layer that simulates a full checkout session — cart with items, address lookup, payment flow, order confirmation. You never need a running backend to iterate on the UI.

Testing against real staging data

To point local dev at the real staging API instead of mocks, edit app/.env:

VITE_USE_MOCK=false
VITE_API_BASE=https://api-staging.zelly.in/v1

Restart the dev server after changing .env. CORS is configured on the staging API to allow localhost:517x origins.

Build & deploy

Build

bash / PowerShell — from app/
npm run build

This runs all three build targets in sequence and writes the output to app/dist/. Each output is a single self-contained HTML file — CSS and JS are inlined, no separate asset files to manage. The dist/ directory is what Cloudflare Pages serves.

Staging deploys — automatic

Connect the zelly-in/zelly-checkout repo to the CF Pages project in the Cloudflare dashboard (Pages → zelly-checkout → Settings → Build & deployments → Connect to Git). After that, every push to main triggers an automatic build and deploy to the production Pages deployment (which serves checkout.zelly.in).

Every push to any other branch gets an automatic preview URL:

https://<branch-name>.checkout-ui-build-worker-new-test-debug.pages.dev/popup.html

Use preview URLs to QA a change on a real CF edge before merging.

!
Staging vs production for Cloudflare Pages. CF Pages does not distinguish "staging" and "production" the same way ECS does. Our convention: main → live checkout.zelly.in. Any other branch → preview URL. For changes that must be tested before going live, develop on a feature branch, verify the preview URL, then merge to main.

Production deploys — manual

Production deploys are intentionally manual to match the rest of the platform's release cadence. Two options:

Option A — Wrangler CLI (recommended)

bash — from app/
# First time only
npm install -g wrangler
wrangler login   # opens browser; authenticate with the zelly CF account

# Build, then deploy to production
npm run build
wrangler pages deploy dist --project-name checkout-ui-build-worker-new-test-debug
PowerShell
npm install -g wrangler
wrangler login

npm run build
wrangler pages deploy dist --project-name checkout-ui-build-worker-new-test-debug
i
CF project name is checkout-ui-build-worker-new-test-debug, not zelly-checkout. This is the name of the existing CF Pages project — always use this exact string in the --project-name flag. If you run wrangler pages project create zelly-checkout, you will create a second, separate project.

Option B — CF Dashboard

In the Cloudflare dashboard: Pages → checkout-ui-build-worker-new-test-debug → Deployments → Deploy. Upload the dist/ folder directly from your browser. Useful when you don't have Wrangler installed.

Cache purge after deploy

CF's edge caches popup.html and desktop.html for up to 5 minutes (s-maxage=300). For an urgent hotfix to be live immediately:

# CF Dashboard → Caching → Configuration → Purge Everything
# Or via Wrangler:
wrangler pages deployment tail --project-name checkout-ui-build-worker-new-test-debug

Shopify integration

The checkout UI is injected into any Shopify store by pasting a single loader script into the theme. The loader handles cart data, iframe lifecycle, and event bridging between the merchant page and the Zelly iframe.

Install the loader script

  1. In Shopify Admin, go to Online Store → Themes → Edit code.
  2. Open layout/theme.liquid and paste the contents of shopify-loader/zelly-loader.js just before </body>.
  3. Set the two config variables at the top of the pasted script:
    // Replace these two values:
    var ZELLY_CHECKOUT_URL = 'https://checkout.zelly.in/popup.html';
    var ZELLY_STORE_ID     = '{{ shop.metafields.zelly.store_id | default: "" }}';
    // The Shopify Liquid expression reads store_id from the store's Zelly metafield.
    // Set the metafield in the Zelly seller panel after onboarding the store.
  4. Add data-zelly-checkout to the checkout button in the theme:
    <!-- In your cart or product template: -->
    <button data-zelly-checkout>Checkout with Zelly</button>

Data flow

Customer clicks [data-zelly-checkout]
        │
        ▼
Loader fetches /cart.js  (Shopify native cart API)
        │
        ▼
Loader creates <iframe src="checkout.zelly.in/popup.html">
        │
        ▼
iframe posts ZELLY_READY
        │
        ▼
Loader posts ZELLY_INIT { storeId, cartToken, cartHint }
        │
        ▼
Checkout calls Zelly API:
  GET /stores/{storeId}/checkout-settings
  GET /carts/{cartToken}
        │
        ▼
Customer completes checkout
        │
        ▼
iframe posts ZELLY_ORDER_SUCCESS { orderId, eta }
        │
        ▼
Loader fires  document event: zelly:order_success

Listening to checkout events

Merchants can hook into lifecycle events fired on the parent page:

// Order placed successfully
document.addEventListener('zelly:order_success', function(e) {
  console.log('Order placed:', e.detail.orderId);
  // e.g. redirect, fire analytics, update cart count
});

// Customer closed without completing
document.addEventListener('zelly:closed', function() { ... });

// Checkout iframe mounted and ready
document.addEventListener('zelly:ready', function() { ... });
iframe headers are set in public/_headers. popup.html has X-Frame-Options: ALLOWALL and Content-Security-Policy: frame-ancestors * so it can load inside any merchant's Shopify store. desktop.html keeps SAMEORIGIN. Do not change these without understanding the Shopify embedding requirement.

Environment variables

Cloudflare Pages does not read .env files at build time or runtime. The .env file only works locally (Vite reads it via dotenv). On CF you must set variables in the dashboard or via Wrangler.

VITE_* variables are build-time — Vite bakes them into the HTML bundle during npm run build. They only work from .env locally; on Cloudflare Pages you must set them in the dashboard or via Wrangler (below).

Local dev variables (.env)

VariableDefaultNotes
VITE_API_BASEhttps://api.zelly.in/v1API base URL. Change to https://api-staging.zelly.in/v1 or http://localhost:4000/v1 for local backend
VITE_USE_MOCKtrueWhen true, returns hardcoded mock responses — no backend needed. Set to false to hit a real API
VITE_DEV_STORE_IDstore_dev_001storeId used when none is supplied via URL param or postMessage
VITE_DEV_CART_TOKENcart_dev_abc123cartToken used when none is supplied via URL param or postMessage
VITE_DEV_SESSION_TOKENtok_dev_abc123Pre-set session token — bypasses the OTP flow so you land directly on address/payment screens
VITE_PAYMENT_MODEgatewaygateway opens Razorpay SDK directly; inline shows expanded in-popup forms
VITE_DEV_THEMEblueAccent colour: blue · lime · orange · violet
VITE_DEV_DARKfalseDark mode toggle
VITE_DEV_DENSITYregularLayout density: compact · regular · comfy
VITE_DEV_SHOW_SPEED_HEROtrueShow the "11-second checkout" speed hero banner

Cloudflare Pages variables (staging / production)

VariablePreview (branches)Production (main)
VITE_API_BASEhttps://api-staging.zelly.in/v1https://api.zelly.in/v1
VITE_USE_MOCKfalsefalse

Set variables in CF dashboard

Pages → checkout-ui-build-worker-new-test-debug → Settings → Environment variables → Add variable. Set them for Production and Preview separately — preview covers all non-main branch deployments.

Set variables via Wrangler CLI

# Production
wrangler pages secret put VITE_API_BASE --project-name checkout-ui-build-worker-new-test-debug --env production
# Prompts: enter https://api.zelly.in/v1

# Preview (staging/branches)
wrangler pages secret put VITE_API_BASE --project-name checkout-ui-build-worker-new-test-debug --env preview
# Prompts: enter https://api-staging.zelly.in/v1
i
After changing environment variables you must trigger a new build — CF does not rebuild on variable changes alone. Push a new commit or manually trigger a deployment from the CF dashboard.

Repo notes

The active source lives in zelly-in/checkout-ui-build. It is cloned locally at d:\zelly\zelly-checkout\app\ (Mac/Linux: wherever you put your workspace). The outer zelly-checkout folder is a legacy repo that is not actively used — do not confuse the two.

i
When cloning a fresh machine, clone zelly-in/checkout-ui-build into zelly-checkout/app/ so it matches the path the rest of this guide assumes.