npm install @insforge/sdk@latest
import { createClient } from '@insforge/sdk';
const insforge = createClient({
baseUrl: 'https://your-app.insforge.app',
anonKey: 'your-anon-key' // Optional: for public/unauthenticated requests
});
Overview
The TypeScript SDK exposes Razorpay runtime helpers for generated app frontends:
insforge.payments.razorpay.createOrder(...)
insforge.payments.razorpay.verifyOrder(...)
insforge.payments.razorpay.createSubscription(...)
insforge.payments.razorpay.verifySubscription(...)
insforge.payments.razorpay.cancelSubscription(...)
insforge.payments.razorpay.pauseSubscription(...)
insforge.payments.razorpay.resumeSubscription(...)
Razorpay does not return a hosted Checkout URL. The backend creates a Razorpay Order or Subscription and returns checkoutOptions. The browser loads Razorpay Checkout and opens it with those options.
See Razorpay Payments for provider setup,
manual webhooks, database tables, and fulfillment patterns.
SDK setup
import { createClient } from '@insforge/sdk';
const insforge = createClient({
baseUrl: 'https://your-project.insforge.app',
anonKey: 'your-anon-key'
});
declare global {
interface Window {
Razorpay?: new (options: Record<string, unknown>) => { open: () => void };
}
}
The SDK calls Razorpay runtime routes with the current InsForge user token. Do not call these routes with provider secret keys.
Load Razorpay Checkout
function loadRazorpayCheckout(): Promise<void> {
return new Promise((resolve, reject) => {
if (window.Razorpay) {
resolve();
return;
}
const script = document.createElement('script');
script.src = 'https://checkout.razorpay.com/v1/checkout.js';
script.onload = () => resolve();
script.onerror = () => reject(new Error('Failed to load Razorpay Checkout'));
document.body.appendChild(script);
});
}
One-time order
Create an app-owned pending order first. Then create the Razorpay Order through InsForge:
Razorpay Orders can be amount-only, but creating Razorpay Items for one-time sellable products is a good practice because synced Items make the catalog visible in InsForge and Razorpay. Treat Orders as payment attempts.
type CreateRazorpayOrderResponse = {
order: {
id: string;
orderId: string | null;
status: string;
};
checkoutOptions: {
key: string;
amount: number;
currency: string;
order_id: string;
name?: string | null;
description?: string | null;
callback_url?: string | null;
prefill: {
name?: string | null;
email?: string | null;
contact?: string | null;
};
};
};
const { data: orderResponse, error } =
await insforge.payments.razorpay.createOrder('test', {
amount: 50000,
currency: 'INR',
receipt: order.id,
subject: { type: 'team', id: teamId },
customerEmail: user.email,
notes: { order_id: order.id }
});
if (error) throw error;
if (!orderResponse) throw new Error('Razorpay order creation returned no data');
If your webhook trigger reads notes.order_id, pass notes: { order_id: ... } when creating the Order.
Open Razorpay Checkout and verify the returned signature:
await loadRazorpayCheckout();
const RazorpayCheckout = window.Razorpay;
if (!RazorpayCheckout) {
throw new Error('Razorpay Checkout failed to load');
}
const checkout = new RazorpayCheckout({
...orderResponse.checkoutOptions,
handler: async (response: {
razorpay_order_id: string;
razorpay_payment_id: string;
razorpay_signature: string;
}) => {
const { error } = await insforge.payments.razorpay.verifyOrder('test', {
orderId: response.razorpay_order_id,
paymentId: response.razorpay_payment_id,
signature: response.razorpay_signature
});
if (error) throw error;
}
});
checkout.open();
Signature verification protects the immediate browser callback. Durable fulfillment should still come from verified Razorpay webhook events.
Subscription
Create or sync a Razorpay Plan first. Then create the subscription through InsForge:
type CreateRazorpaySubscriptionResponse = {
subscription: {
subscriptionId: string;
status: string;
};
checkoutOptions: {
key: string;
subscription_id: string;
name?: string | null;
description?: string | null;
callback_url?: string | null;
prefill: {
name?: string | null;
email?: string | null;
contact?: string | null;
};
};
};
const { data: subscriptionResponse, error } =
await insforge.payments.razorpay.createSubscription('test', {
planId: 'plan_123',
totalCount: 12,
subject: { type: 'team', id: teamId },
customerEmail: user.email
});
if (error) throw error;
if (!subscriptionResponse) throw new Error('Razorpay subscription creation returned no data');
Pass Razorpay subscription notes when webhook triggers need app identifiers later.
Open Razorpay Checkout with the returned subscription ID:
await loadRazorpayCheckout();
const RazorpayCheckout = window.Razorpay;
if (!RazorpayCheckout) {
throw new Error('Razorpay Checkout failed to load');
}
const checkout = new RazorpayCheckout({
...subscriptionResponse.checkoutOptions,
handler: async (response: {
razorpay_subscription_id: string;
razorpay_payment_id: string;
razorpay_signature: string;
}) => {
const { error } = await insforge.payments.razorpay.verifySubscription('test', {
subscriptionId: response.razorpay_subscription_id,
paymentId: response.razorpay_payment_id,
signature: response.razorpay_signature
});
if (error) throw error;
}
});
checkout.open();
Manage subscriptions
Razorpay does not provide a Stripe Billing Portal equivalent. Use backend routes for subscription management:
await insforge.payments.razorpay.cancelSubscription('test', 'sub_123', {
cancelAtCycleEnd: false
});
await insforge.payments.razorpay.pauseSubscription('test', 'sub_123');
await insforge.payments.razorpay.resumeSubscription('test', 'sub_123');
Subscription creation checks INSERT policies on payments.razorpay_subscriptions. Cancel, pause, and resume check UPDATE policies on the same table. PostgreSQL also applies SELECT policies to rows returned by INSERT/UPDATE ... RETURNING, so add matching SELECT visibility for the same billing subject when a policy probe needs to return the row. Add app-specific RLS or server-side membership checks before exposing these controls for shared subjects.
Fulfillment
Do not mark orders paid, grant credits, or activate subscriptions from the Checkout handler alone. Use verified Razorpay webhook events in payments.webhook_events. Do not attach fulfillment triggers to provider mirror tables such as payments.razorpay_subscriptions.
Create app-owned fulfillment tables with RLS, then update them from webhook triggers. See Razorpay Payments for a trigger example.
Live/test environment
Pass 'test' as the first SDK argument while developing. Only switch to 'live' after the developer explicitly approves production Razorpay changes and live Items, Plans, and webhooks are configured.
Never put Razorpay Key Secret or webhook secret in frontend code. The frontend
only receives the public Razorpay Key ID as checkoutOptions.key.