@deiondz/better-auth-razorpay 2.0.15 → 2.0.17
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 +13 -1
- package/dist/index.js +102 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -328,7 +328,8 @@ The plugin automatically creates the following database models via Better Auth's
|
|
|
328
328
|
- `razorpayCustomerId` (string, optional) — Razorpay customer ID when customer creation is enabled.
|
|
329
329
|
|
|
330
330
|
**`subscription`**
|
|
331
|
-
-
|
|
331
|
+
- Primary key: **`id`** (generated by the adapter/database when not provided; the plugin does not pass `id` on create, so the adapter uses its `generateId` or the DB’s default, e.g. UUID or MongoDB `_id`). MongoDB adapters should map `id` ↔ `_id` per the [create-a-db-adapter](https://better-auth.com/docs/guides/create-a-db-adapter) guide.
|
|
332
|
+
- Fields: `id`, `plan`, `planId`, `referenceId`, `razorpayCustomerId`, `razorpaySubscriptionId`, `status`, `trialStart`, `trialEnd`, `periodStart`, `periodEnd`, `cancelAtPeriodEnd`, `seats`, `groupId`, `createdAt`, `updatedAt`
|
|
332
333
|
- `status` values: `created`, `active`, `pending`, `halted`, `cancelled`, `completed`, `expired`, `trialing`
|
|
333
334
|
|
|
334
335
|
### Database Adapters
|
|
@@ -342,6 +343,17 @@ The plugin works with all Better Auth database adapters:
|
|
|
342
343
|
|
|
343
344
|
**Important:** Better Auth uses adapter model names, NOT underlying table names. If your Prisma model is `User` mapping to table `users`, use the model name in configuration.
|
|
344
345
|
|
|
346
|
+
### Primary key and MongoDB
|
|
347
|
+
|
|
348
|
+
Following [Better Auth’s adapter guide](https://better-auth.com/docs/guides/create-a-db-adapter): the plugin always uses the field name **`id`** for the subscription primary key. The subscription **id is generated by the adapter/database** (the plugin does not pass `id` on create and does not use `forceAllowId`), so the adapter uses its `generateId` or the DB’s default (e.g. PostgreSQL `gen_random_uuid()`, MongoDB `_id`).
|
|
349
|
+
|
|
350
|
+
- **SQL / Prisma / Drizzle:** The adapter or DB generates the id; no extra config.
|
|
351
|
+
- **MongoDB:** Better Auth recommends mapping `id` ↔ `_id` so that:
|
|
352
|
+
- On **input** (create/update), `id` is stored as `_id`.
|
|
353
|
+
- On **output** (findOne/findMany), `_id` is returned as `id`.
|
|
354
|
+
|
|
355
|
+
The official `mongodbAdapter` from `better-auth/adapters/mongodb` applies this mapping for all models (including plugin models). If you use a custom MongoDB adapter, configure it with the same mapping (e.g. `mapKeysTransformInput: { id: "_id" }` and `mapKeysTransformOutput: { _id: "id" }` per the [create-a-db-adapter](https://better-auth.com/docs/guides/create-a-db-adapter) guide) so subscription create/update/webhook work correctly.
|
|
356
|
+
|
|
345
357
|
## API Endpoints
|
|
346
358
|
|
|
347
359
|
All endpoints are prefixed with `/api/auth/razorpay/` (or your configured `basePath`).
|
package/dist/index.js
CHANGED
|
@@ -4229,6 +4229,11 @@ function toLocalStatus(razorpayStatus) {
|
|
|
4229
4229
|
};
|
|
4230
4230
|
return map[razorpayStatus] ?? "pending";
|
|
4231
4231
|
}
|
|
4232
|
+
function getPrimaryKey(record) {
|
|
4233
|
+
if (record.id != null && record.id !== "") return { value: record.id, field: "id" };
|
|
4234
|
+
if (record._id != null && record._id !== "") return { value: record._id, field: "_id" };
|
|
4235
|
+
return { value: "", field: "id" };
|
|
4236
|
+
}
|
|
4232
4237
|
var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
4233
4238
|
"/razorpay/subscription/create-or-update",
|
|
4234
4239
|
{ method: "POST", use: [sessionMiddleware2] },
|
|
@@ -4286,9 +4291,6 @@ var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
|
4286
4291
|
}
|
|
4287
4292
|
}
|
|
4288
4293
|
const now = /* @__PURE__ */ new Date();
|
|
4289
|
-
const generateId = ctx.context.generateId;
|
|
4290
|
-
const generated = typeof generateId === "function" ? generateId({ model: "subscription" }) : void 0;
|
|
4291
|
-
const localId = (typeof generated === "string" ? generated : void 0) ?? `sub_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
4292
4294
|
if (body.subscriptionId) {
|
|
4293
4295
|
const existing = await ctx.context.adapter.findOne({
|
|
4294
4296
|
model: "subscription",
|
|
@@ -4361,8 +4363,9 @@ var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
|
4361
4363
|
notes: { referenceId: userId, planName: plan.name }
|
|
4362
4364
|
};
|
|
4363
4365
|
if (subOpts.getSubscriptionCreateParams) {
|
|
4366
|
+
const tempSubPk = appTrialSub ? getPrimaryKey(appTrialSub) : null;
|
|
4364
4367
|
const tempSub = {
|
|
4365
|
-
id:
|
|
4368
|
+
id: tempSubPk?.value ?? "",
|
|
4366
4369
|
plan: plan.name,
|
|
4367
4370
|
planId,
|
|
4368
4371
|
referenceId: userId,
|
|
@@ -4392,9 +4395,16 @@ var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
|
4392
4395
|
const periodEnd = rpSubscription.current_end ? new Date(rpSubscription.current_end * 1e3) : null;
|
|
4393
4396
|
const newStatus = toLocalStatus(rpSubscription.status);
|
|
4394
4397
|
if (appTrialSub) {
|
|
4398
|
+
const trialPk = getPrimaryKey(appTrialSub);
|
|
4399
|
+
if (!trialPk.value) {
|
|
4400
|
+
return {
|
|
4401
|
+
success: false,
|
|
4402
|
+
error: { code: "INVALID_TRIAL", description: "Trial subscription has no primary key" }
|
|
4403
|
+
};
|
|
4404
|
+
}
|
|
4395
4405
|
await ctx.context.adapter.update({
|
|
4396
4406
|
model: "subscription",
|
|
4397
|
-
where: [{ field:
|
|
4407
|
+
where: [{ field: trialPk.field, value: trialPk.value }],
|
|
4398
4408
|
update: {
|
|
4399
4409
|
data: {
|
|
4400
4410
|
plan: plan.name,
|
|
@@ -4412,6 +4422,7 @@ var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
|
4412
4422
|
if (subOpts.onSubscriptionCreated) {
|
|
4413
4423
|
const updatedRecord = {
|
|
4414
4424
|
...appTrialSub,
|
|
4425
|
+
id: trialPk.value,
|
|
4415
4426
|
plan: plan.name,
|
|
4416
4427
|
planId,
|
|
4417
4428
|
razorpaySubscriptionId: rpSubscription.id,
|
|
@@ -4429,7 +4440,7 @@ var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
|
4429
4440
|
});
|
|
4430
4441
|
}
|
|
4431
4442
|
const data2 = {
|
|
4432
|
-
subscriptionId:
|
|
4443
|
+
subscriptionId: trialPk.value,
|
|
4433
4444
|
razorpaySubscriptionId: rpSubscription.id
|
|
4434
4445
|
};
|
|
4435
4446
|
if (!body.embed) {
|
|
@@ -4437,8 +4448,7 @@ var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
|
4437
4448
|
}
|
|
4438
4449
|
return { success: true, data: data2 };
|
|
4439
4450
|
}
|
|
4440
|
-
const
|
|
4441
|
-
id: localId,
|
|
4451
|
+
const subscriptionData = {
|
|
4442
4452
|
plan: plan.name,
|
|
4443
4453
|
planId,
|
|
4444
4454
|
referenceId: userId,
|
|
@@ -4455,11 +4465,28 @@ var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
|
4455
4465
|
createdAt: now,
|
|
4456
4466
|
updatedAt: now
|
|
4457
4467
|
};
|
|
4458
|
-
await ctx.context.adapter.create({
|
|
4468
|
+
const createdRaw = await ctx.context.adapter.create({
|
|
4459
4469
|
model: "subscription",
|
|
4460
|
-
data:
|
|
4461
|
-
forceAllowId: true
|
|
4470
|
+
data: subscriptionData
|
|
4462
4471
|
});
|
|
4472
|
+
const created = createdRaw;
|
|
4473
|
+
let createdId;
|
|
4474
|
+
if (created != null) {
|
|
4475
|
+
if (typeof created.id === "string" && created.id !== "") {
|
|
4476
|
+
createdId = created.id;
|
|
4477
|
+
} else if (typeof created._id === "string" && created._id !== "") {
|
|
4478
|
+
createdId = created._id;
|
|
4479
|
+
} else if (created._id != null) {
|
|
4480
|
+
createdId = String(created._id);
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
if (createdId == null || createdId === "") {
|
|
4484
|
+
return {
|
|
4485
|
+
success: false,
|
|
4486
|
+
error: { code: "CREATE_FAILED", description: "Subscription record was created but no id was returned" }
|
|
4487
|
+
};
|
|
4488
|
+
}
|
|
4489
|
+
const subscriptionRecord = { ...subscriptionData, id: createdId };
|
|
4463
4490
|
if (subOpts.onSubscriptionCreated) {
|
|
4464
4491
|
await subOpts.onSubscriptionCreated({
|
|
4465
4492
|
razorpaySubscription: rpSubscription,
|
|
@@ -4468,7 +4495,7 @@ var createOrUpdateSubscription = (razorpay, options) => createAuthEndpoint2(
|
|
|
4468
4495
|
});
|
|
4469
4496
|
}
|
|
4470
4497
|
const data = {
|
|
4471
|
-
subscriptionId:
|
|
4498
|
+
subscriptionId: createdId,
|
|
4472
4499
|
razorpaySubscriptionId: rpSubscription.id
|
|
4473
4500
|
};
|
|
4474
4501
|
if (!body.embed) {
|
|
@@ -4740,23 +4767,53 @@ function toLocalStatus2(razorpayStatus) {
|
|
|
4740
4767
|
};
|
|
4741
4768
|
return map[razorpayStatus] ?? "pending";
|
|
4742
4769
|
}
|
|
4743
|
-
var
|
|
4744
|
-
|
|
4770
|
+
var WEBHOOK_DEBUG = process.env.NODE_ENV === "development" || process.env.RAZORPAY_WEBHOOK_DEBUG === "true";
|
|
4771
|
+
var log = (msg, data) => {
|
|
4772
|
+
if (WEBHOOK_DEBUG) {
|
|
4773
|
+
const payload = data ? ` ${JSON.stringify(data)}` : "";
|
|
4774
|
+
console.log(`[razorpay-webhook]${payload ? ` ${msg}` : msg}`, payload || "");
|
|
4775
|
+
}
|
|
4776
|
+
};
|
|
4777
|
+
var updateSubscriptionRecord = async (adapter, subscriptionRecordId, whereField, data) => {
|
|
4778
|
+
const updateData = { ...data, updatedAt: /* @__PURE__ */ new Date() };
|
|
4779
|
+
const params = {
|
|
4745
4780
|
model: "subscription",
|
|
4746
|
-
where: [{ field:
|
|
4747
|
-
update:
|
|
4781
|
+
where: [{ field: whereField, value: subscriptionRecordId }],
|
|
4782
|
+
update: updateData
|
|
4783
|
+
};
|
|
4784
|
+
log("updateSubscriptionRecord call", {
|
|
4785
|
+
subscriptionRecordId,
|
|
4786
|
+
whereField,
|
|
4787
|
+
dataKeys: Object.keys(updateData)
|
|
4748
4788
|
});
|
|
4789
|
+
try {
|
|
4790
|
+
await adapter.update(params);
|
|
4791
|
+
log("updateSubscriptionRecord success", { subscriptionRecordId, whereField });
|
|
4792
|
+
} catch (err) {
|
|
4793
|
+
console.error("[razorpay-webhook] updateSubscriptionRecord failed", {
|
|
4794
|
+
subscriptionRecordId,
|
|
4795
|
+
whereField,
|
|
4796
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4797
|
+
stack: err instanceof Error ? err.stack : void 0
|
|
4798
|
+
});
|
|
4799
|
+
throw err;
|
|
4800
|
+
}
|
|
4749
4801
|
};
|
|
4750
|
-
var createStatusHandler = (status, extraFields) => async (adapter,
|
|
4802
|
+
var createStatusHandler = (status, extraFields) => async (adapter, razorpaySubscriptionId, _record, subscription) => {
|
|
4751
4803
|
const periodStart = subscription.current_start ? new Date(subscription.current_start * 1e3) : null;
|
|
4752
4804
|
const periodEnd = subscription.current_end ? new Date(subscription.current_end * 1e3) : null;
|
|
4753
|
-
await updateSubscriptionRecord(
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4805
|
+
await updateSubscriptionRecord(
|
|
4806
|
+
adapter,
|
|
4807
|
+
razorpaySubscriptionId,
|
|
4808
|
+
"razorpaySubscriptionId",
|
|
4809
|
+
{
|
|
4810
|
+
status,
|
|
4811
|
+
planId: subscription.plan_id,
|
|
4812
|
+
...periodStart !== null && { periodStart },
|
|
4813
|
+
...periodEnd !== null && { periodEnd },
|
|
4814
|
+
...extraFields?.(subscription) ?? {}
|
|
4815
|
+
}
|
|
4816
|
+
);
|
|
4760
4817
|
};
|
|
4761
4818
|
var eventHandlers = {
|
|
4762
4819
|
"subscription.authenticated": createStatusHandler("pending"),
|
|
@@ -4853,11 +4910,20 @@ async function processWebhookEvent(adapter, rawBody, fallbackBody, onWebhookEven
|
|
|
4853
4910
|
where: [{ field: "razorpaySubscriptionId", value: subscriptionEntity.id }]
|
|
4854
4911
|
});
|
|
4855
4912
|
if (!record) {
|
|
4913
|
+
log("record not found", { razorpaySubscriptionId: subscriptionEntity.id });
|
|
4856
4914
|
return {
|
|
4857
4915
|
success: false,
|
|
4858
4916
|
message: isDev ? `Subscription record not found for ${subscriptionEntity.id}` : "Subscription record not found"
|
|
4859
4917
|
};
|
|
4860
4918
|
}
|
|
4919
|
+
log("record found", {
|
|
4920
|
+
event,
|
|
4921
|
+
razorpaySubscriptionId: subscriptionEntity.id,
|
|
4922
|
+
whereField: "razorpaySubscriptionId",
|
|
4923
|
+
hasId: "id" in record && record.id != null,
|
|
4924
|
+
has_id: "_id" in record && record._id != null,
|
|
4925
|
+
status: record.status
|
|
4926
|
+
});
|
|
4861
4927
|
const userId = record.referenceId;
|
|
4862
4928
|
if (!userId) {
|
|
4863
4929
|
return {
|
|
@@ -4872,7 +4938,17 @@ async function processWebhookEvent(adapter, rawBody, fallbackBody, onWebhookEven
|
|
|
4872
4938
|
message: isDev ? `Unhandled event: ${event}` : "Unhandled webhook event"
|
|
4873
4939
|
};
|
|
4874
4940
|
}
|
|
4875
|
-
|
|
4941
|
+
log("calling handler", { event, razorpaySubscriptionId: subscriptionEntity.id });
|
|
4942
|
+
try {
|
|
4943
|
+
await handler(adapter, subscriptionEntity.id, record, subscriptionEntity);
|
|
4944
|
+
} catch (handlerError) {
|
|
4945
|
+
console.error("[razorpay-webhook] handler failed", {
|
|
4946
|
+
event,
|
|
4947
|
+
razorpaySubscriptionId: subscriptionEntity.id,
|
|
4948
|
+
error: handlerError instanceof Error ? handlerError.message : String(handlerError)
|
|
4949
|
+
});
|
|
4950
|
+
throw handlerError;
|
|
4951
|
+
}
|
|
4876
4952
|
if (pluginOptions?.onEvent) {
|
|
4877
4953
|
try {
|
|
4878
4954
|
await pluginOptions.onEvent({ event, ...payload });
|
|
@@ -5040,9 +5116,7 @@ var razorpayPlugin = (options) => {
|
|
|
5040
5116
|
const now = /* @__PURE__ */ new Date();
|
|
5041
5117
|
const trialEnd = new Date(now.getTime() + trialOnSignUp.days * 24 * 60 * 60 * 1e3);
|
|
5042
5118
|
const planName = trialOnSignUp.planName ?? "Trial";
|
|
5043
|
-
const localId = `sub_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
5044
5119
|
const subscriptionRecord = {
|
|
5045
|
-
id: localId,
|
|
5046
5120
|
plan: planName,
|
|
5047
5121
|
referenceId: user.id,
|
|
5048
5122
|
razorpayCustomerId: customer.id,
|
|
@@ -5060,8 +5134,7 @@ var razorpayPlugin = (options) => {
|
|
|
5060
5134
|
};
|
|
5061
5135
|
await adapter.create({
|
|
5062
5136
|
model: "subscription",
|
|
5063
|
-
data: subscriptionRecord
|
|
5064
|
-
forceAllowId: true
|
|
5137
|
+
data: subscriptionRecord
|
|
5065
5138
|
});
|
|
5066
5139
|
}
|
|
5067
5140
|
} catch (err) {
|