Skip to content

Repository files navigation

Aisle

Aisle

Agent-ready checkout for the long tail.

Prove a D2C merchant can be bought from by an AI buyer — with policy bounds,
a Razorpay test-mode money trail, and a batch readiness score you can defend.

Next.js 16 TypeScript Razorpay test mode License MIT

Get started · Workflow · Architecture · GitHub


Table of contents


Product overview

AI shopping agents are starting to buy on behalf of people. A long-tail D2C merchant does not have a ChatGPT app, an MCP storefront, or a payments integration built for machines. They have a catalog with gaps, a card they cannot let an agent drain, and no proof that a refusal was policy rather than a bug.

Aisle answers one question:

Is my store ready to be bought from by an AI agent, and can I prove it?

It is not a consumer chatbot, a public MCP cart, or a Chat2Checkout clone. It is a merchant-facing readiness product: a seeded (or Shopify-ingested) catalog, a hard policy engine in front of every rupee, an LLM buyer agent that may only spend through tools, and a 40-case eval suite whose score is derived from attempts — not from a vanity total.

The demo merchant is North & Co (clean beauty, ~34 SKUs with intentional holes). All money movement is Razorpay test mode. Capture is never taken from a browser callback alone.

For the merchant For the reviewer
Am I ready? What do I fix first? Tool I/O, reason codes, webhook payloads
Policy as a brake, not a suggestion Proof no money moved on a blocked attempt

Key features

Shipped

Area What you get
Catalog readiness Per-SKU completeness (price, stock, image, pincodes, returns, …). Missing fields are a work list, not a percentage in isolation.
Policy engine Deterministic quote() before any Razorpay call: per-order cap, daily cap, confirm-above, COD, max qty, blocked SKUs/categories, stock, pincode.
Buyer agent Tool loop: search_catalog, get_product, quote, place_order, pay, recover. LLM when OPENAI_API_KEY is set; otherwise a deterministic buyer so the suite still runs.
Razorpay test checkout Server-side order create, Checkout.js, HMAC checkout verify, webhook HMAC, Payment Link recovery. Payments persist in SQLite.
Eval harness 40 adversarial cases (buy, over-limit, COD, recall, OOS, incomplete, pincode, qty, fail-then-recover). Live runs are stored; dashboard shows a Fixture until you run the suite.
Readiness score 0.35 × catalog + 0.30 × capture precision + 0.25 × policy precision + 0.10 × recovery.
Audit trail Tool calls with hasMoney flags, policy allow/deny, payment status. Attempt drawer is verdict-first.
Shopify ingest https://store/products.json only (HTTPS, no private hosts). Title/price import; delivery and pincodes stay missing on purpose.
Operator UI Dashboard, catalog, policy (save/load), eval runs, test-mode pay page. Mobile header + nav.

Honest limits (not bugs)

  • Payable eval cases pass when the agent reaches a test-mode order. actual stays pending until a verified checkout signature or payment.captured webhook. The suite does not fake capture.
  • Recovery cases inject a failed payment in the harness, then issue a Payment Link. Recovered capture still requires Razorpay.
  • Shopify public JSON is not a full catalog. That incompleteness is the point of the score.
  • There is no public MCP storefront and no live crawl of arbitrary websites.

Planned

See Roadmap: hosted webhook URL, production Turso, demo video, operator polish.


Product structure

Merchant UI                         Money + policy                         Agent + eval
─────────────                       ──────────────                         ────────────
/            marketing landing      quote() ──────────────┐                LLM / deterministic buyer
/app         dashboard              policy (SQLite)        │                tools → search, quote,
/catalog     SKU gaps               orders.ts ─────────────┼── Razorpay     place_order, pay, recover
/policy      save guardrails        payments + webhooks    │    test API
/runs        40-case history        checkout verify ───────┘
/pay         test checkout

Screens

Route Role
/ Marketing landing → console
/app Readiness ring, what to fix first, run the suite
/catalog SKU table, completeness, Shopify URL ingest
/policy Live preview + Save (persisted; used by quote and pay)
/runs, /runs/[id] Eval history, attempt drawer, money axis
/pay, /pay/[orderId] Razorpay Checkout, verify, recover link

Repository layout

src/
  app/                  Next.js App Router (pages + route handlers)
    api/agent/buy       Single interactive agent buy
    api/catalog/ingest  Shopify products.json
    api/eval/           Run suite, list/get runs
    api/merchant        Catalog + policy snapshot
    api/payments/       order, verify, recover, GET :orderId
    api/policy          GET / PUT
    api/quote           Policy quote
    api/webhooks/razorpay
    catalog/ policy/ pay/ runs/
  components/           App chrome, attempt drawer, shadcn/ui
  lib/
    agent.ts            LLM + deterministic buyer
    agent-tools.ts      Tool implementations
    policy.ts           Deterministic quote engine
    orders.ts           Shared create / recover (HTTP + agent)
    eval.ts             40-case runner
    eval-cases.ts       Suite spec
    db.ts               LibSQL schema, seed, CRUD
    razorpay.ts         HMAC + REST
    shopify.ts          URL allowlist + product map
    mock-data.ts        North & Co fixture + UI types
public/brand/           Mark and lockup for docs

Getting started

Prerequisites

  • Node.js 20+
  • npm
  • A Razorpay test-mode key pair (optional for policy/catalog eval; required for real test charges)
  • An OpenAI-compatible API key (optional; eval falls back to the deterministic buyer)

Installation

git clone https://github.com/rakesh0x/aisle.git
cd aisle
npm install
cp .env.example .env

Environment variables

Variable Required Purpose
RAZORPAY_KEY_ID For pay / live orders Test key id (rzp_test_…)
RAZORPAY_KEY_SECRET For pay / verify Signs checkout order_id|payment_id
RAZORPAY_WEBHOOK_SECRET For webhooks HMAC of the raw webhook body
OPENAI_API_KEY For LLM agent If unset, suite uses the deterministic buyer
OPENAI_BASE_URL No Default https://api.openai.com/v1
OPENAI_MODEL No Default gpt-4.1-mini
TURSO_DATABASE_URL No Default file:./data/aisle.db
TURSO_AUTH_TOKEN Turso only LibSQL auth

Do not commit .env. Local SQLite lives under ./data/ (gitignored).

Run locally

npm run dev      # http://localhost:3000
npm test         # src/lib/*.test.ts
npm run build    # production build

Seeded merchant merch_northco (North & Co) is inserted on first boot if missing. Schema (CREATE TABLE IF NOT EXISTS) always runs, including on existing databases.

Webhooks (optional, for real capture)

Razorpay cannot reach localhost. For payment.captured / payment.failed as source of truth:

  1. Expose the app (ngrok, Cloudflare Tunnel, or a deployed HTTPS URL).
  2. Dashboard → Webhooks → https://<host>/api/webhooks/razorpay.
  3. Events: payment.captured, payment.failed, payment_link.paid.
  4. Set RAZORPAY_WEBHOOK_SECRET to the webhook secret.

Without a public URL, /api/payments/verify still confirms Checkout via HMAC + a Razorpay payment fetch.


Usage / workflow

1. See the gap

Open /. Until you run the suite, the score is a fixture. Catalog completeness is the heaviest weight — the dashboard names the missing fields, not just “74”.

2. Tighten policy

Open /policy. Caps, confirm-above, COD, blocked SKUs/categories, max qty. Save policy persists to SQLite. Quotes and Razorpay orders use the saved row.

3. Run the buyer

Click Run suite. Forty cases hit search → product → quote → (if allowed) place_order. With an LLM key the model drives the tools; without one the deterministic buyer still has to pick the expected SKU.

curl -X POST http://localhost:3000/api/eval/run \
  -H 'Content-Type: application/json' \
  -d '{"mode":"deterministic"}'

"mode":"llm" requires OPENAI_API_KEY.

4. Move test money

Open /pay (or /pay/<aisleOrderId>). Test instruments:

Instrument Result
success@razorpay Capture (after verify / webhook)
failure@razorpay Fail — then Send payment link
4111 1111 1111 1111 Test card (any CVV, any future expiry)

Send payment link is a no-op until the payment is failed. Status captured is never set from the Checkout handler without /api/payments/verify.

5. Optional: another store

On /catalog, paste https://your-store.myshopify.com. Ingest is /products.json only. Seed North & Co SKUs remain; Shopify rows are shp_<id> and usually incomplete until delivery data exists. The 40-case suite is still North & Co.

6. One-shot agent buy

curl -X POST http://localhost:3000/api/agent/buy \
  -H 'Content-Type: application/json' \
  -d '{"query":"Buy a moisturizer under ₹500, deliver to 560034","pincode":"560034"}'

Returns quote, order (if allowed), and /pay/<id> when an order exists.


Technical architecture

flowchart LR
  subgraph UI
    Dash["Dashboard / Catalog / Policy / Runs"]
    Pay["/pay Checkout.js"]
  end

  subgraph App["Next.js 16 · Node runtime"]
    Agent["Buyer agent"]
    Quote["policy.quote"]
    Orders["orders.ts"]
    Eval["eval suite"]
    DB[("LibSQL / SQLite")]
  end

  subgraph Rzp["Razorpay test"]
    ROrders["Orders"]
    Checkout["Checkout"]
    Links["Payment Links"]
    WH["Webhooks"]
  end

  Dash --> Agent
  Dash --> Quote
  Agent --> Quote
  Agent --> Orders
  Eval --> Agent
  Quote --> DB
  Orders --> DB
  Orders --> ROrders
  Pay --> Checkout
  Pay --> Orders
  Checkout --> Pay
  WH --> DB
  Links --> WH
Loading

Stack: Next.js 16, React 19, Tailwind 4, shadcn/Base UI, @libsql/client, Razorpay REST + Checkout.js, optional OpenAI-compatible Chat Completions.

Policy is the brake. place_order and HTTP POST /api/payments/order share createMerchantOrder. If quote.allowed is false, Razorpay is not called.

Daily cap is SUM(amount_paise) for the buyer fingerprint for today, statuses created | captured | recovering | recovered. Clients cannot pass a trusted dailySpentPaise on the public order route; eval injects it only inside the harness for OVER_DAILY_CAP cases.

Payment state machine (never overwrite a capture with a failure):

Current Event Next
created payment.captured captured
failed / recovering payment.captured recovered
created / recovering payment.failed failed
captured / recovered payment.failed ignored

LibSQL may return 0n / 1n. Flags use explicit integer checks (asBool), not Boolean(0n).

Reason codes

Code Kind Meaning
OVER_ORDER_LIMIT Guard Above per-order cap
OVER_DAILY_CAP Guard Above daily agent spend
CONFIRMATION_REQUIRED Guard Above confirm-above, no human yes
COD_NOT_ALLOWED Guard COD requested, policy off
SKU_BLOCKED / CATEGORY_BLOCKED Guard Merchant withhold
QTY_EXCEEDED Guard Above max qty
OUT_OF_STOCK Catalog Zero or insufficient stock
INCOMPLETE_CATALOG Catalog Required fields missing
PINCODE_UNSERVICEABLE Catalog Address not in service list

policy_blocked is a success of the brake. catalog_exception is merchant to-do.


HTTP API

All JSON. Node.js runtime.

Method Path Notes
GET /api/merchant Merchant, policy, SKUs
GET / PUT /api/policy Persist guardrails
POST /api/quote { skuId, qty, pincode, cod, confirmationReceived }
POST /api/payments/order { aisleOrderId, skuId, amountPaise, … } — amount must match server quote
GET /api/payments/:orderId Aisle or Razorpay order id
POST /api/payments/verify Checkout signature fields
POST /api/payments/recover Payment Link for failed / recovering
POST /api/webhooks/razorpay Raw body + x-razorpay-signature
POST /api/eval/run { mode?: "llm" | "deterministic" }
GET /api/eval/runs Newest first
GET /api/eval/runs/:id Full attempts
POST /api/agent/buy { query, pincode?, qty?, cod? }
POST /api/catalog/ingest { url } Shopify origin

Configuration & customization

Policy

PUT /api/policy body:

{
  "maxOrderPaise": 500000,
  "dailyCapPaise": 800000,
  "confirmAbovePaise": 150000,
  "allowCod": false,
  "requireInStock": true,
  "blockedCategories": ["adult"],
  "blockedSkuIds": ["sku_blocked_001"],
  "maxQty": 3
}

Integers are paise. Per-order cap cannot exceed daily cap. The UI uses the same contract.

Eval cases

Edit src/lib/eval-cases.ts. Each case has expected, expectedSkuId, and optional pincode, qty, cod, confirmationReceived, dailySpentPaise, injectFailure. Cases are tested against live quote() in src/lib/eval-cases.test.ts.

Agent model

Point OPENAI_BASE_URL + OPENAI_API_KEY at any Chat Completions-compatible host. Tool schemas live in src/lib/agent-tools.ts.

Database

Default file SQLite. For Turso, set TURSO_DATABASE_URL and TURSO_AUTH_TOKEN. Seed runs only when merchant merch_northco is absent.

UI design

DESIGN.md is the visual contract (tokens, type scale, semantic colour). Do not introduce a second palette.


Roadmap

Status Item
Done Durable catalog, policy, payments
Done Quote engine + persisted policy on the money path
Done Checkout verify, webhooks, Payment Link recover
Done Buyer agent + 40-case runner + live run store
Done Shopify /products.json ingest
Next Deploy HTTPS + register Razorpay webhooks so batch captures are webhook-true
Next Turso (or equivalent) for a shared demo DB
Next 5-minute walkthrough: score → policy → fail → recover → audit
Later Operator edits for missing catalog fields (pincode, returns) without leaving Aisle
Out of scope Public MCP cart, WhatsApp/voice storefront, ACP/AP2/x402, claiming ChatGPT discovery

Contributing

This repo is the Aisle prototype (Razorpay AI Buildathon, track 01 — AI Growth & Agentic Commerce). Small, reviewable changes beat drive-by refactors.

  1. Fork / branch from main.
  2. npm install && npm test.
  3. Keep policy and payments deterministic; do not mark captured without verify or webhook.
  4. Match DESIGN.md if you touch UI.
  5. Do not commit .env, data/*.db, or secrets.
npm run lint
npm test
npm run build

Pull requests should say why (guardrail, honesty of score, merchant workflow), not only what files changed.


License

MIT © 2026 Aisle.

Razorpay, Shopify, and OpenAI are trademarks of their owners. Test mode only — no real merchant settlement in this project.

About

Agent-ready checkout for the long tail. Prove a D2C merchant can be bought from by an AI buyer with policy bounds, Razorpay test-mode payments, and a batch readiness score.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages