---
title: "Store API"
description: "Verified `/api/store/*` catalog, guest ring_cart + auth cart mirror, orders, checkout, and PaymentConductor store_order payments"
locale: "en"
---
# Store API

> **Info**
> Use **Founder** / **Developer** tabs in the docs sidebar to filter this page. Legacy copy invented vendor apply endpoints, PUT product/order status routes, and Solidity RING payment contracts — **those are not** under `app/api/store/`. Cart truth: guests use `localStorage` `ring_cart`; authenticated buyers also mirror via `GET`/`POST /api/store/cart` (+ optional soft-hold).

Ring’s multi-vendor store exposes: **catalog + reviews + product agent chat**, **guest cart + session cart mirror**, **orders / checkout**, and **payments** via **PaymentConductor** purpose `store_order` (ledger SSOT in `payment_transactions`).

| Concern | Where it lives |
|---------|----------------|
| Guest cart | Client `ring_cart` (`features/store/context.tsx`) |
| Auth cart mirror | `GET`/`POST /api/store/cart` — binds `session.user.id` only; ignores body `userId` |
| Soft-hold | `POST /api/store/cart/hold` — inventory soft-hold while cart has lines (may **409**) |
| Catalog / create product | `GET`/`POST` `/api/store/products` |
| Place order | `POST` `/api/store/orders` or `POST` `/api/store/checkout` (same `orders` pipeline) |
| Pay | `POST` `/api/store/payments/wayforpay` or `…/credit` → PaymentConductor |
| Vendor onboarding | `/vendor/start` + server actions — not REST under `/api/store/vendors` |

### For founders

## Why this API matters for your clone

Buyers browse the catalog, build a cart in the browser, confirm address, create an order, then pay by card (WayForPay) or internal credit when you enable it. Vendors list products after approval; commissions and settlements run after payment — not at “add to cart.”

  
- **[Store feature](/docs/features/store.md)** — Multi-vendor catalog, checkout UX, vendor desk, ERP inventory.

  
- **[PaymentConductor](/docs/features/payment-conductor.md)** — `store_order` purpose, rails, webhook dispatch, ledger SSOT.

  
- **[Payment integration](/docs/customization/payment-integration.md)** — WayForPay env, credit gates, clone processor overrides.

  
- **[Wallet API](/docs/api/wallet.md)** — Internal credit balance used by store credit checkout.

### Typical buyer flow

1. **Browse** — public `GET /api/store/products` (filters: search, categories, price, stock, sort).
2. **Cart** — guest: `ring_cart` in the browser. Signed-in: hydrate/sync via `GET`/`POST /api/store/cart` (server wins on hydrate).
3. **Address** — store address actions / checkout UI.
4. **Create order** — authenticated `POST /api/store/orders` (or `/checkout`); stock hold may return **409** if inventory fails.
5. **Pay** — WayForPay redirect or credit rail; webhook / conductor marks paid and runs vendor settlement.

### Operator notes

- **Vendor approval** — product create returns **403** until the vendor is approved; onboarding is `/vendor/start`, not `/api/store/vendors/apply`.
- **Commissions / settlements** — after successful payment (`VendorSettlementService`); configure trust tiers and store payment env, not fake on-chain RING contracts on this page.
- **Credit vs card** — credit for UAH carts needs `PAYMENT_CREDIT_BALANCE_ACCEPTED_ORDER_CURRENCIES=UAH` (or your accepted list); otherwise buyers use card.

> **Tip**
> Prefer the store UI and vendor/admin actions for day-to-day ops. Call these routes when automating checkout or integrating a custom storefront against the same order + PaymentConductor pipeline.

### For developers

## End-to-end payment path

```mermaid
sequenceDiagram
  participant B as Browser
  participant LS as localStorage ring_cart
  participant Cart as GET/POST /api/store/cart
  participant API as /api/store/*
  participant PC as PaymentConductor
  participant WH as /api/payments/wayforpay/webhook

  B->>LS: guest add/update
  B->>Cart: session hydrate + POST mirror
  B->>API: POST /orders or /checkout (session)
  API-->>B: orderId (409 if stock fail)
  B->>API: POST /payments/wayforpay or /credit
  API->>PC: createCheckout purpose=store_order
  PC-->>B: paymentUrl or paid
  WH->>PC: dispatch store_order handler
  Note over API,PC: settlement + stock deduct
```

## Route inventory (verified under `app/api/store/`)

### Cart (session mirror)

| Method | Path | Auth | Notes |
|--------|------|------|-------|
| GET | `/api/store/cart` | Session | Returns `{ items: [{ id, qty }], updatedAt }` for `session.user.id` |
| POST | `/api/store/cart` | Session | Body `{ items: [{ productId\|id, qty }] }`; **ignores** client `userId`/`uid`; may **409** on insufficient stock |
| POST | `/api/store/cart/hold` | Session | Soft-hold inventory for current cart lines |

### Catalog & product extras

| Method | Path | Auth | Notes |
|--------|------|------|-------|
| GET | `/api/store/products` | Public | Filters: `search`, `categories`, `priceMin`, `priceMax`, `inStock`, `sortBy`, `currency`, `limit`, `afterId` |
| POST | `/api/store/products` | Session; vendor approved or platform admin | Creates product; `updateTag('store:products')` |
| GET, POST | `/api/store/products/[id]/reviews` | GET public; POST session | Zod rating/content; verified-purchase when order history matches |
| GET, POST | `/api/store/products/[id]/agent-chat` | Session | Product agent conversation; POST may SSE-stream |
| GET | `/api/store/price-range` | Public | Min/max for catalog filters (excludes price slider) |

### Orders & checkout

| Method | Path | Auth | Notes |
|--------|------|------|-------|
| GET | `/api/store/orders` | Session | User’s orders; `limit`, `afterId` |
| POST | `/api/store/orders` | Session | Zod `orderCreateSchema`; referral cookie; `reserveInventoryForOrder` → **409** + cancel on stock fail |
| GET | `/api/store/orders/[id]` | Session (owner) | Order by id |
| POST | `/api/store/checkout` | Session | Canonical checkout → **same** `StoreOrdersService` / `orders` pipeline as POST orders (not legacy `store_orders`) |

### Payments

| Method | Path | Auth | Notes |
|--------|------|------|-------|
| POST | `/api/store/payments/wayforpay` | Session (order owner) | `PaymentConductor.createCheckout({ purpose: 'store_order', rail: 'merchant_redirect' })` |
| POST | `/api/store/payments/credit` | Session (order owner) | Rail `credit_balance`; may reject UAH unless `PAYMENT_CREDIT_BALANCE_ACCEPTED_ORDER_CURRENCIES` allows it |
| GET | `/api/store/payments/[orderId]/status` | Session (owner or admin) | Poll status / WayForPay when initiated |
| POST | `/api/store/payments/wayforpay/webhook` | Gateway | **Deprecated alias** — prefer `/api/payments/wayforpay/webhook` |

**Not live as store REST:** `/api/store/vendors`, `/api/store/vendors/apply`, PUT product or order-status under `/api/store/*`. Vendor MCP lives under `app/api/mcp/v1/store/vendors/*`. Order status advances via admin/store services and actions.

## Auth

- **Session required** for: cart mirror (`/api/store/cart`, `/hold`), product create, orders list/create, checkout, payments, agent-chat, review create, payment status.
- **Public:** product list, reviews GET, price-range.
- Same-origin Auth.js cookies (`auth()` in route handlers). Guests keep `ring_cart` until sign-in hydrates the server mirror.

## Card vs credit checkout

Create the order (`POST /api/store/orders` or `/checkout`) and keep `orderId`.

Call WayForPay initiation — body `{ orderId, returnUrl?, locale? }` (`UK` \| `EN` \| `RU`).

Redirect the buyer to `paymentUrl`. Canonical webhook: `POST /api/payments/wayforpay/webhook` (store alias only for legacy merchants).

{`await PaymentConductor.createCheckout({
  purpose: 'store_order',
  rail: 'merchant_redirect',
  userId,
  userEmail,
  entityId: orderId,
  orderId,
  amount,
  currency: 'UAH',
  items,
  shippingInfo,
  returnUrl,
  locale: 'UK',
})`}

Ensure `PAYMENT_STORE_ALLOW_CREDIT` is not `false` and UAH (if used) is listed in `PAYMENT_CREDIT_BALANCE_ACCEPTED_ORDER_CURRENCIES`.

`POST /api/store/payments/credit` with `{ orderId }` — conductor rail `credit_balance`; on success the route marks the order paid and runs stock deduct + settlements.

## Env (store payments)

| Variable | Role |
|----------|------|
| `PAYMENT_STORE_PROCESSOR` | Optional override for `store_order` processor (else default) |
| `PAYMENT_STORE_ALLOW_CREDIT` | Credit rail for store (default allow unless `false`) |
| `PAYMENT_STORE_ALLOW_TOKEN` | Opt-in token rail (`=== 'true'` in `payment.config`) |
| `PAYMENT_CREDIT_BALANCE_ACCEPTED_ORDER_CURRENCIES` | e.g. `UAH` to allow credit on UAH carts |
| `WAYFORPAY_MERCHANT_ACCOUNT` / `WAYFORPAY_SECRET_KEY` / `WAYFORPAY_DOMAIN` | WayForPay SSOT (**no** `MERCHANT_ID`) |
| `WAYFORPAY_STORE_*` | Optional store-specific overrides of the above |

## Module map

| Path | Role |
|------|------|
| `lib/payments/conductor/payment-conductor.ts` | `createCheckout` entry |
| `lib/payments/processors/wayforpay.processor.ts` | `store_order` merchant redirect |
| `lib/payments/conductor/handlers/store-order.ts` | Webhook / paid handler |
| `features/store/services/orders-service.ts` | Order CRUD / payment status |
| `features/store/services/inventory-sync.ts` | `reserveInventoryForOrder` |
| `features/store/services/vendor-settlement.ts` | Post-pay settlements |
| `features/store/context.tsx` | Guest cart `ring_cart` + hydrate against `/api/store/cart` when signed in |
| `features/store/services/server-cart.ts` | Session cart mirror used by `/api/store/cart` |
| `app/api/store/cart/route.ts`, `…/hold/route.ts` | Auth cart GET/POST + soft-hold |
| `app/_actions/vendor-actions.ts`, `store-products.ts`, `store-address-actions.ts`, `store-erp.ts` | Vendor/admin/address/ERP actions |

{`curl -s "http://localhost:3000/api/store/products?limit=5" | jq '{count:(.products//.items//.)|length}'`}

## Related documentation

  
- **[Store feature](/docs/features/store.md)** — Product pages, vendor desk, ERP surfaces.

  
- **[PaymentConductor](/docs/features/payment-conductor.md)** — Purposes, rails, webhook dispatcher.

  
- **[Admin API — store orders](/docs/api/admin.md)** — Admin order status transitions.

  
- **[Wallet API](/docs/api/wallet.md)** — Credit balance for credit_balance rail.

  
- **[Refcodes](/docs/features/refcodes.md)** — Referral cookie on POST orders.
