@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/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
- }