Better Auth Razorpay Plugin
The `better-auth-razorpay` package provides a robust and type-safe integration of Razorpay payment functionalities into applications utilizing `better-auth`. As of version 2.0.7, it offers comprehensive support for the full Razorpay subscription lifecycle, including creation, upgrades, cancellations, pauses, and resumptions, alongside features like trial abuse prevention and automatic customer synchronization with user details. It caters to various billing models, including organization and seat-based billing, and provides secure, HMAC-SHA256 verified webhook processing for all critical Razorpay events. The plugin also ships with pre-built React hooks leveraging TanStack Query for seamless client-side integration and boasts end-to-end TypeScript coverage, ensuring type safety across its API. While a specific release cadence isn't published, it aligns with `better-auth` ecosystem updates and feature-driven development, often introducing new capabilities or refinements.
Common errors
-
Error: Webhook signature verification failed.
cause The `razorpayWebhookSecret` configured in the plugin does not match the secret set in the Razorpay dashboard, or the raw request body was not properly passed to the handler.fixEnsure `process.env.RAZORPAY_WEBHOOK_SECRET` exactly matches the secret in your Razorpay dashboard. If using a web framework like Express, ensure you are using `express.raw()` or similar middleware for the webhook endpoint to capture the raw request body before parsing. -
Razorpay Error: Bad Request - The subscription plan specified is invalid.
cause The `planId` configured in your `subscription.plans` array does not exist, is inactive, or is incorrectly mapped in your Razorpay account.fixDouble-check all `planId` values in your `better-auth-razorpay` server-side configuration against active plans in your Razorpay dashboard. Ensure the `name` property for each plan is unique and correctly corresponds to the Razorpay `planId`. -
TypeError: Cannot read properties of undefined (reading 'plugins') or Plugin 'razorpay' not found.
cause The `better-auth-razorpay` plugin was not correctly initialized and passed to the `betterAuth` instance's `plugins` array on the server, or the client-side plugin was not passed to `createAuthClient`.fixVerify that `razorpay({...})` is included in the `plugins` array for `betterAuth` on the server, and `razorpayClient({...})` is included in the `plugins` array for `createAuthClient` on the client. Ensure all required configuration options are provided to the plugin. -
TS2307: Cannot find module 'better-auth-razorpay/client' or its corresponding type declarations.
cause Incorrect import path for the client-side plugin or a TypeScript module resolution issue.fixEnsure you are using the correct import statement: `import { razorpayClient } from 'better-auth-razorpay/client';`. If the issue persists, check your `tsconfig.json` for appropriate `moduleResolution` settings and ensure the package is correctly installed.
Warnings
- breaking Major version updates (e.g., from 1.x to 2.x) may introduce breaking changes to the plugin's configuration options or API surface. Always review release notes or migration guides when upgrading major versions to avoid unexpected issues.
- gotcha The `razorpayWebhookSecret` is critical for verifying the authenticity of Razorpay webhooks using HMAC-SHA256. Misconfiguration, exposure, or failure to set this secret can lead to security vulnerabilities or processing of fraudulent webhook events.
- gotcha API keys (`RAZORPAY_KEY_ID`, `RAZORPAY_KEY_SECRET`) and the webhook secret should always be managed as environment variables. Hardcoding these credentials or committing them to version control poses a significant security risk.
- gotcha Failure to select all necessary webhook events in the Razorpay dashboard (as outlined in the documentation, e.g., `subscription.activated`, `subscription.cancelled`, `subscription.updated`) will result in incomplete or incorrect subscription lifecycle management within your application.
- gotcha The `better-auth-razorpay` plugin has distinct server-side (`razorpay`) and client-side (`razorpayClient`) initializers, typically imported from different paths (`better-auth-razorpay` vs. `better-auth-razorpay/client`). Mismatched or incorrect imports are a common source of setup errors.
Install
-
npm install better-auth-razorpay -
yarn add better-auth-razorpay -
pnpm add better-auth-razorpay
Imports
- razorpay
const { razorpay } = require('better-auth-razorpay');import { razorpay } from 'better-auth-razorpay'; - razorpayClient
import { razorpay } from 'better-auth-razorpay/client';import { razorpayClient } from 'better-auth-razorpay/client'; - RazorpayPluginOptions
import { RazorpayPluginOptions } from 'better-auth-razorpay';import type { RazorpayPluginOptions } from 'better-auth-razorpay';
Quickstart
import { betterAuth } from "better-auth";
import { razorpay } from "better-auth-razorpay";
import Razorpay from "razorpay";
const razorpayClient = new Razorpay({
key_id: process.env.RAZORPAY_KEY_ID ?? '',
key_secret: process.env.RAZORPAY_KEY_SECRET ?? ''
});
export const auth = betterAuth({
plugins: [
razorpay({
razorpayClient,
razorpayWebhookSecret: process.env.RAZORPAY_WEBHOOK_SECRET ?? '',
createCustomerOnSignUp: true,
subscription: {
enabled: true,
plans: [
{
planId: "plan_XXXXXXXXXX", // Replace with your Razorpay Plan ID
name: "pro",
totalCount: 12,
},
{
planId: "plan_YYYYYYYYYY", // Replace with your Razorpay Plan ID
annualPlanId: "plan_ZZZZZZZZZZ", // Optional: Annual Plan ID
name: "enterprise",
totalCount: 1,
quantity: 1, // seat-based example
},
],
onSubscriptionActivated: async ({ subscription, plan, event }) => {
console.log(`Subscription activated: ${subscription.id} for plan ${plan.name}`);
// Implement your business logic here, e.g., update user roles
},
// ... other lifecycle callbacks
},
}),
],
});