@alexasomba/better-auth-paystack 1.0.0-rc.2 → 1.0.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/README.md +8 -8
- package/dist/client.d.mts +56 -43
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +2 -2
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +55 -41
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +199 -198
- package/dist/index.mjs.map +1 -1
- package/dist/{types--kIktPs7.d.mts → types-Du5udJ7X.d.mts} +42 -35
- package/dist/{types--kIktPs7.d.mts.map → types-Du5udJ7X.d.mts.map} +1 -1
- package/package.json +46 -9
package/dist/index.mjs
CHANGED
|
@@ -8,11 +8,12 @@ import { mergeSchema } from "better-auth/db";
|
|
|
8
8
|
|
|
9
9
|
//#region src/utils.ts
|
|
10
10
|
async function getPlans(subscriptionOptions) {
|
|
11
|
-
if (subscriptionOptions?.enabled) return typeof subscriptionOptions.plans === "function" ?
|
|
11
|
+
if (subscriptionOptions?.enabled === true) return typeof subscriptionOptions.plans === "function" ? subscriptionOptions.plans() : subscriptionOptions.plans;
|
|
12
12
|
throw new Error("Subscriptions are not enabled in the Paystack options.");
|
|
13
13
|
}
|
|
14
14
|
async function getPlanByName(options, name) {
|
|
15
|
-
return await getPlans(options.subscription).
|
|
15
|
+
if (options.subscription?.enabled === true) return (await getPlans(options.subscription)).find((plan) => plan.name.toLowerCase() === name.toLowerCase()) ?? null;
|
|
16
|
+
return null;
|
|
16
17
|
}
|
|
17
18
|
async function getProducts(productOptions) {
|
|
18
19
|
if (productOptions?.products) return typeof productOptions.products === "function" ? await productOptions.products() : productOptions.products;
|
|
@@ -26,20 +27,22 @@ async function getProductByName(options, name) {
|
|
|
26
27
|
//#region src/middleware.ts
|
|
27
28
|
const referenceMiddleware = (options, action) => createAuthMiddleware(async (ctx) => {
|
|
28
29
|
const session = ctx.context.session;
|
|
29
|
-
if (
|
|
30
|
-
const
|
|
30
|
+
if (session === null || session === void 0) throw new APIError("UNAUTHORIZED");
|
|
31
|
+
const body = ctx.body ?? {};
|
|
32
|
+
const query = ctx.query ?? {};
|
|
33
|
+
const referenceId = body.referenceId ?? query.referenceId ?? session.user.id;
|
|
31
34
|
const subscriptionOptions = options.subscription;
|
|
32
35
|
if (referenceId === session.user.id) return { referenceId };
|
|
33
|
-
if (subscriptionOptions?.enabled && "authorizeReference" in subscriptionOptions && subscriptionOptions.authorizeReference) {
|
|
36
|
+
if (subscriptionOptions?.enabled === true && "authorizeReference" in subscriptionOptions && subscriptionOptions.authorizeReference) {
|
|
34
37
|
if (await subscriptionOptions.authorizeReference({
|
|
35
38
|
user: session.user,
|
|
36
|
-
session,
|
|
39
|
+
session: session.session,
|
|
37
40
|
referenceId,
|
|
38
41
|
action
|
|
39
|
-
}, ctx)) return { referenceId };
|
|
42
|
+
}, ctx) === true) return { referenceId };
|
|
40
43
|
throw new APIError("UNAUTHORIZED");
|
|
41
44
|
}
|
|
42
|
-
if (options.organization?.enabled) {
|
|
45
|
+
if (options.organization?.enabled === true) {
|
|
43
46
|
const member = await ctx.context.adapter.findOne({
|
|
44
47
|
model: "member",
|
|
45
48
|
where: [{
|
|
@@ -50,8 +53,8 @@ const referenceMiddleware = (options, action) => createAuthMiddleware(async (ctx
|
|
|
50
53
|
value: referenceId
|
|
51
54
|
}]
|
|
52
55
|
});
|
|
53
|
-
if (member) {
|
|
54
|
-
|
|
56
|
+
if (member !== null && member !== void 0) {
|
|
57
|
+
logger.debug("DEBUG MIDDLEWARE MEMBER FOUND:", member);
|
|
55
58
|
return { referenceId };
|
|
56
59
|
}
|
|
57
60
|
}
|
|
@@ -62,18 +65,18 @@ const referenceMiddleware = (options, action) => createAuthMiddleware(async (ctx
|
|
|
62
65
|
//#endregion
|
|
63
66
|
//#region src/paystack-sdk.ts
|
|
64
67
|
function isOpenApiFetchResponse(value) {
|
|
65
|
-
return
|
|
68
|
+
return value !== null && value !== void 0 && typeof value === "object" && ("data" in value || "error" in value || "response" in value);
|
|
66
69
|
}
|
|
67
70
|
function unwrapSdkResult(result) {
|
|
68
71
|
if (isOpenApiFetchResponse(result)) {
|
|
69
|
-
if (result.error) throw result.error;
|
|
72
|
+
if (result.error !== void 0 && result.error !== null) throw new Error(typeof result.error === "string" ? result.error : JSON.stringify(result.error));
|
|
70
73
|
return result.data;
|
|
71
74
|
}
|
|
72
|
-
if (result && typeof result === "object" && "data" in result) return result.data ?? result;
|
|
75
|
+
if (result !== null && result !== void 0 && typeof result === "object" && "data" in result) return result.data ?? result;
|
|
73
76
|
return result;
|
|
74
77
|
}
|
|
75
78
|
const normalizeMetadata = (value) => {
|
|
76
|
-
if (
|
|
79
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
77
80
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
78
81
|
};
|
|
79
82
|
const normalizeMetadataBody = (body) => {
|
|
@@ -87,15 +90,15 @@ const normalizeMetadataBody = (body) => {
|
|
|
87
90
|
};
|
|
88
91
|
function getPaystackOps(paystackClient) {
|
|
89
92
|
return {
|
|
90
|
-
customerCreate:
|
|
91
|
-
if (paystackClient?.customer_create) {
|
|
93
|
+
customerCreate: (params) => {
|
|
94
|
+
if (paystackClient?.customer_create !== void 0) {
|
|
92
95
|
const body = normalizeMetadataBody(params);
|
|
93
96
|
return paystackClient.customer_create({ body });
|
|
94
97
|
}
|
|
95
98
|
return paystackClient?.customer?.create?.(params);
|
|
96
99
|
},
|
|
97
|
-
customerUpdate:
|
|
98
|
-
if (paystackClient?.customer_update) {
|
|
100
|
+
customerUpdate: (code, params) => {
|
|
101
|
+
if (paystackClient?.customer_update !== void 0) {
|
|
99
102
|
const body = normalizeMetadataBody(params);
|
|
100
103
|
return paystackClient.customer_update({
|
|
101
104
|
params: { path: { code } },
|
|
@@ -104,28 +107,28 @@ function getPaystackOps(paystackClient) {
|
|
|
104
107
|
}
|
|
105
108
|
return paystackClient?.customer?.update?.(code, params);
|
|
106
109
|
},
|
|
107
|
-
transactionInitialize:
|
|
108
|
-
if (paystackClient?.transaction_initialize) return paystackClient.transaction_initialize({ body });
|
|
110
|
+
transactionInitialize: (body) => {
|
|
111
|
+
if (paystackClient?.transaction_initialize !== void 0) return paystackClient.transaction_initialize({ body });
|
|
109
112
|
return paystackClient?.transaction?.initialize?.(body);
|
|
110
113
|
},
|
|
111
|
-
transactionVerify:
|
|
112
|
-
if (paystackClient?.transaction_verify) return paystackClient.transaction_verify({ params: { path: { reference } } });
|
|
114
|
+
transactionVerify: (reference) => {
|
|
115
|
+
if (paystackClient?.transaction_verify !== void 0) return paystackClient.transaction_verify({ params: { path: { reference } } });
|
|
113
116
|
return paystackClient?.transaction?.verify?.(reference);
|
|
114
117
|
},
|
|
115
|
-
subscriptionCreate:
|
|
116
|
-
if (paystackClient?.subscription_create) return paystackClient.subscription_create({ body });
|
|
118
|
+
subscriptionCreate: (body) => {
|
|
119
|
+
if (paystackClient?.subscription_create !== void 0) return paystackClient.subscription_create({ body });
|
|
117
120
|
return paystackClient?.subscription?.create?.(body);
|
|
118
121
|
},
|
|
119
|
-
subscriptionDisable:
|
|
120
|
-
if (paystackClient?.subscription_disable) return paystackClient.subscription_disable({ body });
|
|
122
|
+
subscriptionDisable: (body) => {
|
|
123
|
+
if (paystackClient?.subscription_disable !== void 0) return paystackClient.subscription_disable({ body });
|
|
121
124
|
return paystackClient?.subscription?.disable?.(body);
|
|
122
125
|
},
|
|
123
|
-
subscriptionEnable:
|
|
124
|
-
if (paystackClient?.subscription_enable) return paystackClient.subscription_enable({ body });
|
|
126
|
+
subscriptionEnable: (body) => {
|
|
127
|
+
if (paystackClient?.subscription_enable !== void 0) return paystackClient.subscription_enable({ body });
|
|
125
128
|
return paystackClient?.subscription?.enable?.(body);
|
|
126
129
|
},
|
|
127
130
|
subscriptionFetch: async (idOrCode) => {
|
|
128
|
-
if (paystackClient?.subscription_fetch) try {
|
|
131
|
+
if (paystackClient?.subscription_fetch !== void 0) try {
|
|
129
132
|
return await paystackClient.subscription_fetch({ params: { path: { code: idOrCode } } });
|
|
130
133
|
} catch {
|
|
131
134
|
const compatFetch = paystackClient.subscription_fetch;
|
|
@@ -133,13 +136,13 @@ function getPaystackOps(paystackClient) {
|
|
|
133
136
|
}
|
|
134
137
|
return paystackClient?.subscription?.fetch?.(idOrCode);
|
|
135
138
|
},
|
|
136
|
-
subscriptionManageLink:
|
|
137
|
-
if (paystackClient?.subscription_manageLink) return paystackClient.subscription_manageLink({ params: { path: { code } } });
|
|
138
|
-
if (paystackClient?.subscription_manage_link) return paystackClient.subscription_manage_link({ params: { path: { code } } });
|
|
139
|
+
subscriptionManageLink: (code) => {
|
|
140
|
+
if (paystackClient?.subscription_manageLink !== void 0) return paystackClient.subscription_manageLink({ params: { path: { code } } });
|
|
141
|
+
if (paystackClient?.subscription_manage_link !== void 0) return paystackClient.subscription_manage_link({ params: { path: { code } } });
|
|
139
142
|
return paystackClient?.subscription?.manage?.link?.(code);
|
|
140
143
|
},
|
|
141
|
-
subscriptionManageEmail:
|
|
142
|
-
if (paystackClient?.subscription_manageEmail) return paystackClient.subscription_manageEmail({ params: { path: { code } } });
|
|
144
|
+
subscriptionManageEmail: (code, email) => {
|
|
145
|
+
if (paystackClient?.subscription_manageEmail !== void 0) return paystackClient.subscription_manageEmail({ params: { path: { code } } });
|
|
143
146
|
return paystackClient?.subscription?.manage?.email?.(code, email);
|
|
144
147
|
}
|
|
145
148
|
};
|
|
@@ -161,8 +164,9 @@ async function hmacSha512Hex(secret, message) {
|
|
|
161
164
|
const encoder = new TextEncoder();
|
|
162
165
|
const keyData = encoder.encode(secret);
|
|
163
166
|
const msgData = encoder.encode(message);
|
|
164
|
-
const
|
|
165
|
-
if (subtle) {
|
|
167
|
+
const crypto = globalThis.crypto;
|
|
168
|
+
if (crypto !== void 0 && crypto !== null && "subtle" in crypto) {
|
|
169
|
+
const subtle = crypto.subtle;
|
|
166
170
|
const key = await subtle.importKey("raw", keyData, {
|
|
167
171
|
name: "HMAC",
|
|
168
172
|
hash: "SHA-512"
|
|
@@ -183,9 +187,11 @@ const paystackWebhook = (options) => {
|
|
|
183
187
|
cloneRequest: true,
|
|
184
188
|
disableBody: true
|
|
185
189
|
}, async (ctx) => {
|
|
186
|
-
const
|
|
190
|
+
const request = ctx.requestClone ?? ctx.request;
|
|
191
|
+
if (!request) throw new APIError("BAD_REQUEST", { message: "Request object is missing from context" });
|
|
192
|
+
const payload = await request.text();
|
|
187
193
|
const signature = (ctx.headers ?? ctx.request?.headers)?.get("x-paystack-signature");
|
|
188
|
-
if (
|
|
194
|
+
if (signature === void 0 || signature === null || signature === "") throw new APIError("UNAUTHORIZED", {
|
|
189
195
|
message: "Missing x-paystack-signature header",
|
|
190
196
|
status: 401
|
|
191
197
|
});
|
|
@@ -194,14 +200,14 @@ const paystackWebhook = (options) => {
|
|
|
194
200
|
status: 401
|
|
195
201
|
});
|
|
196
202
|
const event = JSON.parse(payload);
|
|
197
|
-
if (options.subscription?.enabled) {
|
|
203
|
+
if (options.subscription?.enabled === true) {
|
|
198
204
|
const eventName = String(event?.event ?? "");
|
|
199
205
|
const data = event?.data;
|
|
200
206
|
try {
|
|
201
207
|
if (eventName === "charge.success") {
|
|
202
208
|
const reference = data?.reference;
|
|
203
|
-
const paystackId = data?.id ? String(data.id) : void 0;
|
|
204
|
-
if (reference) await ctx.context.adapter.update({
|
|
209
|
+
const paystackId = data?.id !== void 0 && data?.id !== null ? String(data.id) : void 0;
|
|
210
|
+
if (reference !== void 0 && reference !== null && reference !== "") await ctx.context.adapter.update({
|
|
205
211
|
model: "paystackTransaction",
|
|
206
212
|
update: {
|
|
207
213
|
status: "success",
|
|
@@ -216,7 +222,7 @@ const paystackWebhook = (options) => {
|
|
|
216
222
|
}
|
|
217
223
|
if (eventName === "charge.failure") {
|
|
218
224
|
const reference = data?.reference;
|
|
219
|
-
if (reference) try {
|
|
225
|
+
if (reference !== void 0 && reference !== null && reference !== "") try {
|
|
220
226
|
await ctx.context.adapter.update({
|
|
221
227
|
model: "paystackTransaction",
|
|
222
228
|
update: {
|
|
@@ -240,32 +246,34 @@ const paystackWebhook = (options) => {
|
|
|
240
246
|
if (typeof metadata === "string") try {
|
|
241
247
|
metadata = JSON.parse(metadata);
|
|
242
248
|
} catch {}
|
|
243
|
-
const referenceIdFromMetadata = typeof metadata === "object" && metadata ? metadata.referenceId : void 0;
|
|
244
|
-
let planNameFromMetadata = typeof metadata === "object" && metadata ? metadata.plan : void 0;
|
|
249
|
+
const referenceIdFromMetadata = typeof metadata === "object" && metadata !== null ? metadata.referenceId : void 0;
|
|
250
|
+
let planNameFromMetadata = typeof metadata === "object" && metadata !== null ? metadata.plan : void 0;
|
|
245
251
|
if (typeof planNameFromMetadata === "string") planNameFromMetadata = planNameFromMetadata.toLowerCase();
|
|
246
252
|
const plans = await getPlans(options.subscription);
|
|
247
|
-
const planFromCode = planCode ? plans.find((p) => p.planCode && p.planCode === planCode) : void 0;
|
|
248
|
-
const
|
|
249
|
-
|
|
253
|
+
const planFromCode = planCode !== void 0 && planCode !== null && planCode !== "" ? plans.find((p) => p.planCode !== void 0 && p.planCode !== null && p.planCode === planCode) : void 0;
|
|
254
|
+
const planPart = planFromCode?.name ?? planNameFromMetadata;
|
|
255
|
+
const planName = planPart !== void 0 && planPart !== null && planPart !== "" ? planPart.toLowerCase() : void 0;
|
|
256
|
+
if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
|
|
250
257
|
const where = [];
|
|
251
|
-
if (referenceIdFromMetadata) where.push({
|
|
258
|
+
if (referenceIdFromMetadata !== void 0 && referenceIdFromMetadata !== null && referenceIdFromMetadata !== "") where.push({
|
|
252
259
|
field: "referenceId",
|
|
253
260
|
value: referenceIdFromMetadata
|
|
254
261
|
});
|
|
255
|
-
else if (customerCode) where.push({
|
|
262
|
+
else if (customerCode !== void 0 && customerCode !== null && customerCode !== "") where.push({
|
|
256
263
|
field: "paystackCustomerCode",
|
|
257
264
|
value: customerCode
|
|
258
265
|
});
|
|
259
|
-
if (planName) where.push({
|
|
266
|
+
if (planName !== void 0 && planName !== null && planName !== "") where.push({
|
|
260
267
|
field: "plan",
|
|
261
268
|
value: planName
|
|
262
269
|
});
|
|
263
270
|
if (where.length > 0) {
|
|
264
|
-
const
|
|
271
|
+
const matches = await ctx.context.adapter.findMany({
|
|
265
272
|
model: "subscription",
|
|
266
273
|
where
|
|
267
|
-
})
|
|
268
|
-
|
|
274
|
+
});
|
|
275
|
+
const subscription = matches !== void 0 && matches !== null ? matches[0] : void 0;
|
|
276
|
+
if (subscription !== void 0 && subscription !== null) {
|
|
269
277
|
await ctx.context.adapter.update({
|
|
270
278
|
model: "subscription",
|
|
271
279
|
update: {
|
|
@@ -278,8 +286,8 @@ const paystackWebhook = (options) => {
|
|
|
278
286
|
value: subscription.id
|
|
279
287
|
}]
|
|
280
288
|
});
|
|
281
|
-
const plan = planFromCode ?? (planName ? await getPlanByName(options, planName) : void 0);
|
|
282
|
-
if (plan) {
|
|
289
|
+
const plan = planFromCode ?? (planName !== void 0 && planName !== null && planName !== "" ? await getPlanByName(options, planName) : void 0);
|
|
290
|
+
if (plan !== void 0 && plan !== null) {
|
|
283
291
|
await options.subscription.onSubscriptionComplete?.({
|
|
284
292
|
event,
|
|
285
293
|
subscription: {
|
|
@@ -305,7 +313,7 @@ const paystackWebhook = (options) => {
|
|
|
305
313
|
}
|
|
306
314
|
if (eventName === "subscription.disable" || eventName === "subscription.not_renew") {
|
|
307
315
|
const subscriptionCode = data?.subscription_code ?? data?.subscription?.subscription_code ?? data?.code;
|
|
308
|
-
if (subscriptionCode) {
|
|
316
|
+
if (subscriptionCode !== void 0 && subscriptionCode !== null && subscriptionCode !== "") {
|
|
309
317
|
const existing = await ctx.context.adapter.findOne({
|
|
310
318
|
model: "subscription",
|
|
311
319
|
where: [{
|
|
@@ -333,8 +341,8 @@ const paystackWebhook = (options) => {
|
|
|
333
341
|
}, ctx);
|
|
334
342
|
}
|
|
335
343
|
}
|
|
336
|
-
} catch (
|
|
337
|
-
ctx.context.logger.error("Failed to sync Paystack webhook event",
|
|
344
|
+
} catch (_e) {
|
|
345
|
+
ctx.context.logger.error("Failed to sync Paystack webhook event", _e);
|
|
338
346
|
}
|
|
339
347
|
}
|
|
340
348
|
await options.onEvent?.(event);
|
|
@@ -347,7 +355,7 @@ const initializeTransactionBodySchema = z.object({
|
|
|
347
355
|
amount: z.number().int().positive().optional(),
|
|
348
356
|
currency: z.string().optional(),
|
|
349
357
|
email: z.string().optional(),
|
|
350
|
-
metadata: z.record(z.string(), z.
|
|
358
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
351
359
|
referenceId: z.string().optional(),
|
|
352
360
|
callbackURL: z.string().optional(),
|
|
353
361
|
quantity: z.number().int().positive().optional()
|
|
@@ -357,7 +365,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
357
365
|
return createAuthEndpoint(path, {
|
|
358
366
|
method: "POST",
|
|
359
367
|
body: initializeTransactionBodySchema,
|
|
360
|
-
use: subscriptionOptions?.enabled ? [
|
|
368
|
+
use: subscriptionOptions?.enabled === true ? [
|
|
361
369
|
sessionMiddleware,
|
|
362
370
|
originCheck,
|
|
363
371
|
referenceMiddleware(options, "initialize-transaction")
|
|
@@ -365,7 +373,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
365
373
|
}, async (ctx) => {
|
|
366
374
|
const paystack = getPaystackOps(options.paystackClient);
|
|
367
375
|
const { plan: planName, product: productName, amount: bodyAmount, currency, email, metadata: extraMetadata, callbackURL, quantity } = ctx.body;
|
|
368
|
-
if (callbackURL) {
|
|
376
|
+
if (callbackURL !== void 0 && callbackURL !== null && callbackURL !== "") {
|
|
369
377
|
const checkTrusted = () => {
|
|
370
378
|
try {
|
|
371
379
|
if (!callbackURL) return false;
|
|
@@ -386,65 +394,56 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
386
394
|
const session = await getSessionFromCtx(ctx);
|
|
387
395
|
if (!session) throw new APIError("UNAUTHORIZED");
|
|
388
396
|
const user = session.user;
|
|
389
|
-
if (subscriptionOptions?.enabled && subscriptionOptions.requireEmailVerification && !user.emailVerified) throw new APIError("BAD_REQUEST", {
|
|
397
|
+
if (subscriptionOptions?.enabled === true && subscriptionOptions.requireEmailVerification === true && !user.emailVerified) throw new APIError("BAD_REQUEST", {
|
|
390
398
|
code: "EMAIL_VERIFICATION_REQUIRED",
|
|
391
399
|
message: PAYSTACK_ERROR_CODES.EMAIL_VERIFICATION_REQUIRED
|
|
392
400
|
});
|
|
393
401
|
let plan;
|
|
394
402
|
let product;
|
|
395
|
-
if (planName) {
|
|
396
|
-
if (
|
|
403
|
+
if (planName !== void 0 && planName !== null && planName !== "") {
|
|
404
|
+
if (subscriptionOptions?.enabled !== true) throw new APIError("BAD_REQUEST", { message: "Subscriptions are not enabled." });
|
|
397
405
|
plan = await getPlanByName(options, planName);
|
|
398
406
|
if (!plan) throw new APIError("BAD_REQUEST", {
|
|
399
407
|
code: "SUBSCRIPTION_PLAN_NOT_FOUND",
|
|
400
408
|
message: PAYSTACK_ERROR_CODES.SUBSCRIPTION_PLAN_NOT_FOUND,
|
|
401
409
|
status: 400
|
|
402
410
|
});
|
|
403
|
-
} else if (productName) {
|
|
404
|
-
product = await getProductByName(options, productName);
|
|
411
|
+
} else if (productName !== void 0 && productName !== null && productName !== "") {
|
|
412
|
+
if (typeof productName === "string") product = await getProductByName(options, productName);
|
|
405
413
|
if (!product) throw new APIError("BAD_REQUEST", {
|
|
406
414
|
message: `Product '${productName}' not found.`,
|
|
407
415
|
status: 400
|
|
408
416
|
});
|
|
409
|
-
} else if (
|
|
417
|
+
} else if (bodyAmount === void 0 || bodyAmount === null || bodyAmount === 0) throw new APIError("BAD_REQUEST", {
|
|
410
418
|
message: "Either 'plan', 'product', or 'amount' is required to initialize a transaction.",
|
|
411
419
|
status: 400
|
|
412
420
|
});
|
|
413
|
-
const amount = bodyAmount
|
|
414
|
-
const finalCurrency = currency
|
|
421
|
+
const amount = bodyAmount ?? product?.amount;
|
|
422
|
+
const finalCurrency = currency ?? product?.currency ?? plan?.currency ?? "NGN";
|
|
415
423
|
let url;
|
|
416
424
|
let reference;
|
|
417
425
|
let accessCode;
|
|
418
426
|
const referenceIdFromCtx = ctx.context.referenceId;
|
|
419
|
-
const referenceId = ctx.body.referenceId
|
|
420
|
-
console.log("DEBUG ROUTES REF:", {
|
|
421
|
-
referenceId,
|
|
422
|
-
referenceIdFromCtx,
|
|
423
|
-
bodyRef: ctx.body.referenceId,
|
|
424
|
-
userId: session.user.id,
|
|
425
|
-
orgEnabled: options.organization?.enabled,
|
|
426
|
-
contextKeys: Object.keys(ctx.context || {}),
|
|
427
|
-
fullContext: ctx.context
|
|
428
|
-
});
|
|
427
|
+
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;
|
|
429
428
|
let trialStart;
|
|
430
429
|
let trialEnd;
|
|
431
|
-
if (plan && plan.freeTrial
|
|
430
|
+
if (plan?.freeTrial?.days !== void 0 && plan.freeTrial.days !== null && plan.freeTrial.days > 0) {
|
|
432
431
|
if (!(await ctx.context.adapter.findMany({
|
|
433
432
|
model: "subscription",
|
|
434
433
|
where: [{
|
|
435
434
|
field: "referenceId",
|
|
436
435
|
value: referenceId
|
|
437
436
|
}]
|
|
438
|
-
}))?.some((sub) => sub.trialStart || sub.trialEnd || sub.status === "trialing")) {
|
|
437
|
+
}))?.some((sub) => sub.trialStart !== void 0 && sub.trialStart !== null || sub.trialEnd !== void 0 && sub.trialEnd !== null || sub.status === "trialing")) {
|
|
439
438
|
trialStart = /* @__PURE__ */ new Date();
|
|
440
439
|
trialEnd = /* @__PURE__ */ new Date();
|
|
441
440
|
trialEnd.setDate(trialEnd.getDate() + plan.freeTrial.days);
|
|
442
441
|
}
|
|
443
442
|
}
|
|
444
443
|
try {
|
|
445
|
-
let targetEmail = email
|
|
444
|
+
let targetEmail = email !== void 0 && email !== null && email !== "" ? email : user.email;
|
|
446
445
|
let paystackCustomerCode = user.paystackCustomerCode;
|
|
447
|
-
if (options.organization?.enabled && referenceId && referenceId !== user.id) {
|
|
446
|
+
if (options.organization?.enabled === true && referenceId !== void 0 && referenceId !== null && referenceId !== "" && referenceId !== user.id) {
|
|
448
447
|
const org = await ctx.context.adapter.findOne({
|
|
449
448
|
model: "organization",
|
|
450
449
|
where: [{
|
|
@@ -452,9 +451,9 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
452
451
|
value: referenceId
|
|
453
452
|
}]
|
|
454
453
|
});
|
|
455
|
-
if (org) {
|
|
456
|
-
if (org.paystackCustomerCode) paystackCustomerCode = org.paystackCustomerCode;
|
|
457
|
-
if (org.email) targetEmail = org.email;
|
|
454
|
+
if (org !== void 0 && org !== null) {
|
|
455
|
+
if (org.paystackCustomerCode !== void 0 && org.paystackCustomerCode !== null && org.paystackCustomerCode !== "") paystackCustomerCode = org.paystackCustomerCode;
|
|
456
|
+
if (org.email !== void 0 && org.email !== null && org.email !== "") targetEmail = org.email;
|
|
458
457
|
else {
|
|
459
458
|
const ownerMember = await ctx.context.adapter.findOne({
|
|
460
459
|
model: "member",
|
|
@@ -474,7 +473,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
474
473
|
value: ownerMember.userId
|
|
475
474
|
}]
|
|
476
475
|
});
|
|
477
|
-
if (ownerUser?.email) targetEmail = ownerUser.email;
|
|
476
|
+
if (ownerUser?.email !== void 0 && ownerUser?.email !== null && ownerUser?.email !== "") targetEmail = ownerUser.email;
|
|
478
477
|
}
|
|
479
478
|
}
|
|
480
479
|
}
|
|
@@ -495,25 +494,25 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
495
494
|
currency: finalCurrency,
|
|
496
495
|
quantity
|
|
497
496
|
};
|
|
498
|
-
if (paystackCustomerCode) try {
|
|
497
|
+
if (paystackCustomerCode !== void 0 && paystackCustomerCode !== null && paystackCustomerCode !== "") try {
|
|
499
498
|
const ops = getPaystackOps(options.paystackClient);
|
|
500
|
-
if (initBody.email) await ops.customerUpdate(paystackCustomerCode, { email: initBody.email });
|
|
501
|
-
} catch (
|
|
499
|
+
if (initBody.email !== void 0 && initBody.email !== null && initBody.email !== "") await ops.customerUpdate(paystackCustomerCode, { email: initBody.email });
|
|
500
|
+
} catch (_e) {}
|
|
502
501
|
if (plan) if (trialStart) initBody.amount = 5e3;
|
|
503
502
|
else {
|
|
504
503
|
initBody.plan = plan.planCode;
|
|
505
504
|
initBody.invoice_limit = plan.invoiceLimit;
|
|
506
505
|
const planAmount = amount ?? plan.amount ?? 5e4;
|
|
507
506
|
initBody.amount = Math.max(Math.round(planAmount), 5e4);
|
|
508
|
-
if (quantity) initBody.amount = initBody.amount * quantity;
|
|
507
|
+
if (quantity !== void 0 && quantity !== null && quantity > 0) initBody.amount = initBody.amount * quantity;
|
|
509
508
|
}
|
|
510
509
|
else {
|
|
511
|
-
if (
|
|
510
|
+
if (amount === void 0 || amount === null || amount === 0) throw new APIError("BAD_REQUEST", { message: "Amount is required for one-time payments" });
|
|
512
511
|
initBody.amount = Math.round(amount);
|
|
513
512
|
}
|
|
514
513
|
const initRes = unwrapSdkResult(await paystack.transactionInitialize(initBody));
|
|
515
|
-
let data = initRes && typeof initRes === "object" && "status" in initRes && "data" in initRes ? initRes.data : initRes?.data ?? initRes;
|
|
516
|
-
if (data && typeof data === "object" && "status" in data && "data" in data) data = data.data;
|
|
514
|
+
let data = initRes !== void 0 && initRes !== null && typeof initRes === "object" && "status" in initRes && "data" in initRes ? initRes.data : initRes?.data ?? initRes;
|
|
515
|
+
if (data !== void 0 && data !== null && typeof data === "object" && "status" in data && "data" in data) data = data.data;
|
|
517
516
|
url = data?.authorization_url;
|
|
518
517
|
reference = data?.reference;
|
|
519
518
|
accessCode = data?.access_code;
|
|
@@ -521,7 +520,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
521
520
|
ctx.context.logger.error("Failed to initialize Paystack transaction", error);
|
|
522
521
|
throw new APIError("BAD_REQUEST", {
|
|
523
522
|
code: "FAILED_TO_INITIALIZE_TRANSACTION",
|
|
524
|
-
message: error?.message
|
|
523
|
+
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_INITIALIZE_TRANSACTION
|
|
525
524
|
});
|
|
526
525
|
}
|
|
527
526
|
await ctx.context.adapter.create({
|
|
@@ -530,18 +529,18 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
530
529
|
reference,
|
|
531
530
|
referenceId,
|
|
532
531
|
userId: user.id,
|
|
533
|
-
amount,
|
|
534
|
-
currency: plan?.currency
|
|
532
|
+
amount: amount ?? 0,
|
|
533
|
+
currency: plan?.currency ?? currency ?? "NGN",
|
|
535
534
|
status: "pending",
|
|
536
535
|
plan: plan?.name.toLowerCase(),
|
|
537
|
-
metadata: extraMetadata ? JSON.stringify(extraMetadata) : void 0,
|
|
536
|
+
metadata: extraMetadata !== void 0 && extraMetadata !== null ? JSON.stringify(extraMetadata) : void 0,
|
|
538
537
|
createdAt: /* @__PURE__ */ new Date(),
|
|
539
538
|
updatedAt: /* @__PURE__ */ new Date()
|
|
540
539
|
}
|
|
541
540
|
});
|
|
542
|
-
if (plan) {
|
|
541
|
+
if (plan !== void 0 && plan !== null) {
|
|
543
542
|
let storedCustomerCode = user.paystackCustomerCode;
|
|
544
|
-
if (options.organization?.enabled && referenceId !== user.id) {
|
|
543
|
+
if (options.organization?.enabled === true && referenceId !== user.id) {
|
|
545
544
|
const org = await ctx.context.adapter.findOne({
|
|
546
545
|
model: "organization",
|
|
547
546
|
where: [{
|
|
@@ -549,7 +548,7 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
549
548
|
value: referenceId
|
|
550
549
|
}]
|
|
551
550
|
});
|
|
552
|
-
if (org?.paystackCustomerCode) storedCustomerCode = org.paystackCustomerCode;
|
|
551
|
+
if (org?.paystackCustomerCode !== void 0 && org?.paystackCustomerCode !== null && org.paystackCustomerCode !== "") storedCustomerCode = org.paystackCustomerCode;
|
|
553
552
|
}
|
|
554
553
|
const newSubscription = await ctx.context.adapter.create({
|
|
555
554
|
model: "subscription",
|
|
@@ -558,13 +557,13 @@ const initializeTransaction = (options, path = "/paystack/initialize-transaction
|
|
|
558
557
|
referenceId,
|
|
559
558
|
paystackCustomerCode: storedCustomerCode,
|
|
560
559
|
paystackTransactionReference: reference,
|
|
561
|
-
status: trialStart ? "trialing" : "incomplete",
|
|
560
|
+
status: trialStart !== void 0 && trialStart !== null ? "trialing" : "incomplete",
|
|
562
561
|
seats: quantity,
|
|
563
562
|
trialStart,
|
|
564
563
|
trialEnd
|
|
565
564
|
}
|
|
566
565
|
});
|
|
567
|
-
if (trialStart && newSubscription && plan.freeTrial?.onTrialStart) await plan.freeTrial.onTrialStart(newSubscription);
|
|
566
|
+
if (trialStart !== void 0 && trialStart !== null && newSubscription !== null && plan.freeTrial?.onTrialStart !== void 0 && plan.freeTrial?.onTrialStart !== null) await plan.freeTrial.onTrialStart(newSubscription);
|
|
568
567
|
}
|
|
569
568
|
return ctx.json({
|
|
570
569
|
url,
|
|
@@ -588,7 +587,7 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
588
587
|
return createAuthEndpoint(path, {
|
|
589
588
|
method: "POST",
|
|
590
589
|
body: verifyBodySchema,
|
|
591
|
-
use: subscriptionOptions?.enabled ? [
|
|
590
|
+
use: subscriptionOptions?.enabled === true ? [
|
|
592
591
|
sessionMiddleware,
|
|
593
592
|
originCheck,
|
|
594
593
|
referenceMiddleware(options, "verify-transaction")
|
|
@@ -602,14 +601,14 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
602
601
|
ctx.context.logger.error("Failed to verify Paystack transaction", error);
|
|
603
602
|
throw new APIError("BAD_REQUEST", {
|
|
604
603
|
code: "FAILED_TO_VERIFY_TRANSACTION",
|
|
605
|
-
message: error?.message
|
|
604
|
+
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_VERIFY_TRANSACTION
|
|
606
605
|
});
|
|
607
606
|
}
|
|
608
|
-
let data = verifyRes && typeof verifyRes === "object" && "status" in verifyRes && "data" in verifyRes ? verifyRes.data : verifyRes?.data
|
|
609
|
-
if (data && typeof data === "object" && "status" in data && "data" in data) data = data.data;
|
|
607
|
+
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;
|
|
608
|
+
if (data !== null && data !== void 0 && typeof data === "object" && "status" in data && "data" in data) data = data.data;
|
|
610
609
|
const status = data?.status;
|
|
611
610
|
const reference = data?.reference ?? ctx.body.reference;
|
|
612
|
-
const paystackId = data?.id ? String(data.id) : void 0;
|
|
611
|
+
const paystackId = data?.id !== void 0 && data?.id !== null ? String(data.id) : void 0;
|
|
613
612
|
if (status === "success") try {
|
|
614
613
|
const session = await getSessionFromCtx(ctx);
|
|
615
614
|
const referenceId = (await ctx.context.adapter.findOne({
|
|
@@ -619,17 +618,17 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
619
618
|
value: reference
|
|
620
619
|
}]
|
|
621
620
|
}))?.referenceId ?? (session?.user)?.id;
|
|
622
|
-
if (session && referenceId !== session.user.id) {
|
|
621
|
+
if (session !== null && session !== void 0 && referenceId !== session.user.id) {
|
|
623
622
|
const authRef = subscriptionOptions?.authorizeReference;
|
|
624
623
|
let authorized = false;
|
|
625
|
-
if (authRef) authorized = await authRef({
|
|
624
|
+
if (authRef !== void 0 && authRef !== null) authorized = await authRef({
|
|
626
625
|
user: session.user,
|
|
627
626
|
session,
|
|
628
627
|
referenceId,
|
|
629
628
|
action: "verify-transaction"
|
|
630
629
|
}, ctx);
|
|
631
|
-
else if (options.organization?.enabled) {
|
|
632
|
-
|
|
630
|
+
else if (options.organization?.enabled === true) {
|
|
631
|
+
const member = await ctx.context.adapter.findOne({
|
|
633
632
|
model: "member",
|
|
634
633
|
where: [{
|
|
635
634
|
field: "userId",
|
|
@@ -638,7 +637,8 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
638
637
|
field: "organizationId",
|
|
639
638
|
value: referenceId
|
|
640
639
|
}]
|
|
641
|
-
})
|
|
640
|
+
});
|
|
641
|
+
if (member !== null && member !== void 0) authorized = true;
|
|
642
642
|
}
|
|
643
643
|
if (!authorized) throw new APIError("UNAUTHORIZED");
|
|
644
644
|
}
|
|
@@ -647,8 +647,8 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
647
647
|
update: {
|
|
648
648
|
status: "success",
|
|
649
649
|
paystackId,
|
|
650
|
-
...data?.amount && { amount: data.amount },
|
|
651
|
-
...data?.currency && { currency: data.currency },
|
|
650
|
+
...data?.amount !== void 0 && data?.amount !== null ? { amount: data.amount } : {},
|
|
651
|
+
...data?.currency !== void 0 && data?.currency !== null ? { currency: data.currency } : {},
|
|
652
652
|
updatedAt: /* @__PURE__ */ new Date()
|
|
653
653
|
},
|
|
654
654
|
where: [{
|
|
@@ -656,14 +656,15 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
656
656
|
value: reference
|
|
657
657
|
}]
|
|
658
658
|
});
|
|
659
|
-
const
|
|
660
|
-
|
|
659
|
+
const customer = data?.customer;
|
|
660
|
+
const paystackCustomerCodeFromPaystack = customer !== void 0 && customer !== null && typeof customer === "object" ? customer.customer_code : void 0;
|
|
661
|
+
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({
|
|
661
662
|
model: "organization",
|
|
662
663
|
where: [{
|
|
663
664
|
field: "id",
|
|
664
665
|
value: referenceId
|
|
665
666
|
}]
|
|
666
|
-
}))) await ctx.context.adapter.update({
|
|
667
|
+
}) !== null)) === true) await ctx.context.adapter.update({
|
|
667
668
|
model: "organization",
|
|
668
669
|
update: { paystackCustomerCode: paystackCustomerCodeFromPaystack },
|
|
669
670
|
where: [{
|
|
@@ -682,49 +683,47 @@ const verifyTransaction = (options, path = "/paystack/verify-transaction") => {
|
|
|
682
683
|
let isTrial = false;
|
|
683
684
|
let trialEnd;
|
|
684
685
|
let targetPlan;
|
|
685
|
-
if (data?.metadata) {
|
|
686
|
-
const
|
|
687
|
-
|
|
686
|
+
if (data?.metadata !== void 0 && data?.metadata !== null) {
|
|
687
|
+
const metaRaw = data.metadata;
|
|
688
|
+
const meta = typeof metaRaw === "string" ? JSON.parse(metaRaw) : metaRaw;
|
|
689
|
+
isTrial = meta.isTrial === true || meta.isTrial === "true";
|
|
688
690
|
trialEnd = meta.trialEnd;
|
|
689
691
|
targetPlan = meta.plan;
|
|
690
692
|
}
|
|
691
693
|
let paystackSubscriptionCode;
|
|
692
|
-
if (isTrial && targetPlan && trialEnd) {
|
|
693
|
-
const authorizationCode = data?.authorization?.authorization_code;
|
|
694
|
-
const email = data?.customer?.email;
|
|
694
|
+
if (isTrial === true && targetPlan !== void 0 && targetPlan !== null && targetPlan !== "" && trialEnd !== void 0 && trialEnd !== null && trialEnd !== "") {
|
|
695
|
+
const authorizationCode = (data?.authorization)?.authorization_code;
|
|
696
|
+
const email = (data?.customer)?.email;
|
|
695
697
|
const planConfig = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === targetPlan?.toLowerCase());
|
|
696
|
-
if (authorizationCode && email && planConfig?.planCode) {
|
|
698
|
+
if (authorizationCode !== void 0 && authorizationCode !== null && authorizationCode !== "" && email !== void 0 && email !== null && email !== "" && planConfig?.planCode !== void 0 && planConfig?.planCode !== null && planConfig?.planCode !== "") {
|
|
697
699
|
const subData = unwrapSdkResult(await paystack.subscriptionCreate({
|
|
698
700
|
customer: email,
|
|
699
701
|
plan: planConfig.planCode,
|
|
700
702
|
authorization: authorizationCode,
|
|
701
703
|
start_date: trialEnd
|
|
702
704
|
}));
|
|
703
|
-
|
|
704
|
-
console.log("Trial Subscription Created:", JSON.stringify(cleanSubData, null, 2));
|
|
705
|
-
paystackSubscriptionCode = cleanSubData?.subscription_code;
|
|
705
|
+
paystackSubscriptionCode = (subData?.data ?? subData)?.subscription_code;
|
|
706
706
|
}
|
|
707
707
|
}
|
|
708
708
|
const updatedSubscription = await ctx.context.adapter.update({
|
|
709
709
|
model: "subscription",
|
|
710
710
|
update: {
|
|
711
|
-
status: isTrial ? "trialing" : "active",
|
|
711
|
+
status: isTrial === true ? "trialing" : "active",
|
|
712
712
|
periodStart: /* @__PURE__ */ new Date(),
|
|
713
713
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
714
|
-
...paystackSubscriptionCode && { paystackSubscriptionCode }
|
|
714
|
+
...paystackSubscriptionCode !== void 0 && paystackSubscriptionCode !== null && paystackSubscriptionCode !== "" ? { paystackSubscriptionCode } : {}
|
|
715
715
|
},
|
|
716
716
|
where: [{
|
|
717
717
|
field: "paystackTransactionReference",
|
|
718
718
|
value: reference
|
|
719
|
-
}, ...referenceId ? [{
|
|
719
|
+
}, ...referenceId !== void 0 && referenceId !== null && referenceId !== "" ? [{
|
|
720
720
|
field: "referenceId",
|
|
721
721
|
value: referenceId
|
|
722
722
|
}] : []]
|
|
723
723
|
});
|
|
724
|
-
if (updatedSubscription && subscriptionOptions?.enabled && subscriptionOptions.onSubscriptionComplete) {
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
if (plan) await subOpts.onSubscriptionComplete({
|
|
724
|
+
if (updatedSubscription && subscriptionOptions?.enabled === true && "onSubscriptionComplete" in subscriptionOptions && typeof subscriptionOptions.onSubscriptionComplete === "function") {
|
|
725
|
+
const plan = (await getPlans(subscriptionOptions)).find((p) => p.name.toLowerCase() === updatedSubscription.plan.toLowerCase());
|
|
726
|
+
if (plan) await subscriptionOptions.onSubscriptionComplete({
|
|
728
727
|
event: data,
|
|
729
728
|
subscription: updatedSubscription,
|
|
730
729
|
plan
|
|
@@ -761,16 +760,18 @@ const listSubscriptions = (options) => {
|
|
|
761
760
|
return createAuthEndpoint("/paystack/list-subscriptions", {
|
|
762
761
|
method: "GET",
|
|
763
762
|
query: listQuerySchema,
|
|
764
|
-
use: subscriptionOptions?.enabled ? [
|
|
763
|
+
use: subscriptionOptions?.enabled === true ? [
|
|
765
764
|
sessionMiddleware,
|
|
766
765
|
originCheck,
|
|
767
766
|
referenceMiddleware(options, "list-subscriptions")
|
|
768
767
|
] : [sessionMiddleware, originCheck]
|
|
769
768
|
}, async (ctx) => {
|
|
770
|
-
if (
|
|
769
|
+
if (subscriptionOptions?.enabled !== true) throw new APIError("BAD_REQUEST", { message: "Subscriptions are not enabled in the Paystack options." });
|
|
771
770
|
const session = await getSessionFromCtx(ctx);
|
|
772
771
|
if (!session) throw new APIError("UNAUTHORIZED");
|
|
773
|
-
const
|
|
772
|
+
const referenceIdPart = ctx.context.referenceId;
|
|
773
|
+
const queryRefId = ctx.query?.referenceId;
|
|
774
|
+
const referenceId = referenceIdPart !== void 0 && referenceIdPart !== null && referenceIdPart !== "" ? referenceIdPart : queryRefId !== void 0 && queryRefId !== null && queryRefId !== "" ? queryRefId : session.user.id;
|
|
774
775
|
const res = await ctx.context.adapter.findMany({
|
|
775
776
|
model: "subscription",
|
|
776
777
|
where: [{
|
|
@@ -785,7 +786,7 @@ const listTransactions = (options, path = "/paystack/list-transactions") => {
|
|
|
785
786
|
return createAuthEndpoint(path, {
|
|
786
787
|
method: "GET",
|
|
787
788
|
query: z.object({ referenceId: z.string().optional() }),
|
|
788
|
-
use: options.subscription?.enabled ? [
|
|
789
|
+
use: options.subscription?.enabled === true ? [
|
|
789
790
|
sessionMiddleware,
|
|
790
791
|
originCheck,
|
|
791
792
|
referenceMiddleware(options, "list-transactions")
|
|
@@ -818,7 +819,7 @@ function decodeBase64UrlToString(value) {
|
|
|
818
819
|
function tryGetEmailTokenFromSubscriptionManageLink(link) {
|
|
819
820
|
try {
|
|
820
821
|
const subscriptionToken = new URL(link).searchParams.get("subscription_token");
|
|
821
|
-
if (
|
|
822
|
+
if (subscriptionToken === void 0 || subscriptionToken === null || subscriptionToken === "") return void 0;
|
|
822
823
|
const parts = subscriptionToken.split(".");
|
|
823
824
|
if (parts.length < 2) return void 0;
|
|
824
825
|
const payloadJson = decodeBase64UrlToString(parts[1]);
|
|
@@ -832,7 +833,7 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
|
|
|
832
833
|
return createAuthEndpoint(path, {
|
|
833
834
|
method: "POST",
|
|
834
835
|
body: enableDisableBodySchema,
|
|
835
|
-
use: options.subscription?.enabled ? [
|
|
836
|
+
use: options.subscription?.enabled === true ? [
|
|
836
837
|
sessionMiddleware,
|
|
837
838
|
originCheck,
|
|
838
839
|
referenceMiddleware(options, "disable-subscription")
|
|
@@ -842,17 +843,17 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
|
|
|
842
843
|
const paystack = getPaystackOps(options.paystackClient);
|
|
843
844
|
try {
|
|
844
845
|
let emailToken = ctx.body.emailToken;
|
|
845
|
-
if (
|
|
846
|
+
if (emailToken === void 0 || emailToken === null || emailToken === "") try {
|
|
846
847
|
const fetchRes = unwrapSdkResult(await paystack.subscriptionFetch(subscriptionCode));
|
|
847
|
-
emailToken = (fetchRes && typeof fetchRes === "object" && "status" in fetchRes && "data" in fetchRes ? fetchRes.data : fetchRes?.data
|
|
848
|
+
emailToken = (fetchRes !== null && fetchRes !== void 0 && typeof fetchRes === "object" && "status" in fetchRes && "data" in fetchRes ? fetchRes.data : fetchRes?.data !== void 0 ? fetchRes.data : fetchRes)?.email_token;
|
|
848
849
|
} catch {}
|
|
849
|
-
if (
|
|
850
|
+
if (emailToken === void 0 || emailToken === null || emailToken === "") try {
|
|
850
851
|
const linkRes = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
|
|
851
|
-
const data = linkRes && typeof linkRes === "object" && "status" in linkRes && "data" in linkRes ? linkRes.data : linkRes?.data
|
|
852
|
+
const data = linkRes !== null && linkRes !== void 0 && typeof linkRes === "object" && "status" in linkRes && "data" in linkRes ? linkRes.data : linkRes?.data !== void 0 ? linkRes.data : linkRes;
|
|
852
853
|
const link = typeof data === "string" ? data : data?.link;
|
|
853
|
-
if (link) emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
|
|
854
|
+
if (link !== void 0 && link !== null && link !== "") emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
|
|
854
855
|
} catch {}
|
|
855
|
-
if (
|
|
856
|
+
if (emailToken === void 0 || emailToken === null || emailToken === "") throw new Error("Could not retrieve email_token for subscription disable.");
|
|
856
857
|
await paystack.subscriptionDisable({
|
|
857
858
|
code: subscriptionCode,
|
|
858
859
|
token: emailToken
|
|
@@ -873,7 +874,7 @@ const disablePaystackSubscription = (options, path = "/paystack/disable-subscrip
|
|
|
873
874
|
ctx.context.logger.error("Failed to disable subscription", error);
|
|
874
875
|
throw new APIError("BAD_REQUEST", {
|
|
875
876
|
code: "FAILED_TO_DISABLE_SUBSCRIPTION",
|
|
876
|
-
message: error?.message
|
|
877
|
+
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_DISABLE_SUBSCRIPTION
|
|
877
878
|
});
|
|
878
879
|
}
|
|
879
880
|
});
|
|
@@ -882,7 +883,7 @@ const enablePaystackSubscription = (options, path = "/paystack/enable-subscripti
|
|
|
882
883
|
return createAuthEndpoint(path, {
|
|
883
884
|
method: "POST",
|
|
884
885
|
body: enableDisableBodySchema,
|
|
885
|
-
use: options.subscription?.enabled ? [
|
|
886
|
+
use: options.subscription?.enabled === true ? [
|
|
886
887
|
sessionMiddleware,
|
|
887
888
|
originCheck,
|
|
888
889
|
referenceMiddleware(options, "enable-subscription")
|
|
@@ -892,17 +893,17 @@ const enablePaystackSubscription = (options, path = "/paystack/enable-subscripti
|
|
|
892
893
|
const paystack = getPaystackOps(options.paystackClient);
|
|
893
894
|
try {
|
|
894
895
|
let emailToken = ctx.body.emailToken;
|
|
895
|
-
if (
|
|
896
|
+
if (emailToken === void 0 || emailToken === null || emailToken === "") try {
|
|
896
897
|
const fetchRes = unwrapSdkResult(await paystack.subscriptionFetch(subscriptionCode));
|
|
897
|
-
emailToken = (fetchRes && typeof fetchRes === "object" && "status" in fetchRes && "data" in fetchRes ? fetchRes.data : fetchRes?.data
|
|
898
|
+
emailToken = (fetchRes !== null && fetchRes !== void 0 && typeof fetchRes === "object" && "status" in fetchRes && "data" in fetchRes ? fetchRes.data : fetchRes?.data !== void 0 ? fetchRes.data : fetchRes)?.email_token;
|
|
898
899
|
} catch {}
|
|
899
|
-
if (
|
|
900
|
+
if (emailToken === void 0 || emailToken === null || emailToken === "") try {
|
|
900
901
|
const linkRes = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
|
|
901
|
-
const data = linkRes &&
|
|
902
|
+
const data = linkRes !== null && linkRes !== void 0 && "status" in linkRes && "data" in linkRes ? linkRes.data : linkRes?.data !== void 0 ? linkRes.data : linkRes;
|
|
902
903
|
const link = typeof data === "string" ? data : data?.link;
|
|
903
|
-
if (link) emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
|
|
904
|
+
if (link !== void 0 && link !== null && link !== "") emailToken = tryGetEmailTokenFromSubscriptionManageLink(link);
|
|
904
905
|
} catch {}
|
|
905
|
-
if (
|
|
906
|
+
if (emailToken === void 0 || emailToken === null || emailToken === "") throw new APIError("BAD_REQUEST", { message: "Could not retrieve email_token for subscription enable." });
|
|
906
907
|
await paystack.subscriptionEnable({
|
|
907
908
|
code: subscriptionCode,
|
|
908
909
|
token: emailToken
|
|
@@ -923,7 +924,7 @@ const enablePaystackSubscription = (options, path = "/paystack/enable-subscripti
|
|
|
923
924
|
ctx.context.logger.error("Failed to enable subscription", error);
|
|
924
925
|
throw new APIError("BAD_REQUEST", {
|
|
925
926
|
code: "FAILED_TO_ENABLE_SUBSCRIPTION",
|
|
926
|
-
message: error?.message
|
|
927
|
+
message: error?.message ?? PAYSTACK_ERROR_CODES.FAILED_TO_ENABLE_SUBSCRIPTION
|
|
927
928
|
});
|
|
928
929
|
}
|
|
929
930
|
});
|
|
@@ -932,7 +933,7 @@ const getSubscriptionManageLink = (options) => {
|
|
|
932
933
|
return createAuthEndpoint("/paystack/get-subscription-manage-link", {
|
|
933
934
|
method: "GET",
|
|
934
935
|
query: z.object({ subscriptionCode: z.string() }),
|
|
935
|
-
use: options.subscription?.enabled ? [
|
|
936
|
+
use: options.subscription?.enabled === true ? [
|
|
936
937
|
sessionMiddleware,
|
|
937
938
|
originCheck,
|
|
938
939
|
referenceMiddleware(options, "get-subscription-manage-link")
|
|
@@ -942,12 +943,12 @@ const getSubscriptionManageLink = (options) => {
|
|
|
942
943
|
const paystack = getPaystackOps(options.paystackClient);
|
|
943
944
|
try {
|
|
944
945
|
const res = unwrapSdkResult(await paystack.subscriptionManageLink(subscriptionCode));
|
|
945
|
-
const data = res &&
|
|
946
|
+
const data = res !== null && res !== void 0 && "status" in res && "data" in res ? res.data : res?.data !== void 0 ? res.data : res;
|
|
946
947
|
const link = typeof data === "string" ? data : data?.link;
|
|
947
948
|
return ctx.json({ link });
|
|
948
949
|
} catch (error) {
|
|
949
950
|
ctx.context.logger.error("Failed to get subscription manage link", error);
|
|
950
|
-
throw new APIError("BAD_REQUEST", { message: error?.message
|
|
951
|
+
throw new APIError("BAD_REQUEST", { message: error?.message ?? "Failed to get subscription manage link" });
|
|
951
952
|
}
|
|
952
953
|
});
|
|
953
954
|
};
|
|
@@ -956,7 +957,7 @@ const getConfig = (options) => {
|
|
|
956
957
|
method: "GET",
|
|
957
958
|
metadata: { openapi: { operationId: "getPaystackConfig" } }
|
|
958
959
|
}, async (ctx) => {
|
|
959
|
-
const plans = options.subscription?.enabled ? await getPlans(options.subscription) : [];
|
|
960
|
+
const plans = options.subscription?.enabled === true ? await getPlans(options.subscription) : [];
|
|
960
961
|
const products = await getProducts(options.products);
|
|
961
962
|
return ctx.json({
|
|
962
963
|
plans,
|
|
@@ -1084,7 +1085,7 @@ const organization = { organization: { fields: {
|
|
|
1084
1085
|
} } };
|
|
1085
1086
|
const getSchema = (options) => {
|
|
1086
1087
|
let baseSchema;
|
|
1087
|
-
if (options.subscription?.enabled) baseSchema = {
|
|
1088
|
+
if (options.subscription?.enabled === true) baseSchema = {
|
|
1088
1089
|
...subscriptions,
|
|
1089
1090
|
...transactions,
|
|
1090
1091
|
...user
|
|
@@ -1093,11 +1094,11 @@ const getSchema = (options) => {
|
|
|
1093
1094
|
...user,
|
|
1094
1095
|
...transactions
|
|
1095
1096
|
};
|
|
1096
|
-
if (options.organization?.enabled) baseSchema = {
|
|
1097
|
+
if (options.organization?.enabled === true) baseSchema = {
|
|
1097
1098
|
...baseSchema,
|
|
1098
1099
|
...organization
|
|
1099
1100
|
};
|
|
1100
|
-
if (options.schema &&
|
|
1101
|
+
if (options.schema !== void 0 && options.subscription?.enabled !== true && "subscription" in options.schema) {
|
|
1101
1102
|
const { subscription: _subscription, ...restSchema } = options.schema;
|
|
1102
1103
|
return mergeSchema(baseSchema, restSchema);
|
|
1103
1104
|
}
|
|
@@ -1117,7 +1118,7 @@ const getOrganizationSubscription = async (ctx, organizationId) => {
|
|
|
1117
1118
|
};
|
|
1118
1119
|
const checkSeatLimit = async (ctx, organizationId, seatsToAdd = 1) => {
|
|
1119
1120
|
const subscription = await getOrganizationSubscription(ctx, organizationId);
|
|
1120
|
-
if (
|
|
1121
|
+
if (subscription?.seats === void 0 || subscription.seats === null) return true;
|
|
1121
1122
|
const members = await ctx.context.adapter.findMany({
|
|
1122
1123
|
model: "member",
|
|
1123
1124
|
where: [{
|
|
@@ -1125,7 +1126,7 @@ const checkSeatLimit = async (ctx, organizationId, seatsToAdd = 1) => {
|
|
|
1125
1126
|
value: organizationId
|
|
1126
1127
|
}]
|
|
1127
1128
|
});
|
|
1128
|
-
if (members.length + seatsToAdd > subscription.seats) throw new
|
|
1129
|
+
if (members.length + seatsToAdd > subscription.seats) throw new APIError("FORBIDDEN", { message: `Organization member limit reached. Used: ${members.length}, Max: ${subscription.seats}` });
|
|
1129
1130
|
return true;
|
|
1130
1131
|
};
|
|
1131
1132
|
const checkTeamLimit = async (ctx, organizationId, maxTeams) => {
|
|
@@ -1135,7 +1136,7 @@ const checkTeamLimit = async (ctx, organizationId, maxTeams) => {
|
|
|
1135
1136
|
field: "organizationId",
|
|
1136
1137
|
value: organizationId
|
|
1137
1138
|
}]
|
|
1138
|
-
})).length >= maxTeams) throw new
|
|
1139
|
+
})).length >= maxTeams) throw new APIError("FORBIDDEN", { message: `Organization team limit reached. Max teams: ${maxTeams}` });
|
|
1139
1140
|
return true;
|
|
1140
1141
|
};
|
|
1141
1142
|
|
|
@@ -1146,34 +1147,34 @@ const paystack = (options) => {
|
|
|
1146
1147
|
return {
|
|
1147
1148
|
id: "paystack",
|
|
1148
1149
|
endpoints: {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1150
|
+
initializeTransaction: initializeTransaction(options),
|
|
1151
|
+
verifyTransaction: verifyTransaction(options),
|
|
1152
|
+
listSubscriptions: listSubscriptions(options),
|
|
1153
|
+
paystackWebhook: paystackWebhook(options),
|
|
1154
|
+
listTransactions: listTransactions(options),
|
|
1155
|
+
getConfig: getConfig(options),
|
|
1156
|
+
disableSubscription: disablePaystackSubscription(options),
|
|
1157
|
+
enableSubscription: enablePaystackSubscription(options),
|
|
1158
|
+
getSubscriptionManageLink: getSubscriptionManageLink(options),
|
|
1159
|
+
createSubscription: createSubscription(options),
|
|
1160
|
+
upgradeSubscription: upgradeSubscription(options),
|
|
1161
|
+
cancelSubscription: cancelSubscription(options),
|
|
1162
|
+
restoreSubscription: restoreSubscription(options)
|
|
1162
1163
|
},
|
|
1163
1164
|
schema: getSchema(options),
|
|
1164
|
-
init:
|
|
1165
|
+
init: (ctx) => {
|
|
1165
1166
|
return { options: {
|
|
1166
1167
|
databaseHooks: {
|
|
1167
1168
|
user: { create: { async after(user, hookCtx) {
|
|
1168
|
-
if (
|
|
1169
|
+
if (hookCtx === void 0 || hookCtx === null || options.createCustomerOnSignUp !== true) return;
|
|
1169
1170
|
const data = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate({
|
|
1170
1171
|
email: user.email,
|
|
1171
|
-
first_name: user.name
|
|
1172
|
+
first_name: user.name ?? void 0,
|
|
1172
1173
|
metadata: { userId: user.id }
|
|
1173
1174
|
}));
|
|
1174
|
-
const customerCode = data?.customer_code
|
|
1175
|
-
if (
|
|
1176
|
-
await
|
|
1175
|
+
const customerCode = data?.customer_code ?? (data?.data)?.customer_code;
|
|
1176
|
+
if (customerCode === void 0 || customerCode === null) return;
|
|
1177
|
+
await ctx.adapter.update({
|
|
1177
1178
|
model: "user",
|
|
1178
1179
|
where: [{
|
|
1179
1180
|
field: "id",
|
|
@@ -1182,12 +1183,12 @@ const paystack = (options) => {
|
|
|
1182
1183
|
update: { paystackCustomerCode: customerCode }
|
|
1183
1184
|
});
|
|
1184
1185
|
} } },
|
|
1185
|
-
organization: options.organization?.enabled ? { create: { async after(org, hookCtx) {
|
|
1186
|
+
organization: options.organization?.enabled === true ? { create: { async after(org, hookCtx) {
|
|
1186
1187
|
try {
|
|
1187
1188
|
const extraCreateParams = options.organization?.getCustomerCreateParams ? await options.organization.getCustomerCreateParams(org, hookCtx) : {};
|
|
1188
1189
|
let targetEmail = org.email;
|
|
1189
|
-
if (
|
|
1190
|
-
const ownerMember = await
|
|
1190
|
+
if (targetEmail === void 0 || targetEmail === null) {
|
|
1191
|
+
const ownerMember = await ctx.adapter.findOne({
|
|
1191
1192
|
model: "member",
|
|
1192
1193
|
where: [{
|
|
1193
1194
|
field: "organizationId",
|
|
@@ -1197,7 +1198,7 @@ const paystack = (options) => {
|
|
|
1197
1198
|
value: "owner"
|
|
1198
1199
|
}]
|
|
1199
1200
|
});
|
|
1200
|
-
if (ownerMember) targetEmail = (await
|
|
1201
|
+
if (ownerMember !== null && ownerMember !== void 0) targetEmail = (await ctx.adapter.findOne({
|
|
1201
1202
|
model: "user",
|
|
1202
1203
|
where: [{
|
|
1203
1204
|
field: "id",
|
|
@@ -1205,17 +1206,17 @@ const paystack = (options) => {
|
|
|
1205
1206
|
}]
|
|
1206
1207
|
}))?.email;
|
|
1207
1208
|
}
|
|
1208
|
-
if (
|
|
1209
|
+
if (targetEmail === void 0 || targetEmail === null) return;
|
|
1209
1210
|
const params = defu({
|
|
1210
1211
|
email: targetEmail,
|
|
1211
1212
|
first_name: org.name,
|
|
1212
1213
|
metadata: { organizationId: org.id }
|
|
1213
1214
|
}, extraCreateParams);
|
|
1214
1215
|
const sdkRes = unwrapSdkResult(await getPaystackOps(options.paystackClient).customerCreate(params));
|
|
1215
|
-
const paystackCustomer = sdkRes && typeof sdkRes === "object" && "status" in sdkRes && "data" in sdkRes ? sdkRes.data : sdkRes?.data ?? sdkRes;
|
|
1216
|
+
const paystackCustomer = sdkRes !== null && typeof sdkRes === "object" && "status" in sdkRes && "data" in sdkRes ? sdkRes.data : sdkRes?.data ?? sdkRes;
|
|
1216
1217
|
const customerCode = paystackCustomer?.customer_code;
|
|
1217
|
-
if (
|
|
1218
|
-
await
|
|
1218
|
+
if (customerCode === void 0 || customerCode === null) return;
|
|
1219
|
+
await ctx.internalAdapter.updateOrganization(org.id, { paystackCustomerCode: customerCode });
|
|
1219
1220
|
await options.organization?.onCustomerCreate?.({
|
|
1220
1221
|
paystackCustomer,
|
|
1221
1222
|
organization: {
|
|
@@ -1224,20 +1225,20 @@ const paystack = (options) => {
|
|
|
1224
1225
|
}
|
|
1225
1226
|
}, hookCtx);
|
|
1226
1227
|
} catch (error) {
|
|
1227
|
-
ctx.
|
|
1228
|
+
ctx.logger.error("Failed to create Paystack customer for organization", error);
|
|
1228
1229
|
}
|
|
1229
1230
|
} } } : void 0
|
|
1230
1231
|
},
|
|
1231
1232
|
member: { create: { before: async (member, ctx) => {
|
|
1232
|
-
if (options.subscription?.enabled && member.organizationId && ctx) await checkSeatLimit(ctx, member.organizationId);
|
|
1233
|
+
if (options.subscription?.enabled === true && member.organizationId && ctx !== null && ctx !== void 0) await checkSeatLimit(ctx, member.organizationId);
|
|
1233
1234
|
} } },
|
|
1234
1235
|
invitation: { create: { before: async (invitation, ctx) => {
|
|
1235
|
-
if (options.subscription?.enabled && invitation.organizationId && ctx) await checkSeatLimit(ctx, invitation.organizationId);
|
|
1236
|
+
if (options.subscription?.enabled === true && invitation.organizationId && ctx !== null && ctx !== void 0) await checkSeatLimit(ctx, invitation.organizationId);
|
|
1236
1237
|
} } },
|
|
1237
1238
|
team: { create: { before: async (team, ctx) => {
|
|
1238
|
-
if (options.subscription?.enabled && team.organizationId && ctx) {
|
|
1239
|
+
if (options.subscription?.enabled === true && team.organizationId && ctx !== null && ctx !== void 0) {
|
|
1239
1240
|
const subscription = await getOrganizationSubscription(ctx, team.organizationId);
|
|
1240
|
-
if (subscription) {
|
|
1241
|
+
if (subscription !== null && subscription !== void 0) {
|
|
1241
1242
|
const maxTeams = ((await getPlanByName(options, subscription.plan))?.limits)?.teams;
|
|
1242
1243
|
if (typeof maxTeams === "number") await checkTeamLimit(ctx, team.organizationId, maxTeams);
|
|
1243
1244
|
}
|