@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.
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/client/hooks.d.ts +87 -0
- package/dist/client/hooks.js +182 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client.d.ts +50 -0
- package/dist/client.js +67 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +4921 -0
- package/dist/index.js.map +1 -0
- package/dist/types-B25gyPpX.d.ts +208 -0
- package/dist/types-C0_Sreb6.d.ts +146 -0
- package/package.json +67 -68
- package/api/cancel-subscription.ts +0 -90
- package/api/create-or-update-subscription.ts +0 -254
- package/api/get-plans.ts +0 -36
- package/api/index.ts +0 -7
- package/api/list-subscriptions.ts +0 -79
- package/api/restore-subscription.ts +0 -79
- package/api/verify-payment.ts +0 -87
- package/api/webhook.ts +0 -305
- package/client/hooks.ts +0 -352
- package/client/types.ts +0 -154
- package/client.ts +0 -105
- package/index.ts +0 -162
- package/lib/error-handler.ts +0 -99
- package/lib/index.ts +0 -28
- package/lib/schemas.ts +0 -34
- package/lib/types.ts +0 -207
package/api/webhook.ts
DELETED
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import { createHmac } from 'node:crypto'
|
|
2
|
-
import { createAuthEndpoint } from 'better-auth/api'
|
|
3
|
-
import type {
|
|
4
|
-
OnWebhookEventCallback,
|
|
5
|
-
RazorpayPluginOptions,
|
|
6
|
-
RazorpaySubscription,
|
|
7
|
-
SubscriptionRecord,
|
|
8
|
-
} from '../lib'
|
|
9
|
-
|
|
10
|
-
export interface WebhookResult {
|
|
11
|
-
success: boolean
|
|
12
|
-
message?: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface SubscriptionEntity {
|
|
16
|
-
id: string
|
|
17
|
-
plan_id: string
|
|
18
|
-
status: string
|
|
19
|
-
current_start?: number
|
|
20
|
-
current_end?: number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface WebhookAdapter {
|
|
24
|
-
findOne: (params: {
|
|
25
|
-
model: string
|
|
26
|
-
where: { field: string; value: string }[]
|
|
27
|
-
}) => Promise<unknown>
|
|
28
|
-
update: (params: {
|
|
29
|
-
model: string
|
|
30
|
-
where: { field: string; value: string }[]
|
|
31
|
-
update: { data: Record<string, unknown> }
|
|
32
|
-
}) => Promise<unknown>
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
type EventHandler = (
|
|
36
|
-
adapter: WebhookAdapter,
|
|
37
|
-
razorpaySubscriptionId: string,
|
|
38
|
-
record: SubscriptionRecord,
|
|
39
|
-
subscription: SubscriptionEntity
|
|
40
|
-
) => Promise<void>
|
|
41
|
-
|
|
42
|
-
function toLocalStatus(razorpayStatus: string): SubscriptionRecord['status'] {
|
|
43
|
-
const map: Record<string, SubscriptionRecord['status']> = {
|
|
44
|
-
created: 'created',
|
|
45
|
-
authenticated: 'pending',
|
|
46
|
-
active: 'active',
|
|
47
|
-
pending: 'pending',
|
|
48
|
-
halted: 'halted',
|
|
49
|
-
cancelled: 'cancelled',
|
|
50
|
-
completed: 'completed',
|
|
51
|
-
expired: 'expired',
|
|
52
|
-
}
|
|
53
|
-
return map[razorpayStatus] ?? 'pending'
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const updateSubscriptionRecord = async (
|
|
57
|
-
adapter: WebhookAdapter,
|
|
58
|
-
razorpaySubscriptionId: string,
|
|
59
|
-
data: Record<string, unknown>
|
|
60
|
-
): Promise<void> => {
|
|
61
|
-
await adapter.update({
|
|
62
|
-
model: 'subscription',
|
|
63
|
-
where: [{ field: 'razorpaySubscriptionId', value: razorpaySubscriptionId }],
|
|
64
|
-
update: { data: { ...data, updatedAt: new Date() } },
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const createStatusHandler = (
|
|
69
|
-
status: SubscriptionRecord['status'],
|
|
70
|
-
extraFields?: (sub: SubscriptionEntity) => Record<string, unknown>
|
|
71
|
-
): EventHandler =>
|
|
72
|
-
async (adapter, razorpaySubscriptionId, record, subscription) => {
|
|
73
|
-
const periodStart = subscription.current_start
|
|
74
|
-
? new Date(subscription.current_start * 1000)
|
|
75
|
-
: null
|
|
76
|
-
const periodEnd = subscription.current_end
|
|
77
|
-
? new Date(subscription.current_end * 1000)
|
|
78
|
-
: null
|
|
79
|
-
await updateSubscriptionRecord(adapter, razorpaySubscriptionId, {
|
|
80
|
-
status,
|
|
81
|
-
...(periodStart !== null && { periodStart }),
|
|
82
|
-
...(periodEnd !== null && { periodEnd }),
|
|
83
|
-
...(extraFields?.(subscription) ?? {}),
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const eventHandlers: Record<string, EventHandler> = {
|
|
88
|
-
'subscription.authenticated': createStatusHandler('pending'),
|
|
89
|
-
'subscription.activated': createStatusHandler('active'),
|
|
90
|
-
'subscription.charged': createStatusHandler('active', (sub) => ({
|
|
91
|
-
periodEnd: sub.current_end ? new Date(sub.current_end * 1000) : undefined,
|
|
92
|
-
})),
|
|
93
|
-
'subscription.cancelled': createStatusHandler('cancelled', () => ({ cancelAtPeriodEnd: false })),
|
|
94
|
-
'subscription.paused': createStatusHandler('halted'),
|
|
95
|
-
'subscription.resumed': createStatusHandler('active'),
|
|
96
|
-
'subscription.pending': createStatusHandler('pending'),
|
|
97
|
-
'subscription.halted': createStatusHandler('halted'),
|
|
98
|
-
'subscription.expired': createStatusHandler('expired'),
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const getRawBody = async (request: Request | undefined, fallbackBody: unknown): Promise<string> => {
|
|
102
|
-
if (!request) return JSON.stringify(fallbackBody)
|
|
103
|
-
try {
|
|
104
|
-
const clonedRequest = request.clone()
|
|
105
|
-
const text = await clonedRequest.text()
|
|
106
|
-
return text || JSON.stringify(fallbackBody)
|
|
107
|
-
} catch {
|
|
108
|
-
return JSON.stringify(fallbackBody)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const verifySignature = (rawBody: string, signature: string, secret: string): boolean => {
|
|
113
|
-
const expectedSignature = createHmac('sha256', secret).update(rawBody).digest('hex')
|
|
114
|
-
return signature === expectedSignature
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const invokeCallback = async (
|
|
118
|
-
onWebhookEvent: OnWebhookEventCallback,
|
|
119
|
-
adapter: WebhookAdapter,
|
|
120
|
-
event: string,
|
|
121
|
-
subscription: SubscriptionEntity,
|
|
122
|
-
payload: { payment?: { entity?: { id: string; amount: number; currency?: string } } },
|
|
123
|
-
userId: string
|
|
124
|
-
): Promise<void> => {
|
|
125
|
-
const user = (await adapter.findOne({
|
|
126
|
-
model: 'user',
|
|
127
|
-
where: [{ field: 'id', value: userId }],
|
|
128
|
-
})) as { id: string; email?: string; name?: string } | null
|
|
129
|
-
if (!user) return
|
|
130
|
-
await onWebhookEvent(
|
|
131
|
-
{
|
|
132
|
-
event: event as Parameters<OnWebhookEventCallback>[0]['event'],
|
|
133
|
-
subscription: {
|
|
134
|
-
id: subscription.id,
|
|
135
|
-
plan_id: subscription.plan_id,
|
|
136
|
-
status: subscription.status,
|
|
137
|
-
current_start: subscription.current_start,
|
|
138
|
-
current_end: subscription.current_end,
|
|
139
|
-
},
|
|
140
|
-
payment: payload.payment?.entity
|
|
141
|
-
? {
|
|
142
|
-
id: payload.payment.entity.id,
|
|
143
|
-
amount: payload.payment.entity.amount,
|
|
144
|
-
currency: payload.payment.entity.currency ?? 'INR',
|
|
145
|
-
}
|
|
146
|
-
: undefined,
|
|
147
|
-
},
|
|
148
|
-
{ userId, user: { id: user.id, email: user.email, name: user.name } }
|
|
149
|
-
)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Handles Razorpay webhook events for subscription lifecycle.
|
|
154
|
-
* Updates the subscription model only (no user subscription fields).
|
|
155
|
-
*/
|
|
156
|
-
export const webhook = (
|
|
157
|
-
webhookSecret: string | undefined,
|
|
158
|
-
onWebhookEvent: OnWebhookEventCallback | undefined,
|
|
159
|
-
pluginOptions: Pick<RazorpayPluginOptions, 'subscription' | 'onEvent'>
|
|
160
|
-
) =>
|
|
161
|
-
createAuthEndpoint('/razorpay/webhook', { method: 'POST' }, async (ctx) => {
|
|
162
|
-
if (!webhookSecret) {
|
|
163
|
-
return { success: false, message: 'Webhook secret not configured' }
|
|
164
|
-
}
|
|
165
|
-
const signature = ctx.request?.headers.get('x-razorpay-signature')
|
|
166
|
-
if (!signature) {
|
|
167
|
-
return { success: false, message: 'Missing webhook signature' }
|
|
168
|
-
}
|
|
169
|
-
const rawBody = await getRawBody(ctx.request, ctx.body)
|
|
170
|
-
if (!verifySignature(rawBody, signature, webhookSecret)) {
|
|
171
|
-
return { success: false, message: 'Invalid webhook signature' }
|
|
172
|
-
}
|
|
173
|
-
return processWebhookEvent(
|
|
174
|
-
ctx.context.adapter as unknown as WebhookAdapter,
|
|
175
|
-
rawBody,
|
|
176
|
-
ctx.body,
|
|
177
|
-
onWebhookEvent,
|
|
178
|
-
pluginOptions
|
|
179
|
-
)
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
async function processWebhookEvent(
|
|
183
|
-
adapter: WebhookAdapter,
|
|
184
|
-
rawBody: string,
|
|
185
|
-
fallbackBody: unknown,
|
|
186
|
-
onWebhookEvent?: OnWebhookEventCallback,
|
|
187
|
-
pluginOptions?: Pick<RazorpayPluginOptions, 'subscription' | 'onEvent'>
|
|
188
|
-
): Promise<WebhookResult> {
|
|
189
|
-
const isDev = process.env.NODE_ENV === 'development'
|
|
190
|
-
try {
|
|
191
|
-
const webhookData = rawBody ? JSON.parse(rawBody) : fallbackBody
|
|
192
|
-
const { event, payload } = webhookData
|
|
193
|
-
if (!event || !payload) {
|
|
194
|
-
return {
|
|
195
|
-
success: false,
|
|
196
|
-
message: isDev ? 'Invalid webhook payload: missing event or payload' : 'Invalid webhook payload',
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const subscriptionEntity = payload.subscription?.entity as SubscriptionEntity | undefined
|
|
201
|
-
if (!subscriptionEntity) {
|
|
202
|
-
return {
|
|
203
|
-
success: false,
|
|
204
|
-
message: isDev ? 'Invalid webhook payload: missing subscription data' : 'Invalid webhook payload',
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const record = (await adapter.findOne({
|
|
209
|
-
model: 'subscription',
|
|
210
|
-
where: [{ field: 'razorpaySubscriptionId', value: subscriptionEntity.id }],
|
|
211
|
-
})) as SubscriptionRecord | null
|
|
212
|
-
|
|
213
|
-
if (!record) {
|
|
214
|
-
return {
|
|
215
|
-
success: false,
|
|
216
|
-
message: isDev
|
|
217
|
-
? `Subscription record not found for ${subscriptionEntity.id}`
|
|
218
|
-
: 'Subscription record not found',
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const userId = record.referenceId
|
|
223
|
-
if (!userId) {
|
|
224
|
-
return {
|
|
225
|
-
success: false,
|
|
226
|
-
message: isDev ? 'referenceId not found on subscription record' : 'Invalid subscription record',
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const handler = eventHandlers[event]
|
|
231
|
-
if (!handler) {
|
|
232
|
-
return {
|
|
233
|
-
success: false,
|
|
234
|
-
message: isDev ? `Unhandled event: ${event}` : 'Unhandled webhook event',
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
await handler(adapter, subscriptionEntity.id, record, subscriptionEntity)
|
|
239
|
-
|
|
240
|
-
if (pluginOptions?.onEvent) {
|
|
241
|
-
try {
|
|
242
|
-
await pluginOptions.onEvent({ event, ...payload })
|
|
243
|
-
} catch {
|
|
244
|
-
// ignore
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (pluginOptions?.subscription) {
|
|
249
|
-
const sub = pluginOptions.subscription
|
|
250
|
-
const rpSub = {
|
|
251
|
-
id: subscriptionEntity.id,
|
|
252
|
-
plan_id: subscriptionEntity.plan_id,
|
|
253
|
-
status: subscriptionEntity.status,
|
|
254
|
-
current_start: subscriptionEntity.current_start,
|
|
255
|
-
current_end: subscriptionEntity.current_end,
|
|
256
|
-
} as RazorpaySubscription
|
|
257
|
-
const updatedRecord = { ...record, status: toLocalStatus(subscriptionEntity.status) }
|
|
258
|
-
try {
|
|
259
|
-
if (event === 'subscription.activated' && sub.onSubscriptionActivated) {
|
|
260
|
-
await sub.onSubscriptionActivated({
|
|
261
|
-
event,
|
|
262
|
-
razorpaySubscription: rpSub,
|
|
263
|
-
subscription: updatedRecord as SubscriptionRecord,
|
|
264
|
-
plan: { name: record.plan, monthlyPlanId: subscriptionEntity.plan_id },
|
|
265
|
-
})
|
|
266
|
-
} else if (
|
|
267
|
-
['subscription.cancelled', 'subscription.expired'].includes(event) &&
|
|
268
|
-
sub.onSubscriptionCancel
|
|
269
|
-
) {
|
|
270
|
-
await sub.onSubscriptionCancel({
|
|
271
|
-
event,
|
|
272
|
-
razorpaySubscription: rpSub,
|
|
273
|
-
subscription: updatedRecord as SubscriptionRecord,
|
|
274
|
-
})
|
|
275
|
-
} else if (sub.onSubscriptionUpdate) {
|
|
276
|
-
await sub.onSubscriptionUpdate({ event, subscription: updatedRecord as SubscriptionRecord })
|
|
277
|
-
}
|
|
278
|
-
} catch {
|
|
279
|
-
// ignore callback errors
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (onWebhookEvent) {
|
|
284
|
-
try {
|
|
285
|
-
await invokeCallback(
|
|
286
|
-
onWebhookEvent,
|
|
287
|
-
adapter,
|
|
288
|
-
event,
|
|
289
|
-
subscriptionEntity,
|
|
290
|
-
payload,
|
|
291
|
-
userId
|
|
292
|
-
)
|
|
293
|
-
} catch {
|
|
294
|
-
// ignore
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return { success: true }
|
|
299
|
-
} catch (error) {
|
|
300
|
-
return {
|
|
301
|
-
success: false,
|
|
302
|
-
message: error instanceof Error ? error.message : 'Webhook processing failed',
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
package/client/hooks.ts
DELETED
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React hooks for Razorpay subscription features using TanStack Query.
|
|
3
|
-
* Wrap your app with <RazorpayAuthProvider client={authClient}> and use usePlans(), useSubscriptions(), etc. with no client argument.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createContext, useContext, createElement, type ReactNode } from 'react'
|
|
7
|
-
import {
|
|
8
|
-
useQuery,
|
|
9
|
-
useMutation,
|
|
10
|
-
useQueryClient,
|
|
11
|
-
type UseQueryOptions,
|
|
12
|
-
type UseMutationOptions,
|
|
13
|
-
} from '@tanstack/react-query'
|
|
14
|
-
import type {
|
|
15
|
-
RazorpayAuthClient,
|
|
16
|
-
PlanSummary,
|
|
17
|
-
CreateOrUpdateSubscriptionInput,
|
|
18
|
-
CancelSubscriptionInput,
|
|
19
|
-
RestoreSubscriptionInput,
|
|
20
|
-
ListSubscriptionsInput,
|
|
21
|
-
VerifyPaymentInput,
|
|
22
|
-
GetPlansResponse,
|
|
23
|
-
ListSubscriptionsResponse,
|
|
24
|
-
CreateOrUpdateSubscriptionResponse,
|
|
25
|
-
CancelSubscriptionResponse,
|
|
26
|
-
RestoreSubscriptionResponse,
|
|
27
|
-
VerifyPaymentResponse,
|
|
28
|
-
RazorpayApiError,
|
|
29
|
-
} from './types'
|
|
30
|
-
|
|
31
|
-
const BASE = '/razorpay'
|
|
32
|
-
|
|
33
|
-
const RAZORPAY_NO_CLIENT_MESSAGE =
|
|
34
|
-
'Razorpay hooks require wrapping your app with <RazorpayAuthProvider client={authClient}>.'
|
|
35
|
-
|
|
36
|
-
const RAZORPAY_NO_RAZORPAY_OR_API_MESSAGE =
|
|
37
|
-
'Razorpay hooks require a client created with razorpayClientPlugin() in createAuthClient({ plugins: [...] }).'
|
|
38
|
-
|
|
39
|
-
/** Context holding the Razorpay-capable auth client. Default is null. */
|
|
40
|
-
export const RazorpayAuthContext = createContext<RazorpayAuthClient | null>(null)
|
|
41
|
-
|
|
42
|
-
/** Provider that supplies the auth client to Razorpay hooks. Wrap your app once with client={authClient}. */
|
|
43
|
-
export function RazorpayAuthProvider({
|
|
44
|
-
client,
|
|
45
|
-
children,
|
|
46
|
-
}: {
|
|
47
|
-
client: RazorpayAuthClient | null
|
|
48
|
-
children: ReactNode
|
|
49
|
-
}) {
|
|
50
|
-
return createElement(RazorpayAuthContext.Provider, { value: client }, children)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Returns the Razorpay-capable auth client from context, or null if not wrapped with RazorpayAuthProvider. */
|
|
54
|
-
export function useRazorpayAuthClient(): RazorpayAuthClient | null {
|
|
55
|
-
return useContext(RazorpayAuthContext)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function requireRazorpayOrApi(client: RazorpayAuthClient): void {
|
|
59
|
-
if (!client.razorpay && !client.api) {
|
|
60
|
-
throw new Error(RAZORPAY_NO_RAZORPAY_OR_API_MESSAGE)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Query keys for cache invalidation. */
|
|
65
|
-
export const razorpayQueryKeys = {
|
|
66
|
-
all: ['razorpay'] as const,
|
|
67
|
-
plans: () => [...razorpayQueryKeys.all, 'plans'] as const,
|
|
68
|
-
subscriptions: (referenceId?: string) =>
|
|
69
|
-
[...razorpayQueryKeys.all, 'subscriptions', referenceId ?? 'me'] as const,
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function assertSuccess<T>(res: unknown): asserts res is { success: true; data: T } {
|
|
73
|
-
if (res && typeof res === 'object' && 'success' in res) {
|
|
74
|
-
if ((res as { success: boolean }).success) return
|
|
75
|
-
const err = res as RazorpayApiError
|
|
76
|
-
throw new Error(err.error?.description ?? err.error?.code ?? 'Request failed')
|
|
77
|
-
}
|
|
78
|
-
throw new Error('Invalid response')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Fetch plans (GET /razorpay/get-plans). Prefers client.razorpay when available to avoid 404s. */
|
|
82
|
-
async function fetchPlans(client: RazorpayAuthClient): Promise<PlanSummary[]> {
|
|
83
|
-
requireRazorpayOrApi(client)
|
|
84
|
-
const res = client.razorpay
|
|
85
|
-
? await client.razorpay.getPlans()
|
|
86
|
-
: await client.api!.get(`${BASE}/get-plans`)
|
|
87
|
-
assertSuccess<PlanSummary[]>(res)
|
|
88
|
-
return res.data
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Fetch subscriptions list (GET /razorpay/subscription/list). Prefers client.razorpay when available. */
|
|
92
|
-
async function fetchSubscriptions(
|
|
93
|
-
client: RazorpayAuthClient,
|
|
94
|
-
input?: ListSubscriptionsInput
|
|
95
|
-
): Promise<ListSubscriptionsResponse['data']> {
|
|
96
|
-
requireRazorpayOrApi(client)
|
|
97
|
-
const res = client.razorpay
|
|
98
|
-
? await client.razorpay.listSubscriptions(input)
|
|
99
|
-
: (() => {
|
|
100
|
-
const query: Record<string, string> = {}
|
|
101
|
-
if (input?.referenceId) query.referenceId = input.referenceId
|
|
102
|
-
const path = `${BASE}/subscription/list`
|
|
103
|
-
return Object.keys(query).length > 0
|
|
104
|
-
? client.api!.get(path, { query })
|
|
105
|
-
: client.api!.get(path)
|
|
106
|
-
})()
|
|
107
|
-
assertSuccess<ListSubscriptionsResponse['data']>(res)
|
|
108
|
-
return res.data
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Create or update subscription (POST /razorpay/subscription/create-or-update). Prefers client.razorpay when available. */
|
|
112
|
-
async function createOrUpdateSubscription(
|
|
113
|
-
client: RazorpayAuthClient,
|
|
114
|
-
input: CreateOrUpdateSubscriptionInput
|
|
115
|
-
): Promise<CreateOrUpdateSubscriptionResponse['data']> {
|
|
116
|
-
requireRazorpayOrApi(client)
|
|
117
|
-
const res = client.razorpay
|
|
118
|
-
? await client.razorpay.createOrUpdateSubscription(input)
|
|
119
|
-
: await client.api!.post(`${BASE}/subscription/create-or-update`, {
|
|
120
|
-
body: input as unknown as Record<string, unknown>,
|
|
121
|
-
})
|
|
122
|
-
assertSuccess<CreateOrUpdateSubscriptionResponse['data']>(res)
|
|
123
|
-
return res.data
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Cancel subscription (POST /razorpay/subscription/cancel). Prefers client.razorpay when available. */
|
|
127
|
-
async function cancelSubscription(
|
|
128
|
-
client: RazorpayAuthClient,
|
|
129
|
-
input: CancelSubscriptionInput
|
|
130
|
-
): Promise<CancelSubscriptionResponse['data']> {
|
|
131
|
-
requireRazorpayOrApi(client)
|
|
132
|
-
const res = client.razorpay
|
|
133
|
-
? await client.razorpay.cancelSubscription(input)
|
|
134
|
-
: await client.api!.post(`${BASE}/subscription/cancel`, {
|
|
135
|
-
body: input as unknown as Record<string, unknown>,
|
|
136
|
-
})
|
|
137
|
-
assertSuccess<CancelSubscriptionResponse['data']>(res)
|
|
138
|
-
return res.data
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Restore subscription (POST /razorpay/subscription/restore). Prefers client.razorpay when available. */
|
|
142
|
-
async function restoreSubscription(
|
|
143
|
-
client: RazorpayAuthClient,
|
|
144
|
-
input: RestoreSubscriptionInput
|
|
145
|
-
): Promise<RestoreSubscriptionResponse['data']> {
|
|
146
|
-
requireRazorpayOrApi(client)
|
|
147
|
-
const res = client.razorpay
|
|
148
|
-
? await client.razorpay.restoreSubscription(input)
|
|
149
|
-
: await client.api!.post(`${BASE}/subscription/restore`, {
|
|
150
|
-
body: input as unknown as Record<string, unknown>,
|
|
151
|
-
})
|
|
152
|
-
assertSuccess<RestoreSubscriptionResponse['data']>(res)
|
|
153
|
-
return res.data
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/** Verify payment (POST /razorpay/verify-payment). Prefers client.razorpay when available. */
|
|
157
|
-
async function verifyPayment(
|
|
158
|
-
client: RazorpayAuthClient,
|
|
159
|
-
input: VerifyPaymentInput
|
|
160
|
-
): Promise<VerifyPaymentResponse['data']> {
|
|
161
|
-
requireRazorpayOrApi(client)
|
|
162
|
-
const res = client.razorpay
|
|
163
|
-
? await client.razorpay.verifyPayment(input)
|
|
164
|
-
: await client.api!.post(`${BASE}/verify-payment`, {
|
|
165
|
-
body: input as unknown as Record<string, unknown>,
|
|
166
|
-
})
|
|
167
|
-
assertSuccess<VerifyPaymentResponse['data']>(res)
|
|
168
|
-
return res.data
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export type UsePlansOptions = Omit<
|
|
172
|
-
UseQueryOptions<PlanSummary[], Error, PlanSummary[], readonly string[]>,
|
|
173
|
-
'queryKey' | 'queryFn'
|
|
174
|
-
>
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Fetch configured subscription plans (no auth required).
|
|
178
|
-
* Requires RazorpayAuthProvider above in the tree.
|
|
179
|
-
*/
|
|
180
|
-
export function usePlans(options?: UsePlansOptions) {
|
|
181
|
-
const client = useRazorpayAuthClient()
|
|
182
|
-
return useQuery({
|
|
183
|
-
queryKey: razorpayQueryKeys.plans(),
|
|
184
|
-
queryFn: () => fetchPlans(client!),
|
|
185
|
-
enabled: !!client,
|
|
186
|
-
...options,
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export type UseSubscriptionsOptions = Omit<
|
|
191
|
-
UseQueryOptions<
|
|
192
|
-
ListSubscriptionsResponse['data'],
|
|
193
|
-
Error,
|
|
194
|
-
ListSubscriptionsResponse['data'],
|
|
195
|
-
readonly (string | undefined)[]
|
|
196
|
-
>,
|
|
197
|
-
'queryKey' | 'queryFn'
|
|
198
|
-
> & { referenceId?: string }
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* List active/trialing subscriptions for the current user (or referenceId).
|
|
202
|
-
* Requires RazorpayAuthProvider above in the tree.
|
|
203
|
-
*/
|
|
204
|
-
export function useSubscriptions(
|
|
205
|
-
input?: ListSubscriptionsInput,
|
|
206
|
-
options?: UseSubscriptionsOptions
|
|
207
|
-
) {
|
|
208
|
-
const client = useRazorpayAuthClient()
|
|
209
|
-
const { referenceId, ...queryOptions } = options ?? {}
|
|
210
|
-
const refId = input?.referenceId ?? referenceId
|
|
211
|
-
return useQuery({
|
|
212
|
-
queryKey: razorpayQueryKeys.subscriptions(refId),
|
|
213
|
-
queryFn: () => fetchSubscriptions(client!, input),
|
|
214
|
-
enabled: !!client,
|
|
215
|
-
...queryOptions,
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export type UseCreateOrUpdateSubscriptionOptions = UseMutationOptions<
|
|
220
|
-
CreateOrUpdateSubscriptionResponse['data'],
|
|
221
|
-
Error,
|
|
222
|
-
CreateOrUpdateSubscriptionInput,
|
|
223
|
-
unknown
|
|
224
|
-
>
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Create or update a subscription. Returns checkoutUrl for Razorpay payment page.
|
|
228
|
-
* Invalidates subscriptions list on success.
|
|
229
|
-
* Requires RazorpayAuthProvider above in the tree.
|
|
230
|
-
*/
|
|
231
|
-
export function useCreateOrUpdateSubscription(
|
|
232
|
-
options?: UseCreateOrUpdateSubscriptionOptions
|
|
233
|
-
) {
|
|
234
|
-
const client = useRazorpayAuthClient()
|
|
235
|
-
const queryClient = useQueryClient()
|
|
236
|
-
return useMutation({
|
|
237
|
-
mutationFn: (input: CreateOrUpdateSubscriptionInput) => {
|
|
238
|
-
if (!client) throw new Error(RAZORPAY_NO_CLIENT_MESSAGE)
|
|
239
|
-
return createOrUpdateSubscription(client, input)
|
|
240
|
-
},
|
|
241
|
-
...options,
|
|
242
|
-
onSuccess: (data, variables, onMutateResult, context) => {
|
|
243
|
-
queryClient.invalidateQueries({ queryKey: razorpayQueryKeys.subscriptions() })
|
|
244
|
-
options?.onSuccess?.(data, variables, onMutateResult, context)
|
|
245
|
-
},
|
|
246
|
-
})
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
export type UseCancelSubscriptionOptions = UseMutationOptions<
|
|
250
|
-
CancelSubscriptionResponse['data'],
|
|
251
|
-
Error,
|
|
252
|
-
CancelSubscriptionInput,
|
|
253
|
-
unknown
|
|
254
|
-
>
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Cancel a subscription by local subscription ID (at period end or immediately).
|
|
258
|
-
* Invalidates subscriptions list on success.
|
|
259
|
-
* Requires RazorpayAuthProvider above in the tree.
|
|
260
|
-
*/
|
|
261
|
-
export function useCancelSubscription(options?: UseCancelSubscriptionOptions) {
|
|
262
|
-
const client = useRazorpayAuthClient()
|
|
263
|
-
const queryClient = useQueryClient()
|
|
264
|
-
return useMutation({
|
|
265
|
-
mutationFn: (input: CancelSubscriptionInput) => {
|
|
266
|
-
if (!client) throw new Error(RAZORPAY_NO_CLIENT_MESSAGE)
|
|
267
|
-
return cancelSubscription(client, input)
|
|
268
|
-
},
|
|
269
|
-
...options,
|
|
270
|
-
onSuccess: (data, variables, onMutateResult, context) => {
|
|
271
|
-
queryClient.invalidateQueries({ queryKey: razorpayQueryKeys.subscriptions() })
|
|
272
|
-
options?.onSuccess?.(data, variables, onMutateResult, context)
|
|
273
|
-
},
|
|
274
|
-
})
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export type UseRestoreSubscriptionOptions = UseMutationOptions<
|
|
278
|
-
RestoreSubscriptionResponse['data'],
|
|
279
|
-
Error,
|
|
280
|
-
RestoreSubscriptionInput,
|
|
281
|
-
unknown
|
|
282
|
-
>
|
|
283
|
-
|
|
284
|
-
// Re-export client types for convenience when importing from this entry
|
|
285
|
-
export type {
|
|
286
|
-
RazorpayAuthClient,
|
|
287
|
-
RazorpayClientActions,
|
|
288
|
-
PlanSummary,
|
|
289
|
-
CreateOrUpdateSubscriptionInput,
|
|
290
|
-
CancelSubscriptionInput,
|
|
291
|
-
RestoreSubscriptionInput,
|
|
292
|
-
ListSubscriptionsInput,
|
|
293
|
-
VerifyPaymentInput,
|
|
294
|
-
GetPlansResponse,
|
|
295
|
-
ListSubscriptionsResponse,
|
|
296
|
-
CreateOrUpdateSubscriptionResponse,
|
|
297
|
-
CancelSubscriptionResponse,
|
|
298
|
-
RestoreSubscriptionResponse,
|
|
299
|
-
VerifyPaymentResponse,
|
|
300
|
-
RazorpayApiError,
|
|
301
|
-
RazorpayApiResult,
|
|
302
|
-
} from './types'
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Restore a subscription that was scheduled to cancel at period end.
|
|
306
|
-
* Invalidates subscriptions list on success.
|
|
307
|
-
* Requires RazorpayAuthProvider above in the tree.
|
|
308
|
-
*/
|
|
309
|
-
export function useRestoreSubscription(options?: UseRestoreSubscriptionOptions) {
|
|
310
|
-
const client = useRazorpayAuthClient()
|
|
311
|
-
const queryClient = useQueryClient()
|
|
312
|
-
return useMutation({
|
|
313
|
-
mutationFn: (input: RestoreSubscriptionInput) => {
|
|
314
|
-
if (!client) throw new Error(RAZORPAY_NO_CLIENT_MESSAGE)
|
|
315
|
-
return restoreSubscription(client, input)
|
|
316
|
-
},
|
|
317
|
-
...options,
|
|
318
|
-
onSuccess: (data, variables, onMutateResult, context) => {
|
|
319
|
-
queryClient.invalidateQueries({ queryKey: razorpayQueryKeys.subscriptions() })
|
|
320
|
-
options?.onSuccess?.(data, variables, onMutateResult, context)
|
|
321
|
-
},
|
|
322
|
-
})
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export type UseVerifyPaymentOptions = UseMutationOptions<
|
|
326
|
-
VerifyPaymentResponse['data'],
|
|
327
|
-
Error,
|
|
328
|
-
VerifyPaymentInput,
|
|
329
|
-
unknown
|
|
330
|
-
>
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Verify payment signature after Razorpay checkout success.
|
|
334
|
-
* Call with the payload from the Razorpay success handler (razorpay_payment_id, razorpay_subscription_id, razorpay_signature).
|
|
335
|
-
* Invalidates subscriptions list on success.
|
|
336
|
-
* Requires RazorpayAuthProvider above in the tree.
|
|
337
|
-
*/
|
|
338
|
-
export function useVerifyPayment(options?: UseVerifyPaymentOptions) {
|
|
339
|
-
const client = useRazorpayAuthClient()
|
|
340
|
-
const queryClient = useQueryClient()
|
|
341
|
-
return useMutation({
|
|
342
|
-
mutationFn: (input: VerifyPaymentInput) => {
|
|
343
|
-
if (!client) throw new Error(RAZORPAY_NO_CLIENT_MESSAGE)
|
|
344
|
-
return verifyPayment(client, input)
|
|
345
|
-
},
|
|
346
|
-
...options,
|
|
347
|
-
onSuccess: (data, variables, onMutateResult, context) => {
|
|
348
|
-
queryClient.invalidateQueries({ queryKey: razorpayQueryKeys.subscriptions() })
|
|
349
|
-
options?.onSuccess?.(data, variables, onMutateResult, context)
|
|
350
|
-
},
|
|
351
|
-
})
|
|
352
|
-
}
|