logicspike/docs

Billing

Billing & Subscription — Razorpay Setup Guide

Step-by-step Razorpay Dashboard configuration. Complete this before writing any code (Phase 0 of the implementation plan).

Current Payment Provider: Razorpay (India). For future international/Stripe setup see stripe_setup_guide.md.

Docs: user_journeys.md · architecture.md · api_specification.md


Prerequisites

  • Razorpay account at dashboard.razorpay.com (you already have one ✅)
  • Test Mode active (toggle in top-left of Dashboard)
  • Access to LogicSpike .env.local file for storing keys
  • GST number (optional for invoices, required for GST on invoices)

Step 1: Collect API Keys

Dashboard → Settings → API Keys → Generate Test Key

Key Where to Store Notes
Key ID (rzp_test_...) RAZORPAY_KEY_ID in .env.local Sent to frontend for checkout modal
Key Secret RAZORPAY_KEY_SECRET in .env.local Used on Manager backend only. Never expose to frontend.
# .env.local
RAZORPAY_KEY_ID=rzp_test_...
RAZORPAY_KEY_SECRET=...
RAZORPAY_WEBHOOK_SECRET=...   # Set in Step 5

Security: The Key ID is safe to expose to the frontend (used to init the Razorpay checkout modal). The Key Secret MUST stay server-side only.


Step 2: Configure Business Profile

Dashboard → Settings → Business Profile

  • Set Business Name (appears on payment page and invoices)
  • Set Support email and Support URL
  • Upload Business Logo (appears on Razorpay checkout modal)
  • Set Business CategorySoftwareSaaS

Step 3: Create Subscription Plans

Razorpay Subscriptions work with Plans that define billing interval and amount. You cannot use Checkout for recurring billing — you must use Razorpay Subscriptions API.

Dashboard → Subscriptions → Plans → + Create Plan

Create the following plans. Record every Plan ID — insert into the plans table seed.

3a. Subscription Plans

LogicSpike Starter — Monthly

Field Value
Plan Name LogicSpike Starter Monthly
Billing Amount ₹999 / month (or USD equivalent if using international)
Interval Monthly
Description Starter plan — monthly billing

→ Record: razorpay_plan_id_monthly = plan_...

LogicSpike Starter — Yearly

Field Value
Billing Amount ₹9,990 / year (₹832/mo — 17% savings)
Interval Yearly (set period: yearly, interval: 1)

→ Record: razorpay_plan_id_yearly = plan_...


LogicSpike Pro — Monthly

Field Value
Billing Amount ₹2,499 / month
Interval Monthly

→ Record: razorpay_plan_id_monthly = plan_...

LogicSpike Pro — Yearly

Field Value
Billing Amount ₹24,990 / year (₹2,082/mo — 17% savings)
Interval Yearly

→ Record: razorpay_plan_id_yearly = plan_...


LogicSpike Business — Monthly

Field Value
Billing Amount ₹6,799 / month
Interval Monthly

→ Record: razorpay_plan_id_monthly = plan_...

LogicSpike Business — Yearly

Field Value
Billing Amount ₹67,990 / year (₹5,665/mo — 17% savings)
Interval Yearly

→ Record: razorpay_plan_id_yearly = plan_...


3b. Coin Pack One-Time Orders

Razorpay does not have a "one-time product catalog" like Stripe. Coin packs are handled via Razorpay Orders created dynamically by the backend. No dashboard setup needed — just store amounts in code/DB.

Pack Amount (INR paise) Coins
Small 41500 (₹415 ≈ $5) 500
Medium 166000 (₹1,660 ≈ $20) 2,200 (10% bonus)
Large 415000 (₹4,150 ≈ $50) 6,000 (20% bonus)

Note: Amounts are stored in coin_packs DB table (not Razorpay dashboard). The backend calls razorpay.orders.create({ amount, currency: 'INR' }) at checkout time.


Plan ID Reference Table

Fill in after creating all plans:

Plan Monthly Plan ID Yearly Plan ID
Free — (no subscription)
Starter plan_... plan_...
Pro plan_... plan_...
Business plan_... plan_...

Step 4: Configure Checkout Settings

Dashboard → Settings → Checkout Settings

  • Enable Remember Customer (saves card for future payments)
  • Set Brand Color to match LogicSpike theme
  • Enable UPI payment method
  • Enable Card (Visa, Mastercard, Rupay)
  • Enable Net Banking
  • Enable Wallets (Paytm, Mobikwik — optional)
  • Upload Checkout Logo

Unlike Stripe, Razorpay checkout opens as a modal on your page — it does not redirect to a separate URL. The frontend initializes it via the Razorpay JS SDK.


Step 5: Register Webhook Endpoint

Dashboard → Settings → Webhooks → + Add New Webhook

Production

Field Value
Webhook URL https://api.logicspike.com/webhooks/razorpay
Secret Generate a strong random string (store as RAZORPAY_WEBHOOK_SECRET)
Active

Events to Subscribe

Event Why
subscription.charged Recurring subscription payment succeeded → update subscriptions.current_period_end
subscription.cancelled Subscription canceled → downgrade to Free
subscription.activated Trial or new subscription activated → provision plan
subscription.pending Subscription created, awaiting first payment
payment.captured One-time payment (coin purchase) completed
payment.failed Payment failed → set past_due

Webhook Secret

# .env.local
RAZORPAY_WEBHOOK_SECRET=your_strong_random_secret_here

Local Development

Use ngrok to forward webhook events locally:

# Install ngrok
# Expose local Manager service
ngrok http 8787
 
# Use the ngrok URL in Razorpay Dashboard webhook settings:
# https://abc123.ngrok.io/webhooks/razorpay

Razorpay does not have a CLI equivalent to Stripe CLI. Use ngrok for local webhook testing.


Step 6: Subscription Trial Setup

Razorpay supports trials via Addons on the first invoice or via the trial_duration on the plan.

For the 1-month free trial:

  • Create subscription with trial_duration: 7 (days — min 7 days), OR
  • Use addons to give a 100% discount on the first invoice
  • Razorpay does NOT require a card to start a trial (unlike Stripe)

LogicSpike specific: We want card-required trials (prevents abuse). Use Razorpay's customer_notify: 1 and collect card during subscription creation. The first charge is deferred by trial_duration days.


Step 7: Tax / GST Configuration

Dashboard → Settings → Tax Settings

  • Enable GST
  • Enter your GSTIN (if registered)
  • Set HSN/SAC code for SaaS: 998314 (software services)

Razorpay adds GST automatically to invoices if configured. No code changes needed.


Step 8: Smart Collect (for Failed Payments)

Dashboard → Settings → Smart Collect (if available on your plan)

  • Enable automatic retry on payment failure
  • Set retry interval: 3 retries, 2 days apart
  • Enable Dunning emails (Razorpay sends payment failure emails)

Final Checklist

  • RAZORPAY_KEY_ID in .env.local
  • RAZORPAY_KEY_SECRET in .env.local
  • RAZORPAY_WEBHOOK_SECRET in .env.local
  • 6 subscription plans created (Starter/Pro/Business × Monthly/Yearly)
  • All 6 Plan IDs recorded
  • Webhook registered with 6 events
  • Checkout settings configured with logo + brand color
  • GST configured (if applicable)
  • Test Mode active (not live!)

Environment Variables Summary

# Razorpay API
RAZORPAY_KEY_ID=rzp_test_...
RAZORPAY_KEY_SECRET=...
RAZORPAY_WEBHOOK_SECRET=...
 
# Razorpay Plan IDs (from Step 3)
RAZORPAY_PLAN_STARTER_MONTHLY=plan_...
RAZORPAY_PLAN_STARTER_YEARLY=plan_...
RAZORPAY_PLAN_PRO_MONTHLY=plan_...
RAZORPAY_PLAN_PRO_YEARLY=plan_...
RAZORPAY_PLAN_BUSINESS_MONTHLY=plan_...
RAZORPAY_PLAN_BUSINESS_YEARLY=plan_...
 
# Coin pack amounts are stored in coin_packs DB table (not env vars)

Note: Plan IDs can also be stored directly in the plans DB table seed instead of env vars.


Frontend Integration (Razorpay Checkout Modal)

Unlike Stripe (which redirects), Razorpay opens a modal on your page. The frontend flow:

// 1. Backend creates subscription/order, returns details
const { subscription_id, key_id } = await POST('/billing/checkout', { planId, cycle });
 
// 2. Frontend loads Razorpay script (add to _document.tsx or layout.tsx)
// <script src="https://checkout.razorpay.com/v1/checkout.js"></script>
 
// 3. Open Razorpay modal
const rzp = new window.Razorpay({
  key: key_id,
  subscription_id: subscription_id,  // for subscriptions
  // order_id: order_id,             // for one-time coin purchases
  name: 'LogicSpike',
  description: 'Pro Plan — Monthly',
  handler: function(response) {
    // payment_id + subscription_id returned here
    // Call POST /billing/payment/verify to verify server-side
    await POST('/billing/payment/verify', {
      razorpay_payment_id: response.razorpay_payment_id,
      razorpay_subscription_id: response.razorpay_subscription_id,
      razorpay_signature: response.razorpay_signature,
    });
    // Redirect to billing settings
  },
  prefill: { name: user.name, email: user.email },
  theme: { color: '#your-brand-color' },
});
rzp.open();

No redirect needed — the modal handles everything inline. Verification (/billing/payment/verify) is an extra step not needed with Stripe.


Test Cards & UPI

Method Test Value Scenario
Card 4111 1111 1111 1111 Successful payment
Card 5267 3181 8797 5449 Successful payment (Mastercard)
Card 4000 0000 0000 0002 Payment declined
UPI success@razorpay Successful UPI payment
UPI failure@razorpay Failed UPI payment
Net Banking Any bank Use test credentials shown in modal

CVV: 123 · Expiry: any future date · OTP: 1234 for test


Going Live Checklist

  • Switch to Live Mode in Razorpay Dashboard
  • Generate Live API keys and update .env (production)
  • Recreate all subscription plans in Live Mode
  • Update Plan IDs in DB seed / env vars
  • Register live webhook URL
  • Update RAZORPAY_WEBHOOK_SECRET with live secret
  • Verify GST settings for live transactions
  • Complete Razorpay KYC (required for live) — PAN + bank account
  • Run a ₹1 test transaction and refund via Dashboard
Billing