@deiondz/better-auth-razorpay 2.0.4 → 2.0.7
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/README.md +20 -14
- 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 +32 -0
- package/dist/client.js +49 -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-JYoruGio.d.ts +146 -0
- package/package.json +17 -18
- 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 -305
- package/client/types.ts +0 -152
- 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,305 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React hooks for Razorpay subscription features using TanStack Query.
|
|
3
|
-
* Use with your Better Auth client: usePlans(authClient), useSubscriptions(authClient), etc.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
useQuery,
|
|
8
|
-
useMutation,
|
|
9
|
-
useQueryClient,
|
|
10
|
-
type UseQueryOptions,
|
|
11
|
-
type UseMutationOptions,
|
|
12
|
-
} from '@tanstack/react-query'
|
|
13
|
-
import type {
|
|
14
|
-
RazorpayAuthClient,
|
|
15
|
-
PlanSummary,
|
|
16
|
-
CreateOrUpdateSubscriptionInput,
|
|
17
|
-
CancelSubscriptionInput,
|
|
18
|
-
RestoreSubscriptionInput,
|
|
19
|
-
ListSubscriptionsInput,
|
|
20
|
-
VerifyPaymentInput,
|
|
21
|
-
GetPlansResponse,
|
|
22
|
-
ListSubscriptionsResponse,
|
|
23
|
-
CreateOrUpdateSubscriptionResponse,
|
|
24
|
-
CancelSubscriptionResponse,
|
|
25
|
-
RestoreSubscriptionResponse,
|
|
26
|
-
VerifyPaymentResponse,
|
|
27
|
-
RazorpayApiError,
|
|
28
|
-
} from './types'
|
|
29
|
-
|
|
30
|
-
const BASE = '/razorpay'
|
|
31
|
-
|
|
32
|
-
/** Query keys for cache invalidation. */
|
|
33
|
-
export const razorpayQueryKeys = {
|
|
34
|
-
all: ['razorpay'] as const,
|
|
35
|
-
plans: () => [...razorpayQueryKeys.all, 'plans'] as const,
|
|
36
|
-
subscriptions: (referenceId?: string) =>
|
|
37
|
-
[...razorpayQueryKeys.all, 'subscriptions', referenceId ?? 'me'] as const,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function assertSuccess<T>(res: unknown): asserts res is { success: true; data: T } {
|
|
41
|
-
if (res && typeof res === 'object' && 'success' in res) {
|
|
42
|
-
if ((res as { success: boolean }).success) return
|
|
43
|
-
const err = res as RazorpayApiError
|
|
44
|
-
throw new Error(err.error?.description ?? err.error?.code ?? 'Request failed')
|
|
45
|
-
}
|
|
46
|
-
throw new Error('Invalid response')
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Fetch plans (GET /razorpay/get-plans). Prefers client.razorpay when available to avoid 404s. */
|
|
50
|
-
async function fetchPlans(client: RazorpayAuthClient): Promise<PlanSummary[]> {
|
|
51
|
-
const res = client.razorpay
|
|
52
|
-
? await client.razorpay.getPlans()
|
|
53
|
-
: await client.api.get(`${BASE}/get-plans`)
|
|
54
|
-
assertSuccess<PlanSummary[]>(res)
|
|
55
|
-
return res.data
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Fetch subscriptions list (GET /razorpay/subscription/list). Prefers client.razorpay when available. */
|
|
59
|
-
async function fetchSubscriptions(
|
|
60
|
-
client: RazorpayAuthClient,
|
|
61
|
-
input?: ListSubscriptionsInput
|
|
62
|
-
): Promise<ListSubscriptionsResponse['data']> {
|
|
63
|
-
const res = client.razorpay
|
|
64
|
-
? await client.razorpay.listSubscriptions(input)
|
|
65
|
-
: (() => {
|
|
66
|
-
const query: Record<string, string> = {}
|
|
67
|
-
if (input?.referenceId) query.referenceId = input.referenceId
|
|
68
|
-
const path = `${BASE}/subscription/list`
|
|
69
|
-
return Object.keys(query).length > 0
|
|
70
|
-
? client.api.get(path, { query })
|
|
71
|
-
: client.api.get(path)
|
|
72
|
-
})()
|
|
73
|
-
assertSuccess<ListSubscriptionsResponse['data']>(res)
|
|
74
|
-
return res.data
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Create or update subscription (POST /razorpay/subscription/create-or-update). Prefers client.razorpay when available. */
|
|
78
|
-
async function createOrUpdateSubscription(
|
|
79
|
-
client: RazorpayAuthClient,
|
|
80
|
-
input: CreateOrUpdateSubscriptionInput
|
|
81
|
-
): Promise<CreateOrUpdateSubscriptionResponse['data']> {
|
|
82
|
-
const res = client.razorpay
|
|
83
|
-
? await client.razorpay.createOrUpdateSubscription(input)
|
|
84
|
-
: await client.api.post(`${BASE}/subscription/create-or-update`, {
|
|
85
|
-
body: input as unknown as Record<string, unknown>,
|
|
86
|
-
})
|
|
87
|
-
assertSuccess<CreateOrUpdateSubscriptionResponse['data']>(res)
|
|
88
|
-
return res.data
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Cancel subscription (POST /razorpay/subscription/cancel). Prefers client.razorpay when available. */
|
|
92
|
-
async function cancelSubscription(
|
|
93
|
-
client: RazorpayAuthClient,
|
|
94
|
-
input: CancelSubscriptionInput
|
|
95
|
-
): Promise<CancelSubscriptionResponse['data']> {
|
|
96
|
-
const res = client.razorpay
|
|
97
|
-
? await client.razorpay.cancelSubscription(input)
|
|
98
|
-
: await client.api.post(`${BASE}/subscription/cancel`, {
|
|
99
|
-
body: input as unknown as Record<string, unknown>,
|
|
100
|
-
})
|
|
101
|
-
assertSuccess<CancelSubscriptionResponse['data']>(res)
|
|
102
|
-
return res.data
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** Restore subscription (POST /razorpay/subscription/restore). Prefers client.razorpay when available. */
|
|
106
|
-
async function restoreSubscription(
|
|
107
|
-
client: RazorpayAuthClient,
|
|
108
|
-
input: RestoreSubscriptionInput
|
|
109
|
-
): Promise<RestoreSubscriptionResponse['data']> {
|
|
110
|
-
const res = client.razorpay
|
|
111
|
-
? await client.razorpay.restoreSubscription(input)
|
|
112
|
-
: await client.api.post(`${BASE}/subscription/restore`, {
|
|
113
|
-
body: input as unknown as Record<string, unknown>,
|
|
114
|
-
})
|
|
115
|
-
assertSuccess<RestoreSubscriptionResponse['data']>(res)
|
|
116
|
-
return res.data
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** Verify payment (POST /razorpay/verify-payment). Prefers client.razorpay when available. */
|
|
120
|
-
async function verifyPayment(
|
|
121
|
-
client: RazorpayAuthClient,
|
|
122
|
-
input: VerifyPaymentInput
|
|
123
|
-
): Promise<VerifyPaymentResponse['data']> {
|
|
124
|
-
const res = client.razorpay
|
|
125
|
-
? await client.razorpay.verifyPayment(input)
|
|
126
|
-
: await client.api.post(`${BASE}/verify-payment`, {
|
|
127
|
-
body: input as unknown as Record<string, unknown>,
|
|
128
|
-
})
|
|
129
|
-
assertSuccess<VerifyPaymentResponse['data']>(res)
|
|
130
|
-
return res.data
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export type UsePlansOptions = Omit<
|
|
134
|
-
UseQueryOptions<PlanSummary[], Error, PlanSummary[], readonly string[]>,
|
|
135
|
-
'queryKey' | 'queryFn'
|
|
136
|
-
>
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Fetch configured subscription plans (no auth required).
|
|
140
|
-
*/
|
|
141
|
-
export function usePlans(
|
|
142
|
-
client: RazorpayAuthClient | null | undefined,
|
|
143
|
-
options?: UsePlansOptions
|
|
144
|
-
) {
|
|
145
|
-
return useQuery({
|
|
146
|
-
queryKey: razorpayQueryKeys.plans(),
|
|
147
|
-
queryFn: () => fetchPlans(client!),
|
|
148
|
-
enabled: !!client,
|
|
149
|
-
...options,
|
|
150
|
-
})
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export type UseSubscriptionsOptions = Omit<
|
|
154
|
-
UseQueryOptions<
|
|
155
|
-
ListSubscriptionsResponse['data'],
|
|
156
|
-
Error,
|
|
157
|
-
ListSubscriptionsResponse['data'],
|
|
158
|
-
readonly (string | undefined)[]
|
|
159
|
-
>,
|
|
160
|
-
'queryKey' | 'queryFn'
|
|
161
|
-
> & { referenceId?: string }
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* List active/trialing subscriptions for the current user (or referenceId).
|
|
165
|
-
*/
|
|
166
|
-
export function useSubscriptions(
|
|
167
|
-
client: RazorpayAuthClient | null | undefined,
|
|
168
|
-
input?: ListSubscriptionsInput,
|
|
169
|
-
options?: UseSubscriptionsOptions
|
|
170
|
-
) {
|
|
171
|
-
const { referenceId, ...queryOptions } = options ?? {}
|
|
172
|
-
const refId = input?.referenceId ?? referenceId
|
|
173
|
-
return useQuery({
|
|
174
|
-
queryKey: razorpayQueryKeys.subscriptions(refId),
|
|
175
|
-
queryFn: () => fetchSubscriptions(client!, input),
|
|
176
|
-
enabled: !!client,
|
|
177
|
-
...queryOptions,
|
|
178
|
-
})
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export type UseCreateOrUpdateSubscriptionOptions = UseMutationOptions<
|
|
182
|
-
CreateOrUpdateSubscriptionResponse['data'],
|
|
183
|
-
Error,
|
|
184
|
-
CreateOrUpdateSubscriptionInput,
|
|
185
|
-
unknown
|
|
186
|
-
>
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Create or update a subscription. Returns checkoutUrl for Razorpay payment page.
|
|
190
|
-
* Invalidates subscriptions list on success.
|
|
191
|
-
*/
|
|
192
|
-
export function useCreateOrUpdateSubscription(
|
|
193
|
-
client: RazorpayAuthClient | null | undefined,
|
|
194
|
-
options?: UseCreateOrUpdateSubscriptionOptions
|
|
195
|
-
) {
|
|
196
|
-
const queryClient = useQueryClient()
|
|
197
|
-
return useMutation({
|
|
198
|
-
mutationFn: (input: CreateOrUpdateSubscriptionInput) =>
|
|
199
|
-
createOrUpdateSubscription(client!, input),
|
|
200
|
-
...options,
|
|
201
|
-
onSuccess: (data, variables, onMutateResult, context) => {
|
|
202
|
-
queryClient.invalidateQueries({ queryKey: razorpayQueryKeys.subscriptions() })
|
|
203
|
-
options?.onSuccess?.(data, variables, onMutateResult, context)
|
|
204
|
-
},
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export type UseCancelSubscriptionOptions = UseMutationOptions<
|
|
209
|
-
CancelSubscriptionResponse['data'],
|
|
210
|
-
Error,
|
|
211
|
-
CancelSubscriptionInput,
|
|
212
|
-
unknown
|
|
213
|
-
>
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Cancel a subscription by local subscription ID (at period end or immediately).
|
|
217
|
-
* Invalidates subscriptions list on success.
|
|
218
|
-
*/
|
|
219
|
-
export function useCancelSubscription(
|
|
220
|
-
client: RazorpayAuthClient | null | undefined,
|
|
221
|
-
options?: UseCancelSubscriptionOptions
|
|
222
|
-
) {
|
|
223
|
-
const queryClient = useQueryClient()
|
|
224
|
-
return useMutation({
|
|
225
|
-
mutationFn: (input: CancelSubscriptionInput) => cancelSubscription(client!, input),
|
|
226
|
-
...options,
|
|
227
|
-
onSuccess: (data, variables, onMutateResult, context) => {
|
|
228
|
-
queryClient.invalidateQueries({ queryKey: razorpayQueryKeys.subscriptions() })
|
|
229
|
-
options?.onSuccess?.(data, variables, onMutateResult, context)
|
|
230
|
-
},
|
|
231
|
-
})
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export type UseRestoreSubscriptionOptions = UseMutationOptions<
|
|
235
|
-
RestoreSubscriptionResponse['data'],
|
|
236
|
-
Error,
|
|
237
|
-
RestoreSubscriptionInput,
|
|
238
|
-
unknown
|
|
239
|
-
>
|
|
240
|
-
|
|
241
|
-
// Re-export client types for convenience when importing from this entry
|
|
242
|
-
export type {
|
|
243
|
-
RazorpayAuthClient,
|
|
244
|
-
RazorpayClientActions,
|
|
245
|
-
PlanSummary,
|
|
246
|
-
CreateOrUpdateSubscriptionInput,
|
|
247
|
-
CancelSubscriptionInput,
|
|
248
|
-
RestoreSubscriptionInput,
|
|
249
|
-
ListSubscriptionsInput,
|
|
250
|
-
VerifyPaymentInput,
|
|
251
|
-
GetPlansResponse,
|
|
252
|
-
ListSubscriptionsResponse,
|
|
253
|
-
CreateOrUpdateSubscriptionResponse,
|
|
254
|
-
CancelSubscriptionResponse,
|
|
255
|
-
RestoreSubscriptionResponse,
|
|
256
|
-
VerifyPaymentResponse,
|
|
257
|
-
RazorpayApiError,
|
|
258
|
-
RazorpayApiResult,
|
|
259
|
-
} from './types'
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Restore a subscription that was scheduled to cancel at period end.
|
|
263
|
-
* Invalidates subscriptions list on success.
|
|
264
|
-
*/
|
|
265
|
-
export function useRestoreSubscription(
|
|
266
|
-
client: RazorpayAuthClient | null | undefined,
|
|
267
|
-
options?: UseRestoreSubscriptionOptions
|
|
268
|
-
) {
|
|
269
|
-
const queryClient = useQueryClient()
|
|
270
|
-
return useMutation({
|
|
271
|
-
mutationFn: (input: RestoreSubscriptionInput) => restoreSubscription(client!, input),
|
|
272
|
-
...options,
|
|
273
|
-
onSuccess: (data, variables, onMutateResult, context) => {
|
|
274
|
-
queryClient.invalidateQueries({ queryKey: razorpayQueryKeys.subscriptions() })
|
|
275
|
-
options?.onSuccess?.(data, variables, onMutateResult, context)
|
|
276
|
-
},
|
|
277
|
-
})
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
export type UseVerifyPaymentOptions = UseMutationOptions<
|
|
281
|
-
VerifyPaymentResponse['data'],
|
|
282
|
-
Error,
|
|
283
|
-
VerifyPaymentInput,
|
|
284
|
-
unknown
|
|
285
|
-
>
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Verify payment signature after Razorpay checkout success.
|
|
289
|
-
* Call with the payload from the Razorpay success handler (razorpay_payment_id, razorpay_subscription_id, razorpay_signature).
|
|
290
|
-
* Invalidates subscriptions list on success.
|
|
291
|
-
*/
|
|
292
|
-
export function useVerifyPayment(
|
|
293
|
-
client: RazorpayAuthClient | null | undefined,
|
|
294
|
-
options?: UseVerifyPaymentOptions
|
|
295
|
-
) {
|
|
296
|
-
const queryClient = useQueryClient()
|
|
297
|
-
return useMutation({
|
|
298
|
-
mutationFn: (input: VerifyPaymentInput) => verifyPayment(client!, input),
|
|
299
|
-
...options,
|
|
300
|
-
onSuccess: (data, variables, onMutateResult, context) => {
|
|
301
|
-
queryClient.invalidateQueries({ queryKey: razorpayQueryKeys.subscriptions() })
|
|
302
|
-
options?.onSuccess?.(data, variables, onMutateResult, context)
|
|
303
|
-
},
|
|
304
|
-
})
|
|
305
|
-
}
|