Razorpay Magic Checkout — Storefront
Start and status endpoints for Razorpay's hosted one-click checkout: the storefront creates a one-click Razorpay order from the cart; Razorpay collects address + OTP + coupons in its modal and the webhook places the internal order on completion.
Entry point for Razorpay Magic Checkout, Razorpay's hosted one-click checkout. Unlike standard checkout, Razorpay's own modal collects the customer's phone (OTP login), shipping/billing address, and coupons — the storefront does not collect an address. The server creates a one-click Razorpay order from the cart's line items; the internal order is placed later, by the webhook, once Razorpay has captured payment and collected the address.
Source:
api-modules/payment-razorpay-magic/src/controllers/store-razorpay-magic.controller.ts
This module is a removable plugin mounted by RazorpayMagicPaymentModule.forRoot() in apps/api/src/app.module.ts. It reuses the standard Razorpay account/credentials (payment.razorpay settings).
Enablement
The start endpoint operates only when all hold:
razorpay-magicis inpayment.enabled_providersfor the cart's platform —weband/orapp(admin settings, grouppayment), AND- group
payment.razorpay_magic, keyenabledistrue, AND - the shared Razorpay credentials (
payment.razorpay—key_id/key_secret) are non-empty.
Otherwise the endpoint returns 403 FORBIDDEN. Magic Checkout serves both web (checkout.js) and app (Razorpay Android / iOS / Flutter SDKs) — the platform is taken from the cart, so enable razorpay-magic in the enabled_providers list(s) for the platform(s) you ship.
Operators configure the toggles under Admin → Plugins → Razorpay → Configuration.
The publishable Razorpay keyId is available without an admin token from GET /store/settings/payment (and the payment group of GET /store/settings) — handy for initialising the mobile SDK at app startup. See Store Settings.
GET /store/payment/razorpay-magic/config
Anonymous. What a client calls before rendering a Quick Checkout entry point.
enabled folds together all three conditions above into one boolean, so the client never reassembles the rule and can't drift from what start will accept. keyId is the publishable key (empty when disabled), so this one call both decides whether to show the button and boots the SDK when it's clicked.
Auth: none. Header: x-platform: WEB | APP (defaults to WEB).
{
"data": {
"enabled": true,
"keyId": "rzp_test_xxxxxxxx"
}
}With the plugin unmounted this route 404s — clients should treat that as disabled rather than an error.
POST /store/cart/:cartId/magic-checkout/start
Starts a Magic Checkout for the caller's cart. Requires a session (the cart's anonymous or registered owner). Reserves inventory, creates the one-click Razorpay order from the cart's line items, persists a razorpay_magic_checkout session row, and returns the bootstrap data for checkout.js.
Auth: BetterAuthGuard — the session user must own the cart (cross-user cart ids return 404, never 403).
| Path param | Description |
|---|---|
cartId | The cart to check out. Must belong to the session user. |
Request body: none.
Response 200 OK (standard { data, message, statusCode } envelope):
{
"data": {
"razorpayOrderId": "order_NXg...",
"keyId": "rzp_test_xxxxxxxx",
"amount": 124900, // items total in subunits (paise)
"currency": "INR",
"oneClickCheckout": true
}
}The client opens checkout.js with { key: keyId, order_id: razorpayOrderId, one_click_checkout: true }. Razorpay then drives the modal (address, OTP, coupons, payment). The order amount excludes shipping and coupons — Razorpay adds shipping via the serviceability callback and subtracts coupons via the apply-promotion callback (see the webhooks doc).
| Status | Cause |
|---|---|
403 FORBIDDEN | Magic Checkout disabled (see Enablement). |
404 NOT_FOUND | Cart not found, not owned by the caller, or empty. |
GET /store/cart/:cartId/magic-checkout/status
Polls the outcome of a Magic Checkout. The internal order is placed asynchronously by the completion webhook, so after the Razorpay SDK / checkout.js reports a successful payment the client has only the razorpayOrderId — it must poll here until the order is placed.
Auth: BetterAuthGuard — the session user must own the cart. Returns the most recent Magic session for the cart.
Response 200 OK:
{
"data": {
"status": "paid", // created | processing | paid | failed
"orderId": "01J9...", // internal order id once paid, else null
"orderNumber": "ORD-2026-00000123" // displayId (reserved at start)
}
}Client handling: created / processing → keep polling; paid → route to the order via orderId / orderNumber; failed → show failure.
| Status | Cause |
|---|---|
404 NOT_FOUND | Cart not found / not owned, or no Magic Checkout exists for it. |
Lifecycle
- Start → this endpoint pre-allocates the order's human number (displayId, e.g.
ORD-2026-00000123), uses it as the Razorpay orderreceipt, and creates the Razorpay order + magic-session. The internal order placed at completion carries that same number, so the Razorpay dashboard and the order's displayId always match. (Standard Razorpay checkout does the same —receipt= the order's displayId.) - Modal → Razorpay calls our coupon + shipping callbacks as the customer fills the form.
- Completion → the webhook fires (
payment.capturedfor prepaid,payment.pending+codfor COD); we fetch the collected address, place the internal order, and confirm it. - Confirmation → the order's
ORDER_PLACEDevent drives the guest confirmation email + tracking link (when the guest-checkout plugin is mounted). A later sign-up with the same email migrates the order viaGuestConversionService.
The customer is the cart's anonymous user; Razorpay's contact (email/name/phone) is stored on the guest contact row, exactly as standard guest checkout.
Payment PhonePe Module — Storefront
The customer-side verify endpoint called after PhonePe returns the shopper, from web and mobile alike. Takes no body — PhonePe's client callback carries no payment status, so the server reads the gateway itself.
Payment Razorpay Module — Storefront
HTTP surface for the customer-side payment-verify callback used by the Razorpay Checkout SDK after the user completes payment. Verifies the HMAC signature, pins to the place-time…