@alexasomba/better-auth-paystack 1.1.2 → 1.2.1
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 +22 -2
- package/dist/client.d.mts +25 -4
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +15 -14
- package/dist/client.mjs.map +1 -1
- package/dist/{types-CMXvth6C.d.mts → index-DoMJ9OLF.d.mts} +126 -6
- package/dist/index-DoMJ9OLF.d.mts.map +1 -0
- package/dist/index.d.mts +2 -659
- package/dist/index.mjs +440 -212
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/dist/index.d.mts.map +0 -1
- package/dist/types-CMXvth6C.d.mts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { defineErrorCodes } from "@better-auth/core/utils";
|
|
1
|
+
import { defineErrorCodes } from "@better-auth/core/utils/error-codes";
|
|
2
2
|
import { defu } from "defu";
|
|
3
3
|
import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
|
|
4
4
|
import { HIDE_METADATA, logger } from "better-auth";
|
|
5
5
|
import { APIError, getSessionFromCtx, originCheck, sessionMiddleware } from "better-auth/api";
|
|
6
6
|
import * as z from "zod/v4";
|
|
7
7
|
import { mergeSchema } from "better-auth/db";
|
|
8
|
-
|
|
9
8
|
//#region src/paystack-sdk.ts
|
|
10
9
|
function isOpenApiFetchResponse(value) {
|
|
11
10
|
return value !== null && value !== void 0 && typeof value === "object" && ("data" in value || "error" in value || "response" in value);
|
|
@@ -73,6 +72,17 @@ function getPaystackOps(paystackClient) {
|
|
|
73
72
|
if (paystackClient?.subscription_manageEmail !== void 0) return paystackClient.subscription_manageEmail({ params: { path: { code } } });
|
|
74
73
|
return paystackClient?.subscription?.manage?.email?.(code, email);
|
|
75
74
|
},
|
|
75
|
+
subscriptionUpdate: (params) => {
|
|
76
|
+
if (paystackClient?.subscription_update !== void 0) return paystackClient.subscription_update({
|
|
77
|
+
params: { path: { code: params.code } },
|
|
78
|
+
body: {
|
|
79
|
+
plan: params.plan,
|
|
80
|
+
authorization: params.authorization,
|
|
81
|
+
amount: params.amount
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return paystackClient?.subscription?.update?.(params.code, params);
|
|
85
|
+
},
|
|
76
86
|
transactionChargeAuthorization: (body) => {
|
|
77
87
|
if (paystackClient?.transaction_chargeAuthorization !== void 0) return paystackClient.transaction_chargeAuthorization({ body });
|
|
78
88
|
return paystackClient?.transaction?.chargeAuthorization?.(body);
|
|
@@ -106,7 +116,6 @@ function getPaystackOps(paystackClient) {
|
|
|
106
116
|
}
|
|
107
117
|
};
|
|
108
118
|
}
|
|
109
|
-
|
|
110
119
|
//#endregion
|
|
111
120
|
//#region src/utils.ts
|
|
112
121
|
async function getPlans(subscriptionOptions) {
|
|
@@ -179,8 +188,8 @@ async function syncProductQuantityFromPaystack(ctx, productName, paystackClient)
|
|
|
179
188
|
value: productName.toLowerCase().replace(/\s+/g, "-")
|
|
180
189
|
}]
|
|
181
190
|
});
|
|
182
|
-
if (localProduct?.paystackId === void 0 || localProduct
|
|
183
|
-
if (localProduct && localProduct.unlimited !== true && localProduct.quantity
|
|
191
|
+
if (localProduct?.paystackId === void 0 || localProduct.paystackId === null || localProduct.paystackId === "") {
|
|
192
|
+
if (localProduct !== null && localProduct.unlimited !== true && typeof localProduct.quantity === "number" && localProduct.quantity > 0) await ctx.context.adapter.update({
|
|
184
193
|
model: "paystackProduct",
|
|
185
194
|
update: {
|
|
186
195
|
quantity: localProduct.quantity - 1,
|
|
@@ -207,7 +216,7 @@ async function syncProductQuantityFromPaystack(ctx, productName, paystackClient)
|
|
|
207
216
|
}]
|
|
208
217
|
});
|
|
209
218
|
} catch {
|
|
210
|
-
if (localProduct.unlimited !== true && localProduct.quantity
|
|
219
|
+
if (localProduct !== null && localProduct.unlimited !== true && typeof localProduct.quantity === "number" && localProduct.quantity > 0) await ctx.context.adapter.update({
|
|
211
220
|
model: "paystackProduct",
|
|
212
221
|
update: {
|
|
213
222
|
quantity: localProduct.quantity - 1,
|
|
@@ -220,7 +229,51 @@ async function syncProductQuantityFromPaystack(ctx, productName, paystackClient)
|
|
|
220
229
|
});
|
|
221
230
|
}
|
|
222
231
|
}
|
|
223
|
-
|
|
232
|
+
async function syncSubscriptionSeats(ctx, organizationId, options) {
|
|
233
|
+
if (options.subscription?.enabled !== true) return;
|
|
234
|
+
const adapter = ctx.context.adapter;
|
|
235
|
+
const subscription = await adapter.findOne({
|
|
236
|
+
model: "subscription",
|
|
237
|
+
where: [{
|
|
238
|
+
field: "referenceId",
|
|
239
|
+
value: organizationId
|
|
240
|
+
}]
|
|
241
|
+
});
|
|
242
|
+
if (subscription?.paystackSubscriptionCode === void 0 || subscription.paystackSubscriptionCode === null || subscription.paystackSubscriptionCode === "") return;
|
|
243
|
+
const plan = await getPlanByName(options, subscription.plan);
|
|
244
|
+
if (plan === null) return;
|
|
245
|
+
if (plan.seatAmount === void 0 && plan.seatPlanCode === void 0) return;
|
|
246
|
+
const quantity = (await adapter.findMany({
|
|
247
|
+
model: "member",
|
|
248
|
+
where: [{
|
|
249
|
+
field: "organizationId",
|
|
250
|
+
value: organizationId
|
|
251
|
+
}]
|
|
252
|
+
})).length;
|
|
253
|
+
let totalAmount = plan.amount ?? 0;
|
|
254
|
+
if (plan.seatAmount !== void 0 && plan.seatAmount !== null && typeof plan.seatAmount === "number") totalAmount += quantity * plan.seatAmount;
|
|
255
|
+
const ops = getPaystackOps(options.paystackClient);
|
|
256
|
+
try {
|
|
257
|
+
await ops.subscriptionUpdate({
|
|
258
|
+
code: subscription.paystackSubscriptionCode,
|
|
259
|
+
amount: totalAmount
|
|
260
|
+
});
|
|
261
|
+
await adapter.update({
|
|
262
|
+
model: "subscription",
|
|
263
|
+
where: [{
|
|
264
|
+
field: "id",
|
|
265
|
+
value: subscription.id
|
|
266
|
+
}],
|
|
267
|
+
update: {
|
|
268
|
+
seats: quantity,
|
|
269
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
} catch (e) {
|
|
273
|
+
const log = ctx.context.logger;
|
|
274
|
+
if (log !== void 0 && log !== null) log.error("Failed to sync subscription seats with Paystack", e);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
224
277
|
//#endregion
|
|
225
278
|
//#region src/middleware.ts
|
|
226
279
|
const referenceMiddleware = (options, action) => createAuthMiddleware(async (ctx) => {
|
|
@@ -259,7 +312,40 @@ const referenceMiddleware = (options, action) => createAuthMiddleware(async (ctx
|
|
|
259
312
|
logger.error(`Passing referenceId into a subscription action isn't allowed if subscription.authorizeReference isn't defined in your paystack plugin config and matches no organization membership.`);
|
|
260
313
|
throw new APIError("BAD_REQUEST", { message: "Passing referenceId isn't allowed without subscription.authorizeReference or valid organization membership." });
|
|
261
314
|
});
|
|
262
|
-
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region src/limits.ts
|
|
317
|
+
const getOrganizationSubscription = async (ctx, organizationId) => {
|
|
318
|
+
return await ctx.context.adapter.findOne({
|
|
319
|
+
model: "subscription",
|
|
320
|
+
where: [{
|
|
321
|
+
field: "referenceId",
|
|
322
|
+
value: organizationId
|
|
323
|
+
}]
|
|
324
|
+
});
|
|
325
|
+
};
|
|
326
|
+
const checkSeatLimit = async (ctx, organizationId, seatsToAdd = 1) => {
|
|
327
|
+
const subscription = await getOrganizationSubscription(ctx, organizationId);
|
|
328
|
+
if (subscription?.seats === void 0 || subscription.seats === null) return true;
|
|
329
|
+
const members = await ctx.context.adapter.findMany({
|
|
330
|
+
model: "member",
|
|
331
|
+
where: [{
|
|
332
|
+
field: "organizationId",
|
|
333
|
+
value: organizationId
|
|
334
|
+
}]
|
|
335
|
+
});
|
|
336
|
+
if (members.length + seatsToAdd > subscription.seats) throw new APIError("FORBIDDEN", { message: `Organization member limit reached. Used: ${members.length}, Max: ${subscription.seats}` });
|
|
337
|
+
return true;
|
|
338
|
+
};
|
|
339
|
+
const checkTeamLimit = async (ctx, organizationId, maxTeams) => {
|
|
340
|
+
if ((await ctx.context.adapter.findMany({
|
|
341
|
+
model: "team",
|
|
342
|
+
where: [{
|
|
343
|
+
field: "organizationId",
|
|
344
|
+
value: organizationId
|
|
345
|
+
}]
|
|
346
|
+
})).length >= maxTeams) throw new APIError("FORBIDDEN", { message: `Organization team limit reached. Max teams: ${maxTeams}` });
|
|
347
|
+
return true;
|
|
348
|
+
};
|
|
263
349
|
//#endregion
|
|
264
350
|
//#region src/routes.ts
|
|
265
351
|
const PAYSTACK_ERROR_CODES = defineErrorCodes({
|
|
@@ -300,7 +386,7 @@ const paystackWebhook = (options) => {
|
|
|
300
386
|
disableBody: true
|
|
301
387
|
}, async (ctx) => {
|
|
302
388
|
const request = ctx.requestClone ?? ctx.request;
|
|
303
|
-
if (
|
|
389
|
+
if (request === void 0 || request === null) throw new APIError("BAD_REQUEST", { message: "Request object is missing from context" });
|
|
304
390
|
const payload = await request.text();
|
|
305
391
|
const signature = (ctx.headers ?? ctx.request?.headers)?.get("x-paystack-signature");
|
|
306
392
|
if (signature === void 0 || signature === null || signature === "") throw new APIError("UNAUTHORIZED", {
|
|
@@ -342,7 +428,7 @@ const paystackWebhook = (options) => {
|
|
|
342
428
|
value: reference
|
|
343
429
|
}]
|
|
344
430
|
});
|
|
345
|
-
if (transaction?.product) await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
|
|
431
|
+
if (transaction?.product !== void 0 && transaction.product !== null && transaction.product !== "") await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
|
|
346
432
|
} catch (e) {
|
|
347
433
|
ctx.context.logger.warn("Failed to sync product quantity", e);
|
|
348
434
|
}
|
|
@@ -398,11 +484,10 @@ const paystackWebhook = (options) => {
|
|
|
398
484
|
value: planName
|
|
399
485
|
});
|
|
400
486
|
if (where.length > 0) {
|
|
401
|
-
const
|
|
487
|
+
const subscription = (await ctx.context.adapter.findMany({
|
|
402
488
|
model: "subscription",
|
|
403
489
|
where
|
|
404
|
-
});
|
|
405
|
-
const subscription = matches !== void 0 && matches !== null ? matches[0] : void 0;
|
|
490
|
+
}))?.[0];
|
|
406
491
|
if (subscription !== void 0 && subscription !== null) {
|
|
407
492
|
await ctx.context.adapter.update({
|
|
408
493
|
model: "subscription",
|
|
@@ -410,7 +495,7 @@ const paystackWebhook = (options) => {
|
|
|
410
495
|
paystackSubscriptionCode: subscriptionCode,
|
|
411
496
|
status: "active",
|
|
412
497
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
413
|
-
periodEnd: payloadData?.next_payment_date !== void 0 && payloadData
|
|
498
|
+
periodEnd: payloadData?.next_payment_date !== void 0 && payloadData.next_payment_date !== null && payloadData.next_payment_date !== "" ? new Date(payloadData.next_payment_date) : void 0
|
|
414
499
|
},
|
|
415
500
|
where: [{
|
|
416
501
|
field: "id",
|
|
@@ -455,8 +540,8 @@ const paystackWebhook = (options) => {
|
|
|
455
540
|
});
|
|
456
541
|
let newStatus = "canceled";
|
|
457
542
|
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";
|
|
543
|
+
const periodEnd = nextPaymentDate !== void 0 && nextPaymentDate !== null && nextPaymentDate !== "" ? new Date(nextPaymentDate) : existing?.periodEnd !== void 0 ? new Date(existing.periodEnd) : void 0;
|
|
544
|
+
if (periodEnd !== void 0 && periodEnd > /* @__PURE__ */ new Date()) newStatus = "active";
|
|
460
545
|
await ctx.context.adapter.update({
|
|
461
546
|
model: "subscription",
|
|
462
547
|
update: {
|
|
@@ -470,7 +555,7 @@ const paystackWebhook = (options) => {
|
|
|
470
555
|
value: subscriptionCode
|
|
471
556
|
}]
|
|
472
557
|
});
|
|
473
|
-
if (existing) await options.subscription.onSubscriptionCancel?.({
|
|
558
|
+
if (existing !== void 0 && existing !== null) await options.subscription.onSubscriptionCancel?.({
|
|
474
559
|
event,
|
|
475
560
|
subscription: {
|
|
476
561
|
...existing,
|
|
@@ -479,6 +564,31 @@ const paystackWebhook = (options) => {
|
|
|
479
564
|
}, ctx);
|
|
480
565
|
}
|
|
481
566
|
}
|
|
567
|
+
if (eventName === "charge.success" || eventName === "invoice.update") {
|
|
568
|
+
const payloadData = data;
|
|
569
|
+
const subscriptionCode = payloadData?.subscription?.subscription_code ?? payloadData?.subscription_code;
|
|
570
|
+
if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
|
|
571
|
+
const existingSub = await ctx.context.adapter.findOne({
|
|
572
|
+
model: "subscription",
|
|
573
|
+
where: [{
|
|
574
|
+
field: "paystackSubscriptionCode",
|
|
575
|
+
value: subscriptionCode
|
|
576
|
+
}]
|
|
577
|
+
});
|
|
578
|
+
if (existingSub?.pendingPlan !== void 0 && existingSub.pendingPlan !== null && existingSub.pendingPlan !== "") await ctx.context.adapter.update({
|
|
579
|
+
model: "subscription",
|
|
580
|
+
update: {
|
|
581
|
+
plan: existingSub.pendingPlan,
|
|
582
|
+
pendingPlan: null,
|
|
583
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
584
|
+
},
|
|
585
|
+
where: [{
|
|
586
|
+
field: "id",
|
|
587
|
+
value: existingSub.id
|
|
588
|
+
}]
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
482
592
|
} catch (_e) {
|
|
483
593
|
ctx.context.logger.error("Failed to sync Paystack webhook event", _e);
|
|
484
594
|
}
|
|
@@ -495,7 +605,10 @@ const initializeTransactionBodySchema = z.object({
|
|
|
495
605
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
496
606
|
referenceId: z.string().optional(),
|
|
497
607
|
callbackURL: z.string().optional(),
|
|
498
|
-
quantity: z.number().int().positive().optional()
|
|
608
|
+
quantity: z.number().int().positive().optional(),
|
|
609
|
+
scheduleAtPeriodEnd: z.boolean().optional(),
|
|
610
|
+
cancelAtPeriodEnd: z.boolean().optional(),
|
|
611
|
+
prorateAndCharge: z.boolean().optional()
|
|
499
612
|
});
|
|
500
613
|
const initializeTransaction = (options, path = "/paystack/initialize-transaction") => {
|
|
501
614
|
const subscriptionOptions = options.subscription;
|
|
@@ -509,11 +622,11 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
509
622
|
] : [sessionMiddleware, originCheck]
|
|
510
623
|
}, async (ctx) => {
|
|
511
624
|
const paystack = getPaystackOps(options.paystackClient);
|
|
512
|
-
const { plan: planName, product: productName, amount: bodyAmount, currency, email, metadata: extraMetadata, callbackURL, quantity } = ctx.body;
|
|
625
|
+
const { plan: planName, product: productName, amount: bodyAmount, currency, email, metadata: extraMetadata, callbackURL, quantity, scheduleAtPeriodEnd, cancelAtPeriodEnd, prorateAndCharge } = ctx.body;
|
|
513
626
|
if (callbackURL !== void 0 && callbackURL !== null && callbackURL !== "") {
|
|
514
627
|
const checkTrusted = () => {
|
|
515
628
|
try {
|
|
516
|
-
if (
|
|
629
|
+
if (callbackURL === void 0 || callbackURL === null || callbackURL === "") return false;
|
|
517
630
|
if (callbackURL.startsWith("/")) return true;
|
|
518
631
|
const baseUrl = ctx.context?.baseURL ?? ctx.request?.url ?? "";
|
|
519
632
|
if (!baseUrl) return false;
|
|
@@ -523,7 +636,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
523
636
|
return false;
|
|
524
637
|
}
|
|
525
638
|
};
|
|
526
|
-
if (
|
|
639
|
+
if (checkTrusted() !== true) throw new APIError("FORBIDDEN", {
|
|
527
640
|
message: "callbackURL is not a trusted origin.",
|
|
528
641
|
status: 403
|
|
529
642
|
});
|
|
@@ -531,16 +644,16 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
531
644
|
const session = await getSessionFromCtx(ctx);
|
|
532
645
|
if (!session) throw new APIError("UNAUTHORIZED");
|
|
533
646
|
const user = session.user;
|
|
534
|
-
if (subscriptionOptions?.enabled === true && subscriptionOptions.requireEmailVerification === true &&
|
|
647
|
+
if (subscriptionOptions?.enabled === true && subscriptionOptions.requireEmailVerification === true && user.emailVerified !== true) throw new APIError("BAD_REQUEST", {
|
|
535
648
|
code: "EMAIL_VERIFICATION_REQUIRED",
|
|
536
|
-
message: PAYSTACK_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED
|
|
649
|
+
message: PAYSTACK_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED.message
|
|
537
650
|
});
|
|
538
651
|
let plan;
|
|
539
652
|
let product;
|
|
540
653
|
if (planName !== void 0 && planName !== null && planName !== "") {
|
|
541
654
|
if (subscriptionOptions?.enabled !== true) throw new APIError("BAD_REQUEST", { message: "Subscriptions are not enabled." });
|
|
542
655
|
plan = await getPlanByName(options, planName) ?? void 0;
|
|
543
|
-
if (
|
|
656
|
+
if (plan === null || plan === void 0) {
|
|
544
657
|
const nativePlan = await ctx.context.adapter.findOne({
|
|
545
658
|
model: "paystackPlan",
|
|
546
659
|
where: [{
|
|
@@ -548,7 +661,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
548
661
|
value: planName
|
|
549
662
|
}]
|
|
550
663
|
});
|
|
551
|
-
if (nativePlan) plan = nativePlan;
|
|
664
|
+
if (nativePlan !== void 0 && nativePlan !== null) plan = nativePlan;
|
|
552
665
|
else plan = await ctx.context.adapter.findOne({
|
|
553
666
|
model: "paystackPlan",
|
|
554
667
|
where: [{
|
|
@@ -557,9 +670,9 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
557
670
|
}]
|
|
558
671
|
}) ?? void 0;
|
|
559
672
|
}
|
|
560
|
-
if (
|
|
673
|
+
if (plan === null || plan === void 0) throw new APIError("BAD_REQUEST", {
|
|
561
674
|
code: "SUBSCRIPTION_PLAN_NOT_FOUND",
|
|
562
|
-
message: PAYSTACK_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,
|
|
675
|
+
message: PAYSTACK_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND.message,
|
|
563
676
|
status: 400
|
|
564
677
|
});
|
|
565
678
|
} else if (productName !== void 0 && productName !== null && productName !== "") {
|
|
@@ -573,7 +686,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
573
686
|
}]
|
|
574
687
|
}) ?? void 0;
|
|
575
688
|
}
|
|
576
|
-
if (
|
|
689
|
+
if (product === null || product === void 0) throw new APIError("BAD_REQUEST", {
|
|
577
690
|
message: `Product '${productName}' not found.`,
|
|
578
691
|
status: 400
|
|
579
692
|
});
|
|
@@ -581,23 +694,77 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
581
694
|
message: "Either 'plan', 'product', or 'amount' is required to initialize a transaction.",
|
|
582
695
|
status: 400
|
|
583
696
|
});
|
|
584
|
-
|
|
697
|
+
let amount = bodyAmount ?? product?.price;
|
|
585
698
|
const finalCurrency = currency ?? product?.currency ?? plan?.currency ?? "NGN";
|
|
699
|
+
const referenceIdFromCtx = ctx.context.referenceId;
|
|
700
|
+
const referenceId = ctx.body.referenceId !== void 0 && ctx.body.referenceId !== null && ctx.body.referenceId !== "" ? ctx.body.referenceId : referenceIdFromCtx !== void 0 && referenceIdFromCtx !== null && referenceIdFromCtx !== "" ? referenceIdFromCtx : session.user.id;
|
|
701
|
+
if (plan && scheduleAtPeriodEnd === true) {
|
|
702
|
+
const existingSub = await getOrganizationSubscription(ctx, referenceId);
|
|
703
|
+
if (existingSub?.status === "active") {
|
|
704
|
+
await ctx.context.adapter.update({
|
|
705
|
+
model: "subscription",
|
|
706
|
+
where: [{
|
|
707
|
+
field: "id",
|
|
708
|
+
value: existingSub.id
|
|
709
|
+
}],
|
|
710
|
+
update: {
|
|
711
|
+
pendingPlan: plan.name,
|
|
712
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
return ctx.json({
|
|
716
|
+
status: "success",
|
|
717
|
+
message: "Plan change scheduled at period end.",
|
|
718
|
+
scheduled: true
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (cancelAtPeriodEnd === true) {
|
|
723
|
+
const existingSub = await getOrganizationSubscription(ctx, referenceId);
|
|
724
|
+
if (existingSub?.status === "active") {
|
|
725
|
+
await ctx.context.adapter.update({
|
|
726
|
+
model: "subscription",
|
|
727
|
+
where: [{
|
|
728
|
+
field: "id",
|
|
729
|
+
value: existingSub.id
|
|
730
|
+
}],
|
|
731
|
+
update: {
|
|
732
|
+
cancelAtPeriodEnd: true,
|
|
733
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
return ctx.json({
|
|
737
|
+
status: "success",
|
|
738
|
+
message: "Subscription cancellation scheduled at period end.",
|
|
739
|
+
scheduled: true
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (plan !== null && plan !== void 0 && (plan.seatAmount !== void 0 || "seatPriceId" in plan)) {
|
|
744
|
+
const members = await ctx.context.adapter.findMany({
|
|
745
|
+
model: "member",
|
|
746
|
+
where: [{
|
|
747
|
+
field: "organizationId",
|
|
748
|
+
value: referenceId
|
|
749
|
+
}]
|
|
750
|
+
});
|
|
751
|
+
const seatCount = members.length > 0 ? members.length : 1;
|
|
752
|
+
const quantityToUse = quantity ?? seatCount;
|
|
753
|
+
amount = (plan.amount ?? 0) + quantityToUse * (plan.seatAmount ?? plan.seatPriceId ?? 0);
|
|
754
|
+
}
|
|
586
755
|
let url;
|
|
587
756
|
let reference;
|
|
588
757
|
let accessCode;
|
|
589
|
-
const referenceIdFromCtx = ctx.context.referenceId;
|
|
590
|
-
const referenceId = ctx.body.referenceId !== void 0 && ctx.body.referenceId !== null && ctx.body.referenceId !== "" ? ctx.body.referenceId : referenceIdFromCtx !== void 0 && referenceIdFromCtx !== null && referenceIdFromCtx !== "" ? referenceIdFromCtx : session.user.id;
|
|
591
758
|
let trialStart;
|
|
592
759
|
let trialEnd;
|
|
593
760
|
if (plan?.freeTrial?.days !== void 0 && plan.freeTrial.days !== null && plan.freeTrial.days > 0) {
|
|
594
|
-
if (
|
|
761
|
+
if ((await ctx.context.adapter.findMany({
|
|
595
762
|
model: "subscription",
|
|
596
763
|
where: [{
|
|
597
764
|
field: "referenceId",
|
|
598
765
|
value: referenceId
|
|
599
766
|
}]
|
|
600
|
-
}))?.some((sub) => sub.trialStart !== void 0 && sub.trialStart !== null || sub.trialEnd !== void 0 && sub.trialEnd !== null || sub.status === "trialing")) {
|
|
767
|
+
}))?.some((sub) => sub.trialStart !== void 0 && sub.trialStart !== null || sub.trialEnd !== void 0 && sub.trialEnd !== null || sub.status === "trialing") !== true) {
|
|
601
768
|
trialStart = /* @__PURE__ */ new Date();
|
|
602
769
|
trialEnd = /* @__PURE__ */ new Date();
|
|
603
770
|
trialEnd.setDate(trialEnd.getDate() + plan.freeTrial.days);
|
|
@@ -628,7 +795,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
628
795
|
value: "owner"
|
|
629
796
|
}]
|
|
630
797
|
});
|
|
631
|
-
if (ownerMember) {
|
|
798
|
+
if (ownerMember !== void 0 && ownerMember !== null) {
|
|
632
799
|
const ownerUser = await ctx.context.adapter.findOne({
|
|
633
800
|
model: "user",
|
|
634
801
|
where: [{
|
|
@@ -646,7 +813,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
646
813
|
userId: user.id,
|
|
647
814
|
plan: plan?.name.toLowerCase(),
|
|
648
815
|
product: product?.name.toLowerCase(),
|
|
649
|
-
isTrial:
|
|
816
|
+
isTrial: trialStart !== void 0 && trialStart !== null,
|
|
650
817
|
trialEnd: trialEnd?.toISOString(),
|
|
651
818
|
...extraMetadata
|
|
652
819
|
});
|
|
@@ -661,29 +828,108 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
661
828
|
const ops = getPaystackOps(options.paystackClient);
|
|
662
829
|
if (initBody.email !== void 0 && initBody.email !== null && initBody.email !== "") await ops.customerUpdate(paystackCustomerCode, { email: initBody.email });
|
|
663
830
|
} catch (_e) {}
|
|
664
|
-
if (plan
|
|
831
|
+
if (plan !== void 0 && plan !== null && prorateAndCharge === true) {
|
|
832
|
+
const existingSub = await getOrganizationSubscription(ctx, referenceId);
|
|
833
|
+
if (existingSub?.status === "active" && existingSub.paystackAuthorizationCode !== null && existingSub.paystackAuthorizationCode !== void 0 && existingSub.paystackSubscriptionCode !== null && existingSub.paystackSubscriptionCode !== void 0) {
|
|
834
|
+
const now = /* @__PURE__ */ new Date();
|
|
835
|
+
const periodEndLocal = existingSub.periodEnd ? new Date(existingSub.periodEnd) : new Date(now.getTime() + 720 * 60 * 60 * 1e3);
|
|
836
|
+
const periodStartLocal = existingSub.periodStart ? new Date(existingSub.periodStart) : now;
|
|
837
|
+
const totalDays = Math.max(1, Math.ceil((periodEndLocal.getTime() - periodStartLocal.getTime()) / (1e3 * 60 * 60 * 24)));
|
|
838
|
+
const remainingDays = Math.max(0, Math.ceil((periodEndLocal.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24)));
|
|
839
|
+
let oldAmount = 0;
|
|
840
|
+
if (existingSub.plan !== void 0 && existingSub.plan !== null && existingSub.plan !== "") {
|
|
841
|
+
const oldPlan = await getPlanByName(options, existingSub.plan) ?? await ctx.context.adapter.findOne({
|
|
842
|
+
model: "paystackPlan",
|
|
843
|
+
where: [{
|
|
844
|
+
field: "name",
|
|
845
|
+
value: existingSub.plan
|
|
846
|
+
}]
|
|
847
|
+
});
|
|
848
|
+
if (oldPlan !== null && oldPlan !== void 0) {
|
|
849
|
+
const oldSeatCount = existingSub.seats ?? 1;
|
|
850
|
+
oldAmount = (oldPlan.amount ?? 0) + oldSeatCount * (oldPlan.seatAmount ?? oldPlan.seatPriceId ?? 0);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
let membersCount = 1;
|
|
854
|
+
if (plan.seatAmount !== void 0 || plan.seatPriceId !== void 0) {
|
|
855
|
+
const members = await ctx.context.adapter.findMany({
|
|
856
|
+
model: "member",
|
|
857
|
+
where: [{
|
|
858
|
+
field: "organizationId",
|
|
859
|
+
value: referenceId
|
|
860
|
+
}]
|
|
861
|
+
});
|
|
862
|
+
membersCount = members.length > 0 ? members.length : 1;
|
|
863
|
+
}
|
|
864
|
+
const newSeatCount = quantity ?? existingSub.seats ?? membersCount;
|
|
865
|
+
const newAmount = (plan.amount ?? 0) + newSeatCount * (plan.seatAmount ?? plan.seatPriceId ?? 0);
|
|
866
|
+
const costDifference = newAmount - oldAmount;
|
|
867
|
+
if (costDifference > 0 && remainingDays > 0) {
|
|
868
|
+
const proratedAmount = Math.round(costDifference / totalDays * remainingDays);
|
|
869
|
+
if (proratedAmount >= 5e3) {
|
|
870
|
+
if (unwrapSdkResult(await getPaystackOps(options.paystackClient).transactionChargeAuthorization({
|
|
871
|
+
email: targetEmail,
|
|
872
|
+
amount: proratedAmount,
|
|
873
|
+
authorization_code: existingSub.paystackAuthorizationCode,
|
|
874
|
+
reference: `prorate_${Date.now()}_${Math.random().toString(36).substring(7)}`,
|
|
875
|
+
metadata: {
|
|
876
|
+
type: "proration",
|
|
877
|
+
referenceId,
|
|
878
|
+
newPlan: plan.name,
|
|
879
|
+
oldPlan: existingSub.plan,
|
|
880
|
+
remainingDays
|
|
881
|
+
}
|
|
882
|
+
}))?.status !== "success") throw new APIError("BAD_REQUEST", { message: "Failed to process prorated charge via saved authorization." });
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
await getPaystackOps(options.paystackClient).subscriptionUpdate({
|
|
886
|
+
code: existingSub.paystackSubscriptionCode,
|
|
887
|
+
amount: newAmount,
|
|
888
|
+
plan: plan.planCode
|
|
889
|
+
});
|
|
890
|
+
await ctx.context.adapter.update({
|
|
891
|
+
model: "subscription",
|
|
892
|
+
where: [{
|
|
893
|
+
field: "id",
|
|
894
|
+
value: existingSub.id
|
|
895
|
+
}],
|
|
896
|
+
update: {
|
|
897
|
+
plan: plan.name,
|
|
898
|
+
seats: newSeatCount,
|
|
899
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
return ctx.json({
|
|
903
|
+
status: "success",
|
|
904
|
+
message: "Subscription successfully upgraded with prorated charge.",
|
|
905
|
+
prorated: true
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (plan !== void 0 && plan !== null) if (trialStart !== void 0 && trialStart !== null) initBody.amount = 5e3;
|
|
665
910
|
else {
|
|
666
911
|
initBody.plan = plan.planCode;
|
|
667
912
|
initBody.invoice_limit = plan.invoiceLimit;
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
913
|
+
let finalAmount;
|
|
914
|
+
if (amount !== void 0 && amount !== null) {
|
|
915
|
+
finalAmount = amount;
|
|
916
|
+
initBody.quantity = 1;
|
|
917
|
+
} else finalAmount = (plan.amount ?? 0) * (quantity ?? 1);
|
|
918
|
+
initBody.amount = Math.max(Math.round(finalAmount), 5e3);
|
|
671
919
|
}
|
|
672
920
|
else {
|
|
673
921
|
if (amount === void 0 || amount === null || amount === 0) throw new APIError("BAD_REQUEST", { message: "Amount is required for one-time payments" });
|
|
674
922
|
initBody.amount = Math.round(amount);
|
|
675
923
|
}
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
reference = data?.reference;
|
|
681
|
-
accessCode = data?.access_code;
|
|
924
|
+
const sdkRes = unwrapSdkResult(await paystack.transactionInitialize(initBody));
|
|
925
|
+
url = sdkRes?.authorization_url ?? sdkRes?.data?.authorization_url;
|
|
926
|
+
reference = sdkRes?.reference ?? sdkRes?.data?.reference;
|
|
927
|
+
accessCode = sdkRes?.access_code ?? sdkRes?.data?.access_code;
|
|
682
928
|
} catch (error) {
|
|
683
929
|
ctx.context.logger.error("Failed to initialize Paystack transaction", error);
|
|
684
930
|
throw new APIError("BAD_REQUEST", {
|
|
685
931
|
code: "FAILED_TO_INITIALIZE_TRANSACTION",
|
|
686
|
-
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_INITIALIZE_TRANSACTION
|
|
932
|
+
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_INITIALIZE_TRANSACTION.message
|
|
687
933
|
});
|
|
688
934
|
}
|
|
689
935
|
await ctx.context.adapter.create({
|
|
@@ -697,7 +943,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
697
943
|
status: "pending",
|
|
698
944
|
plan: plan?.name.toLowerCase(),
|
|
699
945
|
product: product?.name.toLowerCase(),
|
|
700
|
-
metadata: extraMetadata !== void 0 && extraMetadata !== null ? JSON.stringify(extraMetadata) : void 0,
|
|
946
|
+
metadata: extraMetadata !== void 0 && extraMetadata !== null && Object.keys(extraMetadata).length > 0 ? JSON.stringify(extraMetadata) : void 0,
|
|
701
947
|
createdAt: /* @__PURE__ */ new Date(),
|
|
702
948
|
updatedAt: /* @__PURE__ */ new Date()
|
|
703
949
|
}
|
|
@@ -712,7 +958,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
712
958
|
value: referenceId
|
|
713
959
|
}]
|
|
714
960
|
});
|
|
715
|
-
if (org?.paystackCustomerCode !== void 0 && org
|
|
961
|
+
if (org?.paystackCustomerCode !== void 0 && org.paystackCustomerCode !== null && org.paystackCustomerCode !== "") storedCustomerCode = org.paystackCustomerCode;
|
|
716
962
|
}
|
|
717
963
|
const newSubscription = await ctx.context.adapter.create({
|
|
718
964
|
model: "subscription",
|
|
@@ -765,10 +1011,11 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
765
1011
|
ctx.context.logger.error("Failed to verify Paystack transaction", error);
|
|
766
1012
|
throw new APIError("BAD_REQUEST", {
|
|
767
1013
|
code: "FAILED_TO_VERIFY_TRANSACTION",
|
|
768
|
-
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_VERIFY_TRANSACTION
|
|
1014
|
+
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_VERIFY_TRANSACTION.message
|
|
769
1015
|
});
|
|
770
1016
|
}
|
|
771
|
-
const
|
|
1017
|
+
const dataRaw = unwrapSdkResult(verifyRes);
|
|
1018
|
+
const data = dataRaw?.data ?? dataRaw;
|
|
772
1019
|
const status = data?.status;
|
|
773
1020
|
const reference = data?.reference ?? ctx.body.reference;
|
|
774
1021
|
const paystackId = data?.id !== void 0 && data?.id !== null ? String(data.id) : void 0;
|
|
@@ -785,24 +1032,26 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
785
1032
|
if (session !== null && session !== void 0 && referenceId !== session.user.id) {
|
|
786
1033
|
const authRef = subscriptionOptions?.authorizeReference;
|
|
787
1034
|
let authorized = false;
|
|
788
|
-
if (authRef !== void 0
|
|
1035
|
+
if (authRef !== void 0) authorized = await authRef({
|
|
789
1036
|
user: session.user,
|
|
790
1037
|
session,
|
|
791
1038
|
referenceId,
|
|
792
1039
|
action: "verify-transaction"
|
|
793
1040
|
}, ctx);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1041
|
+
if (authorized !== true) {
|
|
1042
|
+
if (options.organization?.enabled === true) {
|
|
1043
|
+
const member = await ctx.context.adapter.findOne({
|
|
1044
|
+
model: "member",
|
|
1045
|
+
where: [{
|
|
1046
|
+
field: "userId",
|
|
1047
|
+
value: session.user.id
|
|
1048
|
+
}, {
|
|
1049
|
+
field: "organizationId",
|
|
1050
|
+
value: referenceId
|
|
1051
|
+
}]
|
|
1052
|
+
});
|
|
1053
|
+
if (member !== void 0 && member !== null) authorized = true;
|
|
1054
|
+
}
|
|
806
1055
|
}
|
|
807
1056
|
if (!authorized) throw new APIError("UNAUTHORIZED");
|
|
808
1057
|
}
|
|
@@ -813,7 +1062,7 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
813
1062
|
status: "success",
|
|
814
1063
|
paystackId,
|
|
815
1064
|
...data?.amount !== void 0 && data?.amount !== null ? { amount: data.amount } : {},
|
|
816
|
-
...data?.currency !== void 0 && data?.currency !== null ? { currency: data.currency } : {},
|
|
1065
|
+
...data?.currency !== void 0 && data?.currency !== null && data?.currency !== "" ? { currency: data.currency } : {},
|
|
817
1066
|
updatedAt: /* @__PURE__ */ new Date()
|
|
818
1067
|
},
|
|
819
1068
|
where: [{
|
|
@@ -823,28 +1072,35 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
823
1072
|
});
|
|
824
1073
|
const customer = data?.customer;
|
|
825
1074
|
const paystackCustomerCodeFromPaystack = customer !== void 0 && customer !== null && typeof customer === "object" ? customer.customer_code : void 0;
|
|
826
|
-
if (paystackCustomerCodeFromPaystack !== void 0 && paystackCustomerCodeFromPaystack !== null && paystackCustomerCodeFromPaystack !== "" && referenceId !== void 0 && referenceId !== null && referenceId !== "")
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1075
|
+
if (paystackCustomerCodeFromPaystack !== void 0 && paystackCustomerCodeFromPaystack !== null && paystackCustomerCodeFromPaystack !== "" && referenceId !== void 0 && referenceId !== null && referenceId !== "") {
|
|
1076
|
+
let isOrg = options.organization?.enabled === true && typeof referenceId === "string" && referenceId.startsWith("org_");
|
|
1077
|
+
if (!isOrg && options.organization?.enabled === true) {
|
|
1078
|
+
const org = await ctx.context.adapter.findOne({
|
|
1079
|
+
model: "organization",
|
|
1080
|
+
where: [{
|
|
1081
|
+
field: "id",
|
|
1082
|
+
value: referenceId
|
|
1083
|
+
}]
|
|
1084
|
+
});
|
|
1085
|
+
isOrg = org !== null && org !== void 0;
|
|
1086
|
+
}
|
|
1087
|
+
if (isOrg === true) await ctx.context.adapter.update({
|
|
1088
|
+
model: "organization",
|
|
1089
|
+
update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },
|
|
1090
|
+
where: [{
|
|
1091
|
+
field: "id",
|
|
1092
|
+
value: referenceId
|
|
1093
|
+
}]
|
|
1094
|
+
});
|
|
1095
|
+
else await ctx.context.adapter.update({
|
|
1096
|
+
model: "user",
|
|
1097
|
+
update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },
|
|
1098
|
+
where: [{
|
|
1099
|
+
field: "id",
|
|
1100
|
+
value: referenceId
|
|
1101
|
+
}]
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
848
1104
|
const transaction = await ctx.context.adapter.findOne({
|
|
849
1105
|
model: "paystackTransaction",
|
|
850
1106
|
where: [{
|
|
@@ -852,7 +1108,7 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
852
1108
|
value: reference
|
|
853
1109
|
}]
|
|
854
1110
|
});
|
|
855
|
-
if (transaction?.product) await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
|
|
1111
|
+
if (transaction?.product !== void 0 && transaction?.product !== null && transaction?.product !== "") await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
|
|
856
1112
|
let isTrial = false;
|
|
857
1113
|
let trialEnd;
|
|
858
1114
|
let targetPlan;
|
|
@@ -865,17 +1121,17 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
865
1121
|
}
|
|
866
1122
|
let paystackSubscriptionCode;
|
|
867
1123
|
if (isTrial === true && targetPlan !== void 0 && targetPlan !== null && targetPlan !== "" && trialEnd !== void 0 && trialEnd !== null && trialEnd !== "") {
|
|
868
|
-
const email =
|
|
1124
|
+
const email = data.customer?.email;
|
|
869
1125
|
const planConfig = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === targetPlan?.toLowerCase());
|
|
870
|
-
if (planConfig !== void 0 && (planConfig.planCode === void 0 || planConfig.planCode === null || planConfig.planCode === "")) paystackSubscriptionCode = `LOC_${reference}`;
|
|
871
|
-
if (authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" && email !== void 0 && email !== null && email !== "" && planConfig?.planCode !== void 0 && planConfig
|
|
872
|
-
const
|
|
1126
|
+
if (planConfig !== void 0 && planConfig !== null && (planConfig.planCode === void 0 || planConfig.planCode === null || planConfig.planCode === "")) paystackSubscriptionCode = `LOC_${reference}`;
|
|
1127
|
+
if (authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" && email !== void 0 && email !== null && email !== "" && planConfig?.planCode !== void 0 && planConfig.planCode !== null && planConfig.planCode !== "") {
|
|
1128
|
+
const subRes = unwrapSdkResult(await paystack.subscriptionCreate({
|
|
873
1129
|
customer: email,
|
|
874
1130
|
plan: planConfig.planCode,
|
|
875
1131
|
authorization: authorizationCode,
|
|
876
1132
|
start_date: trialEnd
|
|
877
1133
|
}));
|
|
878
|
-
paystackSubscriptionCode = (
|
|
1134
|
+
paystackSubscriptionCode = (subRes?.data ?? subRes)?.subscription_code;
|
|
879
1135
|
}
|
|
880
1136
|
} else if (isTrial !== true) {
|
|
881
1137
|
const planCodeFromPaystack = (data?.plan)?.plan_code;
|
|
@@ -890,15 +1146,15 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
890
1146
|
}]
|
|
891
1147
|
});
|
|
892
1148
|
let targetSub;
|
|
893
|
-
if (existingSubs && existingSubs.length > 0) targetSub = existingSubs.find((s) =>
|
|
1149
|
+
if (existingSubs !== null && existingSubs !== void 0 && existingSubs.length > 0) targetSub = existingSubs.find((s) => referenceId === void 0 || referenceId === null || referenceId === "" || s.referenceId === referenceId);
|
|
894
1150
|
let updatedSubscription = null;
|
|
895
1151
|
if (targetSub !== void 0 && targetSub !== null) updatedSubscription = await ctx.context.adapter.update({
|
|
896
1152
|
model: "subscription",
|
|
897
1153
|
update: {
|
|
898
|
-
status: isTrial
|
|
1154
|
+
status: isTrial ? "trialing" : "active",
|
|
899
1155
|
periodStart: /* @__PURE__ */ new Date(),
|
|
900
1156
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
901
|
-
...isTrial === true && trialEnd !== void 0 && trialEnd !== null
|
|
1157
|
+
...isTrial === true && trialEnd !== void 0 && trialEnd !== null ? {
|
|
902
1158
|
trialStart: /* @__PURE__ */ new Date(),
|
|
903
1159
|
trialEnd: new Date(trialEnd),
|
|
904
1160
|
periodEnd: new Date(trialEnd)
|
|
@@ -922,20 +1178,6 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
922
1178
|
} catch (e) {
|
|
923
1179
|
ctx.context.logger.error("Failed to update transaction/subscription after verification", e);
|
|
924
1180
|
}
|
|
925
|
-
} else if (status === "failed" || status === "abandoned") try {
|
|
926
|
-
await ctx.context.adapter.update({
|
|
927
|
-
model: "paystackTransaction",
|
|
928
|
-
update: {
|
|
929
|
-
status,
|
|
930
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
931
|
-
},
|
|
932
|
-
where: [{
|
|
933
|
-
field: "reference",
|
|
934
|
-
value: reference
|
|
935
|
-
}]
|
|
936
|
-
});
|
|
937
|
-
} catch (e) {
|
|
938
|
-
ctx.context.logger.error("Failed to update transaction status", e);
|
|
939
1181
|
}
|
|
940
1182
|
return ctx.json({
|
|
941
1183
|
status,
|
|
@@ -961,7 +1203,7 @@ const listSubscriptions = (options) => {
|
|
|
961
1203
|
if (!session) throw new APIError("UNAUTHORIZED");
|
|
962
1204
|
const referenceIdPart = ctx.context.referenceId;
|
|
963
1205
|
const queryRefId = ctx.query?.referenceId;
|
|
964
|
-
const referenceId = referenceIdPart
|
|
1206
|
+
const referenceId = referenceIdPart ?? queryRefId ?? session.user.id;
|
|
965
1207
|
const res = await ctx.context.adapter.findMany({
|
|
966
1208
|
model: "subscription",
|
|
967
1209
|
where: [{
|
|
@@ -998,7 +1240,8 @@ const listTransactions = (options, path = "/paystack/list-transactions") => {
|
|
|
998
1240
|
const enableDisableBodySchema = z.object({
|
|
999
1241
|
referenceId: z.string().optional(),
|
|
1000
1242
|
subscriptionCode: z.string(),
|
|
1001
|
-
emailToken: z.string().optional()
|
|
1243
|
+
emailToken: z.string().optional(),
|
|
1244
|
+
atPeriodEnd: z.boolean().optional()
|
|
1002
1245
|
});
|
|
1003
1246
|
function decodeBase64UrlToString(value) {
|
|
1004
1247
|
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -1029,7 +1272,7 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
|
|
|
1029
1272
|
referenceMiddleware(options, "disable-subscription")
|
|
1030
1273
|
] : [sessionMiddleware, originCheck]
|
|
1031
1274
|
}, async (ctx) => {
|
|
1032
|
-
const { subscriptionCode } = ctx.body;
|
|
1275
|
+
const { subscriptionCode, atPeriodEnd } = ctx.body;
|
|
1033
1276
|
const paystack = getPaystackOps(options.paystackClient);
|
|
1034
1277
|
try {
|
|
1035
1278
|
if (subscriptionCode.startsWith("LOC_")) {
|
|
@@ -1040,12 +1283,12 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
|
|
|
1040
1283
|
value: subscriptionCode
|
|
1041
1284
|
}]
|
|
1042
1285
|
});
|
|
1043
|
-
if (sub) {
|
|
1286
|
+
if (sub !== null && sub !== void 0) {
|
|
1044
1287
|
await ctx.context.adapter.update({
|
|
1045
1288
|
model: "subscription",
|
|
1046
1289
|
update: {
|
|
1047
|
-
status: "active",
|
|
1048
|
-
cancelAtPeriodEnd:
|
|
1290
|
+
status: atPeriodEnd === false ? "canceled" : "active",
|
|
1291
|
+
cancelAtPeriodEnd: atPeriodEnd !== false,
|
|
1049
1292
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1050
1293
|
},
|
|
1051
1294
|
where: [{
|
|
@@ -1061,15 +1304,15 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
|
|
|
1061
1304
|
let nextPaymentDate;
|
|
1062
1305
|
try {
|
|
1063
1306
|
const fetchRes = unwrapSdkResult(await paystack.subscriptionFetch(subscriptionCode));
|
|
1064
|
-
const data = fetchRes
|
|
1307
|
+
const data = fetchRes?.data ?? fetchRes;
|
|
1065
1308
|
if (emailToken === void 0 || emailToken === null || emailToken === "") emailToken = data?.email_token;
|
|
1066
1309
|
nextPaymentDate = data?.next_payment_date;
|
|
1067
1310
|
} catch {}
|
|
1068
1311
|
if (emailToken === void 0 || emailToken === null || emailToken === "") try {
|
|
1069
1312
|
const linkRes = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
|
|
1070
|
-
const data = linkRes
|
|
1071
|
-
const link = typeof data === "string" ? data : data
|
|
1072
|
-
if (
|
|
1313
|
+
const data = linkRes?.data ?? linkRes;
|
|
1314
|
+
const link = typeof data === "string" ? data : data.link;
|
|
1315
|
+
if (typeof link === "string" && link !== "") emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
|
|
1073
1316
|
} catch {}
|
|
1074
1317
|
if (emailToken === void 0 || emailToken === null || emailToken === "") throw new Error("Could not retrieve email_token for subscription disable.");
|
|
1075
1318
|
await paystack.subscriptionDisable({
|
|
@@ -1084,11 +1327,11 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
|
|
|
1084
1327
|
value: subscriptionCode
|
|
1085
1328
|
}]
|
|
1086
1329
|
});
|
|
1087
|
-
if (sub) await ctx.context.adapter.update({
|
|
1330
|
+
if (sub !== void 0 && sub !== null) await ctx.context.adapter.update({
|
|
1088
1331
|
model: "subscription",
|
|
1089
1332
|
update: {
|
|
1090
|
-
status: "active",
|
|
1091
|
-
cancelAtPeriodEnd:
|
|
1333
|
+
status: atPeriodEnd === false ? "canceled" : "active",
|
|
1334
|
+
cancelAtPeriodEnd: atPeriodEnd !== false,
|
|
1092
1335
|
periodEnd,
|
|
1093
1336
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1094
1337
|
},
|
|
@@ -1103,7 +1346,7 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
|
|
|
1103
1346
|
ctx.context.logger.error("Failed to disable subscription", error);
|
|
1104
1347
|
throw new APIError("BAD_REQUEST", {
|
|
1105
1348
|
code: "FAILED_TO_DISABLE_SUBSCRIPTION",
|
|
1106
|
-
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_DISABLE_SUBSCRIPTION
|
|
1349
|
+
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_DISABLE_SUBSCRIPTION.message
|
|
1107
1350
|
});
|
|
1108
1351
|
}
|
|
1109
1352
|
});
|
|
@@ -1124,13 +1367,13 @@ const enablePaystackSubscription = (options, path = "/paystack/enable-subscripti
|
|
|
1124
1367
|
let emailToken = ctx.body.emailToken;
|
|
1125
1368
|
if (emailToken === void 0 || emailToken === null || emailToken === "") try {
|
|
1126
1369
|
const fetchRes = unwrapSdkResult(await paystack.subscriptionFetch(subscriptionCode));
|
|
1127
|
-
emailToken = (fetchRes
|
|
1370
|
+
emailToken = (fetchRes?.data ?? fetchRes)?.email_token;
|
|
1128
1371
|
} catch {}
|
|
1129
1372
|
if (emailToken === void 0 || emailToken === null || emailToken === "") try {
|
|
1130
1373
|
const linkRes = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
|
|
1131
|
-
const data = linkRes
|
|
1132
|
-
const link = typeof data === "string" ? data : data
|
|
1133
|
-
if (
|
|
1374
|
+
const data = linkRes?.data ?? linkRes;
|
|
1375
|
+
const link = typeof data === "string" ? data : data.link;
|
|
1376
|
+
if (typeof link === "string" && link !== "") emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
|
|
1134
1377
|
} catch {}
|
|
1135
1378
|
if (emailToken === void 0 || emailToken === null || emailToken === "") throw new APIError("BAD_REQUEST", { message: "Could not retrieve email_token for subscription enable." });
|
|
1136
1379
|
await paystack.subscriptionEnable({
|
|
@@ -1153,7 +1396,7 @@ const enablePaystackSubscription = (options, path = "/paystack/enable-subscripti
|
|
|
1153
1396
|
ctx.context.logger.error("Failed to enable subscription", error);
|
|
1154
1397
|
throw new APIError("BAD_REQUEST", {
|
|
1155
1398
|
code: "FAILED_TO_ENABLE_SUBSCRIPTION",
|
|
1156
|
-
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_ENABLE_SUBSCRIPTION
|
|
1399
|
+
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_ENABLE_SUBSCRIPTION.message
|
|
1157
1400
|
});
|
|
1158
1401
|
}
|
|
1159
1402
|
});
|
|
@@ -1174,9 +1417,9 @@ const getSubscriptionManageLink = (options, path = "/paystack/get-subscription-m
|
|
|
1174
1417
|
const paystack = getPaystackOps(options.paystackClient);
|
|
1175
1418
|
try {
|
|
1176
1419
|
const res = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
|
|
1177
|
-
const data = res
|
|
1178
|
-
const link = typeof data === "string" ? data : data
|
|
1179
|
-
return ctx.json({ link });
|
|
1420
|
+
const data = res?.data ?? res;
|
|
1421
|
+
const link = typeof data === "string" ? data : data.link;
|
|
1422
|
+
return ctx.json({ link: typeof link === "string" ? link : null });
|
|
1180
1423
|
} catch (error) {
|
|
1181
1424
|
ctx.context.logger.error("Failed to get subscription manage link", error);
|
|
1182
1425
|
throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to get subscription manage link" });
|
|
@@ -1195,16 +1438,14 @@ const syncProducts = (options) => {
|
|
|
1195
1438
|
disableBody: true,
|
|
1196
1439
|
use: [sessionMiddleware]
|
|
1197
1440
|
}, async (ctx) => {
|
|
1198
|
-
console.error("DEBUG: syncProducts endpoint hit!");
|
|
1199
1441
|
const paystack = getPaystackOps(options.paystackClient);
|
|
1200
1442
|
try {
|
|
1201
|
-
const
|
|
1202
|
-
const
|
|
1203
|
-
if (!Array.isArray(
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
for (const product of productsData) {
|
|
1443
|
+
const dataRaw = unwrapSdkResult(await paystack.productList());
|
|
1444
|
+
const productsDataRaw = dataRaw?.data ?? dataRaw;
|
|
1445
|
+
if (!Array.isArray(productsDataRaw)) return ctx.json({ products: [] });
|
|
1446
|
+
const productsData = productsDataRaw;
|
|
1447
|
+
for (const productRaw of productsData) {
|
|
1448
|
+
const product = productRaw;
|
|
1208
1449
|
const paystackId = String(product.id);
|
|
1209
1450
|
const existing = await ctx.context.adapter.findOne({
|
|
1210
1451
|
model: "paystackProduct",
|
|
@@ -1213,21 +1454,21 @@ const syncProducts = (options) => {
|
|
|
1213
1454
|
value: paystackId
|
|
1214
1455
|
}]
|
|
1215
1456
|
});
|
|
1216
|
-
const
|
|
1217
|
-
name: product.name,
|
|
1218
|
-
description: product.description,
|
|
1219
|
-
price: product.price,
|
|
1220
|
-
currency: product.currency,
|
|
1221
|
-
quantity: product.quantity,
|
|
1222
|
-
unlimited: product.unlimited,
|
|
1457
|
+
const productFields = {
|
|
1458
|
+
name: typeof product.name === "string" ? product.name : "",
|
|
1459
|
+
description: typeof product.description === "string" ? product.description : "",
|
|
1460
|
+
price: typeof product.price === "number" ? product.price : 0,
|
|
1461
|
+
currency: typeof product.currency === "string" ? product.currency : "",
|
|
1462
|
+
quantity: typeof product.quantity === "number" ? product.quantity : 0,
|
|
1463
|
+
unlimited: product.unlimited === true,
|
|
1223
1464
|
paystackId,
|
|
1224
|
-
slug: product.slug
|
|
1225
|
-
metadata: product.metadata ? JSON.stringify(product.metadata) : void 0,
|
|
1465
|
+
slug: typeof product.slug === "string" && product.slug !== "" ? product.slug : typeof product.name === "string" ? product.name.toLowerCase().replace(/\s+/g, "-") : "",
|
|
1466
|
+
metadata: product.metadata !== void 0 && product.metadata !== null ? JSON.stringify(product.metadata) : void 0,
|
|
1226
1467
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1227
1468
|
};
|
|
1228
|
-
if (existing) await ctx.context.adapter.update({
|
|
1469
|
+
if (existing !== null && existing !== void 0) await ctx.context.adapter.update({
|
|
1229
1470
|
model: "paystackProduct",
|
|
1230
|
-
update:
|
|
1471
|
+
update: productFields,
|
|
1231
1472
|
where: [{
|
|
1232
1473
|
field: "id",
|
|
1233
1474
|
value: existing.id
|
|
@@ -1236,7 +1477,7 @@ const syncProducts = (options) => {
|
|
|
1236
1477
|
else await ctx.context.adapter.create({
|
|
1237
1478
|
model: "paystackProduct",
|
|
1238
1479
|
data: {
|
|
1239
|
-
...
|
|
1480
|
+
...productFields,
|
|
1240
1481
|
createdAt: /* @__PURE__ */ new Date()
|
|
1241
1482
|
}
|
|
1242
1483
|
});
|
|
@@ -1270,7 +1511,7 @@ const syncPlans = (options) => {
|
|
|
1270
1511
|
const paystack = getPaystackOps(options.paystackClient);
|
|
1271
1512
|
try {
|
|
1272
1513
|
const res = unwrapSdkResult(await paystack.planList());
|
|
1273
|
-
const plansData = res
|
|
1514
|
+
const plansData = res?.data ?? res;
|
|
1274
1515
|
if (!Array.isArray(plansData)) return ctx.json({
|
|
1275
1516
|
status: "success",
|
|
1276
1517
|
count: 0
|
|
@@ -1292,10 +1533,10 @@ const syncPlans = (options) => {
|
|
|
1292
1533
|
interval: plan.interval,
|
|
1293
1534
|
planCode: plan.plan_code,
|
|
1294
1535
|
paystackId,
|
|
1295
|
-
metadata: plan.metadata ? JSON.stringify(plan.metadata) : void 0,
|
|
1536
|
+
metadata: plan.metadata !== void 0 && plan.metadata !== null ? JSON.stringify(plan.metadata) : void 0,
|
|
1296
1537
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1297
1538
|
};
|
|
1298
|
-
if (existing) await ctx.context.adapter.update({
|
|
1539
|
+
if (existing !== void 0 && existing !== null) await ctx.context.adapter.update({
|
|
1299
1540
|
model: "paystackPlan",
|
|
1300
1541
|
update: planData,
|
|
1301
1542
|
where: [{
|
|
@@ -1365,10 +1606,10 @@ const chargeRecurringSubscription = (options) => {
|
|
|
1365
1606
|
value: subscriptionId
|
|
1366
1607
|
}]
|
|
1367
1608
|
});
|
|
1368
|
-
if (subscription ===
|
|
1609
|
+
if (subscription === void 0 || subscription === null) throw new APIError("NOT_FOUND", { message: "Subscription not found" });
|
|
1369
1610
|
if (subscription.paystackAuthorizationCode === void 0 || subscription.paystackAuthorizationCode === null || subscription.paystackAuthorizationCode === "") throw new APIError("BAD_REQUEST", { message: "No authorization code found for this subscription" });
|
|
1370
1611
|
const plan = (await getPlans(options.subscription)).find((p) => p.name.toLowerCase() === subscription.plan.toLowerCase());
|
|
1371
|
-
if (plan
|
|
1612
|
+
if (!plan) throw new APIError("NOT_FOUND", { message: "Plan not found" });
|
|
1372
1613
|
const amount = bodyAmount ?? plan.amount;
|
|
1373
1614
|
if (amount === void 0 || amount === null) throw new APIError("BAD_REQUEST", { message: "Plan amount is not defined" });
|
|
1374
1615
|
let email;
|
|
@@ -1407,7 +1648,7 @@ const chargeRecurringSubscription = (options) => {
|
|
|
1407
1648
|
message: `Amount ${amount} is less than the minimum required for ${finalCurrency}.`,
|
|
1408
1649
|
status: 400
|
|
1409
1650
|
});
|
|
1410
|
-
const
|
|
1651
|
+
const dataRaw = unwrapSdkResult(await getPaystackOps(options.paystackClient).transactionChargeAuthorization({
|
|
1411
1652
|
email,
|
|
1412
1653
|
amount,
|
|
1413
1654
|
authorization_code: subscription.paystackAuthorizationCode,
|
|
@@ -1418,8 +1659,8 @@ const chargeRecurringSubscription = (options) => {
|
|
|
1418
1659
|
plan: plan.name
|
|
1419
1660
|
}
|
|
1420
1661
|
}));
|
|
1421
|
-
const chargeData =
|
|
1422
|
-
if (chargeData?.status === "success") {
|
|
1662
|
+
const chargeData = dataRaw?.data ?? dataRaw;
|
|
1663
|
+
if (chargeData?.status === "success" || dataRaw?.status === "success") {
|
|
1423
1664
|
const now = /* @__PURE__ */ new Date();
|
|
1424
1665
|
const nextPeriodEnd = getNextPeriodEnd(now, plan.interval ?? "monthly");
|
|
1425
1666
|
await ctx.context.adapter.update({
|
|
@@ -1428,7 +1669,7 @@ const chargeRecurringSubscription = (options) => {
|
|
|
1428
1669
|
periodStart: now,
|
|
1429
1670
|
periodEnd: nextPeriodEnd,
|
|
1430
1671
|
updatedAt: now,
|
|
1431
|
-
paystackTransactionReference: chargeData
|
|
1672
|
+
paystackTransactionReference: chargeData?.reference ?? dataRaw?.reference
|
|
1432
1673
|
},
|
|
1433
1674
|
where: [{
|
|
1434
1675
|
field: "id",
|
|
@@ -1446,7 +1687,6 @@ const chargeRecurringSubscription = (options) => {
|
|
|
1446
1687
|
}, { status: 400 });
|
|
1447
1688
|
});
|
|
1448
1689
|
};
|
|
1449
|
-
|
|
1450
1690
|
//#endregion
|
|
1451
1691
|
//#region src/schema.ts
|
|
1452
1692
|
const transactions = { paystackTransaction: { fields: {
|
|
@@ -1568,6 +1808,10 @@ const subscriptions = { subscription: { fields: {
|
|
|
1568
1808
|
seats: {
|
|
1569
1809
|
type: "number",
|
|
1570
1810
|
required: false
|
|
1811
|
+
},
|
|
1812
|
+
pendingPlan: {
|
|
1813
|
+
type: "string",
|
|
1814
|
+
required: false
|
|
1571
1815
|
}
|
|
1572
1816
|
} } };
|
|
1573
1817
|
const user = { user: { fields: { paystackCustomerCode: {
|
|
@@ -1705,45 +1949,9 @@ const getSchema = (options) => {
|
|
|
1705
1949
|
}
|
|
1706
1950
|
return mergeSchema(baseSchema, options.schema);
|
|
1707
1951
|
};
|
|
1708
|
-
|
|
1709
|
-
//#endregion
|
|
1710
|
-
//#region src/limits.ts
|
|
1711
|
-
const getOrganizationSubscription = async (ctx, organizationId) => {
|
|
1712
|
-
return await ctx.context.adapter.findOne({
|
|
1713
|
-
model: "subscription",
|
|
1714
|
-
where: [{
|
|
1715
|
-
field: "referenceId",
|
|
1716
|
-
value: organizationId
|
|
1717
|
-
}]
|
|
1718
|
-
});
|
|
1719
|
-
};
|
|
1720
|
-
const checkSeatLimit = async (ctx, organizationId, seatsToAdd = 1) => {
|
|
1721
|
-
const subscription = await getOrganizationSubscription(ctx, organizationId);
|
|
1722
|
-
if (subscription?.seats === void 0 || subscription.seats === null) return true;
|
|
1723
|
-
const members = await ctx.context.adapter.findMany({
|
|
1724
|
-
model: "member",
|
|
1725
|
-
where: [{
|
|
1726
|
-
field: "organizationId",
|
|
1727
|
-
value: organizationId
|
|
1728
|
-
}]
|
|
1729
|
-
});
|
|
1730
|
-
if (members.length + seatsToAdd > subscription.seats) throw new APIError("FORBIDDEN", { message: `Organization member limit reached. Used: ${members.length}, Max: ${subscription.seats}` });
|
|
1731
|
-
return true;
|
|
1732
|
-
};
|
|
1733
|
-
const checkTeamLimit = async (ctx, organizationId, maxTeams) => {
|
|
1734
|
-
if ((await ctx.context.adapter.findMany({
|
|
1735
|
-
model: "team",
|
|
1736
|
-
where: [{
|
|
1737
|
-
field: "organizationId",
|
|
1738
|
-
value: organizationId
|
|
1739
|
-
}]
|
|
1740
|
-
})).length >= maxTeams) throw new APIError("FORBIDDEN", { message: `Organization team limit reached. Max teams: ${maxTeams}` });
|
|
1741
|
-
return true;
|
|
1742
|
-
};
|
|
1743
|
-
|
|
1744
1952
|
//#endregion
|
|
1745
1953
|
//#region src/index.ts
|
|
1746
|
-
const INTERNAL_ERROR_CODES = defineErrorCodes({ ...PAYSTACK_ERROR_CODES });
|
|
1954
|
+
const INTERNAL_ERROR_CODES = defineErrorCodes({ ...Object.fromEntries(Object.entries(PAYSTACK_ERROR_CODES).map(([key, value]) => [key, typeof value === "string" ? value : value.message])) });
|
|
1747
1955
|
const paystack = (options) => {
|
|
1748
1956
|
const routeOptions = options;
|
|
1749
1957
|
return {
|
|
@@ -1774,14 +1982,14 @@ const paystack = (options) => {
|
|
|
1774
1982
|
return { options: {
|
|
1775
1983
|
databaseHooks: {
|
|
1776
1984
|
user: { create: { async after(user, hookCtx) {
|
|
1777
|
-
if (hookCtx
|
|
1985
|
+
if (!hookCtx || options.createCustomerOnSignUp !== true || user.email === null || user.email === void 0 || user.email === "") return;
|
|
1778
1986
|
const sdkRes = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate({
|
|
1779
1987
|
email: user.email,
|
|
1780
1988
|
first_name: user.name ?? void 0,
|
|
1781
1989
|
metadata: { userId: user.id }
|
|
1782
1990
|
}));
|
|
1783
1991
|
const customerCode = sdkRes?.customer_code ?? (sdkRes?.data)?.customer_code;
|
|
1784
|
-
if (customerCode ===
|
|
1992
|
+
if (customerCode === "" || customerCode === null || customerCode === void 0) return;
|
|
1785
1993
|
await ctx.adapter.update({
|
|
1786
1994
|
model: "user",
|
|
1787
1995
|
where: [{
|
|
@@ -1806,7 +2014,7 @@ const paystack = (options) => {
|
|
|
1806
2014
|
value: "owner"
|
|
1807
2015
|
}]
|
|
1808
2016
|
});
|
|
1809
|
-
if (ownerMember
|
|
2017
|
+
if (ownerMember) targetEmail = (await ctx.adapter.findOne({
|
|
1810
2018
|
model: "user",
|
|
1811
2019
|
where: [{
|
|
1812
2020
|
field: "id",
|
|
@@ -1822,7 +2030,7 @@ const paystack = (options) => {
|
|
|
1822
2030
|
}, extraCreateParams);
|
|
1823
2031
|
const sdkRes = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate(params));
|
|
1824
2032
|
const customerCode = sdkRes?.customer_code ?? (sdkRes?.data)?.customer_code;
|
|
1825
|
-
if (customerCode === void 0 ||
|
|
2033
|
+
if (customerCode === "" || customerCode === null || customerCode === void 0 || sdkRes === null || sdkRes === void 0) return;
|
|
1826
2034
|
await ctx.internalAdapter.updateOrganization(org.id, { paystackCustomerCode: customerCode });
|
|
1827
2035
|
await options.organization?.onCustomerCreate?.({
|
|
1828
2036
|
paystackCustomer: sdkRes,
|
|
@@ -1836,17 +2044,37 @@ const paystack = (options) => {
|
|
|
1836
2044
|
}
|
|
1837
2045
|
} } } : void 0
|
|
1838
2046
|
},
|
|
1839
|
-
member: {
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2047
|
+
member: {
|
|
2048
|
+
create: {
|
|
2049
|
+
before: async (member, ctx) => {
|
|
2050
|
+
if (options.subscription?.enabled === true && member.organizationId && ctx !== null && ctx !== void 0) await checkSeatLimit(ctx, member.organizationId);
|
|
2051
|
+
},
|
|
2052
|
+
after: async (member, ctx) => {
|
|
2053
|
+
if (options.subscription?.enabled === true && typeof member?.organizationId === "string" && ctx) await syncSubscriptionSeats(ctx, member.organizationId, routeOptions);
|
|
2054
|
+
}
|
|
2055
|
+
},
|
|
2056
|
+
delete: { after: async (member, ctx) => {
|
|
2057
|
+
if (options.subscription?.enabled === true && typeof member?.organizationId === "string" && ctx) await syncSubscriptionSeats(ctx, member.organizationId, routeOptions);
|
|
2058
|
+
} }
|
|
2059
|
+
},
|
|
2060
|
+
invitation: {
|
|
2061
|
+
create: {
|
|
2062
|
+
before: async (invitation, ctx) => {
|
|
2063
|
+
if (options.subscription?.enabled === true && invitation.organizationId && ctx !== null && ctx !== void 0) await checkSeatLimit(ctx, invitation.organizationId);
|
|
2064
|
+
},
|
|
2065
|
+
after: async (invitation, ctx) => {
|
|
2066
|
+
if (options.subscription?.enabled === true && typeof invitation?.organizationId === "string" && ctx) await syncSubscriptionSeats(ctx, invitation.organizationId, routeOptions);
|
|
2067
|
+
}
|
|
2068
|
+
},
|
|
2069
|
+
delete: { after: async (invitation, ctx) => {
|
|
2070
|
+
if (options.subscription?.enabled === true && typeof invitation?.organizationId === "string" && ctx) await syncSubscriptionSeats(ctx, invitation.organizationId, routeOptions);
|
|
2071
|
+
} }
|
|
2072
|
+
},
|
|
1845
2073
|
team: { create: { before: async (team, ctx) => {
|
|
1846
|
-
if (options.subscription?.enabled === true && team.organizationId && ctx
|
|
2074
|
+
if (options.subscription?.enabled === true && team.organizationId && ctx) {
|
|
1847
2075
|
const subscription = await getOrganizationSubscription(ctx, team.organizationId);
|
|
1848
|
-
if (subscription
|
|
1849
|
-
const maxTeams = ((await getPlanByName(
|
|
2076
|
+
if (subscription) {
|
|
2077
|
+
const maxTeams = ((await getPlanByName(routeOptions, subscription.plan))?.limits)?.teams;
|
|
1850
2078
|
if (typeof maxTeams === "number") await checkTeamLimit(ctx, team.organizationId, maxTeams);
|
|
1851
2079
|
}
|
|
1852
2080
|
}
|
|
@@ -1856,7 +2084,7 @@ const paystack = (options) => {
|
|
|
1856
2084
|
$ERROR_CODES: INTERNAL_ERROR_CODES
|
|
1857
2085
|
};
|
|
1858
2086
|
};
|
|
1859
|
-
|
|
1860
2087
|
//#endregion
|
|
1861
2088
|
export { paystack };
|
|
2089
|
+
|
|
1862
2090
|
//# sourceMappingURL=index.mjs.map
|