Migrating from Stripe Checkout
Rupay uses a Checkout Session-style server API but hosts a page that can offer card and local payment rails. The main migration is replacing session creation, redirect URL ownership, and webhook verification.
Mapping
| Stripe Checkout | Rupay Hosted Checkout |
|---|---|
stripe.checkout.sessions.create(...) | POST /v1/checkout/sessions |
| Stripe secret key | Rupay rp_test_ or rp_live_ API key |
| Stripe Price IDs | Rupay Price IDs in line_items[].price |
| Stripe-hosted session URL | Rupay-hosted url |
checkout.session.completed from Stripe | checkout.session.completed signed by Rupay |
| Stripe session retrieval | Authenticated Rupay session retrieval |
| Stripe webhook secret | Rupay merchant signing secret |
Rupay does not expose its internal provider invoice IDs. Integrations should
use Rupay session IDs and their own client_reference_id.
Before
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [{ price: "price_123", quantity: 1 }],
success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${origin}/cancel`,
});
After
const response = await fetch(
`${process.env.RUPAY_API_BASE_URL}/v1/checkout/sessions`,
{
method: "POST",
headers: {
authorization: `Bearer ${process.env.RUPAY_API_KEY}`,
"content-type": "application/json",
"idempotency-key": `order-${order.id}`,
},
body: JSON.stringify({
mode: "payment",
client_reference_id: order.id,
currency: "npr",
line_items: [
{
description: "Order total",
quantity: 1,
amount_unit: order.totalMinor,
},
],
success_url: `${origin}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${origin}/cancel`,
metadata: { order_id: order.id },
}),
},
);
if (!response.ok) throw new Error("Rupay session creation failed");
const session = await response.json();
redirect(session.url);
Stripe SDK integration
For the supported fixed-period purchase flow, pin Stripe Node 20.4.1 and
apiVersion: "2026-02-25.clover", set its host/protocol/port to Rupay, and use a
Rupay API key. customers.create/retrieve, products.list/retrieve,
prices.list/retrieve, checkout.sessions.create/retrieve/expire, and
webhookEndpoints.create are supported as described in the
compatibility contract.
line_items: [{ price: "price_...", quantity: 1 }] now accepts Rupay's immutable
catalog Price IDs. Native product/variant checkout requests and inline
price_data continue to work. For fixed-term access, use mode: "payment";
mode: "subscription" is rejected because recurring collection is not implemented.
The SDK sends the pinned Stripe-Version header to select compatible response
shapes. Native requests without it retain Rupay's existing response format.
SDK-created webhook endpoints receive SDK-shaped checkout.session.completed
events with a Stripe-Signature header and a separate endpoint secret. Use
constructEventAsync in Cloudflare/Bun. Existing native endpoints are unchanged.
See the Nepse Edge SDK example for customer mapping, Price configuration, checkout creation, signature verification, and fulfillment. This is a supported subset, not a claim of complete Stripe API compatibility.