How It Works
Stripe decides when and how much to charge. Razorpay executes those charges in India, compliantly, with UPI Autopay, smart retries and pre-debit notifications. Your existing Stripe billing, invoicing and reconciliation workflows remain untouched. For Indian customers, Razorpay handles coupon validation (coupon creation remains at Stripe), mandate creation, PDN delivery, payment execution and status reporting back to Stripe. Razorpay also calculates and adds 18% GST on the amount debited from Indian customers. See Charges, Taxes and Limits.What Changes vs. What Stays the Same
Prerequisites
- Share your Stripe API key with Razorpay.
- Create custom payment methods (
Razorpay_UPIandRazorpay_Card) in Stripe. - Create a plan mapping between your Stripe plans and Razorpay.
- For usage-based billing, configure your webhook endpoint to forward Stripe’s
invoice.payment_attempt_requiredevents to Razorpay’s/v1/cb/stripe/webhook. Your server is the intermediary: Stripe sends the event to your endpoint and you forward it to Razorpay. This is not required for scheduled subscription renewals.
Integration Flows
The India Billing API supports three flows. No billing rebuild is required. Additionally, you can accept one-time credit pack payments using the standard Razorpay Orders flow.Flow 1: Mandate Setup
This is a one-time flow for each customer. When a customer selects a plan on your pricing page, Razorpay opens its checkout, collects customer details and allows the customer to apply coupons.- Customer selects a plan on your pricing page.
- Your server calls the Create Checkout Session API to create a Razorpay checkout session.
- Your frontend invokes Razorpay Custom JS with the
checkout_session_id. The Custom JS renders the pricing breakdown (including GST), lets the customer validate coupons and creates the subscription on authorisation. - Customer authorises the payment. Razorpay creates the mandate and reports the payment outcome back to Stripe.
- Stripe marks the invoice as paid and the subscription as active.
- Your app provisions access via existing Stripe webhooks.
Create Checkout Session
Use this API to create a checkout session for mandate setup.POST /v1/cb-checkout/session>
Request Parameters
Request Parameters
mode mandatory
: string The checkout mode. Set this to subscription for mandate setup.purpose mandatory
: string The purpose of the checkout session. Possible values:create: Set up a new subscription and mandate.upgrade: Change the plan for an existing subscription.migrate: Move an existing Stripe-card customer to a Razorpay mandate.
customer_id mandatory
: string The Stripe customer ID. For example, cus_abc123.amount mandatory
: integer The amount in the smallest currency unit. For example, for $500.00, pass 50000.currency mandatory
: string The currency code. For example, USD.items mandatory
: array An array of line items for the checkout session. Each item contains:price_id mandatory
: string The Stripe price id.quantity mandatory
: integer The quantity for the line item.method_id mandatory
: string The Razorpay custom payment method ID (RAZORPAY_CPMT_ID). This identifies the payment method type (UPI or card) created in Stripe during one-time setup.promocode_id optional
: string The promotional code to apply to the checkout session. Coupon creation happens at Stripe. Razorpay validates and applies the coupon at checkout.metadata optional
: object Additional custom metadata for the session. For example:user_id
: string Your internal user identifier.Response Parameters
Response Parameters
checkout_session_id
: string The unique identifier for the checkout session. Use this to invoke Razorpay Custom JS on your frontend.expiry
: integer The Unix timestamp at which the checkout session expires.expires_in
: integer The time in seconds until the checkout session expires. The session is valid for 30 minutes (1800 seconds).Checkout Purposes
Thepurpose field in the Create Checkout Session API determines the intent of the session.
upgrade and migrate (including plan-change and migration parameters) is shared during onboarding. Contact your Razorpay account manager to enable these flows.Flow 2: Subscription Payments
Subscription payments are fully automated. After the mandate is set up, Razorpay handles scheduled renewals end-to-end with no frontend involvement and no webhook forwarding from you. The entire flow is driven by Razorpay.- A Razorpay cron job identifies subscriptions with an upcoming billing date.
- Razorpay sends a Pre-Debit Notification (PDN) to the customer around 48 hours before the billing date.
- On the billing date, Razorpay auto-debits the customer via the stored mandate. Razorpay automatically retries failed debits in line with RBI norms.
- Razorpay reports the payment outcome to Stripe. Stripe updates the invoice (paid/failed).
- Your app reacts via existing Stripe webhooks.
Flow 3: Usage-Based Billing
Usage-based billing is a coupled, single-step model. When your metering system (Orb, Metronome or custom) creates a usage-based invoice on Stripe, Stripe fires aninvoice.payment_attempt_required event that you forward to Razorpay. Razorpay charges the customer immediately, with no pre-debit notification.
- Your metering system monitors usage and creates an invoice in Stripe when the threshold is reached (recommended within $150).
- Stripe fires
invoice.payment_attempt_requiredto your webhook endpoint. - Your server forwards the event to Razorpay using the Stripe Webhook Forwarding API.
- Razorpay immediately debits the customer using the existing mandate. There is no PDN — the Stripe invoice triggers an immediate charge.
- Razorpay reports the outcome back to Stripe.
- Your billing system sees the Stripe invoice as paid. No India-specific logic is needed on your end.
invoice.payment_attempt_required is a custom invoice event used to signal that an immediate charge is required. It is not part of the standard Stripe SDK event types, so your webhook handler must handle it explicitly before your exhaustive switch on event.type (for example, with a string comparison such as (event.type as string) === "invoice.payment_attempt_required" in TypeScript).Stripe Webhook Forwarding
Use this API to forward Stripe usage-based billing events to Razorpay.POST /v1/cb/stripe/webhook>
Request Parameters
Request Parameters
provider mandatory
: string The billing provider. Set this to stripe.data mandatory
: object The full Stripe webhook event object, forwarded as-is.One-Time Credit Pack Payments
For one-time payments such as credit packs, use the standard Razorpay Orders flow instead of the checkout session flow. This involves creating a customer, creating an order, verifying the payment signature, fetching the payment and capturing it.Step 1: Create a Customer
Create a customer with basic details such as name.POST /v1/customers>
Request Parameters
Request Parameters
name mandatory
: string The customer name.Response Parameters
Response Parameters
id
: string The unique identifier for the customer. For example, cust_1Aa00000000004. Use this as the customer_id when creating an order.Step 2: Create an Order
After a customer is created, an order needs to be generated using the Orders API. This order contains details such as the payment amount, currency, customer details. After the order is created, anorder_id is generated, for example, order_NGrgEcmYJsfUyl. Learn more about Order and Payment states.
POST /v1/orders>
Request Parameters
Request Parameters
amount mandatory
: integer The payment amount in the smallest currency unit. For example, for $100.00, pass 10000.currency mandatory
: string The ISO currency code. For example, USD.customer_details mandatory
: object This contains details about the customer details of the order.name mandatory
: string Customer’s name.- Character length: Between 5 and 50 characters.
- Allowed characters: Uppercase letters (A-Z), lowercase letters (a-z), and spaces (not at the beginning).
- Not allowed characters: Numbers, special characters (e.g., @, ”, ,, ., etc.), Unicode characters, emojis, and non-Latin scripts or regional languages.
- Prohibited names: Names must be meaningful and contextually appropriate.
- Avoid using repetitive patterns (e.g., aaa, xyz, kkk kk).
- Names like litri litri, Hfg Gh, or husi husi are not permitted.
- Curse words and offensive names are prohibited.
- Example:
Gaurav Kumar.
email optional
: string The customer’s email address. A maximum length of 64 characters for the username. For example, in “gaurav.kumar@example.com”, “gaurav.kumar” must not exceed 64 characters.contact optional
: string The customer’s phone number. A maximum length of 15 characters including country code. For example, +919123456780.shipping_address mandatory
: object This contains the shipping address of the order.line1 mandatory
: string Address Line 1 of the address.- Character length: Must be between 3 and 100 characters.
- Allowed characters: Uppercase letters (A-Z), lowercase letters (a-z), numbers (0-9), spaces, and special characters (*&/-()#_+[]:’”.,.).
- Not allowed characters: Regional languages.
city mandatory
: string Name of the city. Must be between 3 and 50 characters in length and can only include uppercase (A-Z) and lowercase (a-z) English letters, and spaces.country mandatory
: string ISO3 country code of the billing address. Only IND is allowed.state mandatory
: string Name of the state. It must be between 3 and 50 characters extended and can only include uppercase (A-Z) and lowercase (a-z) English letters and spaces. Please send the full name of the state, for example, Madhya Pradesh.zipcode mandatory
: string The ZIP code must consist of 6-digit numeric characters. Only valid Indian ZIP codes will be accepted. Refer to the list of supported ZIP codes.payment mandatory
: object Payment capture configuration.capture mandatory
: string Set to manual for manual capture.capture_options mandatory
: object Contains manual_expiry_period.manual_expiry_period mandatory
: integer the time in seconds before the authorisation expires. For example, 7200 for 2 hours.Response Parameters
Response Parameters
id
: string The unique identifier for the order. For example, order_RB58MiP5SPFYyM.amount
: integer The order amount in the smallest currency unit.currency
: string The ISO currency code.Step 3: Verify Payment Signature
Signature verification is a mandatory step to ensure that the callback is sent by Razorpay. Therazorpay_signature contained in the callback can be regenerated by your system and verified as follows.
Create a string for hashing by combining the “razorpay_payment_id” from the callback and the Order ID generated in the initial step, separated by a |. Proceed to hash this string using SHA256 alongside your API Secret.
Generate Signature on your Server
Sample code
Sample code
Step 4: Fetch and Verify Payment
Fetch the payment details to confirm the payment status. See Fetch a Payment With ID.Step 5: Capture Payment
Capture the authorised payment. See Capture a PaymentCoupons and Recurring Discounts
Recurring coupons work out of the box with Razorpay. No coupon sync or offer mapping is required.- Stripe creates the upcoming invoice with the coupon or discount already applied (reduced amount).
- Razorpay’s cron job scans for upcoming Stripe invoices around 48 hours before the billing date and reads the discounted amount.
- Razorpay sends a PDN to the customer for the discounted invoice amount from Stripe.
- On the billing date, Razorpay debits the same discounted amount via the stored mandate.
Charges, Taxes and Limits
Stripe remains the source of truth for your billing and invoice amounts. For Indian customers, Razorpay applies the following on the amount it debits.18% GST
Razorpay calculates and adds 18% GST on the amount debited from Indian customers. The customer is charged the total amount, not just the plan amount.total_amount = plan_amount + tax_amount
Where tax_amount = 18% of plan_amount. The pricing breakdown (plan amount, GST and total) is available on the frontend via Razorpay Custom JS so the customer sees the final amount before authorising.
$1 Minimum Charge
Every debit must be at least 1.00 (for example, after a large coupon or discount), Razorpay charges $1.00 and automatically refunds the difference to the customer.UPI Recurring Debit Cap
UPI recurring debits are capped at $250 per transaction. If a debit exceeds this limit, it fails and the customer must switch to a card mandate to complete the payment.Pre-Debit Notification (PDN) Timelines
Razorpay sends Pre-Debit Notifications to customers before scheduled subscription debits. Usage-based billing charges immediately and has no PDN.Frequently Asked Questions
Do I need to change my Stripe billing logic?
Do I need to change my Stripe billing logic?
What payment methods does Razorpay support for Indian customers?
What payment methods does Razorpay support for Indian customers?
What happens if a recurring payment fails?
What happens if a recurring payment fails?
Is usage-based billing a separate integration?
Is usage-based billing a separate integration?
invoice.payment_attempt_required events to Razorpay, which then charges immediately with no PDN. Scheduled subscription renewals do not use webhook forwarding — Razorpay’s cron job drives them automatically.How are coupons handled?
How are coupons handled?
Does Razorpay add GST to the amount charged?
Does Razorpay add GST to the amount charged?
plan_amount + tax_amount, where tax_amount is 18% of the plan amount. See Charges, Taxes and Limits.Is there a minimum charge amount?
Is there a minimum charge amount?
Is there a limit on UPI recurring debits?
Is there a limit on UPI recurring debits?