@alexasomba/better-auth-paystack 1.1.0 → 1.1.2
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/client.d.mts +11 -1
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +15 -0
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +138 -25
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +231 -45
- package/dist/index.mjs.map +1 -1
- package/dist/{types-BVSLYZGY.d.mts → types-CMXvth6C.d.mts} +35 -15
- package/dist/types-CMXvth6C.d.mts.map +1 -0
- package/package.json +9 -9
- package/dist/types-BVSLYZGY.d.mts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -99,6 +99,10 @@ function getPaystackOps(paystackClient) {
|
|
|
99
99
|
productDelete: (idOrCode) => {
|
|
100
100
|
if (paystackClient?.product_delete !== void 0) return paystackClient.product_delete({ params: { path: { id_or_code: idOrCode } } });
|
|
101
101
|
return paystackClient?.product?.delete?.(idOrCode);
|
|
102
|
+
},
|
|
103
|
+
planList: () => {
|
|
104
|
+
if (paystackClient?.plan_list !== void 0) return paystackClient.plan_list();
|
|
105
|
+
return paystackClient?.plan?.list?.();
|
|
102
106
|
}
|
|
103
107
|
};
|
|
104
108
|
}
|
|
@@ -175,7 +179,7 @@ async function syncProductQuantityFromPaystack(ctx, productName, paystackClient)
|
|
|
175
179
|
value: productName.toLowerCase().replace(/\s+/g, "-")
|
|
176
180
|
}]
|
|
177
181
|
});
|
|
178
|
-
if (
|
|
182
|
+
if (localProduct?.paystackId === void 0 || localProduct?.paystackId === null || localProduct?.paystackId === "") {
|
|
179
183
|
if (localProduct && localProduct.unlimited !== true && localProduct.quantity !== void 0 && localProduct.quantity > 0) await ctx.context.adapter.update({
|
|
180
184
|
model: "paystackProduct",
|
|
181
185
|
update: {
|
|
@@ -364,10 +368,11 @@ const paystackWebhook = (options) => {
|
|
|
364
368
|
}
|
|
365
369
|
if (options.subscription?.enabled === true) try {
|
|
366
370
|
if (eventName === "subscription.create") {
|
|
367
|
-
const
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
+
const payloadData = data;
|
|
372
|
+
const subscriptionCode = payloadData?.subscription_code ?? payloadData?.subscription?.subscription_code ?? payloadData?.code;
|
|
373
|
+
const customerCode = payloadData?.customer?.customer_code ?? payloadData?.customer_code ?? payloadData?.customer?.code;
|
|
374
|
+
const planCode = payloadData?.plan?.plan_code ?? payloadData?.plan_code ?? payloadData?.plan;
|
|
375
|
+
let metadata = payloadData?.metadata;
|
|
371
376
|
if (typeof metadata === "string") try {
|
|
372
377
|
metadata = JSON.parse(metadata);
|
|
373
378
|
} catch {}
|
|
@@ -405,7 +410,7 @@ const paystackWebhook = (options) => {
|
|
|
405
410
|
paystackSubscriptionCode: subscriptionCode,
|
|
406
411
|
status: "active",
|
|
407
412
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
408
|
-
periodEnd:
|
|
413
|
+
periodEnd: payloadData?.next_payment_date !== void 0 && payloadData?.next_payment_date !== null && payloadData?.next_payment_date !== "" ? new Date(payloadData.next_payment_date) : void 0
|
|
409
414
|
},
|
|
410
415
|
where: [{
|
|
411
416
|
field: "id",
|
|
@@ -438,7 +443,8 @@ const paystackWebhook = (options) => {
|
|
|
438
443
|
}
|
|
439
444
|
}
|
|
440
445
|
if (eventName === "subscription.disable" || eventName === "subscription.not_renew") {
|
|
441
|
-
const
|
|
446
|
+
const payloadData = data;
|
|
447
|
+
const subscriptionCode = payloadData?.subscription_code ?? payloadData?.subscription?.subscription_code ?? payloadData?.code;
|
|
442
448
|
if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
|
|
443
449
|
const existing = await ctx.context.adapter.findOne({
|
|
444
450
|
model: "subscription",
|
|
@@ -448,11 +454,15 @@ const paystackWebhook = (options) => {
|
|
|
448
454
|
}]
|
|
449
455
|
});
|
|
450
456
|
let newStatus = "canceled";
|
|
451
|
-
|
|
457
|
+
const nextPaymentDate = data?.next_payment_date;
|
|
458
|
+
const periodEnd = nextPaymentDate ? new Date(nextPaymentDate) : existing?.periodEnd ? new Date(existing.periodEnd) : void 0;
|
|
459
|
+
if (periodEnd && periodEnd > /* @__PURE__ */ new Date()) newStatus = "active";
|
|
452
460
|
await ctx.context.adapter.update({
|
|
453
461
|
model: "subscription",
|
|
454
462
|
update: {
|
|
455
463
|
status: newStatus,
|
|
464
|
+
cancelAtPeriodEnd: true,
|
|
465
|
+
...periodEnd ? { periodEnd } : {},
|
|
456
466
|
updatedAt: /* @__PURE__ */ new Date()
|
|
457
467
|
},
|
|
458
468
|
where: [{
|
|
@@ -530,13 +540,39 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
530
540
|
if (planName !== void 0 && planName !== null && planName !== "") {
|
|
531
541
|
if (subscriptionOptions?.enabled !== true) throw new APIError("BAD_REQUEST", { message: "Subscriptions are not enabled." });
|
|
532
542
|
plan = await getPlanByName(options, planName) ?? void 0;
|
|
543
|
+
if (!plan) {
|
|
544
|
+
const nativePlan = await ctx.context.adapter.findOne({
|
|
545
|
+
model: "paystackPlan",
|
|
546
|
+
where: [{
|
|
547
|
+
field: "name",
|
|
548
|
+
value: planName
|
|
549
|
+
}]
|
|
550
|
+
});
|
|
551
|
+
if (nativePlan) plan = nativePlan;
|
|
552
|
+
else plan = await ctx.context.adapter.findOne({
|
|
553
|
+
model: "paystackPlan",
|
|
554
|
+
where: [{
|
|
555
|
+
field: "planCode",
|
|
556
|
+
value: planName
|
|
557
|
+
}]
|
|
558
|
+
}) ?? void 0;
|
|
559
|
+
}
|
|
533
560
|
if (!plan) throw new APIError("BAD_REQUEST", {
|
|
534
561
|
code: "SUBSCRIPTION_PLAN_NOT_FOUND",
|
|
535
562
|
message: PAYSTACK_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,
|
|
536
563
|
status: 400
|
|
537
564
|
});
|
|
538
565
|
} else if (productName !== void 0 && productName !== null && productName !== "") {
|
|
539
|
-
if (typeof productName === "string")
|
|
566
|
+
if (typeof productName === "string") {
|
|
567
|
+
product ??= await getProductByName(options, productName) ?? void 0;
|
|
568
|
+
product ??= await ctx.context.adapter.findOne({
|
|
569
|
+
model: "paystackProduct",
|
|
570
|
+
where: [{
|
|
571
|
+
field: "name",
|
|
572
|
+
value: productName
|
|
573
|
+
}]
|
|
574
|
+
}) ?? void 0;
|
|
575
|
+
}
|
|
540
576
|
if (!product) throw new APIError("BAD_REQUEST", {
|
|
541
577
|
message: `Product '${productName}' not found.`,
|
|
542
578
|
status: 400
|
|
@@ -846,7 +882,17 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
846
882
|
if (planCodeFromPaystack === void 0 || planCodeFromPaystack === null || planCodeFromPaystack === "") paystackSubscriptionCode = `LOC_${reference}`;
|
|
847
883
|
else paystackSubscriptionCode = (data?.subscription)?.subscription_code;
|
|
848
884
|
}
|
|
849
|
-
const
|
|
885
|
+
const existingSubs = await ctx.context.adapter.findMany({
|
|
886
|
+
model: "subscription",
|
|
887
|
+
where: [{
|
|
888
|
+
field: "paystackTransactionReference",
|
|
889
|
+
value: reference
|
|
890
|
+
}]
|
|
891
|
+
});
|
|
892
|
+
let targetSub;
|
|
893
|
+
if (existingSubs && existingSubs.length > 0) targetSub = existingSubs.find((s) => !(referenceId !== void 0 && referenceId !== null && referenceId !== "") || s.referenceId === referenceId);
|
|
894
|
+
let updatedSubscription = null;
|
|
895
|
+
if (targetSub !== void 0 && targetSub !== null) updatedSubscription = await ctx.context.adapter.update({
|
|
850
896
|
model: "subscription",
|
|
851
897
|
update: {
|
|
852
898
|
status: isTrial === true ? "trialing" : "active",
|
|
@@ -861,12 +907,9 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
861
907
|
...authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" ? { paystackAuthorizationCode: authorizationCode } : {}
|
|
862
908
|
},
|
|
863
909
|
where: [{
|
|
864
|
-
field: "
|
|
865
|
-
value:
|
|
866
|
-
}
|
|
867
|
-
field: "referenceId",
|
|
868
|
-
value: referenceId
|
|
869
|
-
}] : []]
|
|
910
|
+
field: "id",
|
|
911
|
+
value: targetSub.id
|
|
912
|
+
}]
|
|
870
913
|
});
|
|
871
914
|
if (updatedSubscription && subscriptionOptions?.enabled === true && "onSubscriptionComplete" in subscriptionOptions && typeof subscriptionOptions.onSubscriptionComplete === "function") {
|
|
872
915
|
const plan = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === updatedSubscription.plan.toLowerCase());
|
|
@@ -1115,28 +1158,35 @@ const enablePaystackSubscription = (options, path = "/paystack/enable-subscripti
|
|
|
1115
1158
|
}
|
|
1116
1159
|
});
|
|
1117
1160
|
};
|
|
1118
|
-
const getSubscriptionManageLink = (options) => {
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
] : [sessionMiddleware, originCheck]
|
|
1127
|
-
}, async (ctx) => {
|
|
1161
|
+
const getSubscriptionManageLink = (options, path = "/paystack/get-subscription-manage-link") => {
|
|
1162
|
+
const manageLinkQuerySchema = z.object({ subscriptionCode: z.string() });
|
|
1163
|
+
const useMiddlewares = options.subscription?.enabled === true ? [
|
|
1164
|
+
sessionMiddleware,
|
|
1165
|
+
originCheck,
|
|
1166
|
+
referenceMiddleware(options, "get-subscription-manage-link")
|
|
1167
|
+
] : [sessionMiddleware, originCheck];
|
|
1168
|
+
const handler = async (ctx) => {
|
|
1128
1169
|
const { subscriptionCode } = ctx.query;
|
|
1170
|
+
if (subscriptionCode.startsWith("LOC_") || subscriptionCode.startsWith("sub_local_")) return ctx.json({
|
|
1171
|
+
link: null,
|
|
1172
|
+
message: "Local subscriptions cannot be managed on Paystack"
|
|
1173
|
+
});
|
|
1129
1174
|
const paystack = getPaystackOps(options.paystackClient);
|
|
1130
1175
|
try {
|
|
1131
1176
|
const res = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
|
|
1132
|
-
const data = res !== null && res !== void 0 && "status" in res && "data" in res ? res.data : res?.data !== void 0 ? res.data : res;
|
|
1177
|
+
const data = res !== null && res !== void 0 && typeof res === "object" && "status" in res && "data" in res ? res.data : res?.data !== void 0 ? res.data : res;
|
|
1133
1178
|
const link = typeof data === "string" ? data : data?.link;
|
|
1134
1179
|
return ctx.json({ link });
|
|
1135
1180
|
} catch (error) {
|
|
1136
1181
|
ctx.context.logger.error("Failed to get subscription manage link", error);
|
|
1137
1182
|
throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to get subscription manage link" });
|
|
1138
1183
|
}
|
|
1139
|
-
}
|
|
1184
|
+
};
|
|
1185
|
+
return createAuthEndpoint(path, {
|
|
1186
|
+
method: "GET",
|
|
1187
|
+
query: manageLinkQuerySchema,
|
|
1188
|
+
use: useMiddlewares
|
|
1189
|
+
}, handler);
|
|
1140
1190
|
};
|
|
1141
1191
|
const syncProducts = (options) => {
|
|
1142
1192
|
return createAuthEndpoint("/paystack/sync-products", {
|
|
@@ -1201,6 +1251,91 @@ const syncProducts = (options) => {
|
|
|
1201
1251
|
}
|
|
1202
1252
|
});
|
|
1203
1253
|
};
|
|
1254
|
+
const listProducts = (_options) => {
|
|
1255
|
+
return createAuthEndpoint("/paystack/list-products", {
|
|
1256
|
+
method: "GET",
|
|
1257
|
+
metadata: { openapi: { operationId: "listPaystackProducts" } }
|
|
1258
|
+
}, async (ctx) => {
|
|
1259
|
+
const sorted = (await ctx.context.adapter.findMany({ model: "paystackProduct" })).sort((a, b) => a.name.localeCompare(b.name));
|
|
1260
|
+
return ctx.json({ products: sorted });
|
|
1261
|
+
});
|
|
1262
|
+
};
|
|
1263
|
+
const syncPlans = (options) => {
|
|
1264
|
+
return createAuthEndpoint("/paystack/sync-plans", {
|
|
1265
|
+
method: "POST",
|
|
1266
|
+
metadata: { ...HIDE_METADATA },
|
|
1267
|
+
disableBody: true,
|
|
1268
|
+
use: [sessionMiddleware]
|
|
1269
|
+
}, async (ctx) => {
|
|
1270
|
+
const paystack = getPaystackOps(options.paystackClient);
|
|
1271
|
+
try {
|
|
1272
|
+
const res = unwrapSdkResult(await paystack.planList());
|
|
1273
|
+
const plansData = res !== null && typeof res === "object" && "status" in res && "data" in res ? res.data : res?.data ?? res;
|
|
1274
|
+
if (!Array.isArray(plansData)) return ctx.json({
|
|
1275
|
+
status: "success",
|
|
1276
|
+
count: 0
|
|
1277
|
+
});
|
|
1278
|
+
for (const plan of plansData) {
|
|
1279
|
+
const paystackId = String(plan.id);
|
|
1280
|
+
const existing = await ctx.context.adapter.findOne({
|
|
1281
|
+
model: "paystackPlan",
|
|
1282
|
+
where: [{
|
|
1283
|
+
field: "paystackId",
|
|
1284
|
+
value: paystackId
|
|
1285
|
+
}]
|
|
1286
|
+
});
|
|
1287
|
+
const planData = {
|
|
1288
|
+
name: plan.name,
|
|
1289
|
+
description: plan.description,
|
|
1290
|
+
amount: plan.amount,
|
|
1291
|
+
currency: plan.currency,
|
|
1292
|
+
interval: plan.interval,
|
|
1293
|
+
planCode: plan.plan_code,
|
|
1294
|
+
paystackId,
|
|
1295
|
+
metadata: plan.metadata ? JSON.stringify(plan.metadata) : void 0,
|
|
1296
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1297
|
+
};
|
|
1298
|
+
if (existing) await ctx.context.adapter.update({
|
|
1299
|
+
model: "paystackPlan",
|
|
1300
|
+
update: planData,
|
|
1301
|
+
where: [{
|
|
1302
|
+
field: "id",
|
|
1303
|
+
value: existing.id
|
|
1304
|
+
}]
|
|
1305
|
+
});
|
|
1306
|
+
else await ctx.context.adapter.create({
|
|
1307
|
+
model: "paystackPlan",
|
|
1308
|
+
data: {
|
|
1309
|
+
...planData,
|
|
1310
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
return ctx.json({
|
|
1315
|
+
status: "success",
|
|
1316
|
+
count: plansData.length
|
|
1317
|
+
});
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
ctx.context.logger.error("Failed to sync plans", error);
|
|
1320
|
+
throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to sync plans" });
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
};
|
|
1324
|
+
const listPlans = (_options) => {
|
|
1325
|
+
return createAuthEndpoint("/paystack/list-plans", {
|
|
1326
|
+
method: "GET",
|
|
1327
|
+
metadata: { ...HIDE_METADATA },
|
|
1328
|
+
use: [sessionMiddleware]
|
|
1329
|
+
}, async (ctx) => {
|
|
1330
|
+
try {
|
|
1331
|
+
const plans = await ctx.context.adapter.findMany({ model: "paystackPlan" });
|
|
1332
|
+
return ctx.json({ plans });
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
ctx.context.logger.error("Failed to list plans", error);
|
|
1335
|
+
throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to list plans" });
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
};
|
|
1204
1339
|
const getConfig = (options) => {
|
|
1205
1340
|
return createAuthEndpoint("/paystack/get-config", {
|
|
1206
1341
|
method: "GET",
|
|
@@ -1501,18 +1636,64 @@ const products = { paystackProduct: { fields: {
|
|
|
1501
1636
|
required: true
|
|
1502
1637
|
}
|
|
1503
1638
|
} } };
|
|
1639
|
+
const plans = { paystackPlan: { fields: {
|
|
1640
|
+
name: {
|
|
1641
|
+
type: "string",
|
|
1642
|
+
required: true
|
|
1643
|
+
},
|
|
1644
|
+
description: {
|
|
1645
|
+
type: "string",
|
|
1646
|
+
required: false
|
|
1647
|
+
},
|
|
1648
|
+
amount: {
|
|
1649
|
+
type: "number",
|
|
1650
|
+
required: true
|
|
1651
|
+
},
|
|
1652
|
+
currency: {
|
|
1653
|
+
type: "string",
|
|
1654
|
+
required: true
|
|
1655
|
+
},
|
|
1656
|
+
interval: {
|
|
1657
|
+
type: "string",
|
|
1658
|
+
required: true
|
|
1659
|
+
},
|
|
1660
|
+
planCode: {
|
|
1661
|
+
type: "string",
|
|
1662
|
+
required: true,
|
|
1663
|
+
unique: true
|
|
1664
|
+
},
|
|
1665
|
+
paystackId: {
|
|
1666
|
+
type: "string",
|
|
1667
|
+
required: true,
|
|
1668
|
+
unique: true
|
|
1669
|
+
},
|
|
1670
|
+
metadata: {
|
|
1671
|
+
type: "string",
|
|
1672
|
+
required: false
|
|
1673
|
+
},
|
|
1674
|
+
createdAt: {
|
|
1675
|
+
type: "date",
|
|
1676
|
+
required: true
|
|
1677
|
+
},
|
|
1678
|
+
updatedAt: {
|
|
1679
|
+
type: "date",
|
|
1680
|
+
required: true
|
|
1681
|
+
}
|
|
1682
|
+
} } };
|
|
1504
1683
|
const getSchema = (options) => {
|
|
1505
1684
|
let baseSchema;
|
|
1506
1685
|
if (options.subscription?.enabled === true) baseSchema = {
|
|
1507
1686
|
...subscriptions,
|
|
1508
1687
|
...transactions,
|
|
1509
1688
|
...user,
|
|
1510
|
-
...products
|
|
1689
|
+
...products,
|
|
1690
|
+
...plans
|
|
1511
1691
|
};
|
|
1512
1692
|
else baseSchema = {
|
|
1513
1693
|
...user,
|
|
1514
1694
|
...transactions,
|
|
1515
|
-
...products
|
|
1695
|
+
...products,
|
|
1696
|
+
...plans
|
|
1516
1697
|
};
|
|
1517
1698
|
if (options.organization?.enabled === true) baseSchema = {
|
|
1518
1699
|
...baseSchema,
|
|
@@ -1564,24 +1745,29 @@ const checkTeamLimit = async (ctx, organizationId, maxTeams) => {
|
|
|
1564
1745
|
//#region src/index.ts
|
|
1565
1746
|
const INTERNAL_ERROR_CODES = defineErrorCodes({ ...PAYSTACK_ERROR_CODES });
|
|
1566
1747
|
const paystack = (options) => {
|
|
1748
|
+
const routeOptions = options;
|
|
1567
1749
|
return {
|
|
1568
1750
|
id: "paystack",
|
|
1569
1751
|
endpoints: {
|
|
1570
|
-
initializeTransaction: initializeTransaction(
|
|
1571
|
-
verifyTransaction: verifyTransaction(
|
|
1572
|
-
listSubscriptions: listSubscriptions(
|
|
1573
|
-
paystackWebhook: paystackWebhook(
|
|
1574
|
-
listTransactions: listTransactions(
|
|
1575
|
-
getConfig: getConfig(
|
|
1576
|
-
disableSubscription: disablePaystackSubscription(
|
|
1577
|
-
enableSubscription: enablePaystackSubscription(
|
|
1578
|
-
getSubscriptionManageLink: getSubscriptionManageLink(
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1752
|
+
initializeTransaction: initializeTransaction(routeOptions),
|
|
1753
|
+
verifyTransaction: verifyTransaction(routeOptions),
|
|
1754
|
+
listSubscriptions: listSubscriptions(routeOptions),
|
|
1755
|
+
paystackWebhook: paystackWebhook(routeOptions),
|
|
1756
|
+
listTransactions: listTransactions(routeOptions),
|
|
1757
|
+
getConfig: getConfig(routeOptions),
|
|
1758
|
+
disableSubscription: disablePaystackSubscription(routeOptions),
|
|
1759
|
+
enableSubscription: enablePaystackSubscription(routeOptions),
|
|
1760
|
+
getSubscriptionManageLink: getSubscriptionManageLink(routeOptions),
|
|
1761
|
+
subscriptionManageLink: getSubscriptionManageLink(routeOptions, "/paystack/subscription/manage-link"),
|
|
1762
|
+
createSubscription: createSubscription(routeOptions),
|
|
1763
|
+
upgradeSubscription: upgradeSubscription(routeOptions),
|
|
1764
|
+
cancelSubscription: cancelSubscription(routeOptions),
|
|
1765
|
+
restoreSubscription: restoreSubscription(routeOptions),
|
|
1766
|
+
chargeRecurringSubscription: chargeRecurringSubscription(routeOptions),
|
|
1767
|
+
syncProducts: syncProducts(routeOptions),
|
|
1768
|
+
listProducts: listProducts(routeOptions),
|
|
1769
|
+
syncPlans: syncPlans(routeOptions),
|
|
1770
|
+
listPlans: listPlans(routeOptions)
|
|
1585
1771
|
},
|
|
1586
1772
|
schema: getSchema(options),
|
|
1587
1773
|
init: (ctx) => {
|