Magic Checkout Integration Guide
Complete end-to-end guide to integrate Razorpay Magic Checkout. Set up promotions and shipping APIs, accept COD, verify payments and go live safely.
This is a complete, end-to-end guide to accept payments using Razorpay Magic Checkout, the one-click checkout that prefills saved addresses and payment details, supports coupons and discounts, and accepts both prepaid and Cash on Delivery (COD) orders. Magic Checkout reduces COD return-to-origin (RTO) by analysing customer shopping history and address quality.
Once you finish integrating Razorpay using this guide, you will be able to accept payments from your customers using Magic Checkout. To see how this compares with the other integration options, refer to the
.What you are responsible for:
- Enabling the Magic Checkout feature on your account.
- Hosting the promotions and shipping-info APIs that Magic Checkout calls.
- Creating an order before payment.
- Opening Magic Checkout with
one_click_checkout: true. - Verifying the signature on your server.
- Handling prepaid and COD fulfilment via webhooks.
Customer Magic Checkout Your Server Razorpay| | | || | | 1. POST /v1/orders || | |-------------------->|| | | order_id || 2. Open checkout | |<--------------------||------------------->| 3. get-promotions | || |----------------------->| (your API) || | 4. shipping-info | || |----------------------->| (your API) || 5a. Prepaid pay | | ||------------------->|--------------------------------------------->| authorize| 5b. Place COD | | ||------------------->|--------------------------------------------->| order.placed| | 6. success response | || |----------------------->| 7. Verify signature || | | 8. Capture (prepaid)|| | |-------------------->|| | | 9. Webhooks: || | | order.paid / || | | payment.pending / || | | fulfillment.* || | |<--------------------|
- Set up promotions and shipping APIs on your server and register them in the Dashboard (Step 1).
- Create an order for every checkout (Step 2).
- Open Magic Checkout with
one_click_checkout: true; it calls your promotions and shipping APIs (Step 3). - Verify the signature on your server for prepaid payments (Step 4).
- Confirm and fulfil via webhooks; capture prepaid payments and process COD orders (Step 5).
Use Magic Checkout when you run an ecommerce store and want a faster, one-click checkout with prefilled customer details, built-in coupons and COD support. If you only need to accept a payment and do not need addresses, coupons or COD, use
. If your store runs on WooCommerce or Shopify, use the or plugin instead of building this integration.Handy Tips: Magic Checkout is an on-demand feature
Raise a request with your Razorpay SPOC or
to enable Magic Checkout and the Cash on Delivery payment method on your account.Magic Checkout supports prepaid and COD orders, so it has an extra pending state for COD. You can fetch the current state with the
- A with Magic Checkout enabled by your SPOC.
- from the Dashboard. Use Test Mode keys while building and Live Mode keys to go live.
- A server-side backend to call the Orders API, host the promotions and shipping APIs, and verify signatures.
- HTTPS on your website with a valid SSL/TLS certificate.
Magic Checkout calls APIs on your server to fetch promotions and shipping information. Build these first and register their URLs in the Dashboard.
Watch Out: These URLs must be public and unauthenticated
The promotions and shipping-info URLs must be publicly accessible, require no authentication and be hosted on your server.
- Log in to the Dashboard and go to Magic Checkout.
- In Platform Setup, select Custom E-Commerce Platform and select Next.
- In Setup & Settings → Checkout Settings → Coupon Settings, enter the URL for get promotions and the URL for apply promotions, then save.
- Go to Shipping Setup, select API as the shipping service type, enter the URL for shipping info and save.
This API returns shipping serviceability, COD serviceability, shipping fees and COD fees for a list of customer addresses.
{"order_id": "SomeReceiptValue","razorpay_order_id": "EKwxwAgItmXXXX","email": "john.smith@example.com","contact": "+919000090000","addresses": [{ "id": "0", "zipcode": "560060", "state_code": "KA", "country": "IN" }]}
Fees are in paise. If cod is false, set cod_fee to 0.
Before the promotions APIs can return anything, the promotions themselves must exist. You have two options:
- Create coupons on the Razorpay Dashboard. Go to Magic Checkout → Setup & Settings → Coupon Settings and create coupons with a code, type (
fixed_amount,percentageorBXGY), value and conditions such as minimum cart value or customer eligibility. Use this when you want Razorpay to manage coupon storage. - Manage promotions in your own system. Keep coupons in your commerce backend and have your get-promotions and apply-promotions APIs return them. Use this when your promotion logic (bundles, loyalty, personalised offers) already lives on your server.
Either way, Magic Checkout only ever reads promotions through the two APIs below, so they must always return the correct, current set.
The get-promotions API returns the coupons applicable to an order and customer. The apply-promotions API validates a code the customer enters and returns the discount.
{"order_id": "SomeReceiptValue","contact": "+919000090000","email": "john.smith@example.com"}
{"order_id": "SomeReceiptValue","contact": "+919000090000","email": "john.smith@example.com","code": "500OFF"}
The value is always in paise and applied as-is. value_type can be fixed_amount, percentage or BXGY (Buy X Get Y).
Create an order on your server for every checkout, the same as other Razorpay integrations. Save the id from the response and pass it to Magic Checkout in Step 3. Amounts are in paise, for example, ₹450 = 45000.
Watch Out: Set the final payable amount on the order
The amount the customer is charged is always the order amount, not anything passed on the client. Create the order with the final payable amount after any coupon or pre-discount, and never create orders in browser-side code.
curl -X POST https://api.razorpay.com/v1/orders \-u [YOUR_KEY_ID]:[YOUR_KEY_SECRET] \-H 'content-type:application/json' \-d '{"amount": 45000,"currency": "INR","receipt": "rcptid_11"}'
The receipt value you set here is the order_id your promotions and shipping APIs receive. Know more about the
Load magic-checkout.js and open Checkout with one_click_checkout: true. Use the handler function for SPAs or the callback URL for server-rendered apps.
<button id="rzp-button1">Pay</button><script src="https://checkout.razorpay.com/v1/magic-checkout.js"></script><script>var options = {"key": "YOUR_KEY_ID","one_click_checkout": true,"name": "Acme Corp","order_id": "order_EKwxwAgItmXXXX", // from Step 2"show_coupons": true, // set false to hide the coupon widget"handler": function (response){// send these three fields to your server for verification (Step 4)alert(response.razorpay_payment_id);alert(response.razorpay_order_id);alert(response.razorpay_signature);},"prefill": {"name": "John Smith","email": "john.smith@example.com","contact": "9000090000","coupon_code": "500OFF" // auto-applied when Magic opens},"notes": { "address": "ABC Office" }};var rzp1 = new Razorpay(options);rzp1.on('payment.failed', function (response){alert(response.error.code);alert(response.error.description);alert(response.error.reason);});document.getElementById('rzp-button1').onclick = function(e){rzp1.open();e.preventDefault();}</script>
For server-rendered apps, set "callback_url": "https://yourserver.com/callback" and "redirect": "true" instead of the handler function.
For the complete list, see the full
.Handy Tips: Pre-discounts and promotional tags
Use prefill.prediscount to display existing discounts (coupons, gift cards, loyalty points) in the order summary. Pre-discounts are display only and do not change the payment amount, which is always set by your Razorpay order. For gift cards, include gift_card_applied: true. Use prefill.promotional_tag to badge specific products.
Verify the signature on your server before fulfilling, exactly as in other Razorpay integrations. The signature is an HMAC-SHA256 hash of order_id and payment_id, signed with your Key Secret.
const crypto = require('crypto');function verifyPaymentSignature(orderId, paymentId, receivedSig, keySecret) {const body = orderId + '|' + paymentId;const expectedSig = crypto.createHmac('sha256', keySecret).update(body).digest('hex');return crypto.timingSafeEqual(Buffer.from(expectedSig, 'hex'),Buffer.from(receivedSig, 'hex'));}// Use the order_id from YOUR database, not the one from the browser.
Watch Out: Always verify the signature server-side
Use the order_id from your own database, not the one returned in the browser, which could be spoofed. Use a timing-safe comparison and store all three fields for audit and idempotency. A failed check means a tampered payment, so reject it.
Magic Checkout handles prepaid and COD orders, so subscribe to order, payment and fulfilment webhooks.
in Dashboard → Account & Settings → Webhooks, verify theX-Razorpay-Signature header on every event, return HTTP 200 within 5 seconds and process events in a background worker.
The confirmation path is different for prepaid and COD orders:
Handy Tips: COD is webhook-driven, not API-driven
You do not capture or "confirm" a COD payment with an API call. Razorpay confirms the COD order itself (including COD OTP and RTO scoring) and notifies you with order.placed and payment.pending. Your only job is to act on the fulfilment webhooks. The COD payment stays pending until you mark it delivered, and you update fulfilment status as the shipment progresses.
Order events
Payment events
Fulfilment events
For the full async webhook handler pattern, replay-attack guard, idempotency, monitoring metrics, rollback plan and logging strategy, see the shared deep-dives in the
. They apply to Magic Checkout too.Handy Tips: Track the checkout funnel
Magic Checkout emits analytics events you can subscribe to with rzp1.on('mx-analytics', ...), including initiate, otp_initiated, payment_initiated, coupon_applied, payment_failed and checkout_abandoned. Forward these to your analytics platform to optimise conversion.
Test with Test Mode API keys so no real money moves. Use the mock bank page's Success and Failure buttons to simulate outcomes, and place a COD order to exercise the order.placed and payment.pending webhooks.
Use any random CVV and any future expiry date. For netbanking and wallets, choose any bank or wallet and use the Success or Failure buttons. See the full list of
.- The prepaid flow end to end, including signature verification and capture.
- The COD flow: placing a COD order and receiving
order.placedandpayment.pending. - Coupon application through your get-promotions and apply-promotions APIs.
- Shipping-info responses for serviceable, non-serviceable and COD-disabled pincodes.
- If you configured settings in Test Mode, replicate them in Live Mode, including the promotions and shipping URLs.
- Use API keys generated in Live Mode. If you have multiple Razorpay accounts, use the Live key from the account you intend to go live with.
- Verify the signature server-side, fulfil only after capture (for prepaid) or after
order.placed(for COD), and handle all subscribed webhooks idempotently. - Re-test the prepaid flow, the COD flow, coupon application and the shipping-info responses in Test Mode before switching keys.
Magic Checkout is Razorpay's one-click checkout. It prefills saved customer addresses and payment details, supports coupons and discounts, and accepts both prepaid and Cash on Delivery orders, with RTO intelligence to reduce COD returns. Standard Checkout only collects a payment. Magic Checkout loads magic-checkout.js and is opened with one_click_checkout: true, and it requires you to host promotions and shipping-info APIs.
Magic Checkout calls your server to fetch applicable coupons, validate applied codes and determine shipping and COD serviceability and fees for the customer's address. These endpoints must be publicly accessible and unauthenticated, and you register their URLs in the Dashboard under Magic Checkout settings.
COD orders move the payment to the pending state and fire order.placed and payment.pending webhooks. Confirm and process these through the fulfilment events (fulfillment.to_be_shipped through fulfillment.delivered). Enable the COD payment method by raising a request with Razorpay Support.
No. Pre-discounts passed in prefill.prediscount are for display only. The amount charged is always determined by your Razorpay order, so create the order with the final payable amount. For gift cards, include gift_card_applied: true for correct tracking and display.
An authorized payment moves to failed if it is not captured within 45 days. Capture it, or enable auto-capture, to confirm the payment and move it to captured before you fulfil. This applies to prepaid orders only; COD orders are not captured.
- Promotions or shipping URLs behind authentication. They must be public and unauthenticated, or Magic Checkout cannot call them and the coupon and shipping widgets fail.
- Returning
order_idmismatches. Your APIs receive the order'sreceiptvalue asorder_id. Make sure your server maps it correctly. - Expecting a COD capture step. COD is webhook-driven. Do not wait for a capture that never comes; act on
order.placed. - Trusting the client amount. The charge is always the order amount. Create the order with the final payable amount after discounts.
- Not replicating settings in Live Mode. Promotions and shipping URLs configured in Test Mode do not carry over automatically.
- Fees in the wrong unit. All fees and discount values are in paise.
cod_feemust be0whencodisfalse. - Parsing the webhook before verifying the signature. Verify against the raw body first.
Yes. Besides the web SDK, Magic Checkout is available for
, , and . Order creation, promotions, shipping APIs and signature verification stay on your server and are identical across platforms.- (shared webhook, monitoring, rollback and logging deep-dives)
- and
Is this integration guide useful?
ON THIS PAGE