docs.checkout.payments
Payment modes
QuantaRoute supports three payment paths. Mode A and Mode B are available now. Mode C (QuantaRoute-hosted payments via Razorpay Route) is coming soon.
Three payment modes
Choose the mode that matches your existing payment stack. QuantaRoute never holds merchant funds long-term in any mode — customer payment settlement stays with your PG or Razorpay account.
| Mode A — External PG | Mode B — BYO Razorpay | Mode C — Hosted | |
|---|---|---|---|
| Profile | PayU, Cashfree, Paytm, Shopify | Own Razorpay account | No PG — wants simplicity |
| Widget payment | No `enablePayment` | `enablePayment` + own keys | `enablePayment` — no keys (coming soon) |
| Portal setup | BYO PG guide | Paste Razorpay keys | KYC + bank only (coming soon) |
| Settlement | Your PG → your bank | Your Razorpay → your bank | Route Linked Account → bank (coming soon) |
| `session.completed` webhook | No | Yes | Yes (coming soon) |
| Ship status | Available now | Available now | Coming soon |
Mode A — External payment gateway
QuantaRoute handles address capture, buyer identity, and DigiPin verification. Your existing PG handles payment and settlement.
Omit enablePayment (default is false). No QuantaRoute payment keys or Razorpay configuration required. Funds flow entirely through your existing PG to your bank account.
Widget embed
import { CheckoutWidget } from '@quantaroute/checkout';
import type { CompleteAddress } from '@quantaroute/checkout';
import 'maplibre-gl/dist/maplibre-gl.css';
import '@quantaroute/checkout/style.css';
function buildMerchantCheckoutUrl(params: {
sessionId: string;
digipin: string;
formattedAddress: string;
pincode: string;
}) {
const url = new URL('https://yourstore.com/checkout/pay');
url.searchParams.set('session', params.sessionId);
url.searchParams.set('digipin', params.digipin);
url.searchParams.set('address', params.formattedAddress);
url.searchParams.set('pincode', params.pincode);
return url.toString();
}
export default function AddressStep() {
return (
<CheckoutWidget
apiKey={process.env.NEXT_PUBLIC_QUANTAROUTE_KEY!}
merchantId={process.env.NEXT_PUBLIC_MERCHANT_ID!}
supabaseFunctionBaseUrl={process.env.NEXT_PUBLIC_SUPABASE_FUNCTION_URL!}
enableEmailAuth
// enablePayment omitted — merchant handles PG externally
onComplete={(address: CompleteAddress) => {
window.location.href = buildMerchantCheckoutUrl({
sessionId: address.sessionId ?? '',
digipin: address.digipin,
formattedAddress: address.formattedAddress,
pincode: address.pincode,
});
}}
/>
);
}onComplete handoff
When the buyer submits a verified delivery address, onComplete fires with a CompleteAddress object. Use sessionId as the correlation key between QuantaRoute events and your draft order.
https://merchant.com/checkout/pay?session=<sessionId>&digipin=<digipin>&address=<formattedAddress>&pincode=<pincode>Lazy DigiPin verify
If a buyer selects a saved address imported without a DigiPin, the widget shows a map pin step before onComplete fires. Do not create a shippable order until handoff completes with a verified DigiPin.
| Address state | Buyer experience |
|---|---|
| Has DigiPin | OTP → pick address → onComplete immediately |
| Missing DigiPin (imported) | OTP → pick address → map pin + confirm form → onComplete |
| Same address on next order | Skips verification — DigiPin already stored |
session.completed is not fired in Mode A. Mark orders paid after your PG confirms payment. See Webhooks & events for Mode A webhook events.Mode B — BYO Razorpay
Buyer pays inside the QuantaRoute widget using your Razorpay account. Settlement goes directly to your bank.
Set enablePayment={true} and configure Razorpay keys server-side via the checkout-admin API. The widget calls checkout-payment to create orders and verify payments — keys never ship to the browser bundle.
Configure Razorpay keys
curl -X PATCH \
"https://<project>.supabase.co/functions/v1/checkout-admin?action=set_payment_config" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_MERCHANT_ADMIN_KEY" \
-d '{
"razorpay_key_id": "rzp_test_...",
"razorpay_secret_key": "YOUR_TEST_SECRET"
}'Verify with GET checkout-admin?action=get_payment_config — returns razorpay_key_id_set: true. The secret is never returned.
Widget embed with payment
import { CheckoutWidget } from '@quantaroute/checkout';
import 'maplibre-gl/dist/maplibre-gl.css';
import '@quantaroute/checkout/style.css';
export default function CheckoutPage() {
const cartTotalPaise = 499_00; // ₹499.00
return (
<CheckoutWidget
apiKey={process.env.NEXT_PUBLIC_QUANTAROUTE_KEY!}
merchantId={process.env.NEXT_PUBLIC_MERCHANT_ID!}
supabaseFunctionBaseUrl={process.env.NEXT_PUBLIC_SUPABASE_FUNCTION_URL!}
enableEmailAuth
enablePayment
paymentAmountPaise={cartTotalPaise}
orderConfirmationUrl="https://yourstore.com/order/confirm?sid={{sessionId}}"
onComplete={(address) => {
console.log(address.digipin, address.formattedAddress);
}}
onPaymentComplete={(result) => {
console.log(result.paymentMethod);
console.log(result.razorpayOrderId, result.razorpayPaymentId);
}}
/>
);
}session.completed webhook to your backend. See Webhooks & events.Mode C — QuantaRoute-hosted payments
QuantaRoute Payments
Integrated checkout + payments in one signup, without creating your own Razorpay or PayU account. QuantaRoute acts as the Razorpay platform; each merchant is onboarded as a Route Linked Account. Captured payments route to your bank on Razorpay's Route schedule (typically T+2).
on the roadmap
- >Simple Shortest Path routing algorithm for India
- >Turn-by-turn directions and distance matrices
- >SDK guides and interactive API reference
| Prerequisite | Status |
|---|---|
| QuantaRoute master Razorpay account with Route enabled | In progress |
| Backend payment_mode = hosted_razorpay | Planned |
| Portal KYC form + Linked Account status tracking | Planned |
Payment props reference
Payment-related widget props. Core address and auth props are documented on the Widget page.
| Prop | Type | Default | Description |
|---|---|---|---|
enablePayment | boolean | false | Show Razorpay payment step after address confirmation (Mode B). Omit for Mode A external PG handoff. |
paymentAmountPaisereq | number | — | Cart total in paise (integer). Required when enablePayment is true. |
enableCod | boolean | false | Show Cash on Delivery option when merchant COD config is enabled. |
codCartTotalPaise | number | — | Cart total used for COD eligibility checks (cod_max_cart_value_paise, blocked pincodes). |
onPaymentComplete | (result: PaymentResult) => void | — | Called after prepaid Razorpay success or COD selection. Receives paymentMethod, razorpayOrderId, razorpayPaymentId. |
orderConfirmationUrl | string (URL) | — | Browser redirect after successful payment. {{sessionId}} is replaced with the checkout session ID. |
Cash on Delivery (COD)
Enable COD on the merchant record, then pass enableCod on the widget. COD eligibility respects cod_blocked_pincodes and cod_max_cart_value_paise configured via checkout-admin.
curl -X PATCH \
"https://<project>.supabase.co/functions/v1/checkout-admin?action=cod_config" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_MERCHANT_ADMIN_KEY" \
-d '{ "enable_cod": true }'<CheckoutWidget
/* ...base props... */
enableCod
codCartTotalPaise={cartTotalPaise}
enablePayment
paymentAmountPaise={cartTotalPaise}
/>Selecting COD calls checkout-payment record_cod and fires the same session.completed webhook with payment_method: "cod".
Configure webhooks
Set up signed webhook delivery for checkout lifecycle events.