@deiondz/better-auth-razorpay 2.0.5 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,208 @@
1
+ import * as razorpay from 'razorpay';
2
+
3
+ /**
4
+ * Razorpay subscription response from the Razorpay API.
5
+ */
6
+ interface RazorpaySubscription {
7
+ id: string;
8
+ entity: string;
9
+ plan_id: string;
10
+ status: string;
11
+ current_start: number;
12
+ current_end: number;
13
+ ended_at: number | null;
14
+ quantity: number;
15
+ notes: Record<string, string> | null;
16
+ charge_at: number;
17
+ start_at: number;
18
+ end_at: number;
19
+ auth_attempts: number;
20
+ total_count: number;
21
+ paid_count: number;
22
+ customer_notify: boolean;
23
+ created_at: number;
24
+ expire_by: number | null;
25
+ short_url: string;
26
+ has_scheduled_changes: boolean;
27
+ change_scheduled_at: number | null;
28
+ source: string;
29
+ offer_id: string | null;
30
+ remaining_count: string;
31
+ }
32
+ /** Local subscription status aligned with Razorpay and plugin lifecycle. */
33
+ type SubscriptionStatus = 'created' | 'active' | 'pending' | 'halted' | 'cancelled' | 'completed' | 'expired' | 'trialing';
34
+ /** Local subscription record stored in the auth adapter. */
35
+ interface SubscriptionRecord {
36
+ id: string;
37
+ plan: string;
38
+ referenceId: string;
39
+ razorpayCustomerId?: string | null;
40
+ razorpaySubscriptionId?: string | null;
41
+ status: SubscriptionStatus;
42
+ trialStart?: Date | null;
43
+ trialEnd?: Date | null;
44
+ periodStart?: Date | null;
45
+ periodEnd?: Date | null;
46
+ cancelAtPeriodEnd: boolean;
47
+ seats: number;
48
+ groupId?: string | null;
49
+ createdAt: Date;
50
+ updatedAt: Date;
51
+ }
52
+ /** Plan limits (customizable per plan). */
53
+ interface PlanLimits {
54
+ [key: string]: number;
55
+ }
56
+ /** Free trial configuration for a plan. */
57
+ interface PlanFreeTrial {
58
+ days: number;
59
+ onTrialStart?: (subscription: SubscriptionRecord) => Promise<void>;
60
+ onTrialEnd?: (args: {
61
+ subscription: SubscriptionRecord;
62
+ }) => Promise<void>;
63
+ }
64
+ /** Named plan with monthly/annual Razorpay plan IDs and optional trial. */
65
+ interface RazorpayPlan {
66
+ name: string;
67
+ monthlyPlanId: string;
68
+ annualPlanId?: string;
69
+ description?: string;
70
+ limits?: PlanLimits;
71
+ freeTrial?: PlanFreeTrial;
72
+ }
73
+ /** Subscription plugin options (plans, callbacks, authorization). */
74
+ interface SubscriptionOptions {
75
+ enabled: boolean;
76
+ plans: RazorpayPlan[] | (() => Promise<RazorpayPlan[]>);
77
+ requireEmailVerification?: boolean;
78
+ authorizeReference?: (args: {
79
+ user: {
80
+ id: string;
81
+ email?: string;
82
+ name?: string;
83
+ [key: string]: unknown;
84
+ };
85
+ referenceId: string;
86
+ action: string;
87
+ }) => Promise<boolean>;
88
+ getSubscriptionCreateParams?: (args: {
89
+ user: {
90
+ id: string;
91
+ email?: string;
92
+ name?: string;
93
+ [key: string]: unknown;
94
+ };
95
+ session: unknown;
96
+ plan: RazorpayPlan;
97
+ subscription: SubscriptionRecord;
98
+ }) => Promise<{
99
+ params?: Record<string, unknown>;
100
+ }>;
101
+ onSubscriptionCreated?: (args: {
102
+ razorpaySubscription: RazorpaySubscription;
103
+ subscription: SubscriptionRecord;
104
+ plan: RazorpayPlan;
105
+ }) => Promise<void>;
106
+ onSubscriptionActivated?: (args: {
107
+ event: string;
108
+ razorpaySubscription: RazorpaySubscription;
109
+ subscription: SubscriptionRecord;
110
+ plan: RazorpayPlan;
111
+ }) => Promise<void>;
112
+ onSubscriptionUpdate?: (args: {
113
+ event: string;
114
+ subscription: SubscriptionRecord;
115
+ }) => Promise<void>;
116
+ onSubscriptionCancel?: (args: {
117
+ event: string;
118
+ razorpaySubscription: RazorpaySubscription;
119
+ subscription: SubscriptionRecord;
120
+ }) => Promise<void>;
121
+ }
122
+ /** User record shape used by the Razorpay plugin (customer ID on user). */
123
+ interface RazorpayUserRecord {
124
+ id: string;
125
+ email?: string;
126
+ name?: string;
127
+ razorpayCustomerId?: string | null;
128
+ [key: string]: unknown;
129
+ }
130
+ /** Razorpay webhook event types. */
131
+ type RazorpayWebhookEvent = 'subscription.authenticated' | 'subscription.activated' | 'subscription.charged' | 'subscription.cancelled' | 'subscription.paused' | 'subscription.resumed' | 'subscription.pending' | 'subscription.halted' | 'subscription.expired';
132
+ interface RazorpayWebhookPayload {
133
+ event: RazorpayWebhookEvent;
134
+ subscription: {
135
+ id: string;
136
+ plan_id: string;
137
+ status: string;
138
+ current_start?: number;
139
+ current_end?: number;
140
+ [key: string]: unknown;
141
+ };
142
+ payment?: {
143
+ id: string;
144
+ amount: number;
145
+ currency: string;
146
+ [key: string]: unknown;
147
+ };
148
+ }
149
+ interface RazorpayWebhookContext {
150
+ userId: string;
151
+ user: {
152
+ id: string;
153
+ email?: string;
154
+ name?: string;
155
+ [key: string]: unknown;
156
+ };
157
+ }
158
+ type OnWebhookEventCallback = (payload: RazorpayWebhookPayload, context: RazorpayWebhookContext) => Promise<void>;
159
+ /** Main plugin options: client, webhook secret, customer creation, subscription config, callbacks. */
160
+ interface RazorpayPluginOptions {
161
+ /** Initialized Razorpay client instance. */
162
+ razorpayClient: razorpay;
163
+ /** Webhook secret for signature verification. */
164
+ razorpayWebhookSecret?: string;
165
+ /** API key secret for payment signature verification. When set, enables POST /razorpay/verify-payment (same secret as Razorpay client, not webhook secret). */
166
+ razorpayKeySecret?: string;
167
+ /** Create Razorpay customer when user signs up. Default: false. */
168
+ createCustomerOnSignUp?: boolean;
169
+ /** Called after a Razorpay customer is created. */
170
+ onCustomerCreate?: (args: {
171
+ user: RazorpayUserRecord;
172
+ razorpayCustomer: {
173
+ id: string;
174
+ [key: string]: unknown;
175
+ };
176
+ }) => Promise<void>;
177
+ /** Custom params (e.g. notes) when creating Razorpay customer. */
178
+ getCustomerCreateParams?: (args: {
179
+ user: RazorpayUserRecord;
180
+ session: unknown;
181
+ }) => Promise<{
182
+ params?: Record<string, unknown>;
183
+ }>;
184
+ /** Subscription feature config (plans, callbacks). */
185
+ subscription?: SubscriptionOptions;
186
+ /** Global callback for all processed webhook events. */
187
+ onEvent?: (event: {
188
+ event: string;
189
+ [key: string]: unknown;
190
+ }) => Promise<void>;
191
+ /** Legacy: callback after webhook events are processed (payload + context). */
192
+ onWebhookEvent?: OnWebhookEventCallback;
193
+ }
194
+ interface RazorpaySuccessResponse<T = unknown> {
195
+ success: true;
196
+ data: T;
197
+ }
198
+ interface RazorpayErrorResponse {
199
+ success: false;
200
+ error: {
201
+ code: string;
202
+ description: string;
203
+ [key: string]: unknown;
204
+ };
205
+ }
206
+ type RazorpayApiResponse<T = unknown> = RazorpaySuccessResponse<T> | RazorpayErrorResponse;
207
+
208
+ export type { OnWebhookEventCallback as O, RazorpayPluginOptions as R, SubscriptionRecord as S, RazorpayApiResponse as a, RazorpayErrorResponse as b, RazorpayPlan as c, RazorpaySubscription as d, RazorpaySuccessResponse as e, RazorpayUserRecord as f, RazorpayWebhookContext as g, RazorpayWebhookEvent as h, RazorpayWebhookPayload as i, SubscriptionStatus as j };
@@ -0,0 +1,146 @@
1
+ import { S as SubscriptionRecord } from './types-B25gyPpX.js';
2
+
3
+ /**
4
+ * Client-side types for Razorpay plugin hooks and API responses.
5
+ */
6
+
7
+ /** Plan summary returned by GET /razorpay/get-plans (client-safe shape). */
8
+ interface PlanSummary {
9
+ name: string;
10
+ monthlyPlanId: string;
11
+ annualPlanId?: string;
12
+ limits?: Record<string, number>;
13
+ freeTrial?: {
14
+ days: number;
15
+ };
16
+ }
17
+ /** Response shape for get-plans (success). */
18
+ interface GetPlansResponse {
19
+ success: true;
20
+ data: PlanSummary[];
21
+ }
22
+ /** Response shape for subscription/list (success). */
23
+ interface ListSubscriptionsResponse {
24
+ success: true;
25
+ data: SubscriptionRecord[];
26
+ }
27
+ /** Response shape for create-or-update (success). */
28
+ interface CreateOrUpdateSubscriptionResponse {
29
+ success: true;
30
+ data: {
31
+ checkoutUrl: string;
32
+ subscriptionId: string;
33
+ razorpaySubscriptionId: string;
34
+ };
35
+ }
36
+ /** Response shape for cancel (success). */
37
+ interface CancelSubscriptionResponse {
38
+ success: true;
39
+ data: {
40
+ id: string;
41
+ status: string;
42
+ plan_id: string;
43
+ current_end?: number;
44
+ ended_at?: number | null;
45
+ };
46
+ }
47
+ /** Response shape for restore (success). */
48
+ interface RestoreSubscriptionResponse {
49
+ success: true;
50
+ data: {
51
+ id: string;
52
+ status: string;
53
+ };
54
+ }
55
+ /** Error shape from plugin API. */
56
+ interface RazorpayApiError {
57
+ success: false;
58
+ error: {
59
+ code: string;
60
+ description: string;
61
+ [key: string]: unknown;
62
+ };
63
+ }
64
+ /** Any plugin API response (success or error). */
65
+ type RazorpayApiResult<T = unknown> = {
66
+ success: true;
67
+ data: T;
68
+ } | RazorpayApiError;
69
+ /** Razorpay API actions from the client plugin (authClient.razorpay). Use these so requests hit the correct paths. */
70
+ interface RazorpayClientActions {
71
+ getPlans: (fetchOptions?: {
72
+ query?: Record<string, string>;
73
+ }) => Promise<RazorpayApiResult<PlanSummary[]>>;
74
+ listSubscriptions: (input?: ListSubscriptionsInput, fetchOptions?: {
75
+ query?: Record<string, string>;
76
+ }) => Promise<RazorpayApiResult<ListSubscriptionsResponse['data']>>;
77
+ createOrUpdateSubscription: (input: CreateOrUpdateSubscriptionInput, fetchOptions?: {
78
+ body?: Record<string, unknown>;
79
+ }) => Promise<RazorpayApiResult<CreateOrUpdateSubscriptionResponse['data']>>;
80
+ cancelSubscription: (input: CancelSubscriptionInput, fetchOptions?: {
81
+ body?: Record<string, unknown>;
82
+ }) => Promise<RazorpayApiResult<CancelSubscriptionResponse['data']>>;
83
+ restoreSubscription: (input: RestoreSubscriptionInput, fetchOptions?: {
84
+ body?: Record<string, unknown>;
85
+ }) => Promise<RazorpayApiResult<RestoreSubscriptionResponse['data']>>;
86
+ verifyPayment: (input: VerifyPaymentInput, fetchOptions?: {
87
+ body?: Record<string, unknown>;
88
+ }) => Promise<RazorpayApiResult<VerifyPaymentResponse['data']>>;
89
+ }
90
+ /**
91
+ * Minimal auth client interface for Razorpay hooks.
92
+ * Primary: razorpay is set when razorpayClientPlugin() is used in createAuthClient({ plugins: [...] }); prefer it so requests hit the correct paths.
93
+ * Fallback: api is optional for custom clients that implement path-based api.get/api.post.
94
+ */
95
+ interface RazorpayAuthClient {
96
+ /** Set when razorpayClientPlugin() is used in createAuthClient({ plugins: [razorpayClientPlugin()] }). Prefer these methods over api.get/post. */
97
+ razorpay?: RazorpayClientActions;
98
+ /** Optional; for custom clients that implement path-based api.get/post. */
99
+ api?: {
100
+ get: (path: string, options?: {
101
+ query?: Record<string, string>;
102
+ }) => Promise<RazorpayApiResult<unknown>>;
103
+ post: (path: string, options?: {
104
+ body?: Record<string, unknown>;
105
+ }) => Promise<RazorpayApiResult<unknown>>;
106
+ };
107
+ }
108
+ /** Input for create-or-update subscription. */
109
+ interface CreateOrUpdateSubscriptionInput {
110
+ plan: string;
111
+ annual?: boolean;
112
+ seats?: number;
113
+ subscriptionId?: string;
114
+ successUrl?: string;
115
+ disableRedirect?: boolean;
116
+ }
117
+ /** Input for cancel subscription. */
118
+ interface CancelSubscriptionInput {
119
+ subscriptionId: string;
120
+ immediately?: boolean;
121
+ }
122
+ /** Input for restore subscription. */
123
+ interface RestoreSubscriptionInput {
124
+ subscriptionId: string;
125
+ }
126
+ /** Input for list subscriptions (query). */
127
+ interface ListSubscriptionsInput {
128
+ referenceId?: string;
129
+ }
130
+ /** Input for verify-payment (Razorpay checkout success callback payload). */
131
+ interface VerifyPaymentInput {
132
+ razorpay_payment_id: string;
133
+ razorpay_subscription_id: string;
134
+ razorpay_signature: string;
135
+ }
136
+ /** Response shape for verify-payment (success). */
137
+ interface VerifyPaymentResponse {
138
+ success: true;
139
+ data: {
140
+ message: string;
141
+ payment_id: string;
142
+ subscription_id: string;
143
+ };
144
+ }
145
+
146
+ export type { CreateOrUpdateSubscriptionInput as C, GetPlansResponse as G, ListSubscriptionsInput as L, PlanSummary as P, RazorpayApiResult as R, VerifyPaymentInput as V, CancelSubscriptionInput as a, RestoreSubscriptionInput as b, RazorpayAuthClient as c, CancelSubscriptionResponse as d, CreateOrUpdateSubscriptionResponse as e, RestoreSubscriptionResponse as f, ListSubscriptionsResponse as g, VerifyPaymentResponse as h, RazorpayApiError as i, RazorpayClientActions as j };
package/package.json CHANGED
@@ -1,68 +1,67 @@
1
- {
2
- "name": "@deiondz/better-auth-razorpay",
3
- "version": "2.0.5",
4
- "description": "Better Auth plugin for Razorpay subscriptions and payments",
5
- "type": "module",
6
- "main": "./index.ts",
7
- "types": "./index.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./index.ts",
11
- "import": "./index.ts",
12
- "default": "./index.ts"
13
- },
14
- "./client": {
15
- "types": "./client.ts",
16
- "import": "./client.ts",
17
- "default": "./client.ts"
18
- },
19
- "./hooks": {
20
- "types": "./client/hooks.ts",
21
- "import": "./client/hooks.ts",
22
- "default": "./client/hooks.ts"
23
- }
24
- },
25
- "files": [
26
- "index.ts",
27
- "client.ts",
28
- "client",
29
- "api",
30
- "lib",
31
- "README.md",
32
- "RAZORPAY_PLUGIN.md"
33
- ],
34
- "scripts": {
35
- "typecheck": "tsc --noEmit",
36
- "prepublishOnly": "npm run typecheck"
37
- },
38
- "keywords": [
39
- "better-auth",
40
- "razorpay",
41
- "plugin",
42
- "subscriptions",
43
- "payments",
44
- "authentication"
45
- ],
46
- "license": "MIT",
47
- "repository": {
48
- "type": "git",
49
- "url": "git+https://github.com/deiondz/better-auth-razorpay.git"
50
- },
51
- "peerDependencies": {
52
- "@tanstack/react-query": "^5.0.0",
53
- "better-auth": "^1.0.0",
54
- "react": "^18.0.0 || ^19.0.0"
55
- },
56
- "dependencies": {
57
- "razorpay": "^2.9.2",
58
- "zod": "^3.23.0"
59
- },
60
- "devDependencies": {
61
- "@tanstack/react-query": "^5.0.0",
62
- "react": "^18.0.0",
63
- "typescript": "^5.0.0"
64
- },
65
- "engines": {
66
- "node": ">=18"
67
- }
68
- }
1
+ {
2
+ "name": "@deiondz/better-auth-razorpay",
3
+ "version": "2.0.8",
4
+ "description": "Better Auth plugin for Razorpay subscriptions and payments",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./client": {
15
+ "types": "./dist/client.d.ts",
16
+ "import": "./dist/client.js",
17
+ "default": "./dist/client.js"
18
+ },
19
+ "./hooks": {
20
+ "types": "./dist/client/hooks.d.ts",
21
+ "import": "./dist/client/hooks.js",
22
+ "default": "./dist/client/hooks.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "RAZORPAY_PLUGIN.md"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "typecheck": "tsc --noEmit",
33
+ "prepublishOnly": "npm run build && npm run typecheck"
34
+ },
35
+ "keywords": [
36
+ "better-auth",
37
+ "razorpay",
38
+ "plugin",
39
+ "subscriptions",
40
+ "payments",
41
+ "authentication"
42
+ ],
43
+ "license": "MIT",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/deiondz/better-auth-razorpay.git"
47
+ },
48
+ "peerDependencies": {
49
+ "@tanstack/react-query": "^5.0.0",
50
+ "better-auth": "^1.0.0",
51
+ "react": "^18.0.0 || ^19.0.0"
52
+ },
53
+ "dependencies": {
54
+ "razorpay": "^2.9.2",
55
+ "zod": "^3.23.0"
56
+ },
57
+ "devDependencies": {
58
+ "@tanstack/react-query": "^5.0.0",
59
+ "@types/react": "^19.2.10",
60
+ "react": "^18.0.0",
61
+ "tsup": "^8.0.0",
62
+ "typescript": "^5.0.0"
63
+ },
64
+ "engines": {
65
+ "node": ">=18"
66
+ }
67
+ }
@@ -1,90 +0,0 @@
1
- import { createAuthEndpoint, sessionMiddleware } from 'better-auth/api'
2
- import type Razorpay from 'razorpay'
3
- import {
4
- cancelSubscriptionSchema,
5
- handleRazorpayError,
6
- type RazorpaySubscription,
7
- type SubscriptionRecord,
8
- } from '../lib'
9
-
10
- /**
11
- * POST /api/auth/razorpay/subscription/cancel
12
- * Cancels subscription by local subscription ID. Optionally cancel immediately.
13
- */
14
- export const cancelSubscription = (razorpay: Razorpay) =>
15
- createAuthEndpoint(
16
- '/razorpay/subscription/cancel',
17
- { method: 'POST', use: [sessionMiddleware] },
18
- async (ctx) => {
19
- try {
20
- const body = cancelSubscriptionSchema.parse(ctx.body)
21
- const userId = ctx.context.session?.user?.id
22
- if (!userId) {
23
- return {
24
- success: false,
25
- error: { code: 'UNAUTHORIZED', description: 'User not authenticated' },
26
- }
27
- }
28
-
29
- const record = (await ctx.context.adapter.findOne({
30
- model: 'subscription',
31
- where: [{ field: 'id', value: body.subscriptionId }],
32
- })) as SubscriptionRecord | null
33
-
34
- if (!record) {
35
- return {
36
- success: false,
37
- error: { code: 'SUBSCRIPTION_NOT_FOUND', description: 'Subscription not found' },
38
- }
39
- }
40
- if (record.referenceId !== userId) {
41
- return {
42
- success: false,
43
- error: { code: 'FORBIDDEN', description: 'Subscription does not belong to you' },
44
- }
45
- }
46
-
47
- const rpId = record.razorpaySubscriptionId
48
- if (!rpId) {
49
- return {
50
- success: false,
51
- error: { code: 'INVALID_STATE', description: 'No Razorpay subscription linked' },
52
- }
53
- }
54
-
55
- // cancel_at_cycle_end: true = cancel at period end, false = cancel immediately
56
- const subscription = (await razorpay.subscriptions.cancel(
57
- rpId,
58
- !body.immediately
59
- )) as RazorpaySubscription
60
-
61
- await ctx.context.adapter.update({
62
- model: 'subscription',
63
- where: [{ field: 'id', value: body.subscriptionId }],
64
- update: {
65
- data: {
66
- status: 'cancelled',
67
- cancelAtPeriodEnd: !body.immediately,
68
- periodEnd: subscription.current_end
69
- ? new Date(subscription.current_end * 1000)
70
- : record.periodEnd,
71
- updatedAt: new Date(),
72
- },
73
- },
74
- })
75
-
76
- return {
77
- success: true,
78
- data: {
79
- id: subscription.id,
80
- status: subscription.status,
81
- plan_id: subscription.plan_id,
82
- current_end: subscription.current_end,
83
- ended_at: subscription.ended_at,
84
- },
85
- }
86
- } catch (error) {
87
- return handleRazorpayError(error)
88
- }
89
- }
90
- )