Skip to main content

Orders & Payments Endpoints

Two parallel order systems:

  • StoreOrder โ€” current commerce surface (shop products). Endpoints under /api/store-orders.
  • Order โ€” eats orders. Vertical archived 2026-04-17, but historical orders + endpoints preserved.

Store ordersโ€‹

POST /api/store-orders ๐Ÿ”’ auth requiredโ€‹

Place a new store order. Validates feature flag + auto-defaults pickup payment.

Body:

{
storeId: number;
items: Array<{ productId: number; quantity: number; productName?: string }>;
deliveryAddress?: string; // required for delivery
customerPhone?: string;
fulfillmentType?: "delivery" | "pickup_immediate" | "pickup_scheduled"; // default: delivery
pickupTime?: string; // ISO timestamp, required for pickup_scheduled
paymentMethod?: "cod" | "online"; // server overrides for pickup โ†’ "pay_at_pickup"
instructions?: string;
}

Response (201): { order: StoreOrder, contributionInPaise: number }

Errors:

  • 400 โ€” store closed, beyond delivery radius, scheduled pickup time invalid, unknown product, online payment when flag off
  • 401 โ€” not authenticated
  • 404 โ€” store not found / not active

Side effects:

  • Creates StoreOrder + StoreOrderItem rows
  • Notifies seller via SellerNotification + push
  • Sends consumer order confirmation email (best-effort, fire-and-forget)
  • Creates Contribution row (0.5% to charity ledger)
  • Creates ReelEarning if order came from a tagged reel

GET /api/store-orders ๐Ÿ”’ auth requiredโ€‹

List the consumer's own orders.

Response: { orders: StoreOrder[] }

GET /api/store-orders/[id] ๐Ÿ”’ auth requiredโ€‹

Single order detail with items.

Response: { order: StoreOrder & { items: StoreOrderItem[] } }

Returns 404 (NOT 403) for non-owners โ€” prevents id-enumeration leaks.

PATCH /api/store-orders/[id]/cancel ๐Ÿ”’ auth requiredโ€‹

Consumer cancellation. Only allowed if status='pending'.

Body: { reason?: string } (optional) Response: { order }

PATCH /api/seller/stores/[id]/orders ๐Ÿ”’ seller auth requiredโ€‹

Seller-side status update.

Body: { orderId, status, sellerPickupTime?, cancellationReason? } Response: updated order Side effects:

  • Notifies consumer via Notification + push
  • For delivery orders + accepted/preparing status โ†’ fires assignDeliveryPartner() (non-blocking) unless store.selfDelivery
  • On delivered/completed โ†’ confirms ReelEarning
  • On cancelled โ†’ reverses ReelEarning

Paymentsโ€‹

GET /api/payments/upi-configโ€‹

Returns payment configuration + feature flags. Used by frontend to decide which UI to show.

Response:

{
upiEnabled: boolean; // PLATFORM_UPI_ID configured
gatewayEnabled: boolean; // PHONEPE_* configured
paymentEnabled: boolean; // (upiEnabled || gatewayEnabled) && PAYMENTS_ONLINE_ENABLED
paymentsOnlineEnabled: boolean; // raw flag value
reelsCommerceEnabled: boolean; // raw flag value
}

POST /api/payments/initiate ๐Ÿ”’ auth requiredโ€‹

Eats payment session (legacy, archived flow).

Returns 503 when PAYMENTS_ONLINE_ENABLED=false.

POST /api/payments/initiate-store ๐Ÿ”’ auth requiredโ€‹

Store payment session (UPI direct OR PhonePe gateway).

Body: same shape as POST /api/store-orders but order isn't created until payment completes.

Returns 503 when PAYMENTS_ONLINE_ENABLED=false.

POST /api/payments/confirmโ€‹

Manual UPI confirm (user enters UTR). Creates the order if UTR validates.

POST /api/payments/callbackโ€‹

PhonePe gateway webhook. Creates the order on success.

GET /api/payments/status/[id]โ€‹

Poll payment status.

POST /api/payments/cleanupโ€‹

Cron-only. Auto-cancels payments stuck in pending for >5 min (PAYMENT_TIMEOUT_MS).

Eats orders (legacy)โ€‹

These endpoints still work for historical eats orders but no new orders should flow through them (Eats archived 2026-04-17).

GET /api/orders/consumer ๐Ÿ”’โ€‹

Unified store + eats orders for a consumer. Used by web orders page.

GET /api/restaurants / [id]โ€‹

Restaurant catalog (for historical menus).

Common shapesโ€‹

StoreOrderโ€‹

{
id: number;
orderNumber: string;
status: "pending" | "accepted" | "preparing" | "ready" | "ready_for_pickup"
| "out_for_delivery" | "delivered" | "completed" | "cancelled";
customerName: string;
customerPhone: string;
deliveryAddress: string;
fulfillmentType: "delivery" | "pickup_immediate" | "pickup_scheduled" | null;
pickupTime: string | null;
paymentMethod: "cod" | "online" | "pay_at_pickup" | null;
paymentStatus: "unpaid" | "cod" | "pay_at_pickup" | "paid" | "refunded";
subtotal: string; // Decimal as string
deliveryFee: string;
total: string;
platformFee: string;
sellerAmount: string;
cancellationReason: string | null;
acceptedAt: string | null;
preparingAt: string | null;
readyAt: string | null;
readyForPickupAt: string | null;
outForDeliveryAt: string | null;
deliveredAt: string | null;
completedAt: string | null;
cancelledAt: string | null;
storeId: number;
consumerId: number;
items: StoreOrderItem[];
createdAt: string;
updatedAt: string;
}