@alexasomba/better-auth-paystack 1.0.4 → 1.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
@@ -6,6 +6,104 @@ import { APIError, getSessionFromCtx, originCheck, sessionMiddleware } from "bet
6
6
  import * as z from "zod/v4";
7
7
  import { mergeSchema } from "better-auth/db";
8
8
 
9
+ //#region src/paystack-sdk.ts
10
+ function isOpenApiFetchResponse(value) {
11
+ return value !== null && value !== void 0 && typeof value === "object" && ("data" in value || "error" in value || "response" in value);
12
+ }
13
+ function unwrapSdkResult(result) {
14
+ if (isOpenApiFetchResponse(result)) {
15
+ if (result.error !== void 0 && result.error !== null) throw new Error(typeof result.error === "string" ? result.error : JSON.stringify(result.error));
16
+ return result.data ?? result;
17
+ }
18
+ if (result !== null && result !== void 0 && typeof result === "object" && "data" in result) {
19
+ const data = result.data;
20
+ if (data !== null && typeof data === "object" && "data" in data) return data.data;
21
+ return data;
22
+ }
23
+ return result;
24
+ }
25
+ function getPaystackOps(paystackClient) {
26
+ return {
27
+ customerCreate: (params) => {
28
+ if (paystackClient?.customer_create !== void 0) return paystackClient.customer_create({ body: params });
29
+ return paystackClient?.customer?.create?.(params);
30
+ },
31
+ customerUpdate: (code, params) => {
32
+ if (paystackClient?.customer_update !== void 0) return paystackClient.customer_update({
33
+ params: { path: { code } },
34
+ body: params
35
+ });
36
+ return paystackClient?.customer?.update?.(code, params);
37
+ },
38
+ transactionInitialize: (body) => {
39
+ if (paystackClient?.transaction_initialize !== void 0) return paystackClient.transaction_initialize({ body });
40
+ return paystackClient?.transaction?.initialize?.(body);
41
+ },
42
+ transactionVerify: (reference) => {
43
+ if (paystackClient?.transaction_verify !== void 0) return paystackClient.transaction_verify({ params: { path: { reference } } });
44
+ return paystackClient?.transaction?.verify?.(reference);
45
+ },
46
+ subscriptionCreate: (body) => {
47
+ if (paystackClient?.subscription_create !== void 0) return paystackClient.subscription_create({ body });
48
+ return paystackClient?.subscription?.create?.(body);
49
+ },
50
+ subscriptionDisable: (body) => {
51
+ if (paystackClient?.subscription_disable !== void 0) return paystackClient.subscription_disable({ body });
52
+ return paystackClient?.subscription?.disable?.(body);
53
+ },
54
+ subscriptionEnable: (body) => {
55
+ if (paystackClient?.subscription_enable !== void 0) return paystackClient.subscription_enable({ body });
56
+ return paystackClient?.subscription?.enable?.(body);
57
+ },
58
+ subscriptionFetch: async (idOrCode) => {
59
+ if (paystackClient?.subscription_fetch !== void 0) try {
60
+ return await paystackClient.subscription_fetch({ params: { path: { code: idOrCode } } });
61
+ } catch {
62
+ const compatFetch = paystackClient.subscription_fetch;
63
+ return compatFetch({ params: { path: { id_or_code: idOrCode } } });
64
+ }
65
+ return paystackClient?.subscription?.fetch?.(idOrCode);
66
+ },
67
+ subscriptionManageLink: (code) => {
68
+ if (paystackClient?.subscription_manageLink !== void 0) return paystackClient.subscription_manageLink({ params: { path: { code } } });
69
+ if (paystackClient?.subscription_manage_link !== void 0) return paystackClient.subscription_manage_link({ params: { path: { code } } });
70
+ return paystackClient?.subscription?.manage?.link?.(code);
71
+ },
72
+ subscriptionManageEmail: (code, email) => {
73
+ if (paystackClient?.subscription_manageEmail !== void 0) return paystackClient.subscription_manageEmail({ params: { path: { code } } });
74
+ return paystackClient?.subscription?.manage?.email?.(code, email);
75
+ },
76
+ transactionChargeAuthorization: (body) => {
77
+ if (paystackClient?.transaction_chargeAuthorization !== void 0) return paystackClient.transaction_chargeAuthorization({ body });
78
+ return paystackClient?.transaction?.chargeAuthorization?.(body);
79
+ },
80
+ productList: () => {
81
+ if (paystackClient?.product_list !== void 0) return paystackClient.product_list();
82
+ return paystackClient?.product?.list?.();
83
+ },
84
+ productFetch: (idOrCode) => {
85
+ if (paystackClient?.product_fetch !== void 0) return paystackClient.product_fetch({ params: { path: { id_or_code: idOrCode } } });
86
+ return paystackClient?.product?.fetch?.(idOrCode);
87
+ },
88
+ productCreate: (params) => {
89
+ if (paystackClient?.product_create !== void 0) return paystackClient.product_create({ body: params });
90
+ return paystackClient?.product?.create?.(params);
91
+ },
92
+ productUpdate: (idOrCode, params) => {
93
+ if (paystackClient?.product_update !== void 0) return paystackClient.product_update({
94
+ params: { path: { id_or_code: idOrCode } },
95
+ body: params
96
+ });
97
+ return paystackClient?.product?.update?.(idOrCode, params);
98
+ },
99
+ productDelete: (idOrCode) => {
100
+ if (paystackClient?.product_delete !== void 0) return paystackClient.product_delete({ params: { path: { id_or_code: idOrCode } } });
101
+ return paystackClient?.product?.delete?.(idOrCode);
102
+ }
103
+ };
104
+ }
105
+
106
+ //#endregion
9
107
  //#region src/utils.ts
10
108
  async function getPlans(subscriptionOptions) {
11
109
  if (subscriptionOptions?.enabled === true) return typeof subscriptionOptions.plans === "function" ? subscriptionOptions.plans() : subscriptionOptions.plans;
@@ -20,7 +118,7 @@ async function getProducts(productOptions) {
20
118
  return [];
21
119
  }
22
120
  async function getProductByName(options, name) {
23
- return await getProducts(options.products).then((products) => products?.find((product) => product.name.toLowerCase() === name.toLowerCase()));
121
+ return await getProducts(options.products).then((products) => products?.find((product) => product.name.toLowerCase() === name.toLowerCase()) ?? null);
24
122
  }
25
123
  function getNextPeriodEnd(startDate, interval) {
26
124
  const date = new Date(startDate);
@@ -62,6 +160,62 @@ function validateMinAmount(amount, currency) {
62
160
  }[currency.toUpperCase()];
63
161
  return min !== void 0 ? amount >= min : true;
64
162
  }
163
+ async function syncProductQuantityFromPaystack(ctx, productName, paystackClient) {
164
+ let localProduct = await ctx.context.adapter.findOne({
165
+ model: "paystackProduct",
166
+ where: [{
167
+ field: "name",
168
+ value: productName
169
+ }]
170
+ });
171
+ localProduct ??= await ctx.context.adapter.findOne({
172
+ model: "paystackProduct",
173
+ where: [{
174
+ field: "slug",
175
+ value: productName.toLowerCase().replace(/\s+/g, "-")
176
+ }]
177
+ });
178
+ if (!localProduct?.paystackId) {
179
+ if (localProduct && localProduct.unlimited !== true && localProduct.quantity !== void 0 && localProduct.quantity > 0) await ctx.context.adapter.update({
180
+ model: "paystackProduct",
181
+ update: {
182
+ quantity: localProduct.quantity - 1,
183
+ updatedAt: /* @__PURE__ */ new Date()
184
+ },
185
+ where: [{
186
+ field: "id",
187
+ value: localProduct.id
188
+ }]
189
+ });
190
+ return;
191
+ }
192
+ try {
193
+ const remoteQuantity = unwrapSdkResult(await getPaystackOps(paystackClient).productFetch(localProduct.paystackId))?.quantity;
194
+ if (remoteQuantity !== void 0) await ctx.context.adapter.update({
195
+ model: "paystackProduct",
196
+ update: {
197
+ quantity: remoteQuantity,
198
+ updatedAt: /* @__PURE__ */ new Date()
199
+ },
200
+ where: [{
201
+ field: "id",
202
+ value: localProduct.id
203
+ }]
204
+ });
205
+ } catch {
206
+ if (localProduct.unlimited !== true && localProduct.quantity !== void 0 && localProduct.quantity > 0) await ctx.context.adapter.update({
207
+ model: "paystackProduct",
208
+ update: {
209
+ quantity: localProduct.quantity - 1,
210
+ updatedAt: /* @__PURE__ */ new Date()
211
+ },
212
+ where: [{
213
+ field: "id",
214
+ value: localProduct.id
215
+ }]
216
+ });
217
+ }
218
+ }
65
219
 
66
220
  //#endregion
67
221
  //#region src/middleware.ts
@@ -102,96 +256,6 @@ const referenceMiddleware = (options, action) => createAuthMiddleware(async (ctx
102
256
  throw new APIError("BAD_REQUEST", { message: "Passing referenceId isn't allowed without subscription.authorizeReference or valid organization membership." });
103
257
  });
104
258
 
105
- //#endregion
106
- //#region src/paystack-sdk.ts
107
- function isOpenApiFetchResponse(value) {
108
- return value !== null && value !== void 0 && typeof value === "object" && ("data" in value || "error" in value || "response" in value);
109
- }
110
- function unwrapSdkResult(result) {
111
- if (isOpenApiFetchResponse(result)) {
112
- if (result.error !== void 0 && result.error !== null) throw new Error(typeof result.error === "string" ? result.error : JSON.stringify(result.error));
113
- return result.data;
114
- }
115
- if (result !== null && result !== void 0 && typeof result === "object" && "data" in result) return result.data ?? result;
116
- return result;
117
- }
118
- const normalizeMetadata = (value) => {
119
- if (value === void 0 || value === null || value === "") return void 0;
120
- return typeof value === "string" ? value : JSON.stringify(value);
121
- };
122
- const normalizeMetadataBody = (body) => {
123
- const { metadata, ...rest } = body;
124
- const normalized = normalizeMetadata(metadata);
125
- if (normalized === void 0) return rest;
126
- return {
127
- ...rest,
128
- metadata: normalized
129
- };
130
- };
131
- function getPaystackOps(paystackClient) {
132
- return {
133
- customerCreate: (params) => {
134
- if (paystackClient?.customer_create !== void 0) {
135
- const body = normalizeMetadataBody(params);
136
- return paystackClient.customer_create({ body });
137
- }
138
- return paystackClient?.customer?.create?.(params);
139
- },
140
- customerUpdate: (code, params) => {
141
- if (paystackClient?.customer_update !== void 0) {
142
- const body = normalizeMetadataBody(params);
143
- return paystackClient.customer_update({
144
- params: { path: { code } },
145
- body
146
- });
147
- }
148
- return paystackClient?.customer?.update?.(code, params);
149
- },
150
- transactionInitialize: (body) => {
151
- if (paystackClient?.transaction_initialize !== void 0) return paystackClient.transaction_initialize({ body });
152
- return paystackClient?.transaction?.initialize?.(body);
153
- },
154
- transactionVerify: (reference) => {
155
- if (paystackClient?.transaction_verify !== void 0) return paystackClient.transaction_verify({ params: { path: { reference } } });
156
- return paystackClient?.transaction?.verify?.(reference);
157
- },
158
- subscriptionCreate: (body) => {
159
- if (paystackClient?.subscription_create !== void 0) return paystackClient.subscription_create({ body });
160
- return paystackClient?.subscription?.create?.(body);
161
- },
162
- subscriptionDisable: (body) => {
163
- if (paystackClient?.subscription_disable !== void 0) return paystackClient.subscription_disable({ body });
164
- return paystackClient?.subscription?.disable?.(body);
165
- },
166
- subscriptionEnable: (body) => {
167
- if (paystackClient?.subscription_enable !== void 0) return paystackClient.subscription_enable({ body });
168
- return paystackClient?.subscription?.enable?.(body);
169
- },
170
- subscriptionFetch: async (idOrCode) => {
171
- if (paystackClient?.subscription_fetch !== void 0) try {
172
- return await paystackClient.subscription_fetch({ params: { path: { code: idOrCode } } });
173
- } catch {
174
- const compatFetch = paystackClient.subscription_fetch;
175
- return compatFetch({ params: { path: { id_or_code: idOrCode } } });
176
- }
177
- return paystackClient?.subscription?.fetch?.(idOrCode);
178
- },
179
- subscriptionManageLink: (code) => {
180
- if (paystackClient?.subscription_manageLink !== void 0) return paystackClient.subscription_manageLink({ params: { path: { code } } });
181
- if (paystackClient?.subscription_manage_link !== void 0) return paystackClient.subscription_manage_link({ params: { path: { code } } });
182
- return paystackClient?.subscription?.manage?.link?.(code);
183
- },
184
- subscriptionManageEmail: (code, email) => {
185
- if (paystackClient?.subscription_manageEmail !== void 0) return paystackClient.subscription_manageEmail({ params: { path: { code } } });
186
- return paystackClient?.subscription?.manage?.email?.(code, email);
187
- },
188
- transactionChargeAuthorization: (body) => {
189
- if (paystackClient?.transaction_chargeAuthorization !== void 0) return paystackClient.transaction_chargeAuthorization({ body });
190
- return paystackClient?.transaction?.chargeAuthorization?.(body);
191
- }
192
- };
193
- }
194
-
195
259
  //#endregion
196
260
  //#region src/routes.ts
197
261
  const PAYSTACK_ERROR_CODES = defineErrorCodes({
@@ -244,14 +308,14 @@ const paystackWebhook = (options) => {
244
308
  status: 401
245
309
  });
246
310
  const event = JSON.parse(payload);
247
- if (options.subscription?.enabled === true) {
248
- const eventName = String(event?.event ?? "");
249
- const data = event?.data;
250
- try {
251
- if (eventName === "charge.success") {
252
- const reference = data?.reference;
253
- const paystackId = data?.id !== void 0 && data?.id !== null ? String(data.id) : void 0;
254
- if (reference !== void 0 && reference !== null && reference !== "") await ctx.context.adapter.update({
311
+ const eventName = event.event;
312
+ const data = event.data;
313
+ if (eventName === "charge.success") {
314
+ const reference = data?.reference;
315
+ const paystackId = data?.id !== void 0 && data?.id !== null ? String(data.id) : void 0;
316
+ if (reference !== void 0 && reference !== null && reference !== "") {
317
+ try {
318
+ await ctx.context.adapter.update({
255
319
  model: "paystackTransaction",
256
320
  update: {
257
321
  status: "success",
@@ -263,134 +327,150 @@ const paystackWebhook = (options) => {
263
327
  value: reference
264
328
  }]
265
329
  });
330
+ } catch (e) {
331
+ ctx.context.logger.warn("Failed to update transaction status for charge.success", e);
266
332
  }
267
- if (eventName === "charge.failure") {
268
- const reference = data?.reference;
269
- if (reference !== void 0 && reference !== null && reference !== "") try {
270
- await ctx.context.adapter.update({
271
- model: "paystackTransaction",
272
- update: {
273
- status: "failed",
274
- updatedAt: /* @__PURE__ */ new Date()
275
- },
276
- where: [{
277
- field: "reference",
278
- value: reference
279
- }]
280
- });
281
- } catch (e) {
282
- ctx.context.logger.warn("Failed to update transaction status for charge.failure", e);
283
- }
333
+ try {
334
+ const transaction = await ctx.context.adapter.findOne({
335
+ model: "paystackTransaction",
336
+ where: [{
337
+ field: "reference",
338
+ value: reference
339
+ }]
340
+ });
341
+ if (transaction?.product) await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
342
+ } catch (e) {
343
+ ctx.context.logger.warn("Failed to sync product quantity", e);
284
344
  }
285
- if (eventName === "subscription.create") {
286
- const subscriptionCode = data?.subscription_code ?? data?.subscription?.subscription_code ?? data?.code;
287
- const customerCode = data?.customer?.customer_code ?? data?.customer_code ?? data?.customer?.code;
288
- const planCode = data?.plan?.plan_code ?? data?.plan_code ?? data?.plan;
289
- let metadata = data?.metadata;
290
- if (typeof metadata === "string") try {
291
- metadata = JSON.parse(metadata);
292
- } catch {}
293
- const referenceIdFromMetadata = typeof metadata === "object" && metadata !== null ? metadata.referenceId : void 0;
294
- let planNameFromMetadata = typeof metadata === "object" && metadata !== null ? metadata.plan : void 0;
295
- if (typeof planNameFromMetadata === "string") planNameFromMetadata = planNameFromMetadata.toLowerCase();
296
- const plans = await getPlans(options.subscription);
297
- const planFromCode = planCode !== void 0 && planCode !== null && planCode !== "" ? plans.find((p) => p.planCode !== void 0 && p.planCode !== null && p.planCode === planCode) : void 0;
298
- const planPart = planFromCode?.name ?? planNameFromMetadata;
299
- const planName = planPart !== void 0 && planPart !== null && planPart !== "" ? planPart.toLowerCase() : void 0;
300
- if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
301
- const where = [];
302
- if (referenceIdFromMetadata !== void 0 && referenceIdFromMetadata !== null && referenceIdFromMetadata !== "") where.push({
303
- field: "referenceId",
304
- value: referenceIdFromMetadata
305
- });
306
- else if (customerCode !== void 0 && customerCode !== null && customerCode !== "") where.push({
307
- field: "paystackCustomerCode",
308
- value: customerCode
309
- });
310
- if (planName !== void 0 && planName !== null && planName !== "") where.push({
311
- field: "plan",
312
- value: planName
345
+ }
346
+ }
347
+ if (eventName === "charge.failure") {
348
+ const reference = data?.reference;
349
+ if (reference !== void 0 && reference !== null && reference !== "") try {
350
+ await ctx.context.adapter.update({
351
+ model: "paystackTransaction",
352
+ update: {
353
+ status: "failed",
354
+ updatedAt: /* @__PURE__ */ new Date()
355
+ },
356
+ where: [{
357
+ field: "reference",
358
+ value: reference
359
+ }]
360
+ });
361
+ } catch (e) {
362
+ ctx.context.logger.warn("Failed to update transaction status for charge.failure", e);
363
+ }
364
+ }
365
+ if (options.subscription?.enabled === true) try {
366
+ if (eventName === "subscription.create") {
367
+ const subscriptionCode = data?.subscription_code ?? data?.subscription?.subscription_code ?? data?.code;
368
+ const customerCode = data?.customer?.customer_code ?? data?.customer_code ?? data?.customer?.code;
369
+ const planCode = data?.plan?.plan_code ?? data?.plan_code ?? data?.plan;
370
+ let metadata = data?.metadata;
371
+ if (typeof metadata === "string") try {
372
+ metadata = JSON.parse(metadata);
373
+ } catch {}
374
+ const referenceIdFromMetadata = typeof metadata === "object" && metadata !== null ? metadata.referenceId : void 0;
375
+ let planNameFromMetadata = typeof metadata === "object" && metadata !== null ? metadata.plan : void 0;
376
+ if (typeof planNameFromMetadata === "string") planNameFromMetadata = planNameFromMetadata.toLowerCase();
377
+ const plans = await getPlans(options.subscription);
378
+ const planFromCode = planCode !== void 0 && planCode !== null && planCode !== "" ? plans.find((p) => p.planCode !== void 0 && p.planCode !== null && p.planCode === planCode) : void 0;
379
+ const planPart = planFromCode?.name ?? planNameFromMetadata;
380
+ const planName = planPart !== void 0 && planPart !== null && planPart !== "" ? planPart.toLowerCase() : void 0;
381
+ if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
382
+ const where = [];
383
+ if (referenceIdFromMetadata !== void 0 && referenceIdFromMetadata !== null && referenceIdFromMetadata !== "") where.push({
384
+ field: "referenceId",
385
+ value: referenceIdFromMetadata
386
+ });
387
+ else if (customerCode !== void 0 && customerCode !== null && customerCode !== "") where.push({
388
+ field: "paystackCustomerCode",
389
+ value: customerCode
390
+ });
391
+ if (planName !== void 0 && planName !== null && planName !== "") where.push({
392
+ field: "plan",
393
+ value: planName
394
+ });
395
+ if (where.length > 0) {
396
+ const matches = await ctx.context.adapter.findMany({
397
+ model: "subscription",
398
+ where
313
399
  });
314
- if (where.length > 0) {
315
- const matches = await ctx.context.adapter.findMany({
400
+ const subscription = matches !== void 0 && matches !== null ? matches[0] : void 0;
401
+ if (subscription !== void 0 && subscription !== null) {
402
+ await ctx.context.adapter.update({
316
403
  model: "subscription",
317
- where
404
+ update: {
405
+ paystackSubscriptionCode: subscriptionCode,
406
+ status: "active",
407
+ updatedAt: /* @__PURE__ */ new Date(),
408
+ periodEnd: data?.next_payment_date !== void 0 && data?.next_payment_date !== null && data?.next_payment_date !== "" ? new Date(data.next_payment_date) : void 0
409
+ },
410
+ where: [{
411
+ field: "id",
412
+ value: subscription.id
413
+ }]
318
414
  });
319
- const subscription = matches !== void 0 && matches !== null ? matches[0] : void 0;
320
- if (subscription !== void 0 && subscription !== null) {
321
- await ctx.context.adapter.update({
322
- model: "subscription",
323
- update: {
415
+ const plan = planFromCode ?? (planName !== void 0 && planName !== null && planName !== "" ? await getPlanByName(options, planName) : void 0);
416
+ if (plan !== void 0 && plan !== null) {
417
+ await options.subscription.onSubscriptionComplete?.({
418
+ event,
419
+ subscription: {
420
+ ...subscription,
324
421
  paystackSubscriptionCode: subscriptionCode,
325
- status: "active",
326
- updatedAt: /* @__PURE__ */ new Date(),
327
- periodEnd: data?.next_payment_date !== void 0 && data?.next_payment_date !== null && data?.next_payment_date !== "" ? new Date(data.next_payment_date) : void 0
422
+ status: "active"
328
423
  },
329
- where: [{
330
- field: "id",
331
- value: subscription.id
332
- }]
333
- });
334
- const plan = planFromCode ?? (planName !== void 0 && planName !== null && planName !== "" ? await getPlanByName(options, planName) : void 0);
335
- if (plan !== void 0 && plan !== null) {
336
- await options.subscription.onSubscriptionComplete?.({
337
- event,
338
- subscription: {
339
- ...subscription,
340
- paystackSubscriptionCode: subscriptionCode,
341
- status: "active"
342
- },
343
- plan
344
- }, ctx);
345
- await options.subscription.onSubscriptionCreated?.({
346
- event,
347
- subscription: {
348
- ...subscription,
349
- paystackSubscriptionCode: subscriptionCode,
350
- status: "active"
351
- },
352
- plan
353
- }, ctx);
354
- }
424
+ plan
425
+ }, ctx);
426
+ await options.subscription.onSubscriptionCreated?.({
427
+ event,
428
+ subscription: {
429
+ ...subscription,
430
+ paystackSubscriptionCode: subscriptionCode,
431
+ status: "active"
432
+ },
433
+ plan
434
+ }, ctx);
355
435
  }
356
436
  }
357
437
  }
358
438
  }
359
- if (eventName === "subscription.disable" || eventName === "subscription.not_renew") {
360
- const subscriptionCode = data?.subscription_code ?? data?.subscription?.subscription_code ?? data?.code;
361
- if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
362
- const existing = await ctx.context.adapter.findOne({
363
- model: "subscription",
364
- where: [{
365
- field: "paystackSubscriptionCode",
366
- value: subscriptionCode
367
- }]
368
- });
369
- let newStatus = "canceled";
370
- if (existing?.cancelAtPeriodEnd === true && existing.periodEnd !== void 0 && existing.periodEnd !== null && new Date(existing.periodEnd) > /* @__PURE__ */ new Date()) newStatus = "active";
371
- await ctx.context.adapter.update({
372
- model: "subscription",
373
- update: {
374
- status: newStatus,
375
- updatedAt: /* @__PURE__ */ new Date()
376
- },
377
- where: [{
378
- field: "paystackSubscriptionCode",
379
- value: subscriptionCode
380
- }]
381
- });
382
- if (existing) await options.subscription.onSubscriptionCancel?.({
383
- event,
384
- subscription: {
385
- ...existing,
386
- status: "canceled"
387
- }
388
- }, ctx);
389
- }
439
+ }
440
+ if (eventName === "subscription.disable" || eventName === "subscription.not_renew") {
441
+ const subscriptionCode = data?.subscription_code ?? data?.subscription?.subscription_code ?? data?.code;
442
+ if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
443
+ const existing = await ctx.context.adapter.findOne({
444
+ model: "subscription",
445
+ where: [{
446
+ field: "paystackSubscriptionCode",
447
+ value: subscriptionCode
448
+ }]
449
+ });
450
+ let newStatus = "canceled";
451
+ if (existing?.cancelAtPeriodEnd === true && existing.periodEnd !== void 0 && existing.periodEnd !== null && new Date(existing.periodEnd) > /* @__PURE__ */ new Date()) newStatus = "active";
452
+ await ctx.context.adapter.update({
453
+ model: "subscription",
454
+ update: {
455
+ status: newStatus,
456
+ updatedAt: /* @__PURE__ */ new Date()
457
+ },
458
+ where: [{
459
+ field: "paystackSubscriptionCode",
460
+ value: subscriptionCode
461
+ }]
462
+ });
463
+ if (existing) await options.subscription.onSubscriptionCancel?.({
464
+ event,
465
+ subscription: {
466
+ ...existing,
467
+ status: "canceled"
468
+ }
469
+ }, ctx);
390
470
  }
391
- } catch (_e) {
392
- ctx.context.logger.error("Failed to sync Paystack webhook event", _e);
393
471
  }
472
+ } catch (_e) {
473
+ ctx.context.logger.error("Failed to sync Paystack webhook event", _e);
394
474
  }
395
475
  await options.onEvent?.(event);
396
476
  return ctx.json({ received: true });
@@ -449,14 +529,14 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
449
529
  let product;
450
530
  if (planName !== void 0 && planName !== null && planName !== "") {
451
531
  if (subscriptionOptions?.enabled !== true) throw new APIError("BAD_REQUEST", { message: "Subscriptions are not enabled." });
452
- plan = await getPlanByName(options, planName);
532
+ plan = await getPlanByName(options, planName) ?? void 0;
453
533
  if (!plan) throw new APIError("BAD_REQUEST", {
454
534
  code: "SUBSCRIPTION_PLAN_NOT_FOUND",
455
535
  message: PAYSTACK_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,
456
536
  status: 400
457
537
  });
458
538
  } else if (productName !== void 0 && productName !== null && productName !== "") {
459
- if (typeof productName === "string") product = await getProductByName(options, productName);
539
+ if (typeof productName === "string") product = await getProductByName(options, productName) ?? void 0;
460
540
  if (!product) throw new APIError("BAD_REQUEST", {
461
541
  message: `Product '${productName}' not found.`,
462
542
  status: 400
@@ -465,7 +545,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
465
545
  message: "Either 'plan', 'product', or 'amount' is required to initialize a transaction.",
466
546
  status: 400
467
547
  });
468
- const amount = bodyAmount ?? product?.amount;
548
+ const amount = bodyAmount ?? product?.price;
469
549
  const finalCurrency = currency ?? product?.currency ?? plan?.currency ?? "NGN";
470
550
  let url;
471
551
  let reference;
@@ -580,6 +660,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
580
660
  currency: plan?.currency ?? currency ?? "NGN",
581
661
  status: "pending",
582
662
  plan: plan?.name.toLowerCase(),
663
+ product: product?.name.toLowerCase(),
583
664
  metadata: extraMetadata !== void 0 && extraMetadata !== null ? JSON.stringify(extraMetadata) : void 0,
584
665
  createdAt: /* @__PURE__ */ new Date(),
585
666
  updatedAt: /* @__PURE__ */ new Date()
@@ -651,13 +732,12 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
651
732
  message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_VERIFY_TRANSACTION
652
733
  });
653
734
  }
654
- let data = verifyRes !== null && verifyRes !== void 0 && typeof verifyRes === "object" && "status" in verifyRes && "data" in verifyRes ? verifyRes.data : verifyRes?.data !== void 0 ? verifyRes.data : verifyRes;
655
- if (data !== null && data !== void 0 && typeof data === "object" && "status" in data && "data" in data) data = data.data;
735
+ const data = unwrapSdkResult(verifyRes);
656
736
  const status = data?.status;
657
737
  const reference = data?.reference ?? ctx.body.reference;
658
738
  const paystackId = data?.id !== void 0 && data?.id !== null ? String(data.id) : void 0;
659
739
  const authorizationCode = (data?.authorization)?.authorization_code;
660
- if (status === "success") try {
740
+ if (status === "success") {
661
741
  const session = await getSessionFromCtx(ctx);
662
742
  const referenceId = (await ctx.context.adapter.findOne({
663
743
  model: "paystackTransaction",
@@ -690,107 +770,116 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
690
770
  }
691
771
  if (!authorized) throw new APIError("UNAUTHORIZED");
692
772
  }
693
- await ctx.context.adapter.update({
694
- model: "paystackTransaction",
695
- update: {
696
- status: "success",
697
- paystackId,
698
- ...data?.amount !== void 0 && data?.amount !== null ? { amount: data.amount } : {},
699
- ...data?.currency !== void 0 && data?.currency !== null ? { currency: data.currency } : {},
700
- updatedAt: /* @__PURE__ */ new Date()
701
- },
702
- where: [{
703
- field: "reference",
704
- value: reference
705
- }]
706
- });
707
- const customer = data?.customer;
708
- const paystackCustomerCodeFromPaystack = customer !== void 0 && customer !== null && typeof customer === "object" ? customer.customer_code : void 0;
709
- if (paystackCustomerCodeFromPaystack !== void 0 && paystackCustomerCodeFromPaystack !== null && paystackCustomerCodeFromPaystack !== "" && referenceId !== void 0 && referenceId !== null && referenceId !== "") if ((options.organization?.enabled === true && (referenceId.startsWith("org_") || await ctx.context.adapter.findOne({
710
- model: "organization",
711
- where: [{
712
- field: "id",
713
- value: referenceId
714
- }]
715
- }) !== null)) === true) await ctx.context.adapter.update({
716
- model: "organization",
717
- update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },
718
- where: [{
719
- field: "id",
720
- value: referenceId
721
- }]
722
- });
723
- else await ctx.context.adapter.update({
724
- model: "user",
725
- update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },
726
- where: [{
727
- field: "id",
728
- value: referenceId
729
- }]
730
- });
731
- let isTrial = false;
732
- let trialEnd;
733
- let targetPlan;
734
- if (data?.metadata !== void 0 && data?.metadata !== null) {
735
- const metaRaw = data.metadata;
736
- const meta = typeof metaRaw === "string" ? JSON.parse(metaRaw) : metaRaw;
737
- isTrial = meta.isTrial === true || meta.isTrial === "true";
738
- trialEnd = meta.trialEnd;
739
- targetPlan = meta.plan;
740
- }
741
- let paystackSubscriptionCode;
742
- if (isTrial === true && targetPlan !== void 0 && targetPlan !== null && targetPlan !== "" && trialEnd !== void 0 && trialEnd !== null && trialEnd !== "") {
743
- const email = (data?.customer)?.email;
744
- const planConfig = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === targetPlan?.toLowerCase());
745
- if (planConfig !== void 0 && (planConfig.planCode === void 0 || planConfig.planCode === null || planConfig.planCode === "")) paystackSubscriptionCode = `LOC_${reference}`;
746
- if (authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" && email !== void 0 && email !== null && email !== "" && planConfig?.planCode !== void 0 && planConfig?.planCode !== null && planConfig?.planCode !== "") {
747
- const subData = unwrapSdkResult(await paystack.subscriptionCreate({
748
- customer: email,
749
- plan: planConfig.planCode,
750
- authorization: authorizationCode,
751
- start_date: trialEnd
752
- }));
753
- paystackSubscriptionCode = (subData?.data ?? subData)?.subscription_code;
773
+ try {
774
+ await ctx.context.adapter.update({
775
+ model: "paystackTransaction",
776
+ update: {
777
+ status: "success",
778
+ paystackId,
779
+ ...data?.amount !== void 0 && data?.amount !== null ? { amount: data.amount } : {},
780
+ ...data?.currency !== void 0 && data?.currency !== null ? { currency: data.currency } : {},
781
+ updatedAt: /* @__PURE__ */ new Date()
782
+ },
783
+ where: [{
784
+ field: "reference",
785
+ value: reference
786
+ }]
787
+ });
788
+ const customer = data?.customer;
789
+ const paystackCustomerCodeFromPaystack = customer !== void 0 && customer !== null && typeof customer === "object" ? customer.customer_code : void 0;
790
+ if (paystackCustomerCodeFromPaystack !== void 0 && paystackCustomerCodeFromPaystack !== null && paystackCustomerCodeFromPaystack !== "" && referenceId !== void 0 && referenceId !== null && referenceId !== "") if ((options.organization?.enabled === true && (referenceId.startsWith("org_") || await ctx.context.adapter.findOne({
791
+ model: "organization",
792
+ where: [{
793
+ field: "id",
794
+ value: referenceId
795
+ }]
796
+ }) !== null)) === true) await ctx.context.adapter.update({
797
+ model: "organization",
798
+ update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },
799
+ where: [{
800
+ field: "id",
801
+ value: referenceId
802
+ }]
803
+ });
804
+ else await ctx.context.adapter.update({
805
+ model: "user",
806
+ update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },
807
+ where: [{
808
+ field: "id",
809
+ value: referenceId
810
+ }]
811
+ });
812
+ const transaction = await ctx.context.adapter.findOne({
813
+ model: "paystackTransaction",
814
+ where: [{
815
+ field: "reference",
816
+ value: reference
817
+ }]
818
+ });
819
+ if (transaction?.product) await syncProductQuantityFromPaystack(ctx, transaction.product, options.paystackClient);
820
+ let isTrial = false;
821
+ let trialEnd;
822
+ let targetPlan;
823
+ if (data?.metadata !== void 0 && data?.metadata !== null) {
824
+ const metaRaw = data.metadata;
825
+ const meta = typeof metaRaw === "string" ? JSON.parse(metaRaw) : metaRaw;
826
+ isTrial = meta.isTrial === true || meta.isTrial === "true";
827
+ trialEnd = meta.trialEnd;
828
+ targetPlan = meta.plan;
754
829
  }
755
- } else if (isTrial !== true) {
756
- const planCodeFromPaystack = (data?.plan)?.plan_code;
757
- if (planCodeFromPaystack === void 0 || planCodeFromPaystack === null || planCodeFromPaystack === "") paystackSubscriptionCode = `LOC_${reference}`;
758
- else paystackSubscriptionCode = (data?.subscription)?.subscription_code;
759
- }
760
- const updatedSubscription = await ctx.context.adapter.update({
761
- model: "subscription",
762
- update: {
763
- status: isTrial === true ? "trialing" : "active",
764
- periodStart: /* @__PURE__ */ new Date(),
765
- updatedAt: /* @__PURE__ */ new Date(),
766
- ...isTrial === true && trialEnd !== void 0 && trialEnd !== null && trialEnd !== "" ? {
767
- trialStart: /* @__PURE__ */ new Date(),
768
- trialEnd: new Date(trialEnd),
769
- periodEnd: new Date(trialEnd)
770
- } : {},
771
- ...paystackSubscriptionCode !== void 0 && paystackSubscriptionCode !== null && paystackSubscriptionCode !== "" ? { paystackSubscriptionCode } : {},
772
- ...authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" ? { paystackAuthorizationCode: authorizationCode } : {}
773
- },
774
- where: [{
775
- field: "paystackTransactionReference",
776
- value: reference
777
- }, ...referenceId !== void 0 && referenceId !== null && referenceId !== "" ? [{
778
- field: "referenceId",
779
- value: referenceId
780
- }] : []]
781
- });
782
- if (updatedSubscription && subscriptionOptions?.enabled === true && "onSubscriptionComplete" in subscriptionOptions && typeof subscriptionOptions.onSubscriptionComplete === "function") {
783
- const plan = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === updatedSubscription.plan.toLowerCase());
784
- if (plan) await subscriptionOptions.onSubscriptionComplete({
785
- event: data,
786
- subscription: updatedSubscription,
787
- plan
788
- }, ctx);
830
+ let paystackSubscriptionCode;
831
+ if (isTrial === true && targetPlan !== void 0 && targetPlan !== null && targetPlan !== "" && trialEnd !== void 0 && trialEnd !== null && trialEnd !== "") {
832
+ const email = (data?.customer)?.email;
833
+ const planConfig = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === targetPlan?.toLowerCase());
834
+ if (planConfig !== void 0 && (planConfig.planCode === void 0 || planConfig.planCode === null || planConfig.planCode === "")) paystackSubscriptionCode = `LOC_${reference}`;
835
+ if (authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" && email !== void 0 && email !== null && email !== "" && planConfig?.planCode !== void 0 && planConfig?.planCode !== null && planConfig?.planCode !== "") {
836
+ const subData = unwrapSdkResult(await paystack.subscriptionCreate({
837
+ customer: email,
838
+ plan: planConfig.planCode,
839
+ authorization: authorizationCode,
840
+ start_date: trialEnd
841
+ }));
842
+ paystackSubscriptionCode = (subData?.data ?? subData)?.subscription_code;
843
+ }
844
+ } else if (isTrial !== true) {
845
+ const planCodeFromPaystack = (data?.plan)?.plan_code;
846
+ if (planCodeFromPaystack === void 0 || planCodeFromPaystack === null || planCodeFromPaystack === "") paystackSubscriptionCode = `LOC_${reference}`;
847
+ else paystackSubscriptionCode = (data?.subscription)?.subscription_code;
848
+ }
849
+ const updatedSubscription = await ctx.context.adapter.update({
850
+ model: "subscription",
851
+ update: {
852
+ status: isTrial === true ? "trialing" : "active",
853
+ periodStart: /* @__PURE__ */ new Date(),
854
+ updatedAt: /* @__PURE__ */ new Date(),
855
+ ...isTrial === true && trialEnd !== void 0 && trialEnd !== null && trialEnd !== "" ? {
856
+ trialStart: /* @__PURE__ */ new Date(),
857
+ trialEnd: new Date(trialEnd),
858
+ periodEnd: new Date(trialEnd)
859
+ } : {},
860
+ ...paystackSubscriptionCode !== void 0 && paystackSubscriptionCode !== null && paystackSubscriptionCode !== "" ? { paystackSubscriptionCode } : {},
861
+ ...authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" ? { paystackAuthorizationCode: authorizationCode } : {}
862
+ },
863
+ where: [{
864
+ field: "paystackTransactionReference",
865
+ value: reference
866
+ }, ...referenceId !== void 0 && referenceId !== null && referenceId !== "" ? [{
867
+ field: "referenceId",
868
+ value: referenceId
869
+ }] : []]
870
+ });
871
+ if (updatedSubscription && subscriptionOptions?.enabled === true && "onSubscriptionComplete" in subscriptionOptions && typeof subscriptionOptions.onSubscriptionComplete === "function") {
872
+ const plan = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === updatedSubscription.plan.toLowerCase());
873
+ if (plan) await subscriptionOptions.onSubscriptionComplete({
874
+ event: data,
875
+ subscription: updatedSubscription,
876
+ plan
877
+ }, ctx);
878
+ }
879
+ } catch (e) {
880
+ ctx.context.logger.error("Failed to update transaction/subscription after verification", e);
789
881
  }
790
- } catch (e) {
791
- ctx.context.logger.error("Failed to update transaction/subscription after verification", e);
792
- }
793
- else if (status === "failed" || status === "abandoned") try {
882
+ } else if (status === "failed" || status === "abandoned") try {
794
883
  await ctx.context.adapter.update({
795
884
  model: "paystackTransaction",
796
885
  update: {
@@ -1049,6 +1138,69 @@ const getSubscriptionManageLink = (options) => {
1049
1138
  }
1050
1139
  });
1051
1140
  };
1141
+ const syncProducts = (options) => {
1142
+ return createAuthEndpoint("/paystack/sync-products", {
1143
+ method: "POST",
1144
+ metadata: { ...HIDE_METADATA },
1145
+ disableBody: true,
1146
+ use: [sessionMiddleware]
1147
+ }, async (ctx) => {
1148
+ console.error("DEBUG: syncProducts endpoint hit!");
1149
+ const paystack = getPaystackOps(options.paystackClient);
1150
+ try {
1151
+ const res = unwrapSdkResult(await paystack.productList());
1152
+ const productsData = res !== null && typeof res === "object" && "status" in res && "data" in res ? res.data : res?.data ?? res;
1153
+ if (!Array.isArray(productsData)) return ctx.json({
1154
+ status: "success",
1155
+ count: 0
1156
+ });
1157
+ for (const product of productsData) {
1158
+ const paystackId = String(product.id);
1159
+ const existing = await ctx.context.adapter.findOne({
1160
+ model: "paystackProduct",
1161
+ where: [{
1162
+ field: "paystackId",
1163
+ value: paystackId
1164
+ }]
1165
+ });
1166
+ const productData = {
1167
+ name: product.name,
1168
+ description: product.description,
1169
+ price: product.price,
1170
+ currency: product.currency,
1171
+ quantity: product.quantity,
1172
+ unlimited: product.unlimited,
1173
+ paystackId,
1174
+ slug: product.slug ?? product.name.toLowerCase().replace(/\s+/g, "-"),
1175
+ metadata: product.metadata ? JSON.stringify(product.metadata) : void 0,
1176
+ updatedAt: /* @__PURE__ */ new Date()
1177
+ };
1178
+ if (existing) await ctx.context.adapter.update({
1179
+ model: "paystackProduct",
1180
+ update: productData,
1181
+ where: [{
1182
+ field: "id",
1183
+ value: existing.id
1184
+ }]
1185
+ });
1186
+ else await ctx.context.adapter.create({
1187
+ model: "paystackProduct",
1188
+ data: {
1189
+ ...productData,
1190
+ createdAt: /* @__PURE__ */ new Date()
1191
+ }
1192
+ });
1193
+ }
1194
+ return ctx.json({
1195
+ status: "success",
1196
+ count: productsData.length
1197
+ });
1198
+ } catch (error) {
1199
+ ctx.context.logger.error("Failed to sync products", error);
1200
+ throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to sync products" });
1201
+ }
1202
+ });
1203
+ };
1052
1204
  const getConfig = (options) => {
1053
1205
  return createAuthEndpoint("/paystack/get-config", {
1054
1206
  method: "GET",
@@ -1115,7 +1267,11 @@ const chargeRecurringSubscription = (options) => {
1115
1267
  }
1116
1268
  }
1117
1269
  if (email === void 0 || email === null || email === "") throw new APIError("NOT_FOUND", { message: "User email not found" });
1118
- if (!validateMinAmount(amount, plan.currency ?? "NGN")) throw new APIError("BAD_REQUEST", { message: `Amount ${amount} is below minimum for ${plan.currency ?? "NGN"}` });
1270
+ const finalCurrency = plan.currency ?? "NGN";
1271
+ if (!validateMinAmount(amount, finalCurrency)) throw new APIError("BAD_REQUEST", {
1272
+ message: `Amount ${amount} is less than the minimum required for ${finalCurrency}.`,
1273
+ status: 400
1274
+ });
1119
1275
  const data = unwrapSdkResult(await getPaystackOps(options.paystackClient).transactionChargeAuthorization({
1120
1276
  email,
1121
1277
  amount,
@@ -1161,7 +1317,8 @@ const chargeRecurringSubscription = (options) => {
1161
1317
  const transactions = { paystackTransaction: { fields: {
1162
1318
  reference: {
1163
1319
  type: "string",
1164
- required: true
1320
+ required: true,
1321
+ unique: true
1165
1322
  },
1166
1323
  paystackId: {
1167
1324
  type: "string",
@@ -1169,11 +1326,13 @@ const transactions = { paystackTransaction: { fields: {
1169
1326
  },
1170
1327
  referenceId: {
1171
1328
  type: "string",
1172
- required: true
1329
+ required: true,
1330
+ index: true
1173
1331
  },
1174
1332
  userId: {
1175
1333
  type: "string",
1176
- required: true
1334
+ required: true,
1335
+ index: true
1177
1336
  },
1178
1337
  amount: {
1179
1338
  type: "number",
@@ -1191,6 +1350,10 @@ const transactions = { paystackTransaction: { fields: {
1191
1350
  type: "string",
1192
1351
  required: false
1193
1352
  },
1353
+ product: {
1354
+ type: "string",
1355
+ required: false
1356
+ },
1194
1357
  metadata: {
1195
1358
  type: "string",
1196
1359
  required: false
@@ -1207,23 +1370,28 @@ const transactions = { paystackTransaction: { fields: {
1207
1370
  const subscriptions = { subscription: { fields: {
1208
1371
  plan: {
1209
1372
  type: "string",
1210
- required: true
1373
+ required: true,
1374
+ index: true
1211
1375
  },
1212
1376
  referenceId: {
1213
1377
  type: "string",
1214
- required: true
1378
+ required: true,
1379
+ index: true
1215
1380
  },
1216
1381
  paystackCustomerCode: {
1217
1382
  type: "string",
1218
- required: false
1383
+ required: false,
1384
+ index: true
1219
1385
  },
1220
1386
  paystackSubscriptionCode: {
1221
1387
  type: "string",
1222
- required: false
1388
+ required: false,
1389
+ unique: true
1223
1390
  },
1224
1391
  paystackTransactionReference: {
1225
1392
  type: "string",
1226
- required: false
1393
+ required: false,
1394
+ index: true
1227
1395
  },
1228
1396
  paystackAuthorizationCode: {
1229
1397
  type: "string",
@@ -1269,28 +1437,82 @@ const subscriptions = { subscription: { fields: {
1269
1437
  } } };
1270
1438
  const user = { user: { fields: { paystackCustomerCode: {
1271
1439
  type: "string",
1272
- required: false
1440
+ required: false,
1441
+ index: true
1273
1442
  } } } };
1274
1443
  const organization = { organization: { fields: {
1275
1444
  paystackCustomerCode: {
1276
1445
  type: "string",
1277
- required: false
1446
+ required: false,
1447
+ index: true
1278
1448
  },
1279
1449
  email: {
1280
1450
  type: "string",
1281
1451
  required: false
1282
1452
  }
1283
1453
  } } };
1454
+ const products = { paystackProduct: { fields: {
1455
+ name: {
1456
+ type: "string",
1457
+ required: true
1458
+ },
1459
+ description: {
1460
+ type: "string",
1461
+ required: false
1462
+ },
1463
+ price: {
1464
+ type: "number",
1465
+ required: true
1466
+ },
1467
+ currency: {
1468
+ type: "string",
1469
+ required: true
1470
+ },
1471
+ quantity: {
1472
+ type: "number",
1473
+ required: false,
1474
+ defaultValue: 0
1475
+ },
1476
+ unlimited: {
1477
+ type: "boolean",
1478
+ required: false,
1479
+ defaultValue: true
1480
+ },
1481
+ paystackId: {
1482
+ type: "string",
1483
+ required: false,
1484
+ unique: true
1485
+ },
1486
+ slug: {
1487
+ type: "string",
1488
+ required: true,
1489
+ unique: true
1490
+ },
1491
+ metadata: {
1492
+ type: "string",
1493
+ required: false
1494
+ },
1495
+ createdAt: {
1496
+ type: "date",
1497
+ required: true
1498
+ },
1499
+ updatedAt: {
1500
+ type: "date",
1501
+ required: true
1502
+ }
1503
+ } } };
1284
1504
  const getSchema = (options) => {
1285
1505
  let baseSchema;
1286
1506
  if (options.subscription?.enabled === true) baseSchema = {
1287
1507
  ...subscriptions,
1288
1508
  ...transactions,
1289
- ...user
1509
+ ...user,
1510
+ ...products
1290
1511
  };
1291
1512
  else baseSchema = {
1292
1513
  ...user,
1293
- ...transactions
1514
+ ...transactions,
1515
+ ...products
1294
1516
  };
1295
1517
  if (options.organization?.enabled === true) baseSchema = {
1296
1518
  ...baseSchema,
@@ -1358,7 +1580,8 @@ const paystack = (options) => {
1358
1580
  upgradeSubscription: upgradeSubscription(options),
1359
1581
  cancelSubscription: cancelSubscription(options),
1360
1582
  restoreSubscription: restoreSubscription(options),
1361
- chargeRecurringSubscription: chargeRecurringSubscription(options)
1583
+ chargeRecurringSubscription: chargeRecurringSubscription(options),
1584
+ syncProducts: syncProducts(options)
1362
1585
  },
1363
1586
  schema: getSchema(options),
1364
1587
  init: (ctx) => {
@@ -1366,12 +1589,12 @@ const paystack = (options) => {
1366
1589
  databaseHooks: {
1367
1590
  user: { create: { async after(user, hookCtx) {
1368
1591
  if (hookCtx === void 0 || hookCtx === null || options.createCustomerOnSignUp !== true) return;
1369
- const data = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate({
1592
+ const sdkRes = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate({
1370
1593
  email: user.email,
1371
1594
  first_name: user.name ?? void 0,
1372
1595
  metadata: { userId: user.id }
1373
1596
  }));
1374
- const customerCode = data?.customer_code ?? (data?.data)?.customer_code;
1597
+ const customerCode = sdkRes?.customer_code ?? (sdkRes?.data)?.customer_code;
1375
1598
  if (customerCode === void 0 || customerCode === null) return;
1376
1599
  await ctx.adapter.update({
1377
1600
  model: "user",
@@ -1412,12 +1635,11 @@ const paystack = (options) => {
1412
1635
  metadata: { organizationId: org.id }
1413
1636
  }, extraCreateParams);
1414
1637
  const sdkRes = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate(params));
1415
- const paystackCustomer = sdkRes !== null && typeof sdkRes === "object" && "status" in sdkRes && "data" in sdkRes ? sdkRes.data : sdkRes?.data ?? sdkRes;
1416
- const customerCode = paystackCustomer?.customer_code;
1638
+ const customerCode = sdkRes?.customer_code ?? (sdkRes?.data)?.customer_code;
1417
1639
  if (customerCode === void 0 || customerCode === null) return;
1418
1640
  await ctx.internalAdapter.updateOrganization(org.id, { paystackCustomerCode: customerCode });
1419
1641
  await options.organization?.onCustomerCreate?.({
1420
- paystackCustomer,
1642
+ paystackCustomer: sdkRes,
1421
1643
  organization: {
1422
1644
  ...org,
1423
1645
  paystackCustomerCode: customerCode