@alexasomba/better-auth-paystack 1.2.1 → 2.1.0

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/index.mjs CHANGED
@@ -3,118 +3,45 @@ 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
- import * as z from "zod/v4";
6
+ import { z } from "zod";
7
+ import { PaystackResponse } from "@alexasomba/paystack-node";
7
8
  import { mergeSchema } from "better-auth/db";
8
9
  //#region src/paystack-sdk.ts
9
- function isOpenApiFetchResponse(value) {
10
- return value !== null && value !== void 0 && typeof value === "object" && ("data" in value || "error" in value || "response" in value);
10
+ /**
11
+ * Interface for checking if a result is a PaystackResponse from the SDK v1.9.1+
12
+ */
13
+ function IsPaystackResponse(value) {
14
+ return value instanceof PaystackResponse;
11
15
  }
16
+ /**
17
+ * Unwraps a Paystack SDK result, extracting the data or throwing an APIError if the request failed.
18
+ * Leverages the native .unwrap() method in SDK v1.9.1+ if available.
19
+ */
12
20
  function unwrapSdkResult(result) {
13
- if (isOpenApiFetchResponse(result)) {
14
- if (result.error !== void 0 && result.error !== null) throw new Error(typeof result.error === "string" ? result.error : JSON.stringify(result.error));
15
- return result.data ?? result;
21
+ if (IsPaystackResponse(result)) try {
22
+ return result.unwrap();
23
+ } catch (e) {
24
+ throw new APIError("BAD_REQUEST", { message: e?.message ?? "Paystack API error" });
16
25
  }
17
- if (result !== null && result !== void 0 && typeof result === "object" && "data" in result) {
18
- const data = result.data;
19
- if (data !== null && typeof data === "object" && "data" in data) return data.data;
20
- return data;
26
+ let current = result;
27
+ while (current !== null && current !== void 0 && typeof current === "object") {
28
+ const body = current;
29
+ if (body.status === false) throw new APIError("BAD_REQUEST", { message: body.message ?? "Paystack API error" });
30
+ if ("authorization_url" in body || "reference" in body || "customer_code" in body) break;
31
+ if ("data" in body && body.data !== void 0 && body.data !== null && typeof body.data === "object") {
32
+ current = body.data;
33
+ continue;
34
+ }
35
+ break;
21
36
  }
22
- return result;
37
+ return current;
23
38
  }
24
- function getPaystackOps(paystackClient) {
25
- return {
26
- customerCreate: (params) => {
27
- if (paystackClient?.customer_create !== void 0) return paystackClient.customer_create({ body: params });
28
- return paystackClient?.customer?.create?.(params);
29
- },
30
- customerUpdate: (code, params) => {
31
- if (paystackClient?.customer_update !== void 0) return paystackClient.customer_update({
32
- params: { path: { code } },
33
- body: params
34
- });
35
- return paystackClient?.customer?.update?.(code, params);
36
- },
37
- transactionInitialize: (body) => {
38
- if (paystackClient?.transaction_initialize !== void 0) return paystackClient.transaction_initialize({ body });
39
- return paystackClient?.transaction?.initialize?.(body);
40
- },
41
- transactionVerify: (reference) => {
42
- if (paystackClient?.transaction_verify !== void 0) return paystackClient.transaction_verify({ params: { path: { reference } } });
43
- return paystackClient?.transaction?.verify?.(reference);
44
- },
45
- subscriptionCreate: (body) => {
46
- if (paystackClient?.subscription_create !== void 0) return paystackClient.subscription_create({ body });
47
- return paystackClient?.subscription?.create?.(body);
48
- },
49
- subscriptionDisable: (body) => {
50
- if (paystackClient?.subscription_disable !== void 0) return paystackClient.subscription_disable({ body });
51
- return paystackClient?.subscription?.disable?.(body);
52
- },
53
- subscriptionEnable: (body) => {
54
- if (paystackClient?.subscription_enable !== void 0) return paystackClient.subscription_enable({ body });
55
- return paystackClient?.subscription?.enable?.(body);
56
- },
57
- subscriptionFetch: async (idOrCode) => {
58
- if (paystackClient?.subscription_fetch !== void 0) try {
59
- return await paystackClient.subscription_fetch({ params: { path: { code: idOrCode } } });
60
- } catch {
61
- const compatFetch = paystackClient.subscription_fetch;
62
- return compatFetch({ params: { path: { id_or_code: idOrCode } } });
63
- }
64
- return paystackClient?.subscription?.fetch?.(idOrCode);
65
- },
66
- subscriptionManageLink: (code) => {
67
- if (paystackClient?.subscription_manageLink !== void 0) return paystackClient.subscription_manageLink({ params: { path: { code } } });
68
- if (paystackClient?.subscription_manage_link !== void 0) return paystackClient.subscription_manage_link({ params: { path: { code } } });
69
- return paystackClient?.subscription?.manage?.link?.(code);
70
- },
71
- subscriptionManageEmail: (code, email) => {
72
- if (paystackClient?.subscription_manageEmail !== void 0) return paystackClient.subscription_manageEmail({ params: { path: { code } } });
73
- return paystackClient?.subscription?.manage?.email?.(code, email);
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
- },
86
- transactionChargeAuthorization: (body) => {
87
- if (paystackClient?.transaction_chargeAuthorization !== void 0) return paystackClient.transaction_chargeAuthorization({ body });
88
- return paystackClient?.transaction?.chargeAuthorization?.(body);
89
- },
90
- productList: () => {
91
- if (paystackClient?.product_list !== void 0) return paystackClient.product_list();
92
- return paystackClient?.product?.list?.();
93
- },
94
- productFetch: (idOrCode) => {
95
- if (paystackClient?.product_fetch !== void 0) return paystackClient.product_fetch({ params: { path: { id_or_code: idOrCode } } });
96
- return paystackClient?.product?.fetch?.(idOrCode);
97
- },
98
- productCreate: (params) => {
99
- if (paystackClient?.product_create !== void 0) return paystackClient.product_create({ body: params });
100
- return paystackClient?.product?.create?.(params);
101
- },
102
- productUpdate: (idOrCode, params) => {
103
- if (paystackClient?.product_update !== void 0) return paystackClient.product_update({
104
- params: { path: { id_or_code: idOrCode } },
105
- body: params
106
- });
107
- return paystackClient?.product?.update?.(idOrCode, params);
108
- },
109
- productDelete: (idOrCode) => {
110
- if (paystackClient?.product_delete !== void 0) return paystackClient.product_delete({ params: { path: { id_or_code: idOrCode } } });
111
- return paystackClient?.product?.delete?.(idOrCode);
112
- },
113
- planList: () => {
114
- if (paystackClient?.plan_list !== void 0) return paystackClient.plan_list();
115
- return paystackClient?.plan?.list?.();
116
- }
117
- };
39
+ /**
40
+ * Returns the operations object from a Paystack client.
41
+ * For v1.9.1+, the client itself uses the grouped structure.
42
+ */
43
+ function getPaystackOps(client) {
44
+ return client;
118
45
  }
119
46
  //#endregion
120
47
  //#region src/utils.ts
@@ -123,7 +50,12 @@ async function getPlans(subscriptionOptions) {
123
50
  throw new Error("Subscriptions are not enabled in the Paystack options.");
124
51
  }
125
52
  async function getPlanByName(options, name) {
126
- if (options.subscription?.enabled === true) return (await getPlans(options.subscription)).find((plan) => plan.name.toLowerCase() === name.toLowerCase()) ?? null;
53
+ if (typeof name !== "string" || name.trim() === "") return null;
54
+ if (options.subscription?.enabled === true) {
55
+ const plans = await getPlans(options.subscription);
56
+ const normalizedName = name.toLowerCase();
57
+ return plans.find((plan) => typeof plan.name === "string" && plan.name.toLowerCase() === normalizedName) ?? null;
58
+ }
127
59
  return null;
128
60
  }
129
61
  async function getProducts(productOptions) {
@@ -131,7 +63,7 @@ async function getProducts(productOptions) {
131
63
  return [];
132
64
  }
133
65
  async function getProductByName(options, name) {
134
- return await getProducts(options.products).then((products) => products?.find((product) => product.name.toLowerCase() === name.toLowerCase()) ?? null);
66
+ return await getProducts(options.products).then((products) => products !== void 0 && products !== null ? products.find((product) => product.name.toLowerCase() === name.toLowerCase()) ?? null : null);
135
67
  }
136
68
  function getNextPeriodEnd(startDate, interval) {
137
69
  const date = new Date(startDate);
@@ -189,7 +121,7 @@ async function syncProductQuantityFromPaystack(ctx, productName, paystackClient)
189
121
  }]
190
122
  });
191
123
  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({
124
+ if (localProduct?.id !== void 0 && localProduct.unlimited !== true && typeof localProduct.quantity === "number" && localProduct.quantity > 0) await ctx.context.adapter.update({
193
125
  model: "paystackProduct",
194
126
  update: {
195
127
  quantity: localProduct.quantity - 1,
@@ -203,8 +135,8 @@ async function syncProductQuantityFromPaystack(ctx, productName, paystackClient)
203
135
  return;
204
136
  }
205
137
  try {
206
- const remoteQuantity = unwrapSdkResult(await getPaystackOps(paystackClient).productFetch(localProduct.paystackId))?.quantity;
207
- if (remoteQuantity !== void 0) await ctx.context.adapter.update({
138
+ const remoteQuantity = unwrapSdkResult(await paystackClient.product?.fetch(localProduct.paystackId))?.quantity;
139
+ if (remoteQuantity !== void 0 && localProduct.id !== void 0) await ctx.context.adapter.update({
208
140
  model: "paystackProduct",
209
141
  update: {
210
142
  quantity: remoteQuantity,
@@ -216,7 +148,7 @@ async function syncProductQuantityFromPaystack(ctx, productName, paystackClient)
216
148
  }]
217
149
  });
218
150
  } catch {
219
- if (localProduct !== null && localProduct.unlimited !== true && typeof localProduct.quantity === "number" && localProduct.quantity > 0) await ctx.context.adapter.update({
151
+ if (localProduct?.id !== void 0 && localProduct.unlimited !== true && typeof localProduct.quantity === "number" && localProduct.quantity > 0) await ctx.context.adapter.update({
220
152
  model: "paystackProduct",
221
153
  update: {
222
154
  quantity: localProduct.quantity - 1,
@@ -240,9 +172,10 @@ async function syncSubscriptionSeats(ctx, organizationId, options) {
240
172
  }]
241
173
  });
242
174
  if (subscription?.paystackSubscriptionCode === void 0 || subscription.paystackSubscriptionCode === null || subscription.paystackSubscriptionCode === "") return;
175
+ if (subscription === null || subscription === void 0) return;
243
176
  const plan = await getPlanByName(options, subscription.plan);
244
- if (plan === null) return;
245
- if (plan.seatAmount === void 0 && plan.seatPlanCode === void 0) return;
177
+ if (plan === null || plan === void 0) return;
178
+ if (plan.seatAmount === void 0) return;
246
179
  const quantity = (await adapter.findMany({
247
180
  model: "member",
248
181
  where: [{
@@ -252,12 +185,10 @@ async function syncSubscriptionSeats(ctx, organizationId, options) {
252
185
  })).length;
253
186
  let totalAmount = plan.amount ?? 0;
254
187
  if (plan.seatAmount !== void 0 && plan.seatAmount !== null && typeof plan.seatAmount === "number") totalAmount += quantity * plan.seatAmount;
255
- const ops = getPaystackOps(options.paystackClient);
256
188
  try {
257
- await ops.subscriptionUpdate({
258
- code: subscription.paystackSubscriptionCode,
259
- amount: totalAmount
260
- });
189
+ const client = options.paystackClient;
190
+ if (client === void 0 || client === null) return;
191
+ unwrapSdkResult(await client.subscription?.update(subscription.paystackSubscriptionCode, { body: { amount: totalAmount } }));
261
192
  await adapter.update({
262
193
  model: "subscription",
263
194
  where: [{
@@ -284,7 +215,7 @@ const referenceMiddleware = (options, action) => createAuthMiddleware(async (ctx
284
215
  const referenceId = body.referenceId ?? query.referenceId ?? session.user.id;
285
216
  const subscriptionOptions = options.subscription;
286
217
  if (referenceId === session.user.id) return { referenceId };
287
- if (subscriptionOptions?.enabled === true && "authorizeReference" in subscriptionOptions && subscriptionOptions.authorizeReference) {
218
+ if (subscriptionOptions?.enabled === true && "authorizeReference" in subscriptionOptions && typeof subscriptionOptions.authorizeReference === "function") {
288
219
  if (await subscriptionOptions.authorizeReference({
289
220
  user: session.user,
290
221
  session: session.session,
@@ -325,7 +256,7 @@ const getOrganizationSubscription = async (ctx, organizationId) => {
325
256
  };
326
257
  const checkSeatLimit = async (ctx, organizationId, seatsToAdd = 1) => {
327
258
  const subscription = await getOrganizationSubscription(ctx, organizationId);
328
- if (subscription?.seats === void 0 || subscription.seats === null) return true;
259
+ if (subscription?.seats === null) return true;
329
260
  const members = await ctx.context.adapter.findMany({
330
261
  model: "member",
331
262
  where: [{
@@ -333,6 +264,7 @@ const checkSeatLimit = async (ctx, organizationId, seatsToAdd = 1) => {
333
264
  value: organizationId
334
265
  }]
335
266
  });
267
+ if (!subscription) return true;
336
268
  if (members.length + seatsToAdd > subscription.seats) throw new APIError("FORBIDDEN", { message: `Organization member limit reached. Used: ${members.length}, Max: ${subscription.seats}` });
337
269
  return true;
338
270
  };
@@ -375,8 +307,8 @@ async function hmacSha512Hex(secret, message) {
375
307
  const { createHmac } = await import("node:crypto");
376
308
  return createHmac("sha512", secret).update(message).digest("hex");
377
309
  }
378
- const paystackWebhook = (options) => {
379
- return createAuthEndpoint("/paystack/webhook", {
310
+ const paystackWebhook = (options, path = "/webhook") => {
311
+ return createAuthEndpoint(path, {
380
312
  method: "POST",
381
313
  metadata: {
382
314
  ...HIDE_METADATA,
@@ -388,12 +320,25 @@ const paystackWebhook = (options) => {
388
320
  const request = ctx.requestClone ?? ctx.request;
389
321
  if (request === void 0 || request === null) throw new APIError("BAD_REQUEST", { message: "Request object is missing from context" });
390
322
  const payload = await request.text();
391
- const signature = (ctx.headers ?? ctx.request?.headers)?.get("x-paystack-signature");
323
+ const headers = ctx.headers ?? ctx.request?.headers;
324
+ const signature = headers?.get("x-paystack-signature");
325
+ if (options.webhook?.verifyIP === true) {
326
+ const trustedIPs = options.webhook.trustedIPs ?? [
327
+ "52.31.139.75",
328
+ "52.49.173.169",
329
+ "52.214.14.220"
330
+ ];
331
+ const clientIP = headers?.get("x-forwarded-for")?.split(",")[0]?.trim() ?? headers?.get("x-real-ip") ?? ctx.request.ip;
332
+ if (clientIP !== void 0 && clientIP !== null && trustedIPs.includes(clientIP) === false) throw new APIError("UNAUTHORIZED", {
333
+ message: `Forbidden IP: ${clientIP}`,
334
+ status: 401
335
+ });
336
+ }
392
337
  if (signature === void 0 || signature === null || signature === "") throw new APIError("UNAUTHORIZED", {
393
338
  message: "Missing x-paystack-signature header",
394
339
  status: 401
395
340
  });
396
- if (await hmacSha512Hex(options.paystackWebhookSecret, payload) !== signature) throw new APIError("UNAUTHORIZED", {
341
+ if (await hmacSha512Hex(options.webhook?.secret ?? options.secretKey, payload) !== signature) throw new APIError("UNAUTHORIZED", {
397
342
  message: "Invalid Paystack webhook signature",
398
343
  status: 401
399
344
  });
@@ -402,7 +347,8 @@ const paystackWebhook = (options) => {
402
347
  const data = event.data;
403
348
  if (eventName === "charge.success") {
404
349
  const reference = data?.reference;
405
- const paystackId = data?.id !== void 0 && data?.id !== null ? String(data.id) : void 0;
350
+ const paystackIdRaw = data?.id;
351
+ const paystackId = paystackIdRaw !== void 0 && paystackIdRaw !== null ? String(paystackIdRaw) : void 0;
406
352
  if (reference !== void 0 && reference !== null && reference !== "") {
407
353
  try {
408
354
  await ctx.context.adapter.update({
@@ -428,7 +374,9 @@ const paystackWebhook = (options) => {
428
374
  value: reference
429
375
  }]
430
376
  });
431
- if (transaction?.product !== void 0 && transaction.product !== null && transaction.product !== "") await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
377
+ if (transaction !== void 0 && transaction !== null && transaction.product !== void 0 && transaction.product !== null && transaction.product !== "") {
378
+ if (options.paystackClient !== void 0 && options.paystackClient !== null) await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
379
+ }
432
380
  } catch (e) {
433
381
  ctx.context.logger.warn("Failed to sync product quantity", e);
434
382
  }
@@ -454,19 +402,20 @@ const paystackWebhook = (options) => {
454
402
  }
455
403
  if (options.subscription?.enabled === true) try {
456
404
  if (eventName === "subscription.create") {
457
- const payloadData = data;
458
- const subscriptionCode = payloadData?.subscription_code ?? payloadData?.subscription?.subscription_code ?? payloadData?.code;
459
- const customerCode = payloadData?.customer?.customer_code ?? payloadData?.customer_code ?? payloadData?.customer?.code;
460
- const planCode = payloadData?.plan?.plan_code ?? payloadData?.plan_code ?? payloadData?.plan;
461
- let metadata = payloadData?.metadata;
405
+ const subscriptionData = data;
406
+ const subscriptionCode = subscriptionData.subscription_code ?? "";
407
+ const customerCode = subscriptionData.customer?.customer_code;
408
+ const planCode = subscriptionData.plan?.plan_code;
409
+ let metadata = subscriptionData.metadata;
462
410
  if (typeof metadata === "string") try {
463
411
  metadata = JSON.parse(metadata);
464
412
  } catch {}
465
- const referenceIdFromMetadata = typeof metadata === "object" && metadata !== null ? metadata.referenceId : void 0;
466
- let planNameFromMetadata = typeof metadata === "object" && metadata !== null ? metadata.plan : void 0;
413
+ const metadataObj = metadata !== void 0 && metadata !== null && typeof metadata === "object" ? metadata : {};
414
+ const referenceIdFromMetadata = typeof metadataObj.referenceId === "string" ? metadataObj.referenceId : void 0;
415
+ let planNameFromMetadata = typeof metadataObj.plan === "string" ? metadataObj.plan : void 0;
467
416
  if (typeof planNameFromMetadata === "string") planNameFromMetadata = planNameFromMetadata.toLowerCase();
468
417
  const plans = await getPlans(options.subscription);
469
- const planFromCode = planCode !== void 0 && planCode !== null && planCode !== "" ? plans.find((p) => p.planCode !== void 0 && p.planCode !== null && p.planCode === planCode) : void 0;
418
+ const planFromCode = planCode !== void 0 && planCode !== null && planCode !== "" ? plans.find((p) => p.planCode === planCode) : void 0;
470
419
  const planPart = planFromCode?.name ?? planNameFromMetadata;
471
420
  const planName = planPart !== void 0 && planPart !== null && planPart !== "" ? planPart.toLowerCase() : void 0;
472
421
  if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
@@ -495,7 +444,7 @@ const paystackWebhook = (options) => {
495
444
  paystackSubscriptionCode: subscriptionCode,
496
445
  status: "active",
497
446
  updatedAt: /* @__PURE__ */ new Date(),
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
447
+ periodEnd: subscriptionData.next_payment_date !== void 0 && subscriptionData.next_payment_date !== null ? new Date(subscriptionData.next_payment_date) : void 0
499
448
  },
500
449
  where: [{
501
450
  field: "id",
@@ -528,9 +477,9 @@ const paystackWebhook = (options) => {
528
477
  }
529
478
  }
530
479
  if (eventName === "subscription.disable" || eventName === "subscription.not_renew") {
531
- const payloadData = data;
532
- const subscriptionCode = payloadData?.subscription_code ?? payloadData?.subscription?.subscription_code ?? payloadData?.code;
533
- if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
480
+ const subscriptionData = data;
481
+ const subscriptionCode = subscriptionData.subscription_code ?? "";
482
+ if (subscriptionCode !== "") {
534
483
  const existing = await ctx.context.adapter.findOne({
535
484
  model: "subscription",
536
485
  where: [{
@@ -539,9 +488,9 @@ const paystackWebhook = (options) => {
539
488
  }]
540
489
  });
541
490
  let newStatus = "canceled";
542
- const nextPaymentDate = data?.next_payment_date;
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";
491
+ const nextPaymentDate = subscriptionData.next_payment_date;
492
+ const periodEnd = nextPaymentDate !== void 0 && nextPaymentDate !== null && nextPaymentDate !== "" ? new Date(nextPaymentDate) : existing?.periodEnd !== void 0 && existing.periodEnd !== null ? new Date(existing.periodEnd) : void 0;
493
+ if (periodEnd !== void 0 && periodEnd.getTime() > Date.now()) newStatus = "active";
545
494
  await ctx.context.adapter.update({
546
495
  model: "subscription",
547
496
  update: {
@@ -555,7 +504,7 @@ const paystackWebhook = (options) => {
555
504
  value: subscriptionCode
556
505
  }]
557
506
  });
558
- if (existing !== void 0 && existing !== null) await options.subscription.onSubscriptionCancel?.({
507
+ if (existing) await options.subscription.onSubscriptionCancel?.({
559
508
  event,
560
509
  subscription: {
561
510
  ...existing,
@@ -565,9 +514,9 @@ const paystackWebhook = (options) => {
565
514
  }
566
515
  }
567
516
  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 !== "") {
517
+ const subscriptionCodeRaw = (data?.subscription)?.subscription_code ?? data?.subscription_code;
518
+ const subscriptionCode = subscriptionCodeRaw !== void 0 && subscriptionCodeRaw !== null && subscriptionCodeRaw !== "" ? subscriptionCodeRaw : void 0;
519
+ if (subscriptionCode !== void 0) {
571
520
  const existingSub = await ctx.context.adapter.findOne({
572
521
  model: "subscription",
573
522
  where: [{
@@ -575,7 +524,7 @@ const paystackWebhook = (options) => {
575
524
  value: subscriptionCode
576
525
  }]
577
526
  });
578
- if (existingSub?.pendingPlan !== void 0 && existingSub.pendingPlan !== null && existingSub.pendingPlan !== "") await ctx.context.adapter.update({
527
+ if (existingSub !== void 0 && existingSub !== null && existingSub.pendingPlan !== void 0 && existingSub.pendingPlan !== null && existingSub.pendingPlan !== "") await ctx.context.adapter.update({
579
528
  model: "subscription",
580
529
  update: {
581
530
  plan: existingSub.pendingPlan,
@@ -610,7 +559,7 @@ const initializeTransactionBodySchema = z.object({
610
559
  cancelAtPeriodEnd: z.boolean().optional(),
611
560
  prorateAndCharge: z.boolean().optional()
612
561
  });
613
- const initializeTransaction = (options, path = "/paystack/initialize-transaction") => {
562
+ const initializeTransaction = (options, path = "/initialize-transaction") => {
614
563
  const subscriptionOptions = options.subscription;
615
564
  return createAuthEndpoint(path, {
616
565
  method: "POST",
@@ -626,23 +575,22 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
626
575
  if (callbackURL !== void 0 && callbackURL !== null && callbackURL !== "") {
627
576
  const checkTrusted = () => {
628
577
  try {
629
- if (callbackURL === void 0 || callbackURL === null || callbackURL === "") return false;
630
578
  if (callbackURL.startsWith("/")) return true;
631
579
  const baseUrl = ctx.context?.baseURL ?? ctx.request?.url ?? "";
632
- if (!baseUrl) return false;
580
+ if (baseUrl === "") return false;
633
581
  const baseOrigin = new URL(baseUrl).origin;
634
582
  return new URL(callbackURL).origin === baseOrigin;
635
583
  } catch {
636
584
  return false;
637
585
  }
638
586
  };
639
- if (checkTrusted() !== true) throw new APIError("FORBIDDEN", {
587
+ if (checkTrusted() === false) throw new APIError("FORBIDDEN", {
640
588
  message: "callbackURL is not a trusted origin.",
641
589
  status: 403
642
590
  });
643
591
  }
644
592
  const session = await getSessionFromCtx(ctx);
645
- if (!session) throw new APIError("UNAUTHORIZED");
593
+ if (session === void 0 || session === null) throw new APIError("UNAUTHORIZED");
646
594
  const user = session.user;
647
595
  if (subscriptionOptions?.enabled === true && subscriptionOptions.requireEmailVerification === true && user.emailVerified !== true) throw new APIError("BAD_REQUEST", {
648
596
  code: "EMAIL_VERIFICATION_REQUIRED",
@@ -653,7 +601,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
653
601
  if (planName !== void 0 && planName !== null && planName !== "") {
654
602
  if (subscriptionOptions?.enabled !== true) throw new APIError("BAD_REQUEST", { message: "Subscriptions are not enabled." });
655
603
  plan = await getPlanByName(options, planName) ?? void 0;
656
- if (plan === null || plan === void 0) {
604
+ if (plan === void 0 || plan === null) try {
657
605
  const nativePlan = await ctx.context.adapter.findOne({
658
606
  model: "paystackPlan",
659
607
  where: [{
@@ -669,15 +617,17 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
669
617
  value: planName
670
618
  }]
671
619
  }) ?? void 0;
620
+ } catch {
621
+ plan = void 0;
672
622
  }
673
- if (plan === null || plan === void 0) throw new APIError("BAD_REQUEST", {
623
+ if (plan === void 0 || plan === null) throw new APIError("BAD_REQUEST", {
674
624
  code: "SUBSCRIPTION_PLAN_NOT_FOUND",
675
625
  message: PAYSTACK_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND.message,
676
626
  status: 400
677
627
  });
678
628
  } else if (productName !== void 0 && productName !== null && productName !== "") {
679
629
  if (typeof productName === "string") {
680
- product ??= await getProductByName(options, productName) ?? void 0;
630
+ product = await getProductByName(options, productName) ?? void 0;
681
631
  product ??= await ctx.context.adapter.findOne({
682
632
  model: "paystackProduct",
683
633
  where: [{
@@ -686,19 +636,19 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
686
636
  }]
687
637
  }) ?? void 0;
688
638
  }
689
- if (product === null || product === void 0) throw new APIError("BAD_REQUEST", {
639
+ if (product === void 0 || product === null) throw new APIError("BAD_REQUEST", {
690
640
  message: `Product '${productName}' not found.`,
691
641
  status: 400
692
642
  });
693
- } else if (bodyAmount === void 0 || bodyAmount === null || bodyAmount === 0) throw new APIError("BAD_REQUEST", {
643
+ } else if (bodyAmount === void 0 || bodyAmount === null) throw new APIError("BAD_REQUEST", {
694
644
  message: "Either 'plan', 'product', or 'amount' is required to initialize a transaction.",
695
645
  status: 400
696
646
  });
697
- let amount = bodyAmount ?? product?.price;
698
- const finalCurrency = currency ?? product?.currency ?? plan?.currency ?? "NGN";
647
+ let amount = bodyAmount ?? product?.price ?? product?.amount;
648
+ const finalCurrency = currency ?? product?.currency ?? product?.currency ?? plan?.currency ?? "NGN";
699
649
  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) {
650
+ const referenceId = ctx.body.referenceId ?? referenceIdFromCtx ?? session.user.id;
651
+ if (plan !== void 0 && scheduleAtPeriodEnd === true) {
702
652
  const existingSub = await getOrganizationSubscription(ctx, referenceId);
703
653
  if (existingSub?.status === "active") {
704
654
  await ctx.context.adapter.update({
@@ -740,7 +690,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
740
690
  });
741
691
  }
742
692
  }
743
- if (plan !== null && plan !== void 0 && (plan.seatAmount !== void 0 || "seatPriceId" in plan)) {
693
+ if (plan !== void 0 && (plan.seatAmount !== void 0 || plan.seatPriceId !== void 0)) {
744
694
  const members = await ctx.context.adapter.findMany({
745
695
  model: "member",
746
696
  where: [{
@@ -757,23 +707,23 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
757
707
  let accessCode;
758
708
  let trialStart;
759
709
  let trialEnd;
760
- if (plan?.freeTrial?.days !== void 0 && plan.freeTrial.days !== null && plan.freeTrial.days > 0) {
710
+ if (plan?.freeTrial?.days !== void 0 && plan.freeTrial.days > 0) {
761
711
  if ((await ctx.context.adapter.findMany({
762
712
  model: "subscription",
763
713
  where: [{
764
714
  field: "referenceId",
765
715
  value: referenceId
766
716
  }]
767
- }))?.some((sub) => sub.trialStart !== void 0 && sub.trialStart !== null || sub.trialEnd !== void 0 && sub.trialEnd !== null || sub.status === "trialing") !== true) {
717
+ }))?.some((sub) => sub.trialStart !== void 0 && sub.trialStart !== null || sub.trialEnd !== void 0 && sub.trialEnd !== null || sub.status === "trialing") === false) {
768
718
  trialStart = /* @__PURE__ */ new Date();
769
719
  trialEnd = /* @__PURE__ */ new Date();
770
720
  trialEnd.setDate(trialEnd.getDate() + plan.freeTrial.days);
771
721
  }
772
722
  }
773
723
  try {
774
- let targetEmail = email !== void 0 && email !== null && email !== "" ? email : user.email;
724
+ let targetEmail = email ?? user.email;
775
725
  let paystackCustomerCode = user.paystackCustomerCode;
776
- if (options.organization?.enabled === true && referenceId !== void 0 && referenceId !== null && referenceId !== "" && referenceId !== user.id) {
726
+ if (options.organization?.enabled === true && referenceId !== void 0 && referenceId !== null && referenceId !== user.id) {
777
727
  const org = await ctx.context.adapter.findOne({
778
728
  model: "organization",
779
729
  where: [{
@@ -782,8 +732,10 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
782
732
  }]
783
733
  });
784
734
  if (org !== void 0 && org !== null) {
785
- if (org.paystackCustomerCode !== void 0 && org.paystackCustomerCode !== null && org.paystackCustomerCode !== "") paystackCustomerCode = org.paystackCustomerCode;
786
- if (org.email !== void 0 && org.email !== null && org.email !== "") targetEmail = org.email;
735
+ const paystackOrg = org;
736
+ if (paystackOrg.paystackCustomerCode !== void 0 && paystackOrg.paystackCustomerCode !== null && paystackOrg.paystackCustomerCode !== "") paystackCustomerCode = paystackOrg.paystackCustomerCode;
737
+ const orgWithEmail = org;
738
+ if (orgWithEmail.email !== void 0 && orgWithEmail.email !== null && orgWithEmail.email !== "") targetEmail = orgWithEmail.email;
787
739
  else {
788
740
  const ownerMember = await ctx.context.adapter.findOne({
789
741
  model: "member",
@@ -803,7 +755,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
803
755
  value: ownerMember.userId
804
756
  }]
805
757
  });
806
- if (ownerUser?.email !== void 0 && ownerUser?.email !== null && ownerUser?.email !== "") targetEmail = ownerUser.email;
758
+ if (ownerUser !== void 0 && ownerUser !== null && ownerUser.email !== void 0 && ownerUser.email !== null && ownerUser.email !== "") targetEmail = ownerUser.email;
807
759
  }
808
760
  }
809
761
  }
@@ -811,102 +763,109 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
811
763
  const metadata = JSON.stringify({
812
764
  referenceId,
813
765
  userId: user.id,
814
- plan: plan?.name.toLowerCase(),
815
- product: product?.name.toLowerCase(),
816
- isTrial: trialStart !== void 0 && trialStart !== null,
817
- trialEnd: trialEnd?.toISOString(),
766
+ plan: plan !== void 0 ? plan.name.toLowerCase() : void 0,
767
+ product: product !== void 0 ? product.name.toLowerCase() : void 0,
768
+ isTrial: trialStart !== void 0,
769
+ trialEnd: trialEnd !== void 0 ? trialEnd.toISOString() : void 0,
818
770
  ...extraMetadata
819
771
  });
820
772
  const initBody = {
821
773
  email: targetEmail,
822
- callback_url: callbackURL,
774
+ callback_url: callbackURL ?? void 0,
823
775
  metadata,
824
776
  currency: finalCurrency,
825
777
  quantity
826
778
  };
827
779
  if (paystackCustomerCode !== void 0 && paystackCustomerCode !== null && paystackCustomerCode !== "") try {
828
780
  const ops = getPaystackOps(options.paystackClient);
829
- if (initBody.email !== void 0 && initBody.email !== null && initBody.email !== "") await ops.customerUpdate(paystackCustomerCode, { email: initBody.email });
781
+ if (ops !== void 0 && ops !== null && initBody.email !== "") await ops.customer?.update(paystackCustomerCode, { body: { email: initBody.email } });
830
782
  } catch (_e) {}
831
- if (plan !== void 0 && plan !== null && prorateAndCharge === true) {
783
+ if (plan !== void 0 && prorateAndCharge === true) {
832
784
  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);
785
+ if (existingSub?.status === "active" && existingSub.paystackAuthorizationCode !== void 0 && existingSub.paystackAuthorizationCode !== null && existingSub.paystackAuthorizationCode !== "" && existingSub.paystackSubscriptionCode !== void 0 && existingSub.paystackSubscriptionCode !== null && existingSub.paystackSubscriptionCode !== "") {
786
+ if (existingSub.periodEnd !== void 0 && existingSub.periodEnd !== null && existingSub.periodStart !== void 0 && existingSub.periodStart !== null) {
787
+ const now = /* @__PURE__ */ new Date();
788
+ const periodEndLocal = new Date(existingSub.periodEnd);
789
+ const periodStartLocal = new Date(existingSub.periodStart);
790
+ const totalDays = Math.max(1, Math.ceil((periodEndLocal.getTime() - periodStartLocal.getTime()) / (1e3 * 60 * 60 * 24)));
791
+ const remainingDays = Math.max(0, Math.ceil((periodEndLocal.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24)));
792
+ let oldAmount = 0;
793
+ if (existingSub.plan !== "") {
794
+ const oldPlan = await getPlanByName(options, existingSub.plan) ?? await ctx.context.adapter.findOne({
795
+ model: "paystackPlan",
796
+ where: [{
797
+ field: "name",
798
+ value: existingSub.plan
799
+ }]
800
+ }) ?? void 0;
801
+ if (oldPlan !== void 0 && oldPlan !== null) {
802
+ const oldSeatCount = existingSub.seats;
803
+ oldAmount = (oldPlan.amount ?? 0) + oldSeatCount * (oldPlan.seatAmount ?? oldPlan.seatPriceId ?? 0);
804
+ }
851
805
  }
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
806
+ let membersCount = 1;
807
+ if (plan.seatAmount !== void 0 || plan.seatPriceId !== void 0) {
808
+ const members = await ctx.context.adapter.findMany({
809
+ model: "member",
810
+ where: [{
811
+ field: "organizationId",
812
+ value: referenceId
813
+ }]
814
+ });
815
+ membersCount = members.length > 0 ? members.length : 1;
816
+ }
817
+ const newSeatCount = quantity ?? existingSub.seats ?? membersCount;
818
+ const newAmount = (plan.amount ?? 0) + newSeatCount * (plan.seatAmount ?? plan.seatPriceId ?? 0);
819
+ const costDifference = newAmount - oldAmount;
820
+ if (costDifference > 0 && remainingDays > 0) {
821
+ const proratedAmount = Math.round(costDifference / totalDays * remainingDays);
822
+ if (proratedAmount >= 5e3) {
823
+ const ops = getPaystackOps(options.paystackClient);
824
+ if (ops === void 0 || ops === null) {
825
+ ctx.context.logger.error("Paystack client not configured for proration charge");
826
+ return;
881
827
  }
882
- }))?.status !== "success") throw new APIError("BAD_REQUEST", { message: "Failed to process prorated charge via saved authorization." });
828
+ if (unwrapSdkResult(await ops.transaction?.chargeAuthorization({ body: {
829
+ email: targetEmail,
830
+ amount: proratedAmount,
831
+ authorization_code: existingSub.paystackAuthorizationCode,
832
+ reference: `upg_${existingSub.id}_${Date.now()}_${Math.random().toString(36).substring(7)}`,
833
+ metadata: {
834
+ type: "proration",
835
+ referenceId,
836
+ newPlan: plan.name,
837
+ oldPlan: existingSub.plan,
838
+ remainingDays
839
+ }
840
+ } }))?.status !== "success") throw new APIError("BAD_REQUEST", { message: "Failed to process prorated charge via saved authorization." });
841
+ }
883
842
  }
843
+ const ops = getPaystackOps(options.paystackClient);
844
+ if (ops !== void 0 && ops !== null) await ops.subscription?.update(existingSub.paystackSubscriptionCode, { body: {
845
+ amount: newAmount,
846
+ plan: plan.planCode
847
+ } });
848
+ await ctx.context.adapter.update({
849
+ model: "subscription",
850
+ where: [{
851
+ field: "id",
852
+ value: existingSub.id
853
+ }],
854
+ update: {
855
+ plan: plan.name,
856
+ seats: newSeatCount,
857
+ updatedAt: /* @__PURE__ */ new Date()
858
+ }
859
+ });
860
+ return ctx.json({
861
+ status: "success",
862
+ message: "Subscription successfully upgraded with prorated charge.",
863
+ prorated: true
864
+ });
884
865
  }
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
866
  }
908
867
  }
909
- if (plan !== void 0 && plan !== null) if (trialStart !== void 0 && trialStart !== null) initBody.amount = 5e3;
868
+ if (plan !== void 0) if (trialStart !== void 0) initBody.amount = 5e3;
910
869
  else {
911
870
  initBody.plan = plan.planCode;
912
871
  initBody.invoice_limit = plan.invoiceLimit;
@@ -918,37 +877,37 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
918
877
  initBody.amount = Math.max(Math.round(finalAmount), 5e3);
919
878
  }
920
879
  else {
921
- if (amount === void 0 || amount === null || amount === 0) throw new APIError("BAD_REQUEST", { message: "Amount is required for one-time payments" });
880
+ if (amount === void 0 || amount === null) throw new APIError("BAD_REQUEST", { message: "Amount is required for one-time payments" });
922
881
  initBody.amount = Math.round(amount);
923
882
  }
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;
883
+ const sdkRes = unwrapSdkResult(await paystack?.transaction?.initialize({ body: initBody }));
884
+ url = sdkRes?.authorization_url;
885
+ reference = sdkRes?.reference;
886
+ accessCode = sdkRes?.access_code;
928
887
  } catch (error) {
929
888
  ctx.context.logger.error("Failed to initialize Paystack transaction", error);
930
889
  throw new APIError("BAD_REQUEST", {
931
890
  code: "FAILED_TO_INITIALIZE_TRANSACTION",
932
- message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_INITIALIZE_TRANSACTION.message
891
+ message: error instanceof Error ? error.message : PAYSTACK_ERROR_CODES.FAILED_TO_INITIALIZE_TRANSACTION.message
933
892
  });
934
893
  }
935
894
  await ctx.context.adapter.create({
936
895
  model: "paystackTransaction",
937
896
  data: {
938
- reference,
897
+ reference: reference ?? "",
939
898
  referenceId,
940
899
  userId: user.id,
941
900
  amount: amount ?? 0,
942
901
  currency: plan?.currency ?? currency ?? "NGN",
943
902
  status: "pending",
944
- plan: plan?.name.toLowerCase(),
945
- product: product?.name.toLowerCase(),
946
- metadata: extraMetadata !== void 0 && extraMetadata !== null && Object.keys(extraMetadata).length > 0 ? JSON.stringify(extraMetadata) : void 0,
903
+ plan: plan !== void 0 ? plan.name.toLowerCase() : void 0,
904
+ product: product !== void 0 ? product.name.toLowerCase() : void 0,
905
+ metadata: extraMetadata !== void 0 && Object.keys(extraMetadata).length > 0 ? JSON.stringify(extraMetadata) : void 0,
947
906
  createdAt: /* @__PURE__ */ new Date(),
948
907
  updatedAt: /* @__PURE__ */ new Date()
949
908
  }
950
909
  });
951
- if (plan !== void 0 && plan !== null) {
910
+ if (plan !== void 0) {
952
911
  let storedCustomerCode = user.paystackCustomerCode;
953
912
  if (options.organization?.enabled === true && referenceId !== user.id) {
954
913
  const org = await ctx.context.adapter.findOne({
@@ -958,22 +917,34 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
958
917
  value: referenceId
959
918
  }]
960
919
  });
961
- if (org?.paystackCustomerCode !== void 0 && org.paystackCustomerCode !== null && org.paystackCustomerCode !== "") storedCustomerCode = org.paystackCustomerCode;
920
+ if (org !== void 0 && org !== null) {
921
+ const paystackOrg = org;
922
+ if (paystackOrg.paystackCustomerCode !== void 0 && paystackOrg.paystackCustomerCode !== null && paystackOrg.paystackCustomerCode !== "") storedCustomerCode = paystackOrg.paystackCustomerCode;
923
+ }
962
924
  }
963
925
  const newSubscription = await ctx.context.adapter.create({
964
926
  model: "subscription",
965
927
  data: {
966
928
  plan: plan.name.toLowerCase(),
967
929
  referenceId,
968
- paystackCustomerCode: storedCustomerCode,
969
- paystackTransactionReference: reference,
970
- status: trialStart !== void 0 && trialStart !== null ? "trialing" : "incomplete",
971
- seats: quantity,
930
+ userId: user.id,
931
+ paystackCustomerCode: storedCustomerCode ?? "",
932
+ paystackSubscriptionCode: "",
933
+ paystackPlanCode: plan.planCode,
934
+ paystackAuthorizationCode: "",
935
+ paystackTransactionReference: reference ?? "",
936
+ status: trialStart !== void 0 ? "trialing" : "incomplete",
937
+ seats: quantity ?? 1,
938
+ periodStart: /* @__PURE__ */ new Date(),
939
+ periodEnd: new Date(Date.now() + 720 * 60 * 60 * 1e3),
940
+ cancelAtPeriodEnd: false,
972
941
  trialStart,
973
- trialEnd
942
+ trialEnd,
943
+ createdAt: /* @__PURE__ */ new Date(),
944
+ updatedAt: /* @__PURE__ */ new Date()
974
945
  }
975
946
  });
976
- if (trialStart !== void 0 && trialStart !== null && newSubscription !== null && plan.freeTrial?.onTrialStart !== void 0 && plan.freeTrial?.onTrialStart !== null) await plan.freeTrial.onTrialStart(newSubscription);
947
+ if (trialStart !== void 0 && newSubscription !== void 0 && newSubscription !== null && plan.freeTrial?.onTrialStart !== void 0) await plan.freeTrial.onTrialStart(newSubscription);
977
948
  }
978
949
  return ctx.json({
979
950
  url,
@@ -983,15 +954,11 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
983
954
  });
984
955
  });
985
956
  };
986
- const createSubscription = (options) => initializeTransaction(options, "/paystack/create-subscription");
987
- const upgradeSubscription = (options) => initializeTransaction(options, "/paystack/upgrade-subscription");
988
- const restoreSubscription = (options) => {
989
- return enablePaystackSubscription(options, "/paystack/restore-subscription");
990
- };
991
- const cancelSubscription = (options) => {
992
- return disablePaystackSubscription(options, "/paystack/cancel-subscription");
993
- };
994
- const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
957
+ const createSubscription = (options, path = "/create-subscription") => initializeTransaction(options, path);
958
+ const upgradeSubscription = (options, path = "/upgrade-subscription") => initializeTransaction(options, path);
959
+ const cancelSubscription = (options, path = "/cancel-subscription") => disablePaystackSubscription(options, path);
960
+ const restoreSubscription = (options, path = "/restore-subscription") => enablePaystackSubscription(options, path);
961
+ const verifyTransaction = (options, path = "/verify-transaction") => {
995
962
  const verifyBodySchema = z.object({ reference: z.string() });
996
963
  const subscriptionOptions = options.subscription;
997
964
  return createAuthEndpoint(path, {
@@ -1004,56 +971,55 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
1004
971
  ] : [sessionMiddleware, originCheck]
1005
972
  }, async (ctx) => {
1006
973
  const paystack = getPaystackOps(options.paystackClient);
1007
- let verifyRes;
974
+ let data;
1008
975
  try {
1009
- verifyRes = unwrapSdkResult(await paystack.transactionVerify(ctx.body.reference));
976
+ data = unwrapSdkResult(await paystack?.transaction?.verify(ctx.body.reference));
1010
977
  } catch (error) {
1011
978
  ctx.context.logger.error("Failed to verify Paystack transaction", error);
1012
979
  throw new APIError("BAD_REQUEST", {
1013
980
  code: "FAILED_TO_VERIFY_TRANSACTION",
1014
- message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_VERIFY_TRANSACTION.message
981
+ message: error instanceof Error ? error.message : PAYSTACK_ERROR_CODES.FAILED_TO_VERIFY_TRANSACTION.message
1015
982
  });
1016
983
  }
1017
- const dataRaw = unwrapSdkResult(verifyRes);
1018
- const data = dataRaw?.data ?? dataRaw;
1019
- const status = data?.status;
1020
- const reference = data?.reference ?? ctx.body.reference;
1021
- const paystackId = data?.id !== void 0 && data?.id !== null ? String(data.id) : void 0;
1022
- const authorizationCode = (data?.authorization)?.authorization_code;
984
+ if (data === void 0 || data === null) throw new APIError("BAD_REQUEST", { message: "Failed to fetch transaction data from Paystack." });
985
+ const status = data.status ?? "failed";
986
+ const reference = data.reference ?? ctx.body.reference;
987
+ const paystackIdRaw = data.id;
988
+ const paystackId = paystackIdRaw !== void 0 && paystackIdRaw !== null ? String(paystackIdRaw) : void 0;
989
+ const authorizationCode = data.authorization?.authorization_code;
1023
990
  if (status === "success") {
1024
991
  const session = await getSessionFromCtx(ctx);
1025
- const referenceId = (await ctx.context.adapter.findOne({
992
+ const txRecord = await ctx.context.adapter.findOne({
1026
993
  model: "paystackTransaction",
1027
994
  where: [{
1028
995
  field: "reference",
1029
996
  value: reference
1030
997
  }]
1031
- }))?.referenceId ?? (session?.user)?.id;
1032
- if (session !== null && session !== void 0 && referenceId !== session.user.id) {
998
+ });
999
+ const referenceId = txRecord !== void 0 && txRecord !== null && txRecord.referenceId !== void 0 && txRecord.referenceId !== null && txRecord.referenceId !== "" ? txRecord.referenceId : session !== void 0 && session !== null ? session.user.id : void 0;
1000
+ if (session !== void 0 && session !== null && referenceId !== void 0 && referenceId !== null && referenceId !== "" && referenceId !== session.user.id) {
1033
1001
  const authRef = subscriptionOptions?.authorizeReference;
1034
1002
  let authorized = false;
1035
- if (authRef !== void 0) authorized = await authRef({
1003
+ if (authRef !== void 0 && authRef !== null) authorized = await authRef({
1036
1004
  user: session.user,
1037
- session,
1005
+ session: session.session,
1038
1006
  referenceId,
1039
1007
  action: "verify-transaction"
1040
1008
  }, ctx);
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
- }
1009
+ if (authorized === false && options.organization?.enabled === true) {
1010
+ const member = await ctx.context.adapter.findOne({
1011
+ model: "member",
1012
+ where: [{
1013
+ field: "userId",
1014
+ value: session.user.id
1015
+ }, {
1016
+ field: "organizationId",
1017
+ value: referenceId
1018
+ }]
1019
+ });
1020
+ if (member !== void 0 && member !== null) authorized = true;
1055
1021
  }
1056
- if (!authorized) throw new APIError("UNAUTHORIZED");
1022
+ if (authorized === false) throw new APIError("UNAUTHORIZED");
1057
1023
  }
1058
1024
  try {
1059
1025
  await ctx.context.adapter.update({
@@ -1061,8 +1027,8 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
1061
1027
  update: {
1062
1028
  status: "success",
1063
1029
  paystackId,
1064
- ...data?.amount !== void 0 && data?.amount !== null ? { amount: data.amount } : {},
1065
- ...data?.currency !== void 0 && data?.currency !== null && data?.currency !== "" ? { currency: data.currency } : {},
1030
+ amount: data.amount,
1031
+ currency: data.currency,
1066
1032
  updatedAt: /* @__PURE__ */ new Date()
1067
1033
  },
1068
1034
  where: [{
@@ -1070,11 +1036,10 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
1070
1036
  value: reference
1071
1037
  }]
1072
1038
  });
1073
- const customer = data?.customer;
1074
- const paystackCustomerCodeFromPaystack = customer !== void 0 && customer !== null && typeof customer === "object" ? customer.customer_code : void 0;
1039
+ const paystackCustomerCodeFromPaystack = data.customer?.customer_code;
1075
1040
  if (paystackCustomerCodeFromPaystack !== void 0 && paystackCustomerCodeFromPaystack !== null && paystackCustomerCodeFromPaystack !== "" && referenceId !== void 0 && referenceId !== null && referenceId !== "") {
1076
1041
  let isOrg = options.organization?.enabled === true && typeof referenceId === "string" && referenceId.startsWith("org_");
1077
- if (!isOrg && options.organization?.enabled === true) {
1042
+ if (isOrg === false && options.organization?.enabled === true) {
1078
1043
  const org = await ctx.context.adapter.findOne({
1079
1044
  model: "organization",
1080
1045
  where: [{
@@ -1082,9 +1047,9 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
1082
1047
  value: referenceId
1083
1048
  }]
1084
1049
  });
1085
- isOrg = org !== null && org !== void 0;
1050
+ isOrg = org !== void 0 && org !== null;
1086
1051
  }
1087
- if (isOrg === true) await ctx.context.adapter.update({
1052
+ if (isOrg) await ctx.context.adapter.update({
1088
1053
  model: "organization",
1089
1054
  update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },
1090
1055
  where: [{
@@ -1108,45 +1073,39 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
1108
1073
  value: reference
1109
1074
  }]
1110
1075
  });
1111
- if (transaction?.product !== void 0 && transaction?.product !== null && transaction?.product !== "") await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
1076
+ if (transaction !== void 0 && transaction !== null && transaction.product !== void 0 && transaction.product !== null && transaction.product !== "" && options.paystackClient !== void 0 && options.paystackClient !== null) await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
1112
1077
  let isTrial = false;
1113
1078
  let trialEnd;
1114
1079
  let targetPlan;
1115
- if (data?.metadata !== void 0 && data?.metadata !== null) {
1116
- const metaRaw = data.metadata;
1117
- const meta = typeof metaRaw === "string" ? JSON.parse(metaRaw) : metaRaw;
1080
+ if (data.metadata !== void 0 && data.metadata !== null && data.metadata !== "") {
1081
+ const meta = typeof data.metadata === "string" ? JSON.parse(data.metadata) : data.metadata;
1118
1082
  isTrial = meta.isTrial === true || meta.isTrial === "true";
1119
1083
  trialEnd = meta.trialEnd;
1120
1084
  targetPlan = meta.plan;
1121
1085
  }
1122
1086
  let paystackSubscriptionCode;
1123
- if (isTrial === true && targetPlan !== void 0 && targetPlan !== null && targetPlan !== "" && trialEnd !== void 0 && trialEnd !== null && trialEnd !== "") {
1087
+ if (isTrial && targetPlan !== void 0 && trialEnd !== void 0) {
1124
1088
  const email = data.customer?.email;
1125
1089
  const planConfig = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === targetPlan?.toLowerCase());
1126
1090
  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({
1129
- customer: email,
1130
- plan: planConfig.planCode,
1131
- authorization: authorizationCode,
1132
- start_date: trialEnd
1133
- }));
1134
- paystackSubscriptionCode = (subRes?.data ?? subRes)?.subscription_code;
1135
- }
1136
- } else if (isTrial !== true) {
1137
- const planCodeFromPaystack = (data?.plan)?.plan_code;
1091
+ if (authorizationCode !== void 0 && authorizationCode !== null && email !== void 0 && email !== null && email !== "" && planConfig?.planCode !== void 0 && planConfig.planCode !== null && planConfig.planCode !== "") paystackSubscriptionCode = unwrapSdkResult(await paystack?.subscription?.create({ body: {
1092
+ customer: email,
1093
+ plan: planConfig.planCode,
1094
+ authorization: authorizationCode,
1095
+ start_date: trialEnd
1096
+ } }))?.subscription_code;
1097
+ } else if (isTrial === false) {
1098
+ const planCodeFromPaystack = data.plan?.plan_code;
1138
1099
  if (planCodeFromPaystack === void 0 || planCodeFromPaystack === null || planCodeFromPaystack === "") paystackSubscriptionCode = `LOC_${reference}`;
1139
- else paystackSubscriptionCode = (data?.subscription)?.subscription_code;
1100
+ else paystackSubscriptionCode = data.subscription?.subscription_code ?? void 0;
1140
1101
  }
1141
- const existingSubs = await ctx.context.adapter.findMany({
1102
+ const targetSub = (await ctx.context.adapter.findMany({
1142
1103
  model: "subscription",
1143
1104
  where: [{
1144
1105
  field: "paystackTransactionReference",
1145
1106
  value: reference
1146
1107
  }]
1147
- });
1148
- let targetSub;
1149
- if (existingSubs !== null && existingSubs !== void 0 && existingSubs.length > 0) targetSub = existingSubs.find((s) => referenceId === void 0 || referenceId === null || referenceId === "" || s.referenceId === referenceId);
1108
+ }))?.find((s) => referenceId === void 0 || referenceId === null || referenceId === "" || s.referenceId === referenceId);
1150
1109
  let updatedSubscription = null;
1151
1110
  if (targetSub !== void 0 && targetSub !== null) updatedSubscription = await ctx.context.adapter.update({
1152
1111
  model: "subscription",
@@ -1154,22 +1113,22 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
1154
1113
  status: isTrial ? "trialing" : "active",
1155
1114
  periodStart: /* @__PURE__ */ new Date(),
1156
1115
  updatedAt: /* @__PURE__ */ new Date(),
1157
- ...isTrial === true && trialEnd !== void 0 && trialEnd !== null ? {
1116
+ ...isTrial && trialEnd !== void 0 ? {
1158
1117
  trialStart: /* @__PURE__ */ new Date(),
1159
1118
  trialEnd: new Date(trialEnd),
1160
1119
  periodEnd: new Date(trialEnd)
1161
1120
  } : {},
1162
- ...paystackSubscriptionCode !== void 0 && paystackSubscriptionCode !== null && paystackSubscriptionCode !== "" ? { paystackSubscriptionCode } : {},
1163
- ...authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" ? { paystackAuthorizationCode: authorizationCode } : {}
1121
+ ...paystackSubscriptionCode !== void 0 ? { paystackSubscriptionCode } : {},
1122
+ ...authorizationCode !== void 0 && authorizationCode !== null ? { paystackAuthorizationCode: authorizationCode } : {}
1164
1123
  },
1165
1124
  where: [{
1166
1125
  field: "id",
1167
1126
  value: targetSub.id
1168
1127
  }]
1169
1128
  });
1170
- if (updatedSubscription && subscriptionOptions?.enabled === true && "onSubscriptionComplete" in subscriptionOptions && typeof subscriptionOptions.onSubscriptionComplete === "function") {
1129
+ if (updatedSubscription !== void 0 && updatedSubscription !== null && subscriptionOptions?.onSubscriptionComplete !== void 0) {
1171
1130
  const plan = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === updatedSubscription.plan.toLowerCase());
1172
- if (plan) await subscriptionOptions.onSubscriptionComplete({
1131
+ if (plan !== void 0) await subscriptionOptions.onSubscriptionComplete({
1173
1132
  event: data,
1174
1133
  subscription: updatedSubscription,
1175
1134
  plan
@@ -1186,10 +1145,10 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
1186
1145
  });
1187
1146
  });
1188
1147
  };
1189
- const listSubscriptions = (options) => {
1148
+ const listSubscriptions = (options, path = "/list-subscriptions") => {
1190
1149
  const listQuerySchema = z.object({ referenceId: z.string().optional() });
1191
1150
  const subscriptionOptions = options.subscription;
1192
- return createAuthEndpoint("/paystack/list-subscriptions", {
1151
+ return createAuthEndpoint(path, {
1193
1152
  method: "GET",
1194
1153
  query: listQuerySchema,
1195
1154
  use: subscriptionOptions?.enabled === true ? [
@@ -1200,7 +1159,7 @@ const listSubscriptions = (options) => {
1200
1159
  }, async (ctx) => {
1201
1160
  if (subscriptionOptions?.enabled !== true) throw new APIError("BAD_REQUEST", { message: "Subscriptions are not enabled in the Paystack options." });
1202
1161
  const session = await getSessionFromCtx(ctx);
1203
- if (!session) throw new APIError("UNAUTHORIZED");
1162
+ if (session === void 0 || session === null) throw new APIError("UNAUTHORIZED");
1204
1163
  const referenceIdPart = ctx.context.referenceId;
1205
1164
  const queryRefId = ctx.query?.referenceId;
1206
1165
  const referenceId = referenceIdPart ?? queryRefId ?? session.user.id;
@@ -1214,7 +1173,7 @@ const listSubscriptions = (options) => {
1214
1173
  return ctx.json({ subscriptions: res });
1215
1174
  });
1216
1175
  };
1217
- const listTransactions = (options, path = "/paystack/list-transactions") => {
1176
+ const listTransactions = (options, path = "/list-transactions") => {
1218
1177
  return createAuthEndpoint(path, {
1219
1178
  method: "GET",
1220
1179
  query: z.object({ referenceId: z.string().optional() }),
@@ -1225,15 +1184,17 @@ const listTransactions = (options, path = "/paystack/list-transactions") => {
1225
1184
  ] : [sessionMiddleware, originCheck]
1226
1185
  }, async (ctx) => {
1227
1186
  const session = await getSessionFromCtx(ctx);
1228
- if (!session) throw new APIError("UNAUTHORIZED");
1229
- const referenceId = ctx.context.referenceId ?? ctx.query?.referenceId ?? session.user.id;
1187
+ if (session === void 0 || session === null) throw new APIError("UNAUTHORIZED");
1188
+ const referenceIdPart = ctx.context.referenceId;
1189
+ const queryRefId = ctx.query?.referenceId;
1190
+ const referenceId = referenceIdPart ?? queryRefId ?? session.user.id;
1230
1191
  const sorted = (await ctx.context.adapter.findMany({
1231
1192
  model: "paystackTransaction",
1232
1193
  where: [{
1233
1194
  field: "referenceId",
1234
1195
  value: referenceId
1235
1196
  }]
1236
- })).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
1197
+ })).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
1237
1198
  return ctx.json({ transactions: sorted });
1238
1199
  });
1239
1200
  };
@@ -1246,8 +1207,10 @@ const enableDisableBodySchema = z.object({
1246
1207
  function decodeBase64UrlToString(value) {
1247
1208
  const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
1248
1209
  const padded = normalized + "===".slice((normalized.length + 3) % 4);
1249
- if (typeof globalThis.atob === "function") return globalThis.atob(padded);
1250
- return Buffer.from(padded, "base64").toString("utf8");
1210
+ const binaryString = atob(padded);
1211
+ const bytes = new Uint8Array(binaryString.length);
1212
+ for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i);
1213
+ return new TextDecoder().decode(bytes);
1251
1214
  }
1252
1215
  function tryGetEmailTokenFromSubscriptionManageLink(link) {
1253
1216
  try {
@@ -1257,12 +1220,12 @@ function tryGetEmailTokenFromSubscriptionManageLink(link) {
1257
1220
  if (parts.length < 2) return void 0;
1258
1221
  const payloadJson = decodeBase64UrlToString(parts[1]);
1259
1222
  const payload = JSON.parse(payloadJson);
1260
- return typeof payload?.email_token === "string" ? payload.email_token : void 0;
1223
+ return typeof payload.email_token === "string" ? payload.email_token : void 0;
1261
1224
  } catch {
1262
1225
  return;
1263
1226
  }
1264
1227
  }
1265
- const disablePaystackSubscription = (options, path = "/paystack/disable-subscription") => {
1228
+ const disablePaystackSubscription = (options, path = "/disable-subscription") => {
1266
1229
  return createAuthEndpoint(path, {
1267
1230
  method: "POST",
1268
1231
  body: enableDisableBodySchema,
@@ -1275,7 +1238,7 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
1275
1238
  const { subscriptionCode, atPeriodEnd } = ctx.body;
1276
1239
  const paystack = getPaystackOps(options.paystackClient);
1277
1240
  try {
1278
- if (subscriptionCode.startsWith("LOC_")) {
1241
+ if (subscriptionCode.startsWith("LOC_") || subscriptionCode.startsWith("sub_local_")) {
1279
1242
  const sub = await ctx.context.adapter.findOne({
1280
1243
  model: "subscription",
1281
1244
  where: [{
@@ -1283,7 +1246,7 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
1283
1246
  value: subscriptionCode
1284
1247
  }]
1285
1248
  });
1286
- if (sub !== null && sub !== void 0) {
1249
+ if (sub) {
1287
1250
  await ctx.context.adapter.update({
1288
1251
  model: "subscription",
1289
1252
  update: {
@@ -1303,22 +1266,21 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
1303
1266
  let emailToken = ctx.body.emailToken;
1304
1267
  let nextPaymentDate;
1305
1268
  try {
1306
- const fetchRes = unwrapSdkResult(await paystack.subscriptionFetch(subscriptionCode));
1307
- const data = fetchRes?.data ?? fetchRes;
1308
- if (emailToken === void 0 || emailToken === null || emailToken === "") emailToken = data?.email_token;
1309
- nextPaymentDate = data?.next_payment_date;
1269
+ const fetchRes = unwrapSdkResult(await paystack?.subscription?.fetch(subscriptionCode));
1270
+ if (fetchRes !== void 0 && fetchRes !== null) {
1271
+ emailToken ??= fetchRes.email_token ?? void 0;
1272
+ nextPaymentDate = fetchRes.next_payment_date ?? void 0;
1273
+ }
1310
1274
  } catch {}
1311
1275
  if (emailToken === void 0 || emailToken === null || emailToken === "") try {
1312
- const linkRes = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
1313
- const data = linkRes?.data ?? linkRes;
1314
- const link = typeof data === "string" ? data : data.link;
1315
- if (typeof link === "string" && link !== "") emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
1276
+ const link = unwrapSdkResult(await paystack?.subscription?.manageLink(subscriptionCode))?.link;
1277
+ if (link !== void 0 && link !== null && link !== "") emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
1316
1278
  } catch {}
1317
1279
  if (emailToken === void 0 || emailToken === null || emailToken === "") throw new Error("Could not retrieve email_token for subscription disable.");
1318
- await paystack.subscriptionDisable({
1280
+ await paystack?.subscription?.disable({ body: {
1319
1281
  code: subscriptionCode,
1320
1282
  token: emailToken
1321
- });
1283
+ } });
1322
1284
  const periodEnd = nextPaymentDate !== void 0 && nextPaymentDate !== null && nextPaymentDate !== "" ? new Date(nextPaymentDate) : void 0;
1323
1285
  const sub = await ctx.context.adapter.findOne({
1324
1286
  model: "subscription",
@@ -1346,12 +1308,12 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
1346
1308
  ctx.context.logger.error("Failed to disable subscription", error);
1347
1309
  throw new APIError("BAD_REQUEST", {
1348
1310
  code: "FAILED_TO_DISABLE_SUBSCRIPTION",
1349
- message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_DISABLE_SUBSCRIPTION.message
1311
+ message: error instanceof Error ? error.message : PAYSTACK_ERROR_CODES.FAILED_TO_DISABLE_SUBSCRIPTION.message
1350
1312
  });
1351
1313
  }
1352
1314
  });
1353
1315
  };
1354
- const enablePaystackSubscription = (options, path = "/paystack/enable-subscription") => {
1316
+ const enablePaystackSubscription = (options, path = "/enable-subscription") => {
1355
1317
  return createAuthEndpoint(path, {
1356
1318
  method: "POST",
1357
1319
  body: enableDisableBodySchema,
@@ -1366,20 +1328,18 @@ const enablePaystackSubscription = (options, path = "/paystack/enable-subscripti
1366
1328
  try {
1367
1329
  let emailToken = ctx.body.emailToken;
1368
1330
  if (emailToken === void 0 || emailToken === null || emailToken === "") try {
1369
- const fetchRes = unwrapSdkResult(await paystack.subscriptionFetch(subscriptionCode));
1370
- emailToken = (fetchRes?.data ?? fetchRes)?.email_token;
1331
+ const fetchRes = unwrapSdkResult(await paystack?.subscription?.fetch(subscriptionCode));
1332
+ if (fetchRes !== void 0 && fetchRes !== null) emailToken = fetchRes.email_token ?? void 0;
1371
1333
  } catch {}
1372
1334
  if (emailToken === void 0 || emailToken === null || emailToken === "") try {
1373
- const linkRes = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
1374
- const data = linkRes?.data ?? linkRes;
1375
- const link = typeof data === "string" ? data : data.link;
1376
- if (typeof link === "string" && link !== "") emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
1335
+ const link = unwrapSdkResult(await paystack?.subscription?.manageLink(subscriptionCode))?.link;
1336
+ if (link !== void 0 && link !== null && link !== "") emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
1377
1337
  } catch {}
1378
1338
  if (emailToken === void 0 || emailToken === null || emailToken === "") throw new APIError("BAD_REQUEST", { message: "Could not retrieve email_token for subscription enable." });
1379
- await paystack.subscriptionEnable({
1339
+ await paystack?.subscription?.enable({ body: {
1380
1340
  code: subscriptionCode,
1381
1341
  token: emailToken
1382
- });
1342
+ } });
1383
1343
  await ctx.context.adapter.update({
1384
1344
  model: "subscription",
1385
1345
  update: {
@@ -1396,12 +1356,12 @@ const enablePaystackSubscription = (options, path = "/paystack/enable-subscripti
1396
1356
  ctx.context.logger.error("Failed to enable subscription", error);
1397
1357
  throw new APIError("BAD_REQUEST", {
1398
1358
  code: "FAILED_TO_ENABLE_SUBSCRIPTION",
1399
- message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_ENABLE_SUBSCRIPTION.message
1359
+ message: error instanceof Error ? error.message : PAYSTACK_ERROR_CODES.FAILED_TO_ENABLE_SUBSCRIPTION.message
1400
1360
  });
1401
1361
  }
1402
1362
  });
1403
1363
  };
1404
- const getSubscriptionManageLink = (options, path = "/paystack/get-subscription-manage-link") => {
1364
+ const getSubscriptionManageLink = (options, path = "/subscription-manage-link") => {
1405
1365
  const manageLinkQuerySchema = z.object({ subscriptionCode: z.string() });
1406
1366
  const useMiddlewares = options.subscription?.enabled === true ? [
1407
1367
  sessionMiddleware,
@@ -1416,13 +1376,11 @@ const getSubscriptionManageLink = (options, path = "/paystack/get-subscription-m
1416
1376
  });
1417
1377
  const paystack = getPaystackOps(options.paystackClient);
1418
1378
  try {
1419
- const res = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
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 });
1379
+ const res = unwrapSdkResult(await paystack?.subscription?.manageLink(subscriptionCode));
1380
+ return ctx.json({ link: res?.link || null });
1423
1381
  } catch (error) {
1424
1382
  ctx.context.logger.error("Failed to get subscription manage link", error);
1425
- throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to get subscription manage link" });
1383
+ throw new APIError("BAD_REQUEST", { message: error instanceof Error ? error.message : "Failed to get subscription manage link" });
1426
1384
  }
1427
1385
  };
1428
1386
  return createAuthEndpoint(path, {
@@ -1431,69 +1389,8 @@ const getSubscriptionManageLink = (options, path = "/paystack/get-subscription-m
1431
1389
  use: useMiddlewares
1432
1390
  }, handler);
1433
1391
  };
1434
- const syncProducts = (options) => {
1435
- return createAuthEndpoint("/paystack/sync-products", {
1436
- method: "POST",
1437
- metadata: { ...HIDE_METADATA },
1438
- disableBody: true,
1439
- use: [sessionMiddleware]
1440
- }, async (ctx) => {
1441
- const paystack = getPaystackOps(options.paystackClient);
1442
- try {
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;
1449
- const paystackId = String(product.id);
1450
- const existing = await ctx.context.adapter.findOne({
1451
- model: "paystackProduct",
1452
- where: [{
1453
- field: "paystackId",
1454
- value: paystackId
1455
- }]
1456
- });
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,
1464
- paystackId,
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,
1467
- updatedAt: /* @__PURE__ */ new Date()
1468
- };
1469
- if (existing !== null && existing !== void 0) await ctx.context.adapter.update({
1470
- model: "paystackProduct",
1471
- update: productFields,
1472
- where: [{
1473
- field: "id",
1474
- value: existing.id
1475
- }]
1476
- });
1477
- else await ctx.context.adapter.create({
1478
- model: "paystackProduct",
1479
- data: {
1480
- ...productFields,
1481
- createdAt: /* @__PURE__ */ new Date()
1482
- }
1483
- });
1484
- }
1485
- return ctx.json({
1486
- status: "success",
1487
- count: productsData.length
1488
- });
1489
- } catch (error) {
1490
- ctx.context.logger.error("Failed to sync products", error);
1491
- throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to sync products" });
1492
- }
1493
- });
1494
- };
1495
- const listProducts = (_options) => {
1496
- return createAuthEndpoint("/paystack/list-products", {
1392
+ const listProducts = (_options, path = "/list-products") => {
1393
+ return createAuthEndpoint(path, {
1497
1394
  method: "GET",
1498
1395
  metadata: { openapi: { operationId: "listPaystackProducts" } }
1499
1396
  }, async (ctx) => {
@@ -1501,69 +1398,8 @@ const listProducts = (_options) => {
1501
1398
  return ctx.json({ products: sorted });
1502
1399
  });
1503
1400
  };
1504
- const syncPlans = (options) => {
1505
- return createAuthEndpoint("/paystack/sync-plans", {
1506
- method: "POST",
1507
- metadata: { ...HIDE_METADATA },
1508
- disableBody: true,
1509
- use: [sessionMiddleware]
1510
- }, async (ctx) => {
1511
- const paystack = getPaystackOps(options.paystackClient);
1512
- try {
1513
- const res = unwrapSdkResult(await paystack.planList());
1514
- const plansData = res?.data ?? res;
1515
- if (!Array.isArray(plansData)) return ctx.json({
1516
- status: "success",
1517
- count: 0
1518
- });
1519
- for (const plan of plansData) {
1520
- const paystackId = String(plan.id);
1521
- const existing = await ctx.context.adapter.findOne({
1522
- model: "paystackPlan",
1523
- where: [{
1524
- field: "paystackId",
1525
- value: paystackId
1526
- }]
1527
- });
1528
- const planData = {
1529
- name: plan.name,
1530
- description: plan.description,
1531
- amount: plan.amount,
1532
- currency: plan.currency,
1533
- interval: plan.interval,
1534
- planCode: plan.plan_code,
1535
- paystackId,
1536
- metadata: plan.metadata !== void 0 && plan.metadata !== null ? JSON.stringify(plan.metadata) : void 0,
1537
- updatedAt: /* @__PURE__ */ new Date()
1538
- };
1539
- if (existing !== void 0 && existing !== null) await ctx.context.adapter.update({
1540
- model: "paystackPlan",
1541
- update: planData,
1542
- where: [{
1543
- field: "id",
1544
- value: existing.id
1545
- }]
1546
- });
1547
- else await ctx.context.adapter.create({
1548
- model: "paystackPlan",
1549
- data: {
1550
- ...planData,
1551
- createdAt: /* @__PURE__ */ new Date()
1552
- }
1553
- });
1554
- }
1555
- return ctx.json({
1556
- status: "success",
1557
- count: plansData.length
1558
- });
1559
- } catch (error) {
1560
- ctx.context.logger.error("Failed to sync plans", error);
1561
- throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to sync plans" });
1562
- }
1563
- });
1564
- };
1565
- const listPlans = (_options) => {
1566
- return createAuthEndpoint("/paystack/list-plans", {
1401
+ const listPlans = (_options, path = "/list-plans") => {
1402
+ return createAuthEndpoint(path, {
1567
1403
  method: "GET",
1568
1404
  metadata: { ...HIDE_METADATA },
1569
1405
  use: [sessionMiddleware]
@@ -1573,12 +1409,12 @@ const listPlans = (_options) => {
1573
1409
  return ctx.json({ plans });
1574
1410
  } catch (error) {
1575
1411
  ctx.context.logger.error("Failed to list plans", error);
1576
- throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to list plans" });
1412
+ throw new APIError("BAD_REQUEST", { message: error instanceof Error ? error.message : "Failed to list plans" });
1577
1413
  }
1578
1414
  });
1579
1415
  };
1580
- const getConfig = (options) => {
1581
- return createAuthEndpoint("/paystack/get-config", {
1416
+ const getConfig = (options, path = "/get-config") => {
1417
+ return createAuthEndpoint(path, {
1582
1418
  method: "GET",
1583
1419
  metadata: { openapi: { operationId: "getPaystackConfig" } }
1584
1420
  }, async (ctx) => {
@@ -1590,103 +1426,6 @@ const getConfig = (options) => {
1590
1426
  });
1591
1427
  });
1592
1428
  };
1593
- const chargeRecurringSubscription = (options) => {
1594
- return createAuthEndpoint("/paystack/charge-recurring", {
1595
- method: "POST",
1596
- body: z.object({
1597
- subscriptionId: z.string(),
1598
- amount: z.number().optional()
1599
- })
1600
- }, async (ctx) => {
1601
- const { subscriptionId, amount: bodyAmount } = ctx.body;
1602
- const subscription = await ctx.context.adapter.findOne({
1603
- model: "subscription",
1604
- where: [{
1605
- field: "id",
1606
- value: subscriptionId
1607
- }]
1608
- });
1609
- if (subscription === void 0 || subscription === null) throw new APIError("NOT_FOUND", { message: "Subscription not found" });
1610
- if (subscription.paystackAuthorizationCode === void 0 || subscription.paystackAuthorizationCode === null || subscription.paystackAuthorizationCode === "") throw new APIError("BAD_REQUEST", { message: "No authorization code found for this subscription" });
1611
- const plan = (await getPlans(options.subscription)).find((p) => p.name.toLowerCase() === subscription.plan.toLowerCase());
1612
- if (!plan) throw new APIError("NOT_FOUND", { message: "Plan not found" });
1613
- const amount = bodyAmount ?? plan.amount;
1614
- if (amount === void 0 || amount === null) throw new APIError("BAD_REQUEST", { message: "Plan amount is not defined" });
1615
- let email;
1616
- if (subscription.referenceId !== void 0 && subscription.referenceId !== null && subscription.referenceId !== "") {
1617
- const user = await ctx.context.adapter.findOne({
1618
- model: "user",
1619
- where: [{
1620
- field: "id",
1621
- value: subscription.referenceId
1622
- }]
1623
- });
1624
- if (user !== void 0 && user !== null) email = user.email;
1625
- else if (options.organization?.enabled === true) {
1626
- const ownerMember = await ctx.context.adapter.findOne({
1627
- model: "member",
1628
- where: [{
1629
- field: "organizationId",
1630
- value: subscription.referenceId
1631
- }, {
1632
- field: "role",
1633
- value: "owner"
1634
- }]
1635
- });
1636
- if (ownerMember !== void 0 && ownerMember !== null) email = (await ctx.context.adapter.findOne({
1637
- model: "user",
1638
- where: [{
1639
- field: "id",
1640
- value: ownerMember.userId
1641
- }]
1642
- }))?.email;
1643
- }
1644
- }
1645
- if (email === void 0 || email === null || email === "") throw new APIError("NOT_FOUND", { message: "User email not found" });
1646
- const finalCurrency = plan.currency ?? "NGN";
1647
- if (!validateMinAmount(amount, finalCurrency)) throw new APIError("BAD_REQUEST", {
1648
- message: `Amount ${amount} is less than the minimum required for ${finalCurrency}.`,
1649
- status: 400
1650
- });
1651
- const dataRaw = unwrapSdkResult(await getPaystackOps(options.paystackClient).transactionChargeAuthorization({
1652
- email,
1653
- amount,
1654
- authorization_code: subscription.paystackAuthorizationCode,
1655
- currency: plan.currency,
1656
- metadata: {
1657
- subscriptionId,
1658
- referenceId: subscription.referenceId,
1659
- plan: plan.name
1660
- }
1661
- }));
1662
- const chargeData = dataRaw?.data ?? dataRaw;
1663
- if (chargeData?.status === "success" || dataRaw?.status === "success") {
1664
- const now = /* @__PURE__ */ new Date();
1665
- const nextPeriodEnd = getNextPeriodEnd(now, plan.interval ?? "monthly");
1666
- await ctx.context.adapter.update({
1667
- model: "subscription",
1668
- update: {
1669
- periodStart: now,
1670
- periodEnd: nextPeriodEnd,
1671
- updatedAt: now,
1672
- paystackTransactionReference: chargeData?.reference ?? dataRaw?.reference
1673
- },
1674
- where: [{
1675
- field: "id",
1676
- value: subscription.id
1677
- }]
1678
- });
1679
- return ctx.json({
1680
- status: "success",
1681
- data: chargeData
1682
- });
1683
- }
1684
- return ctx.json({
1685
- status: "failed",
1686
- data: chargeData
1687
- }, { status: 400 });
1688
- });
1689
- };
1690
1429
  //#endregion
1691
1430
  //#region src/schema.ts
1692
1431
  const transactions = { paystackTransaction: { fields: {
@@ -1950,32 +1689,226 @@ const getSchema = (options) => {
1950
1689
  return mergeSchema(baseSchema, options.schema);
1951
1690
  };
1952
1691
  //#endregion
1692
+ //#region src/operations.ts
1693
+ async function syncPaystackProducts(ctx, options) {
1694
+ const paystack = getPaystackOps(options.paystackClient);
1695
+ try {
1696
+ const productsData = unwrapSdkResult(await paystack?.product?.list({}));
1697
+ if (!Array.isArray(productsData)) return {
1698
+ status: "success",
1699
+ count: 0
1700
+ };
1701
+ for (const product of productsData) {
1702
+ const paystackId = String(product.id);
1703
+ const existing = await ctx.context.adapter.findOne({
1704
+ model: "paystackProduct",
1705
+ where: [{
1706
+ field: "paystackId",
1707
+ value: paystackId
1708
+ }]
1709
+ });
1710
+ const productFields = {
1711
+ name: product.name ?? "",
1712
+ description: product.description ?? "",
1713
+ price: product.price ?? 0,
1714
+ currency: product.currency ?? "",
1715
+ quantity: product.quantity ?? 0,
1716
+ unlimited: product.unlimited !== void 0 && product.unlimited !== null && product.unlimited !== false,
1717
+ paystackId,
1718
+ slug: product.slug ?? product.name?.toLowerCase().replace(/\s+/g, "-") ?? "",
1719
+ metadata: product.metadata !== void 0 && product.metadata !== null ? JSON.stringify(product.metadata) : void 0,
1720
+ updatedAt: /* @__PURE__ */ new Date()
1721
+ };
1722
+ if (existing !== void 0 && existing !== null) await ctx.context.adapter.update({
1723
+ model: "paystackProduct",
1724
+ update: productFields,
1725
+ where: [{
1726
+ field: "id",
1727
+ value: String(existing.id)
1728
+ }]
1729
+ });
1730
+ else await ctx.context.adapter.create({
1731
+ model: "paystackProduct",
1732
+ data: {
1733
+ ...productFields,
1734
+ createdAt: /* @__PURE__ */ new Date()
1735
+ }
1736
+ });
1737
+ }
1738
+ return {
1739
+ status: "success",
1740
+ count: productsData.length
1741
+ };
1742
+ } catch (error) {
1743
+ ctx.context.logger.error("Failed to sync products", error);
1744
+ throw new APIError("BAD_REQUEST", { message: error instanceof Error ? error.message : "Failed to sync products" });
1745
+ }
1746
+ }
1747
+ async function syncPaystackPlans(ctx, options) {
1748
+ const paystack = getPaystackOps(options.paystackClient);
1749
+ try {
1750
+ const plansData = unwrapSdkResult(await paystack?.plan?.list());
1751
+ if (!Array.isArray(plansData)) return {
1752
+ status: "success",
1753
+ count: 0
1754
+ };
1755
+ for (const plan of plansData) {
1756
+ const paystackId = String(plan.id);
1757
+ const existing = await ctx.context.adapter.findOne({
1758
+ model: "paystackPlan",
1759
+ where: [{
1760
+ field: "paystackId",
1761
+ value: paystackId
1762
+ }]
1763
+ });
1764
+ const planData = {
1765
+ name: plan.name ?? "",
1766
+ description: plan.description ?? "",
1767
+ amount: plan.amount ?? 0,
1768
+ currency: plan.currency ?? "",
1769
+ interval: plan.interval ?? "",
1770
+ planCode: plan.plan_code ?? "",
1771
+ paystackId,
1772
+ metadata: plan.metadata !== void 0 && plan.metadata !== null ? JSON.stringify(plan.metadata) : void 0,
1773
+ updatedAt: /* @__PURE__ */ new Date()
1774
+ };
1775
+ if (existing !== void 0 && existing !== null) await ctx.context.adapter.update({
1776
+ model: "paystackPlan",
1777
+ update: planData,
1778
+ where: [{
1779
+ field: "id",
1780
+ value: existing.id
1781
+ }]
1782
+ });
1783
+ else await ctx.context.adapter.create({
1784
+ model: "paystackPlan",
1785
+ data: {
1786
+ ...planData,
1787
+ createdAt: /* @__PURE__ */ new Date()
1788
+ }
1789
+ });
1790
+ }
1791
+ return {
1792
+ status: "success",
1793
+ count: plansData.length
1794
+ };
1795
+ } catch (error) {
1796
+ ctx.context.logger.error("Failed to sync plans", error);
1797
+ throw new APIError("BAD_REQUEST", { message: error instanceof Error ? error.message : "Failed to sync plans" });
1798
+ }
1799
+ }
1800
+ async function chargeSubscriptionRenewal(ctx, options, input) {
1801
+ const { subscriptionId, amount: bodyAmount } = input;
1802
+ const subscription = await ctx.context.adapter.findOne({
1803
+ model: "subscription",
1804
+ where: [{
1805
+ field: "id",
1806
+ value: subscriptionId
1807
+ }]
1808
+ });
1809
+ if (subscription === void 0 || subscription === null) throw new APIError("NOT_FOUND", { message: "Subscription not found" });
1810
+ if (subscription.paystackAuthorizationCode === void 0 || subscription.paystackAuthorizationCode === null || subscription.paystackAuthorizationCode === "") throw new APIError("BAD_REQUEST", { message: "No authorization code found for this subscription" });
1811
+ const plan = (await getPlans(options.subscription)).find((candidate) => candidate.name.toLowerCase() === subscription.plan.toLowerCase());
1812
+ if (plan === void 0 || plan === null) throw new APIError("NOT_FOUND", { message: "Plan not found" });
1813
+ const amount = bodyAmount ?? plan.amount;
1814
+ if (amount === void 0 || amount === null) throw new APIError("BAD_REQUEST", { message: "Plan amount is not defined" });
1815
+ let email;
1816
+ const referenceId = subscription.referenceId;
1817
+ if (referenceId !== void 0 && referenceId !== null && referenceId !== "") {
1818
+ const user = await ctx.context.adapter.findOne({
1819
+ model: "user",
1820
+ where: [{
1821
+ field: "id",
1822
+ value: referenceId
1823
+ }]
1824
+ });
1825
+ if (user !== void 0 && user !== null) email = user.email;
1826
+ else if (options.organization?.enabled === true) {
1827
+ const ownerMember = await ctx.context.adapter.findOne({
1828
+ model: "member",
1829
+ where: [{
1830
+ field: "organizationId",
1831
+ value: referenceId
1832
+ }, {
1833
+ field: "role",
1834
+ value: "owner"
1835
+ }]
1836
+ });
1837
+ if (ownerMember !== void 0 && ownerMember !== null) email = (await ctx.context.adapter.findOne({
1838
+ model: "user",
1839
+ where: [{
1840
+ field: "id",
1841
+ value: ownerMember.userId
1842
+ }]
1843
+ }))?.email;
1844
+ }
1845
+ }
1846
+ if (email === void 0 || email === null || email === "") throw new APIError("NOT_FOUND", { message: "User email not found" });
1847
+ const finalCurrency = plan.currency ?? "NGN";
1848
+ if (!validateMinAmount(amount, finalCurrency)) throw new APIError("BAD_REQUEST", {
1849
+ message: `Amount ${amount} is less than the minimum required for ${finalCurrency}.`,
1850
+ status: 400
1851
+ });
1852
+ const chargeData = unwrapSdkResult(await getPaystackOps(options.paystackClient)?.transaction?.chargeAuthorization({ body: {
1853
+ email,
1854
+ amount,
1855
+ authorization_code: subscription.paystackAuthorizationCode,
1856
+ reference: `rec_${subscription.id}_${Date.now()}`,
1857
+ metadata: {
1858
+ subscriptionId,
1859
+ referenceId
1860
+ }
1861
+ } }));
1862
+ if (chargeData?.status === "success" && chargeData.reference !== void 0) {
1863
+ const now = /* @__PURE__ */ new Date();
1864
+ const nextPeriodEnd = getNextPeriodEnd(now, plan.interval ?? "monthly");
1865
+ await ctx.context.adapter.update({
1866
+ model: "subscription",
1867
+ update: {
1868
+ periodStart: now,
1869
+ periodEnd: nextPeriodEnd,
1870
+ updatedAt: now,
1871
+ paystackTransactionReference: chargeData.reference
1872
+ },
1873
+ where: [{
1874
+ field: "id",
1875
+ value: subscription.id
1876
+ }]
1877
+ });
1878
+ return {
1879
+ status: "success",
1880
+ data: chargeData
1881
+ };
1882
+ }
1883
+ return {
1884
+ status: "failed",
1885
+ data: chargeData
1886
+ };
1887
+ }
1888
+ //#endregion
1953
1889
  //#region src/index.ts
1954
- const INTERNAL_ERROR_CODES = defineErrorCodes({ ...Object.fromEntries(Object.entries(PAYSTACK_ERROR_CODES).map(([key, value]) => [key, typeof value === "string" ? value : value.message])) });
1890
+ const INTERNAL_ERROR_CODES = defineErrorCodes(Object.fromEntries(Object.entries(PAYSTACK_ERROR_CODES).map(([key, value]) => [key, typeof value === "string" ? value : value.message])));
1955
1891
  const paystack = (options) => {
1956
1892
  const routeOptions = options;
1957
1893
  return {
1958
1894
  id: "paystack",
1959
1895
  endpoints: {
1960
- initializeTransaction: initializeTransaction(routeOptions),
1961
- verifyTransaction: verifyTransaction(routeOptions),
1962
- listSubscriptions: listSubscriptions(routeOptions),
1963
- paystackWebhook: paystackWebhook(routeOptions),
1964
- listTransactions: listTransactions(routeOptions),
1965
- getConfig: getConfig(routeOptions),
1966
- disableSubscription: disablePaystackSubscription(routeOptions),
1967
- enableSubscription: enablePaystackSubscription(routeOptions),
1968
- getSubscriptionManageLink: getSubscriptionManageLink(routeOptions),
1896
+ initializeTransaction: initializeTransaction(routeOptions, "/paystack/initialize-transaction"),
1897
+ verifyTransaction: verifyTransaction(routeOptions, "/paystack/verify-transaction"),
1898
+ listSubscriptions: listSubscriptions(routeOptions, "/paystack/list-subscriptions"),
1899
+ paystackWebhook: paystackWebhook(routeOptions, "/paystack/webhook"),
1900
+ listTransactions: listTransactions(routeOptions, "/paystack/list-transactions"),
1901
+ getConfig: getConfig(routeOptions, "/paystack/config"),
1902
+ disableSubscription: disablePaystackSubscription(routeOptions, "/paystack/disable-subscription"),
1903
+ enableSubscription: enablePaystackSubscription(routeOptions, "/paystack/enable-subscription"),
1904
+ getSubscriptionManageLink: getSubscriptionManageLink(routeOptions, "/paystack/subscription-manage-link"),
1969
1905
  subscriptionManageLink: getSubscriptionManageLink(routeOptions, "/paystack/subscription/manage-link"),
1970
- createSubscription: createSubscription(routeOptions),
1971
- upgradeSubscription: upgradeSubscription(routeOptions),
1972
- cancelSubscription: cancelSubscription(routeOptions),
1973
- restoreSubscription: restoreSubscription(routeOptions),
1974
- chargeRecurringSubscription: chargeRecurringSubscription(routeOptions),
1975
- syncProducts: syncProducts(routeOptions),
1976
- listProducts: listProducts(routeOptions),
1977
- syncPlans: syncPlans(routeOptions),
1978
- listPlans: listPlans(routeOptions)
1906
+ createSubscription: createSubscription(routeOptions, "/paystack/create-subscription"),
1907
+ upgradeSubscription: upgradeSubscription(routeOptions, "/paystack/upgrade-subscription"),
1908
+ cancelSubscription: cancelSubscription(routeOptions, "/paystack/cancel-subscription"),
1909
+ restoreSubscription: restoreSubscription(routeOptions, "/paystack/restore-subscription"),
1910
+ listProducts: listProducts(routeOptions, "/paystack/list-products"),
1911
+ listPlans: listPlans(routeOptions, "/paystack/list-plans")
1979
1912
  },
1980
1913
  schema: getSchema(options),
1981
1914
  init: (ctx) => {
@@ -1983,25 +1916,39 @@ const paystack = (options) => {
1983
1916
  databaseHooks: {
1984
1917
  user: { create: { async after(user, hookCtx) {
1985
1918
  if (!hookCtx || options.createCustomerOnSignUp !== true || user.email === null || user.email === void 0 || user.email === "") return;
1986
- const sdkRes = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate({
1987
- email: user.email,
1988
- first_name: user.name ?? void 0,
1989
- metadata: { userId: user.id }
1990
- }));
1991
- const customerCode = sdkRes?.customer_code ?? (sdkRes?.data)?.customer_code;
1992
- if (customerCode === "" || customerCode === null || customerCode === void 0) return;
1993
- await ctx.adapter.update({
1994
- model: "user",
1995
- where: [{
1996
- field: "id",
1997
- value: user.id
1998
- }],
1999
- update: { paystackCustomerCode: customerCode }
2000
- });
1919
+ try {
1920
+ const paystackOps = getPaystackOps(options.paystackClient);
1921
+ if (!paystackOps) return;
1922
+ const sdkRes = unwrapSdkResult(await paystackOps.customer?.create({ body: {
1923
+ email: user.email,
1924
+ first_name: user.name ?? void 0,
1925
+ metadata: { userId: user.id }
1926
+ } }) ?? await Promise.reject(/* @__PURE__ */ new Error("Paystack client missing customer ops")));
1927
+ const customerCode = sdkRes?.customer_code;
1928
+ if (customerCode !== void 0 && customerCode !== null && customerCode !== "") {
1929
+ await ctx.adapter.update({
1930
+ model: "user",
1931
+ where: [{
1932
+ field: "id",
1933
+ value: user.id
1934
+ }],
1935
+ update: { paystackCustomerCode: customerCode }
1936
+ });
1937
+ if (typeof options.onCustomerCreate === "function") await options.onCustomerCreate({
1938
+ paystackCustomer: sdkRes,
1939
+ user: {
1940
+ ...user,
1941
+ paystackCustomerCode: customerCode
1942
+ }
1943
+ }, hookCtx);
1944
+ }
1945
+ } catch (error) {
1946
+ ctx.logger.error("Failed to create Paystack customer for user", error);
1947
+ }
2001
1948
  } } },
2002
1949
  organization: options.organization?.enabled === true ? { create: { async after(org, hookCtx) {
2003
1950
  try {
2004
- const extraCreateParams = options.organization?.getCustomerCreateParams ? await options.organization.getCustomerCreateParams(org, hookCtx) : {};
1951
+ const extraCreateParams = typeof options.organization?.getCustomerCreateParams === "function" ? await options.organization.getCustomerCreateParams(org, hookCtx) : {};
2005
1952
  let targetEmail = org.email;
2006
1953
  if (targetEmail === void 0 || targetEmail === null) {
2007
1954
  const ownerMember = await ctx.adapter.findOne({
@@ -2014,7 +1961,7 @@ const paystack = (options) => {
2014
1961
  value: "owner"
2015
1962
  }]
2016
1963
  });
2017
- if (ownerMember) targetEmail = (await ctx.adapter.findOne({
1964
+ if (ownerMember !== null && ownerMember !== void 0) targetEmail = (await ctx.adapter.findOne({
2018
1965
  model: "user",
2019
1966
  where: [{
2020
1967
  field: "id",
@@ -2028,17 +1975,20 @@ const paystack = (options) => {
2028
1975
  first_name: org.name,
2029
1976
  metadata: { organizationId: org.id }
2030
1977
  }, extraCreateParams);
2031
- const sdkRes = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate(params));
2032
- const customerCode = sdkRes?.customer_code ?? (sdkRes?.data)?.customer_code;
2033
- if (customerCode === "" || customerCode === null || customerCode === void 0 || sdkRes === null || sdkRes === void 0) return;
2034
- await ctx.internalAdapter.updateOrganization(org.id, { paystackCustomerCode: customerCode });
2035
- await options.organization?.onCustomerCreate?.({
2036
- paystackCustomer: sdkRes,
2037
- organization: {
2038
- ...org,
2039
- paystackCustomerCode: customerCode
2040
- }
2041
- }, hookCtx);
1978
+ const paystackOps = getPaystackOps(options.paystackClient);
1979
+ if (!paystackOps) return;
1980
+ const sdkRes = unwrapSdkResult(await paystackOps.customer?.create({ body: params }) ?? await Promise.reject(/* @__PURE__ */ new Error("Paystack client missing customer ops")));
1981
+ const customerCode = sdkRes?.customer_code;
1982
+ if (customerCode !== void 0 && customerCode !== null && customerCode !== "" && sdkRes !== void 0 && sdkRes !== null) {
1983
+ await ctx.internalAdapter.updateOrganization(org.id, { paystackCustomerCode: customerCode });
1984
+ if (typeof options.organization?.onCustomerCreate === "function") await options.organization.onCustomerCreate({
1985
+ paystackCustomer: sdkRes,
1986
+ organization: {
1987
+ ...org,
1988
+ paystackCustomerCode: customerCode
1989
+ }
1990
+ }, hookCtx);
1991
+ }
2042
1992
  } catch (error) {
2043
1993
  ctx.logger.error("Failed to create Paystack customer for organization", error);
2044
1994
  }
@@ -2073,7 +2023,7 @@ const paystack = (options) => {
2073
2023
  team: { create: { before: async (team, ctx) => {
2074
2024
  if (options.subscription?.enabled === true && team.organizationId && ctx) {
2075
2025
  const subscription = await getOrganizationSubscription(ctx, team.organizationId);
2076
- if (subscription) {
2026
+ if (subscription !== null && subscription !== void 0) {
2077
2027
  const maxTeams = ((await getPlanByName(routeOptions, subscription.plan))?.limits)?.teams;
2078
2028
  if (typeof maxTeams === "number") await checkTeamLimit(ctx, team.organizationId, maxTeams);
2079
2029
  }
@@ -2081,10 +2031,11 @@ const paystack = (options) => {
2081
2031
  } } }
2082
2032
  } };
2083
2033
  },
2084
- $ERROR_CODES: INTERNAL_ERROR_CODES
2034
+ $ERROR_CODES: INTERNAL_ERROR_CODES,
2035
+ options
2085
2036
  };
2086
2037
  };
2087
2038
  //#endregion
2088
- export { paystack };
2039
+ export { chargeSubscriptionRenewal, paystack, syncPaystackPlans, syncPaystackProducts };
2089
2040
 
2090
2041
  //# sourceMappingURL=index.mjs.map