@goweekdays/core 0.0.11 → 0.0.12
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/CHANGELOG.md +6 -0
- package/dist/index.d.ts +165 -11
- package/dist/index.js +1153 -265
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1165 -267
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -12389,10 +12389,10 @@ import { BadRequestError as BadRequestError7 } from "@goweekdays/utils";
|
|
|
12389
12389
|
import Joi2 from "joi";
|
|
12390
12390
|
import { ObjectId as ObjectId8 } from "mongodb";
|
|
12391
12391
|
function MMember(value) {
|
|
12392
|
-
const
|
|
12392
|
+
const schema3 = Joi2.object({
|
|
12393
12393
|
_id: Joi2.string().hex().optional().allow("", null),
|
|
12394
|
-
org: Joi2.string().hex().
|
|
12395
|
-
orgName: Joi2.string().
|
|
12394
|
+
org: Joi2.string().hex().optional().allow("", null),
|
|
12395
|
+
orgName: Joi2.string().optional().allow("", null),
|
|
12396
12396
|
name: Joi2.string().required(),
|
|
12397
12397
|
user: Joi2.string().hex().required(),
|
|
12398
12398
|
role: Joi2.string().hex().required(),
|
|
@@ -12401,7 +12401,7 @@ function MMember(value) {
|
|
|
12401
12401
|
updatedAt: Joi2.string().optional().allow("", null),
|
|
12402
12402
|
deletedAt: Joi2.string().optional().allow("", null)
|
|
12403
12403
|
});
|
|
12404
|
-
const { error } =
|
|
12404
|
+
const { error } = schema3.validate(value);
|
|
12405
12405
|
if (error) {
|
|
12406
12406
|
throw new BadRequestError7(error.message);
|
|
12407
12407
|
}
|
|
@@ -12428,8 +12428,8 @@ function MMember(value) {
|
|
|
12428
12428
|
}
|
|
12429
12429
|
return {
|
|
12430
12430
|
_id: value._id,
|
|
12431
|
-
org: value.org,
|
|
12432
|
-
orgName: value.orgName,
|
|
12431
|
+
org: value.org ?? "",
|
|
12432
|
+
orgName: value.orgName ?? "",
|
|
12433
12433
|
name: value.name,
|
|
12434
12434
|
user: value.user,
|
|
12435
12435
|
role: value.role,
|
|
@@ -13492,7 +13492,10 @@ function useRoleRepo() {
|
|
|
13492
13492
|
}
|
|
13493
13493
|
async function createUniqueIndex() {
|
|
13494
13494
|
try {
|
|
13495
|
-
await collection.createIndex(
|
|
13495
|
+
await collection.createIndex(
|
|
13496
|
+
{ name: 1, type: 1, org: 1 },
|
|
13497
|
+
{ unique: true }
|
|
13498
|
+
);
|
|
13496
13499
|
} catch (error) {
|
|
13497
13500
|
throw new InternalServerError12("Failed to create unique index on role.");
|
|
13498
13501
|
}
|
|
@@ -13506,7 +13509,7 @@ function useRoleRepo() {
|
|
|
13506
13509
|
logger4.log({ level: "error", message: `${error}` });
|
|
13507
13510
|
const isDuplicated = error.message.includes("duplicate");
|
|
13508
13511
|
if (isDuplicated) {
|
|
13509
|
-
throw new BadRequestError13("
|
|
13512
|
+
throw new BadRequestError13("Role already exists");
|
|
13510
13513
|
}
|
|
13511
13514
|
throw new InternalServerError12("Failed to create role.");
|
|
13512
13515
|
}
|
|
@@ -15526,36 +15529,75 @@ function useCommentController() {
|
|
|
15526
15529
|
|
|
15527
15530
|
// src/models/subscription.model.ts
|
|
15528
15531
|
import { BadRequestError as BadRequestError22 } from "@goweekdays/utils";
|
|
15532
|
+
import Joi11 from "joi";
|
|
15529
15533
|
import { ObjectId as ObjectId20 } from "mongodb";
|
|
15530
15534
|
function MSubscription(value) {
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
15548
|
-
|
|
15549
|
-
|
|
15535
|
+
const schema3 = Joi11.object({
|
|
15536
|
+
_id: Joi11.string().hex().optional().allow("", null),
|
|
15537
|
+
user: Joi11.string().hex().optional().allow("", null),
|
|
15538
|
+
org: Joi11.string().hex().optional().allow("", null),
|
|
15539
|
+
customerId: Joi11.string().required(),
|
|
15540
|
+
paymentMethodId: Joi11.string().required(),
|
|
15541
|
+
amount: Joi11.number().positive().min(0).required(),
|
|
15542
|
+
currency: Joi11.string().required(),
|
|
15543
|
+
description: Joi11.string().optional().allow("", null),
|
|
15544
|
+
promoCode: Joi11.string().optional().allow("", null),
|
|
15545
|
+
type: Joi11.string().valid("organization", "affiliate").required(),
|
|
15546
|
+
seats: Joi11.number().optional().min(0).allow(null, ""),
|
|
15547
|
+
status: Joi11.string().optional().allow("", null),
|
|
15548
|
+
billingCycle: Joi11.string().valid("monthly", "yearly").required(),
|
|
15549
|
+
// Ensure valid values
|
|
15550
|
+
nextBillingDate: Joi11.date().optional(),
|
|
15551
|
+
lastPaymentStatus: Joi11.string().optional().allow("", null),
|
|
15552
|
+
failedAttempts: Joi11.number().optional().allow("", null),
|
|
15553
|
+
createdAt: Joi11.date().optional(),
|
|
15554
|
+
updatedAt: Joi11.string().optional().allow("", null),
|
|
15555
|
+
deletedAt: Joi11.string().optional().allow("", null)
|
|
15556
|
+
}).custom((value2, helpers) => {
|
|
15557
|
+
if (!value2.user && !value2.org) {
|
|
15558
|
+
return helpers.error("any.invalid", {
|
|
15559
|
+
message: "Either user or org is required."
|
|
15560
|
+
});
|
|
15550
15561
|
}
|
|
15551
|
-
|
|
15562
|
+
return value2;
|
|
15563
|
+
});
|
|
15564
|
+
const { error } = schema3.validate(value);
|
|
15565
|
+
if (error) {
|
|
15566
|
+
throw new BadRequestError22(error.details[0].message);
|
|
15567
|
+
}
|
|
15568
|
+
if (value._id)
|
|
15569
|
+
value._id = new ObjectId20(value._id);
|
|
15570
|
+
if (value.user)
|
|
15571
|
+
value.user = new ObjectId20(value.user);
|
|
15572
|
+
if (value.org)
|
|
15573
|
+
value.org = new ObjectId20(value.org);
|
|
15574
|
+
const createdAt = value.createdAt ? new Date(value.createdAt) : /* @__PURE__ */ new Date();
|
|
15575
|
+
const nextBillingDate = new Date(createdAt);
|
|
15576
|
+
if (value.billingCycle === "monthly") {
|
|
15577
|
+
nextBillingDate.setMonth(nextBillingDate.getMonth() + 1);
|
|
15578
|
+
} else if (value.billingCycle === "yearly") {
|
|
15579
|
+
nextBillingDate.setFullYear(nextBillingDate.getFullYear() + 1);
|
|
15580
|
+
}
|
|
15581
|
+
nextBillingDate.setDate(nextBillingDate.getDate() + 1);
|
|
15552
15582
|
return {
|
|
15553
15583
|
_id: value._id,
|
|
15554
15584
|
user: value.user ?? "",
|
|
15555
15585
|
org: value.org ?? "",
|
|
15556
|
-
|
|
15586
|
+
customerId: value.customerId,
|
|
15587
|
+
paymentMethodId: value.paymentMethodId,
|
|
15588
|
+
amount: value.amount,
|
|
15589
|
+
currency: value.currency,
|
|
15590
|
+
description: value.description ?? "",
|
|
15591
|
+
type: value.type,
|
|
15592
|
+
promoCode: value.promoCode ?? "",
|
|
15593
|
+
seats: value.seats ?? 0,
|
|
15557
15594
|
status: "active",
|
|
15558
|
-
|
|
15595
|
+
billingCycle: value.billingCycle,
|
|
15596
|
+
nextBillingDate: nextBillingDate.toISOString(),
|
|
15597
|
+
// Fixed nextBillingDate logic
|
|
15598
|
+
lastPaymentStatus: value.lastPaymentStatus ?? "success",
|
|
15599
|
+
failedAttempts: value.failedAttempts ?? 0,
|
|
15600
|
+
createdAt: createdAt.toISOString(),
|
|
15559
15601
|
updatedAt: value.updatedAt ?? "",
|
|
15560
15602
|
deletedAt: value.deletedAt ?? ""
|
|
15561
15603
|
};
|
|
@@ -15564,6 +15606,7 @@ function MSubscription(value) {
|
|
|
15564
15606
|
// src/repositories/subscription.repository.ts
|
|
15565
15607
|
import { BadRequestError as BadRequestError23, logger as logger10, paginate as paginate9, useAtlas as useAtlas15 } from "@goweekdays/utils";
|
|
15566
15608
|
import { ObjectId as ObjectId21 } from "mongodb";
|
|
15609
|
+
import Joi12 from "joi";
|
|
15567
15610
|
function useSubscriptionRepo() {
|
|
15568
15611
|
const db = useAtlas15.getDb();
|
|
15569
15612
|
if (!db) {
|
|
@@ -15583,7 +15626,10 @@ function useSubscriptionRepo() {
|
|
|
15583
15626
|
}
|
|
15584
15627
|
async function createUniqueIndex() {
|
|
15585
15628
|
try {
|
|
15586
|
-
await collection.createIndex(
|
|
15629
|
+
await collection.createIndex(
|
|
15630
|
+
{ user: 1, org: 1, status: 1 },
|
|
15631
|
+
{ unique: true }
|
|
15632
|
+
);
|
|
15587
15633
|
} catch (error) {
|
|
15588
15634
|
throw new BadRequestError23(
|
|
15589
15635
|
"Failed to create unique index on subscription."
|
|
@@ -15596,6 +15642,7 @@ function useSubscriptionRepo() {
|
|
|
15596
15642
|
const res = await collection.insertOne(value, { session });
|
|
15597
15643
|
return res.insertedId;
|
|
15598
15644
|
} catch (error) {
|
|
15645
|
+
console.log(error);
|
|
15599
15646
|
throw new BadRequestError23("Failed to create subscription.");
|
|
15600
15647
|
}
|
|
15601
15648
|
}
|
|
@@ -15623,6 +15670,36 @@ function useSubscriptionRepo() {
|
|
|
15623
15670
|
throw new BadRequestError23("Failed to get subscription by ID.");
|
|
15624
15671
|
}
|
|
15625
15672
|
}
|
|
15673
|
+
async function getByAffiliateUserId(user) {
|
|
15674
|
+
try {
|
|
15675
|
+
user = new ObjectId21(user);
|
|
15676
|
+
} catch (error) {
|
|
15677
|
+
throw new BadRequestError23("Invalid user ID.");
|
|
15678
|
+
}
|
|
15679
|
+
try {
|
|
15680
|
+
return await collection.findOne({
|
|
15681
|
+
user,
|
|
15682
|
+
type: "affiliate"
|
|
15683
|
+
});
|
|
15684
|
+
} catch (error) {
|
|
15685
|
+
throw new BadRequestError23("Failed to get subscription by ID.");
|
|
15686
|
+
}
|
|
15687
|
+
}
|
|
15688
|
+
async function getByOrgId(org) {
|
|
15689
|
+
try {
|
|
15690
|
+
org = new ObjectId21(org);
|
|
15691
|
+
} catch (error) {
|
|
15692
|
+
throw new BadRequestError23("Invalid org ID.");
|
|
15693
|
+
}
|
|
15694
|
+
try {
|
|
15695
|
+
return await collection.findOne({
|
|
15696
|
+
org,
|
|
15697
|
+
type: "organization"
|
|
15698
|
+
});
|
|
15699
|
+
} catch (error) {
|
|
15700
|
+
throw new BadRequestError23("Failed to get subscription by ID.");
|
|
15701
|
+
}
|
|
15702
|
+
}
|
|
15626
15703
|
async function getBySubscriptionId(subscriptionId) {
|
|
15627
15704
|
try {
|
|
15628
15705
|
return await collection.findOne({ subscriptionId });
|
|
@@ -15672,6 +15749,112 @@ function useSubscriptionRepo() {
|
|
|
15672
15749
|
throw new BadRequestError23("Failed to update subscription status.");
|
|
15673
15750
|
}
|
|
15674
15751
|
}
|
|
15752
|
+
async function getDueSubscriptions(BATCH_SIZE = 100) {
|
|
15753
|
+
const today = /* @__PURE__ */ new Date();
|
|
15754
|
+
today.setUTCHours(0, 0, 0, 0);
|
|
15755
|
+
try {
|
|
15756
|
+
return await collection.find({
|
|
15757
|
+
status: "active",
|
|
15758
|
+
nextBillingDate: { $lte: today }
|
|
15759
|
+
}).sort({ nextBillingDate: 1 }).limit(BATCH_SIZE).toArray();
|
|
15760
|
+
} catch (error) {
|
|
15761
|
+
throw new BadRequestError23("Failed to get due subscriptions.");
|
|
15762
|
+
}
|
|
15763
|
+
}
|
|
15764
|
+
async function getFailedSubscriptions(BATCH_SIZE = 100, maxAttempts = 3) {
|
|
15765
|
+
try {
|
|
15766
|
+
return await collection.find({
|
|
15767
|
+
lastPaymentStatus: "failed",
|
|
15768
|
+
failedAttempts: { $lt: maxAttempts }
|
|
15769
|
+
}).limit(BATCH_SIZE).toArray();
|
|
15770
|
+
} catch (error) {
|
|
15771
|
+
throw new BadRequestError23("Failed to get failed subscriptions.");
|
|
15772
|
+
}
|
|
15773
|
+
}
|
|
15774
|
+
async function processSuccessfulPayment(value, session) {
|
|
15775
|
+
const schema3 = Joi12.object({
|
|
15776
|
+
_id: Joi12.string().hex().required(),
|
|
15777
|
+
nextBillingDate: Joi12.date().required()
|
|
15778
|
+
});
|
|
15779
|
+
const { error } = schema3.validate(value);
|
|
15780
|
+
if (error) {
|
|
15781
|
+
throw new BadRequestError23(error.message);
|
|
15782
|
+
}
|
|
15783
|
+
try {
|
|
15784
|
+
value._id = new ObjectId21(value._id);
|
|
15785
|
+
} catch (error2) {
|
|
15786
|
+
throw new BadRequestError23("Invalid ID.");
|
|
15787
|
+
}
|
|
15788
|
+
const date = value.nextBillingDate;
|
|
15789
|
+
try {
|
|
15790
|
+
await collection.updateOne(
|
|
15791
|
+
{ _id: value._id },
|
|
15792
|
+
{
|
|
15793
|
+
$set: {
|
|
15794
|
+
nextBillingDate: new Date(
|
|
15795
|
+
new Date(date).setMonth(new Date(date).getMonth() + 1)
|
|
15796
|
+
).toISOString(),
|
|
15797
|
+
lastPaymentStatus: "success",
|
|
15798
|
+
failedAttempts: 0
|
|
15799
|
+
}
|
|
15800
|
+
},
|
|
15801
|
+
{ session }
|
|
15802
|
+
);
|
|
15803
|
+
return "Successfully updated subscription.";
|
|
15804
|
+
} catch (_) {
|
|
15805
|
+
throw new BadRequestError23("Failed to update subscription.");
|
|
15806
|
+
}
|
|
15807
|
+
}
|
|
15808
|
+
async function markSubscriptionAsFailed({ _id, failed } = {}, session) {
|
|
15809
|
+
const schema3 = Joi12.object({
|
|
15810
|
+
_id: Joi12.string().hex().required()
|
|
15811
|
+
});
|
|
15812
|
+
const { error } = schema3.validate({ _id });
|
|
15813
|
+
if (error) {
|
|
15814
|
+
throw new BadRequestError23(error.message);
|
|
15815
|
+
}
|
|
15816
|
+
try {
|
|
15817
|
+
_id = new ObjectId21(_id);
|
|
15818
|
+
} catch (error2) {
|
|
15819
|
+
throw new BadRequestError23("Invalid ID.");
|
|
15820
|
+
}
|
|
15821
|
+
const updateOptions = {
|
|
15822
|
+
$inc: { failedAttempts: 1 }
|
|
15823
|
+
};
|
|
15824
|
+
if (failed) {
|
|
15825
|
+
updateOptions.$set = { lastPaymentStatus: "failed" };
|
|
15826
|
+
}
|
|
15827
|
+
try {
|
|
15828
|
+
return await collection.updateOne({ _id }, updateOptions, { session });
|
|
15829
|
+
} catch (error2) {
|
|
15830
|
+
throw new BadRequestError23("Failed to update subscription.");
|
|
15831
|
+
}
|
|
15832
|
+
}
|
|
15833
|
+
async function markSubscriptionAsCanceled(_id, session) {
|
|
15834
|
+
const schema3 = Joi12.object({
|
|
15835
|
+
_id: Joi12.string().hex().required()
|
|
15836
|
+
});
|
|
15837
|
+
const { error } = schema3.validate({ _id });
|
|
15838
|
+
if (error) {
|
|
15839
|
+
throw new BadRequestError23(error.message);
|
|
15840
|
+
}
|
|
15841
|
+
try {
|
|
15842
|
+
_id = new ObjectId21(_id);
|
|
15843
|
+
} catch (error2) {
|
|
15844
|
+
throw new BadRequestError23("Invalid ID.");
|
|
15845
|
+
}
|
|
15846
|
+
try {
|
|
15847
|
+
return await collection.updateOne(
|
|
15848
|
+
{ _id },
|
|
15849
|
+
{
|
|
15850
|
+
$set: { status: "canceled" }
|
|
15851
|
+
},
|
|
15852
|
+
{ session }
|
|
15853
|
+
);
|
|
15854
|
+
} catch (error2) {
|
|
15855
|
+
throw new BadRequestError23("Failed to update subscription.");
|
|
15856
|
+
}
|
|
15857
|
+
}
|
|
15675
15858
|
return {
|
|
15676
15859
|
createIndex,
|
|
15677
15860
|
createUniqueIndex,
|
|
@@ -15680,12 +15863,19 @@ function useSubscriptionRepo() {
|
|
|
15680
15863
|
getBySubscriptionId,
|
|
15681
15864
|
getByUserId,
|
|
15682
15865
|
getSubscriptions,
|
|
15683
|
-
updateStatus
|
|
15866
|
+
updateStatus,
|
|
15867
|
+
getByOrgId,
|
|
15868
|
+
getByAffiliateUserId,
|
|
15869
|
+
getDueSubscriptions,
|
|
15870
|
+
getFailedSubscriptions,
|
|
15871
|
+
processSuccessfulPayment,
|
|
15872
|
+
markSubscriptionAsFailed,
|
|
15873
|
+
markSubscriptionAsCanceled
|
|
15684
15874
|
};
|
|
15685
15875
|
}
|
|
15686
15876
|
|
|
15687
15877
|
// src/services/subscription.service.ts
|
|
15688
|
-
import { BadRequestError as
|
|
15878
|
+
import { BadRequestError as BadRequestError33, logger as logger15, useAtlas as useAtlas20 } from "@goweekdays/utils";
|
|
15689
15879
|
|
|
15690
15880
|
// node_modules/axios/lib/helpers/bind.js
|
|
15691
15881
|
function bind(fn, thisArg) {
|
|
@@ -18559,7 +18749,7 @@ validators.spelling = function spelling(correctSpelling) {
|
|
|
18559
18749
|
return true;
|
|
18560
18750
|
};
|
|
18561
18751
|
};
|
|
18562
|
-
function assertOptions(options,
|
|
18752
|
+
function assertOptions(options, schema3, allowUnknown) {
|
|
18563
18753
|
if (typeof options !== "object") {
|
|
18564
18754
|
throw new AxiosError_default("options must be an object", AxiosError_default.ERR_BAD_OPTION_VALUE);
|
|
18565
18755
|
}
|
|
@@ -18567,7 +18757,7 @@ function assertOptions(options, schema, allowUnknown) {
|
|
|
18567
18757
|
let i = keys.length;
|
|
18568
18758
|
while (i-- > 0) {
|
|
18569
18759
|
const opt = keys[i];
|
|
18570
|
-
const validator =
|
|
18760
|
+
const validator = schema3[opt];
|
|
18571
18761
|
if (validator) {
|
|
18572
18762
|
const value = options[opt];
|
|
18573
18763
|
const result = value === void 0 || validator(value, opt, options);
|
|
@@ -18989,8 +19179,9 @@ var {
|
|
|
18989
19179
|
} = axios_default;
|
|
18990
19180
|
|
|
18991
19181
|
// src/services/xendit.service.ts
|
|
18992
|
-
import { BadRequestError as BadRequestError24, logger as logger11 } from "@goweekdays/utils";
|
|
19182
|
+
import { AppError as AppError7, BadRequestError as BadRequestError24, logger as logger11 } from "@goweekdays/utils";
|
|
18993
19183
|
import { ObjectId as ObjectId22 } from "mongodb";
|
|
19184
|
+
import Joi13 from "joi";
|
|
18994
19185
|
function useXenditService() {
|
|
18995
19186
|
const basicAuth = Buffer.from(`${XENDIT_SECRET_KEY}:`).toString("base64");
|
|
18996
19187
|
const axios2 = axios_default.create({
|
|
@@ -19149,7 +19340,8 @@ function useXenditService() {
|
|
|
19149
19340
|
amount = 0,
|
|
19150
19341
|
paymentMethod = "",
|
|
19151
19342
|
description = "",
|
|
19152
|
-
interval = "MONTH"
|
|
19343
|
+
interval = "MONTH",
|
|
19344
|
+
seats = 1
|
|
19153
19345
|
} = {}) {
|
|
19154
19346
|
try {
|
|
19155
19347
|
const res = await axios2.post("/recurring/plans", {
|
|
@@ -19182,7 +19374,9 @@ function useXenditService() {
|
|
|
19182
19374
|
},
|
|
19183
19375
|
failed_cycle_action: "STOP",
|
|
19184
19376
|
immediate_action_type: "FULL_AMOUNT",
|
|
19185
|
-
metadata:
|
|
19377
|
+
metadata: {
|
|
19378
|
+
seats
|
|
19379
|
+
},
|
|
19186
19380
|
description
|
|
19187
19381
|
});
|
|
19188
19382
|
return res.data;
|
|
@@ -19209,6 +19403,52 @@ function useXenditService() {
|
|
|
19209
19403
|
throw new BadRequestError24("Failed to get subscription status.");
|
|
19210
19404
|
}
|
|
19211
19405
|
}
|
|
19406
|
+
async function getSubscription(id) {
|
|
19407
|
+
try {
|
|
19408
|
+
const res = await axios2.get(`/recurring/plans/${id}`);
|
|
19409
|
+
return res.data;
|
|
19410
|
+
} catch (error) {
|
|
19411
|
+
throw new BadRequestError24("Failed to get subscription.");
|
|
19412
|
+
}
|
|
19413
|
+
}
|
|
19414
|
+
async function getSubscriptionCycles(id) {
|
|
19415
|
+
try {
|
|
19416
|
+
const res = await axios2.get(`/recurring/plans/${id}/cycles`);
|
|
19417
|
+
return res.data;
|
|
19418
|
+
} catch (error) {
|
|
19419
|
+
throw new BadRequestError24("Failed to get subscription cycles.");
|
|
19420
|
+
}
|
|
19421
|
+
}
|
|
19422
|
+
async function pay(value) {
|
|
19423
|
+
try {
|
|
19424
|
+
const schema3 = Joi13.object({
|
|
19425
|
+
amount: Joi13.number().positive().min(0).required(),
|
|
19426
|
+
currency: Joi13.string().required(),
|
|
19427
|
+
payment_method_id: Joi13.string().required(),
|
|
19428
|
+
customer_id: Joi13.string().required(),
|
|
19429
|
+
description: Joi13.string().optional().allow("", null),
|
|
19430
|
+
metadata: Joi13.object().optional()
|
|
19431
|
+
});
|
|
19432
|
+
const { error } = schema3.validate(value);
|
|
19433
|
+
if (error) {
|
|
19434
|
+
throw new BadRequestError24(error.message);
|
|
19435
|
+
}
|
|
19436
|
+
const res = await axios2.post("/payment_requests", {
|
|
19437
|
+
amount: value.amount,
|
|
19438
|
+
currency: value.currency,
|
|
19439
|
+
payment_method_id: value.payment_method_id,
|
|
19440
|
+
customer_id: value.customer_id,
|
|
19441
|
+
description: value.description ?? "",
|
|
19442
|
+
metadata: value.metadata ?? {}
|
|
19443
|
+
});
|
|
19444
|
+
return res.data;
|
|
19445
|
+
} catch (error) {
|
|
19446
|
+
if (error instanceof AppError7) {
|
|
19447
|
+
throw error;
|
|
19448
|
+
}
|
|
19449
|
+
throw new BadRequestError24("Failed to initiate payment request.");
|
|
19450
|
+
}
|
|
19451
|
+
}
|
|
19212
19452
|
return {
|
|
19213
19453
|
createCustomer,
|
|
19214
19454
|
linkPaymentMethodEWallet,
|
|
@@ -19217,7 +19457,10 @@ function useXenditService() {
|
|
|
19217
19457
|
linkPaymentMethodCard,
|
|
19218
19458
|
eWalletSubsequentPayment,
|
|
19219
19459
|
checkSubscriptionStatus,
|
|
19220
|
-
cancelSubscription
|
|
19460
|
+
cancelSubscription,
|
|
19461
|
+
getSubscription,
|
|
19462
|
+
getSubscriptionCycles,
|
|
19463
|
+
pay
|
|
19221
19464
|
};
|
|
19222
19465
|
}
|
|
19223
19466
|
|
|
@@ -19257,7 +19500,8 @@ function MPaymentMethod(value) {
|
|
|
19257
19500
|
paymentId: value.paymentId ?? "",
|
|
19258
19501
|
customerId: value.customerId ?? "",
|
|
19259
19502
|
number: value.number,
|
|
19260
|
-
|
|
19503
|
+
month_expiry: value.month_expiry ?? "",
|
|
19504
|
+
year_expiry: value.year_expiry ?? "",
|
|
19261
19505
|
cvv: value.cvv ?? "",
|
|
19262
19506
|
status: value.status ?? "active",
|
|
19263
19507
|
createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -19406,7 +19650,7 @@ function usePaymentMethodRepo() {
|
|
|
19406
19650
|
|
|
19407
19651
|
// src/repositories/organization.repository.ts
|
|
19408
19652
|
import {
|
|
19409
|
-
AppError as
|
|
19653
|
+
AppError as AppError8,
|
|
19410
19654
|
BadRequestError as BadRequestError28,
|
|
19411
19655
|
InternalServerError as InternalServerError20,
|
|
19412
19656
|
logger as logger13,
|
|
@@ -19416,23 +19660,23 @@ import {
|
|
|
19416
19660
|
|
|
19417
19661
|
// src/models/organization.model.ts
|
|
19418
19662
|
import { BadRequestError as BadRequestError27 } from "@goweekdays/utils";
|
|
19419
|
-
import
|
|
19663
|
+
import Joi14 from "joi";
|
|
19420
19664
|
import { ObjectId as ObjectId25 } from "mongodb";
|
|
19421
19665
|
function MOrg(value) {
|
|
19422
|
-
const
|
|
19423
|
-
_id:
|
|
19424
|
-
name:
|
|
19425
|
-
email:
|
|
19426
|
-
contact:
|
|
19427
|
-
type:
|
|
19428
|
-
busInst:
|
|
19429
|
-
description:
|
|
19430
|
-
status:
|
|
19431
|
-
createdAt:
|
|
19432
|
-
updatedAt:
|
|
19433
|
-
deletedAt:
|
|
19666
|
+
const schema3 = Joi14.object({
|
|
19667
|
+
_id: Joi14.string().hex().optional().allow("", null),
|
|
19668
|
+
name: Joi14.string().required(),
|
|
19669
|
+
email: Joi14.string().email().required(),
|
|
19670
|
+
contact: Joi14.string().required(),
|
|
19671
|
+
type: Joi14.string().required(),
|
|
19672
|
+
busInst: Joi14.string().optional().allow("", null),
|
|
19673
|
+
description: Joi14.string().optional().allow("", null),
|
|
19674
|
+
status: Joi14.string().optional().allow("", null),
|
|
19675
|
+
createdAt: Joi14.string().optional().allow("", null),
|
|
19676
|
+
updatedAt: Joi14.string().optional().allow("", null),
|
|
19677
|
+
deletedAt: Joi14.string().optional().allow("", null)
|
|
19434
19678
|
});
|
|
19435
|
-
const { error } =
|
|
19679
|
+
const { error } = schema3.validate(value);
|
|
19436
19680
|
if (error) {
|
|
19437
19681
|
throw new BadRequestError27(error.message);
|
|
19438
19682
|
}
|
|
@@ -19508,7 +19752,7 @@ function useOrgRepo() {
|
|
|
19508
19752
|
level: "error",
|
|
19509
19753
|
message: error.message
|
|
19510
19754
|
});
|
|
19511
|
-
if (error instanceof
|
|
19755
|
+
if (error instanceof AppError8) {
|
|
19512
19756
|
throw error;
|
|
19513
19757
|
} else {
|
|
19514
19758
|
const isDuplicated = error.message.includes("duplicate");
|
|
@@ -19568,7 +19812,7 @@ function useOrgRepo() {
|
|
|
19568
19812
|
}
|
|
19569
19813
|
return result;
|
|
19570
19814
|
} catch (error) {
|
|
19571
|
-
if (error instanceof
|
|
19815
|
+
if (error instanceof AppError8) {
|
|
19572
19816
|
throw error;
|
|
19573
19817
|
} else {
|
|
19574
19818
|
throw new InternalServerError20("Failed to get organization.");
|
|
@@ -19583,7 +19827,7 @@ function useOrgRepo() {
|
|
|
19583
19827
|
}
|
|
19584
19828
|
return result;
|
|
19585
19829
|
} catch (error) {
|
|
19586
|
-
if (error instanceof
|
|
19830
|
+
if (error instanceof AppError8) {
|
|
19587
19831
|
throw error;
|
|
19588
19832
|
} else {
|
|
19589
19833
|
throw new InternalServerError20("Failed to get organization.");
|
|
@@ -19721,27 +19965,205 @@ function useAddressRepo() {
|
|
|
19721
19965
|
};
|
|
19722
19966
|
}
|
|
19723
19967
|
|
|
19968
|
+
// src/repositories/order.repository.ts
|
|
19969
|
+
import {
|
|
19970
|
+
AppError as AppError9,
|
|
19971
|
+
BadRequestError as BadRequestError32,
|
|
19972
|
+
InternalServerError as InternalServerError21,
|
|
19973
|
+
logger as logger14,
|
|
19974
|
+
paginate as paginate11,
|
|
19975
|
+
useAtlas as useAtlas19
|
|
19976
|
+
} from "@goweekdays/utils";
|
|
19977
|
+
|
|
19978
|
+
// src/models/order.model.ts
|
|
19979
|
+
import { ObjectId as ObjectId29 } from "mongodb";
|
|
19980
|
+
|
|
19981
|
+
// src/validations/order.schema.ts
|
|
19982
|
+
import Joi15 from "joi";
|
|
19983
|
+
var schema = Joi15.object({
|
|
19984
|
+
_id: Joi15.string().hex().optional().allow("", null),
|
|
19985
|
+
payment: Joi15.string().required(),
|
|
19986
|
+
user: Joi15.string().hex().required(),
|
|
19987
|
+
org: Joi15.string().hex().optional().allow("", null),
|
|
19988
|
+
type: Joi15.string().required(),
|
|
19989
|
+
amount: Joi15.number().positive().min(0).required(),
|
|
19990
|
+
currency: Joi15.string().required(),
|
|
19991
|
+
description: Joi15.string().optional().allow("", null),
|
|
19992
|
+
metadata: Joi15.object({
|
|
19993
|
+
subscriptionId: Joi15.string().hex().optional().allow("", null),
|
|
19994
|
+
cycle: Joi15.number().optional().allow("", null),
|
|
19995
|
+
seats: Joi15.number().optional().allow("", null),
|
|
19996
|
+
promoCode: Joi15.string().optional().allow("", null)
|
|
19997
|
+
}).optional().allow("", null),
|
|
19998
|
+
status: Joi15.string().optional().allow("", null),
|
|
19999
|
+
createdAt: Joi15.string().optional().allow("", null),
|
|
20000
|
+
updatedAt: Joi15.string().optional().allow("", null),
|
|
20001
|
+
deletedAt: Joi15.string().optional().allow("", null)
|
|
20002
|
+
});
|
|
20003
|
+
|
|
20004
|
+
// src/models/order.model.ts
|
|
20005
|
+
import { BadRequestError as BadRequestError31 } from "@goweekdays/utils";
|
|
20006
|
+
function MOrder(value) {
|
|
20007
|
+
const { error } = schema.validate(value);
|
|
20008
|
+
if (error) {
|
|
20009
|
+
throw new BadRequestError31(error.message);
|
|
20010
|
+
}
|
|
20011
|
+
if (value._id) {
|
|
20012
|
+
try {
|
|
20013
|
+
value._id = new ObjectId29(value._id);
|
|
20014
|
+
} catch (error2) {
|
|
20015
|
+
throw new BadRequestError31("Invalid ID.");
|
|
20016
|
+
}
|
|
20017
|
+
}
|
|
20018
|
+
if (value.user) {
|
|
20019
|
+
try {
|
|
20020
|
+
value.user = new ObjectId29(value.user);
|
|
20021
|
+
} catch (error2) {
|
|
20022
|
+
throw new BadRequestError31("Invalid user ID.");
|
|
20023
|
+
}
|
|
20024
|
+
}
|
|
20025
|
+
if (value.org) {
|
|
20026
|
+
try {
|
|
20027
|
+
value.org = new ObjectId29(value.org);
|
|
20028
|
+
} catch (error2) {
|
|
20029
|
+
throw new BadRequestError31("Invalid org ID.");
|
|
20030
|
+
}
|
|
20031
|
+
}
|
|
20032
|
+
if (value.metadata?.subscriptionId) {
|
|
20033
|
+
try {
|
|
20034
|
+
value.metadata.subscriptionId = new ObjectId29(
|
|
20035
|
+
value.metadata.subscriptionId
|
|
20036
|
+
);
|
|
20037
|
+
} catch (error2) {
|
|
20038
|
+
throw new BadRequestError31("Invalid subscription ID.");
|
|
20039
|
+
}
|
|
20040
|
+
}
|
|
20041
|
+
return {
|
|
20042
|
+
_id: value._id,
|
|
20043
|
+
payment: value.payment,
|
|
20044
|
+
user: value.user ?? "",
|
|
20045
|
+
org: value.org ?? "",
|
|
20046
|
+
type: value.type,
|
|
20047
|
+
amount: value.amount,
|
|
20048
|
+
currency: value.currency,
|
|
20049
|
+
description: value.description ?? "",
|
|
20050
|
+
metadata: value.metadata ?? {},
|
|
20051
|
+
status: value.status ?? "succeeded",
|
|
20052
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20053
|
+
updatedAt: value.updatedAt ?? "",
|
|
20054
|
+
deletedAt: value.deletedAt ?? ""
|
|
20055
|
+
};
|
|
20056
|
+
}
|
|
20057
|
+
|
|
20058
|
+
// src/repositories/order.repository.ts
|
|
20059
|
+
import { ObjectId as ObjectId30 } from "mongodb";
|
|
20060
|
+
function useOrderRepo() {
|
|
20061
|
+
const db = useAtlas19.getDb();
|
|
20062
|
+
if (!db) {
|
|
20063
|
+
throw new InternalServerError21("Unable to connect to server.");
|
|
20064
|
+
}
|
|
20065
|
+
const collection = db.collection("orders");
|
|
20066
|
+
function createIndex() {
|
|
20067
|
+
try {
|
|
20068
|
+
collection.createIndexes([
|
|
20069
|
+
{ key: { "metadata.subscriptionId": 1 } },
|
|
20070
|
+
{ key: { status: 1 } },
|
|
20071
|
+
{ key: { type: 1 } }
|
|
20072
|
+
]);
|
|
20073
|
+
} catch (error) {
|
|
20074
|
+
throw new Error("Failed to create index for promo code.");
|
|
20075
|
+
}
|
|
20076
|
+
}
|
|
20077
|
+
function add(value, session) {
|
|
20078
|
+
try {
|
|
20079
|
+
value = MOrder(value);
|
|
20080
|
+
collection.insertOne(value, { session });
|
|
20081
|
+
} catch (error) {
|
|
20082
|
+
logger14.log({ level: "error", message: `${error}` });
|
|
20083
|
+
if (error instanceof AppError9) {
|
|
20084
|
+
throw error;
|
|
20085
|
+
}
|
|
20086
|
+
throw new InternalServerError21("Internal server error.");
|
|
20087
|
+
}
|
|
20088
|
+
}
|
|
20089
|
+
async function getOrders({
|
|
20090
|
+
search = "",
|
|
20091
|
+
page = 1,
|
|
20092
|
+
limit = 10,
|
|
20093
|
+
sort = {},
|
|
20094
|
+
status = "active",
|
|
20095
|
+
type = "",
|
|
20096
|
+
id = ""
|
|
20097
|
+
} = {}) {
|
|
20098
|
+
page = page > 0 ? page - 1 : 0;
|
|
20099
|
+
const query = { status };
|
|
20100
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
20101
|
+
if (search) {
|
|
20102
|
+
query.$text = { $search: search };
|
|
20103
|
+
}
|
|
20104
|
+
if (type) {
|
|
20105
|
+
query.type = type;
|
|
20106
|
+
}
|
|
20107
|
+
if (id) {
|
|
20108
|
+
try {
|
|
20109
|
+
query["metadata.subscriptionId"] = new ObjectId30(id);
|
|
20110
|
+
} catch (error) {
|
|
20111
|
+
throw new BadRequestError32("Invalid subscription ID.");
|
|
20112
|
+
}
|
|
20113
|
+
}
|
|
20114
|
+
try {
|
|
20115
|
+
const items = await collection.aggregate([
|
|
20116
|
+
{ $match: query },
|
|
20117
|
+
{ $sort: sort },
|
|
20118
|
+
{ $skip: page * limit },
|
|
20119
|
+
{ $limit: limit }
|
|
20120
|
+
]).toArray();
|
|
20121
|
+
const length = await collection.countDocuments(query);
|
|
20122
|
+
return paginate11(items, page, limit, length);
|
|
20123
|
+
} catch (error) {
|
|
20124
|
+
logger14.log({ level: "error", message: `${error}` });
|
|
20125
|
+
throw new InternalServerError21("Internal server error.");
|
|
20126
|
+
}
|
|
20127
|
+
}
|
|
20128
|
+
return {
|
|
20129
|
+
createIndex,
|
|
20130
|
+
add,
|
|
20131
|
+
getOrders
|
|
20132
|
+
};
|
|
20133
|
+
}
|
|
20134
|
+
|
|
19724
20135
|
// src/services/subscription.service.ts
|
|
19725
20136
|
function useSubscriptionService() {
|
|
19726
|
-
const {
|
|
20137
|
+
const {
|
|
20138
|
+
getByUserId: _getByUserId,
|
|
20139
|
+
add,
|
|
20140
|
+
getByOrgId: _getByOrgId,
|
|
20141
|
+
getDueSubscriptions,
|
|
20142
|
+
processSuccessfulPayment,
|
|
20143
|
+
markSubscriptionAsFailed,
|
|
20144
|
+
getFailedSubscriptions,
|
|
20145
|
+
markSubscriptionAsCanceled
|
|
20146
|
+
} = useSubscriptionRepo();
|
|
19727
20147
|
const { getUserById: _getUserByUserId, updateUserFieldById } = useUserRepo();
|
|
19728
|
-
const {
|
|
19729
|
-
|
|
20148
|
+
const {
|
|
20149
|
+
checkSubscriptionStatus,
|
|
20150
|
+
getSubscription,
|
|
20151
|
+
getSubscriptionCycles,
|
|
20152
|
+
pay
|
|
20153
|
+
} = useXenditService();
|
|
20154
|
+
const { add: addPaymentMethod } = usePaymentMethodRepo();
|
|
19730
20155
|
const { add: addOrg } = useOrgRepo();
|
|
19731
20156
|
const { add: addAddress } = useAddressRepo();
|
|
19732
20157
|
const { addRole } = useRoleRepo();
|
|
19733
20158
|
const { add: addMember } = useMemberRepo();
|
|
20159
|
+
const { add: addOrder } = useOrderRepo();
|
|
19734
20160
|
async function createOrgSubscription(value) {
|
|
19735
|
-
const session =
|
|
20161
|
+
const session = useAtlas20.getClient()?.startSession();
|
|
19736
20162
|
session?.startTransaction();
|
|
19737
20163
|
try {
|
|
19738
20164
|
const _user = await _getUserByUserId(value.user);
|
|
19739
20165
|
if (!_user) {
|
|
19740
|
-
throw new
|
|
19741
|
-
}
|
|
19742
|
-
const payment = await getByPaymentMethodId(value.payment_method_id);
|
|
19743
|
-
if (!payment) {
|
|
19744
|
-
throw new BadRequestError31("Payment method not found.");
|
|
20166
|
+
throw new BadRequestError33("User not found.");
|
|
19745
20167
|
}
|
|
19746
20168
|
const org = await addOrg(value.organization, session);
|
|
19747
20169
|
const role = await addRole(
|
|
@@ -19770,27 +20192,62 @@ function useSubscriptionService() {
|
|
|
19770
20192
|
session
|
|
19771
20193
|
);
|
|
19772
20194
|
}
|
|
19773
|
-
|
|
19774
|
-
payment.org = org;
|
|
19775
|
-
delete payment._id;
|
|
19776
|
-
await addPaymentMethod(payment, session);
|
|
19777
|
-
value.billingAddress.org = org;
|
|
19778
|
-
await addAddress(value.billingAddress, session);
|
|
19779
|
-
const subscription = await initSubscription({
|
|
19780
|
-
customer: value.customer_id,
|
|
19781
|
-
paymentMethod: value.payment_method_id,
|
|
19782
|
-
amount: value.amount,
|
|
19783
|
-
description: "GoWeekdays Organization Monthly Subscription."
|
|
19784
|
-
});
|
|
19785
|
-
await add(
|
|
20195
|
+
await addPaymentMethod(
|
|
19786
20196
|
{
|
|
19787
20197
|
org,
|
|
19788
|
-
|
|
20198
|
+
paymentId: value.payment_method_id,
|
|
20199
|
+
customerId: value.customer_id,
|
|
20200
|
+
type: value.payment_method_type,
|
|
20201
|
+
name: value.payment_method_channel,
|
|
20202
|
+
number: value.payment_method_number,
|
|
20203
|
+
month_expiry: value.payment_method_expiry_month,
|
|
20204
|
+
year_expiry: value.payment_method_expiry_year,
|
|
20205
|
+
cvv: value.payment_method_cvv
|
|
19789
20206
|
},
|
|
19790
20207
|
session
|
|
19791
20208
|
);
|
|
19792
|
-
|
|
19793
|
-
|
|
20209
|
+
value.billingAddress.org = org;
|
|
20210
|
+
await addAddress(value.billingAddress, session);
|
|
20211
|
+
const description = "GoWeekdays Organization Monthly Subscription.";
|
|
20212
|
+
const subscription = await add(
|
|
20213
|
+
{
|
|
20214
|
+
org: org.toString(),
|
|
20215
|
+
customerId: value.customer_id,
|
|
20216
|
+
paymentMethodId: value.payment_method_id,
|
|
20217
|
+
amount: value.amount,
|
|
20218
|
+
currency: value.currency,
|
|
20219
|
+
type: "organization",
|
|
20220
|
+
description,
|
|
20221
|
+
seats: value.seats,
|
|
20222
|
+
billingCycle: "monthly"
|
|
20223
|
+
},
|
|
20224
|
+
session
|
|
20225
|
+
);
|
|
20226
|
+
const payment = await pay({
|
|
20227
|
+
customer_id: value.customer_id,
|
|
20228
|
+
payment_method_id: value.payment_method_id,
|
|
20229
|
+
amount: value.amount,
|
|
20230
|
+
currency: value.currency,
|
|
20231
|
+
description
|
|
20232
|
+
});
|
|
20233
|
+
await addOrder(
|
|
20234
|
+
{
|
|
20235
|
+
payment: payment.id,
|
|
20236
|
+
user: value.user,
|
|
20237
|
+
org: org.toString(),
|
|
20238
|
+
type: "organization",
|
|
20239
|
+
amount: value.amount,
|
|
20240
|
+
currency: value.currency,
|
|
20241
|
+
description,
|
|
20242
|
+
metadata: { subscriptionId: subscription.toString() }
|
|
20243
|
+
},
|
|
20244
|
+
session
|
|
20245
|
+
);
|
|
20246
|
+
await session?.commitTransaction();
|
|
20247
|
+
return {
|
|
20248
|
+
message: "Subscription created successfully.",
|
|
20249
|
+
data: { org: org.toString() }
|
|
20250
|
+
};
|
|
19794
20251
|
} catch (error) {
|
|
19795
20252
|
await session?.abortTransaction();
|
|
19796
20253
|
throw error;
|
|
@@ -19798,36 +20255,55 @@ function useSubscriptionService() {
|
|
|
19798
20255
|
session?.endSession();
|
|
19799
20256
|
}
|
|
19800
20257
|
}
|
|
19801
|
-
async function createAffiliateSubscription({
|
|
19802
|
-
|
|
19803
|
-
amount = 0,
|
|
19804
|
-
payment_method_id = "",
|
|
19805
|
-
customer_id = ""
|
|
19806
|
-
} = {}) {
|
|
19807
|
-
const session = useAtlas19.getClient()?.startSession();
|
|
20258
|
+
async function createAffiliateSubscription(value) {
|
|
20259
|
+
const session = useAtlas20.getClient()?.startSession();
|
|
19808
20260
|
session?.startTransaction();
|
|
19809
20261
|
try {
|
|
19810
|
-
const _user = await _getUserByUserId(user);
|
|
20262
|
+
const _user = await _getUserByUserId(value.user);
|
|
19811
20263
|
if (!_user) {
|
|
19812
|
-
throw new
|
|
20264
|
+
throw new BadRequestError33("User not found.");
|
|
19813
20265
|
}
|
|
19814
|
-
|
|
19815
|
-
|
|
19816
|
-
|
|
19817
|
-
amount,
|
|
19818
|
-
description: "GoWeekdays Affiliate Annual Subscription.",
|
|
19819
|
-
interval: "YEAR"
|
|
19820
|
-
});
|
|
19821
|
-
await add(
|
|
20266
|
+
await addAddress(value.billingAddress, session);
|
|
20267
|
+
const description = "GoWeekdays affiliate yearly Subscription.";
|
|
20268
|
+
const subscription = await add(
|
|
19822
20269
|
{
|
|
19823
|
-
user,
|
|
19824
|
-
|
|
20270
|
+
user: value.user,
|
|
20271
|
+
customerId: value.customer_id,
|
|
20272
|
+
paymentMethodId: value.payment_method_id,
|
|
20273
|
+
amount: value.amount,
|
|
20274
|
+
currency: value.currency,
|
|
20275
|
+
type: "affiliate",
|
|
20276
|
+
description,
|
|
20277
|
+
billingCycle: "yearly"
|
|
20278
|
+
},
|
|
20279
|
+
session
|
|
20280
|
+
);
|
|
20281
|
+
let payment = {};
|
|
20282
|
+
if (value.amount) {
|
|
20283
|
+
payment = await pay({
|
|
20284
|
+
customer_id: value.customer_id,
|
|
20285
|
+
payment_method_id: value.payment_method_id,
|
|
20286
|
+
amount: value.amount,
|
|
20287
|
+
currency: value.currency,
|
|
20288
|
+
description
|
|
20289
|
+
});
|
|
20290
|
+
}
|
|
20291
|
+
await addOrder(
|
|
20292
|
+
{
|
|
20293
|
+
payment: payment.id,
|
|
20294
|
+
user: value.user,
|
|
20295
|
+
type: "affiliate",
|
|
20296
|
+
amount: value.amount,
|
|
20297
|
+
currency: value.currency,
|
|
20298
|
+
description,
|
|
20299
|
+
metadata: { subscriptionId: subscription.toString() }
|
|
19825
20300
|
},
|
|
19826
20301
|
session
|
|
19827
20302
|
);
|
|
19828
20303
|
await session?.commitTransaction();
|
|
19829
20304
|
return "Subscription created successfully.";
|
|
19830
20305
|
} catch (error) {
|
|
20306
|
+
console.log(error);
|
|
19831
20307
|
await session?.abortTransaction();
|
|
19832
20308
|
throw error;
|
|
19833
20309
|
} finally {
|
|
@@ -19838,9 +20314,10 @@ function useSubscriptionService() {
|
|
|
19838
20314
|
try {
|
|
19839
20315
|
const subscription = await _getByUserId(id);
|
|
19840
20316
|
if (!subscription) {
|
|
19841
|
-
throw new
|
|
20317
|
+
throw new BadRequestError33("Subscription not found.");
|
|
19842
20318
|
}
|
|
19843
|
-
|
|
20319
|
+
const customerSubscription = await getSubscription(id);
|
|
20320
|
+
return customerSubscription;
|
|
19844
20321
|
} catch (error) {
|
|
19845
20322
|
throw error;
|
|
19846
20323
|
}
|
|
@@ -19853,57 +20330,204 @@ function useSubscriptionService() {
|
|
|
19853
20330
|
);
|
|
19854
20331
|
return status;
|
|
19855
20332
|
} catch (error) {
|
|
19856
|
-
throw new
|
|
20333
|
+
throw new BadRequestError33("Failed to fetch subscription status");
|
|
20334
|
+
}
|
|
20335
|
+
}
|
|
20336
|
+
async function processSubscriptions(batchSize = 100) {
|
|
20337
|
+
while (true) {
|
|
20338
|
+
const subscriptions = await getDueSubscriptions(batchSize);
|
|
20339
|
+
if (subscriptions.length === 0) {
|
|
20340
|
+
logger15.log({
|
|
20341
|
+
level: "info",
|
|
20342
|
+
message: "No more subscriptions to process."
|
|
20343
|
+
});
|
|
20344
|
+
break;
|
|
20345
|
+
}
|
|
20346
|
+
await Promise.allSettled(
|
|
20347
|
+
subscriptions.map(async (sub) => {
|
|
20348
|
+
const session = useAtlas20.getClient()?.startSession();
|
|
20349
|
+
session?.startTransaction();
|
|
20350
|
+
try {
|
|
20351
|
+
const payment = await pay({
|
|
20352
|
+
customer_id: sub.customerId,
|
|
20353
|
+
payment_method_id: sub.paymentMethodId,
|
|
20354
|
+
amount: sub.amount,
|
|
20355
|
+
currency: sub.currency,
|
|
20356
|
+
description: "GoWeekdays Monthly Subscription"
|
|
20357
|
+
});
|
|
20358
|
+
await addOrder(
|
|
20359
|
+
{
|
|
20360
|
+
payment: payment.id,
|
|
20361
|
+
user: sub.user,
|
|
20362
|
+
org: sub.org,
|
|
20363
|
+
type: "organization",
|
|
20364
|
+
amount: sub.amount,
|
|
20365
|
+
currency: sub.currency,
|
|
20366
|
+
description: "GoWeekdays Monthly Subscription",
|
|
20367
|
+
metadata: { subscriptionId: sub._id }
|
|
20368
|
+
},
|
|
20369
|
+
session
|
|
20370
|
+
);
|
|
20371
|
+
await processSuccessfulPayment(
|
|
20372
|
+
{ _id: sub._id, nextBillingDate: sub.nextBillingDate },
|
|
20373
|
+
session
|
|
20374
|
+
);
|
|
20375
|
+
await session?.commitTransaction();
|
|
20376
|
+
logger15.log({
|
|
20377
|
+
level: "info",
|
|
20378
|
+
message: `Processed subscription ${sub._id} successfully.`
|
|
20379
|
+
});
|
|
20380
|
+
} catch (error) {
|
|
20381
|
+
await session?.abortTransaction();
|
|
20382
|
+
logger15.log({
|
|
20383
|
+
level: "error",
|
|
20384
|
+
message: `Failed to process ${sub._id}: ${error}`
|
|
20385
|
+
});
|
|
20386
|
+
await markSubscriptionAsFailed({
|
|
20387
|
+
_id: String(sub._id),
|
|
20388
|
+
failed: true
|
|
20389
|
+
});
|
|
20390
|
+
} finally {
|
|
20391
|
+
session?.endSession();
|
|
20392
|
+
}
|
|
20393
|
+
})
|
|
20394
|
+
);
|
|
20395
|
+
logger15.log({
|
|
20396
|
+
level: "info",
|
|
20397
|
+
message: "Processed a batch of subscriptions."
|
|
20398
|
+
});
|
|
20399
|
+
}
|
|
20400
|
+
}
|
|
20401
|
+
async function retryFailedPayments(batchSize = 100) {
|
|
20402
|
+
let hasMore = true;
|
|
20403
|
+
while (hasMore) {
|
|
20404
|
+
const failedSubs = await getFailedSubscriptions(batchSize);
|
|
20405
|
+
if (failedSubs.length === 0) {
|
|
20406
|
+
hasMore = false;
|
|
20407
|
+
break;
|
|
20408
|
+
}
|
|
20409
|
+
await Promise.allSettled(
|
|
20410
|
+
failedSubs.map(async (sub) => {
|
|
20411
|
+
const session = useAtlas20.getClient()?.startSession();
|
|
20412
|
+
session?.startTransaction();
|
|
20413
|
+
try {
|
|
20414
|
+
const payment = await pay({
|
|
20415
|
+
customer_id: sub.customerId,
|
|
20416
|
+
payment_method_id: sub.paymentMethodId,
|
|
20417
|
+
amount: sub.amount,
|
|
20418
|
+
currency: sub.currency,
|
|
20419
|
+
description: "GoWeekdays Monthly Subscription - Retry"
|
|
20420
|
+
});
|
|
20421
|
+
await addOrder(
|
|
20422
|
+
{
|
|
20423
|
+
payment: payment.id,
|
|
20424
|
+
user: sub.user,
|
|
20425
|
+
org: sub.org,
|
|
20426
|
+
type: "organization",
|
|
20427
|
+
amount: sub.amount,
|
|
20428
|
+
currency: sub.currency,
|
|
20429
|
+
description: "GoWeekdays Monthly Subscription",
|
|
20430
|
+
metadata: { subscriptionId: sub._id }
|
|
20431
|
+
},
|
|
20432
|
+
session
|
|
20433
|
+
);
|
|
20434
|
+
await processSuccessfulPayment(
|
|
20435
|
+
{ _id: sub._id, nextBillingDate: sub.nextBillingDate },
|
|
20436
|
+
session
|
|
20437
|
+
);
|
|
20438
|
+
await session?.commitTransaction();
|
|
20439
|
+
logger15.log({
|
|
20440
|
+
level: "info",
|
|
20441
|
+
message: `Successfully retried subscription ${sub._id}.`
|
|
20442
|
+
});
|
|
20443
|
+
} catch (error) {
|
|
20444
|
+
await session?.abortTransaction();
|
|
20445
|
+
logger15.log({
|
|
20446
|
+
level: "error",
|
|
20447
|
+
message: `Retry failed for ${sub._id}: ${error}`
|
|
20448
|
+
});
|
|
20449
|
+
await markSubscriptionAsFailed({ _id: String(sub._id) });
|
|
20450
|
+
if (sub.failedAttempts && sub.failedAttempts + 1 >= 3) {
|
|
20451
|
+
await markSubscriptionAsCanceled(String(sub._id));
|
|
20452
|
+
logger15.log({
|
|
20453
|
+
level: "info",
|
|
20454
|
+
message: `Subscription ${sub._id} canceled after max retry attempts.`
|
|
20455
|
+
});
|
|
20456
|
+
}
|
|
20457
|
+
} finally {
|
|
20458
|
+
session?.endSession();
|
|
20459
|
+
}
|
|
20460
|
+
})
|
|
20461
|
+
);
|
|
20462
|
+
logger15.log({
|
|
20463
|
+
level: "info",
|
|
20464
|
+
message: `Processed batch of ${failedSubs.length}.`
|
|
20465
|
+
});
|
|
19857
20466
|
}
|
|
20467
|
+
logger15.log({ level: "info", message: "All failed payments retried." });
|
|
19858
20468
|
}
|
|
19859
20469
|
return {
|
|
19860
20470
|
getByUserId,
|
|
19861
20471
|
getStatusByUser,
|
|
19862
20472
|
createAffiliateSubscription,
|
|
19863
|
-
createOrgSubscription
|
|
20473
|
+
createOrgSubscription,
|
|
20474
|
+
processSubscriptions,
|
|
20475
|
+
retryFailedPayments
|
|
19864
20476
|
};
|
|
19865
20477
|
}
|
|
19866
20478
|
|
|
19867
20479
|
// src/controllers/subscription.controller.ts
|
|
19868
|
-
import
|
|
19869
|
-
import { BadRequestError as
|
|
20480
|
+
import Joi17 from "joi";
|
|
20481
|
+
import { BadRequestError as BadRequestError34 } from "@goweekdays/utils";
|
|
19870
20482
|
|
|
19871
20483
|
// src/validations/subscription.schema.ts
|
|
19872
|
-
import
|
|
20484
|
+
import Joi16 from "joi";
|
|
19873
20485
|
function useSubscriptionSchema() {
|
|
19874
|
-
const
|
|
19875
|
-
user:
|
|
19876
|
-
amount:
|
|
19877
|
-
customer_id:
|
|
19878
|
-
payment_method_id:
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
19883
|
-
|
|
19884
|
-
|
|
19885
|
-
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
|
|
19889
|
-
|
|
19890
|
-
|
|
19891
|
-
|
|
19892
|
-
|
|
19893
|
-
|
|
19894
|
-
|
|
19895
|
-
|
|
19896
|
-
|
|
20486
|
+
const schema3 = Joi16.object({
|
|
20487
|
+
user: Joi16.string().required().min(0),
|
|
20488
|
+
amount: Joi16.number().required(),
|
|
20489
|
+
customer_id: Joi16.string().required(),
|
|
20490
|
+
payment_method_id: Joi16.string().required(),
|
|
20491
|
+
payment_method_type: Joi16.string().required(),
|
|
20492
|
+
payment_method_number: Joi16.string().required(),
|
|
20493
|
+
payment_method_channel: Joi16.string().required(),
|
|
20494
|
+
payment_method_month_expiry: Joi16.string().optional().allow(null, ""),
|
|
20495
|
+
payment_method_year_expiry: Joi16.string().optional().allow(null, ""),
|
|
20496
|
+
payment_method_cvv: Joi16.string().optional().allow(null, ""),
|
|
20497
|
+
currency: Joi16.string().optional().allow("", null),
|
|
20498
|
+
seats: Joi16.number().optional().min(0).allow(null),
|
|
20499
|
+
organization: Joi16.object({
|
|
20500
|
+
name: Joi16.string().required(),
|
|
20501
|
+
description: Joi16.string().optional().allow("", null),
|
|
20502
|
+
type: Joi16.string().allow("personal", "business").required(),
|
|
20503
|
+
email: Joi16.string().email().required(),
|
|
20504
|
+
contact: Joi16.string().required(),
|
|
20505
|
+
busInst: Joi16.string().optional().allow(null, "")
|
|
20506
|
+
}).optional().allow({}),
|
|
20507
|
+
billingAddress: Joi16.object({
|
|
20508
|
+
type: Joi16.string().required(),
|
|
20509
|
+
country: Joi16.string().required(),
|
|
20510
|
+
address: Joi16.string().required(),
|
|
20511
|
+
continuedAddress: Joi16.string().optional().allow(null, ""),
|
|
20512
|
+
city: Joi16.string().required(),
|
|
20513
|
+
province: Joi16.string().required(),
|
|
20514
|
+
postalCode: Joi16.string().required(),
|
|
20515
|
+
taxId: Joi16.string().optional().allow(null, "")
|
|
19897
20516
|
}).required()
|
|
19898
20517
|
});
|
|
19899
20518
|
return {
|
|
19900
|
-
schema
|
|
20519
|
+
schema: schema3
|
|
19901
20520
|
};
|
|
19902
20521
|
}
|
|
19903
20522
|
|
|
19904
20523
|
// src/controllers/subscription.controller.ts
|
|
19905
20524
|
function useSubscriptionController() {
|
|
19906
|
-
const {
|
|
20525
|
+
const {
|
|
20526
|
+
add: _add,
|
|
20527
|
+
getSubscriptions: _getSubscriptions,
|
|
20528
|
+
getByAffiliateUserId: _getByAffiliateUserId,
|
|
20529
|
+
getByOrgId: _getByOrgId
|
|
20530
|
+
} = useSubscriptionRepo();
|
|
19907
20531
|
const {
|
|
19908
20532
|
getByUserId: _getByUserId,
|
|
19909
20533
|
getStatusByUser: _getStatusByUser,
|
|
@@ -19912,13 +20536,13 @@ function useSubscriptionController() {
|
|
|
19912
20536
|
} = useSubscriptionService();
|
|
19913
20537
|
async function add(req, res, next) {
|
|
19914
20538
|
const value = req.body;
|
|
19915
|
-
const validation =
|
|
19916
|
-
user:
|
|
19917
|
-
subscriptionId:
|
|
20539
|
+
const validation = Joi17.object({
|
|
20540
|
+
user: Joi17.string().required(),
|
|
20541
|
+
subscriptionId: Joi17.string().required()
|
|
19918
20542
|
});
|
|
19919
20543
|
const { error } = validation.validate(value);
|
|
19920
20544
|
if (error) {
|
|
19921
|
-
next(new
|
|
20545
|
+
next(new BadRequestError34(error.message));
|
|
19922
20546
|
}
|
|
19923
20547
|
try {
|
|
19924
20548
|
const value2 = req.body;
|
|
@@ -19931,10 +20555,10 @@ function useSubscriptionController() {
|
|
|
19931
20555
|
}
|
|
19932
20556
|
async function getByUserId(req, res, next) {
|
|
19933
20557
|
const id = req.params.id;
|
|
19934
|
-
const validation =
|
|
20558
|
+
const validation = Joi17.string().required();
|
|
19935
20559
|
const { error } = validation.validate(id);
|
|
19936
20560
|
if (error) {
|
|
19937
|
-
next(new
|
|
20561
|
+
next(new BadRequestError34(error.message));
|
|
19938
20562
|
}
|
|
19939
20563
|
try {
|
|
19940
20564
|
const subscription = await _getByUserId(id);
|
|
@@ -19944,18 +20568,48 @@ function useSubscriptionController() {
|
|
|
19944
20568
|
next(error2);
|
|
19945
20569
|
}
|
|
19946
20570
|
}
|
|
20571
|
+
async function getByAffiliateUserId(req, res, next) {
|
|
20572
|
+
const id = req.params.id;
|
|
20573
|
+
const validation = Joi17.string().required();
|
|
20574
|
+
const { error } = validation.validate(id);
|
|
20575
|
+
if (error) {
|
|
20576
|
+
next(new BadRequestError34(error.message));
|
|
20577
|
+
}
|
|
20578
|
+
try {
|
|
20579
|
+
const subscription = await _getByAffiliateUserId(id);
|
|
20580
|
+
res.json(subscription);
|
|
20581
|
+
return;
|
|
20582
|
+
} catch (error2) {
|
|
20583
|
+
next(error2);
|
|
20584
|
+
}
|
|
20585
|
+
}
|
|
20586
|
+
async function getByOrgId(req, res, next) {
|
|
20587
|
+
const id = req.params.id;
|
|
20588
|
+
const validation = Joi17.string().required();
|
|
20589
|
+
const { error } = validation.validate(id);
|
|
20590
|
+
if (error) {
|
|
20591
|
+
next(new BadRequestError34(error.message));
|
|
20592
|
+
}
|
|
20593
|
+
try {
|
|
20594
|
+
const subscription = await _getByOrgId(id);
|
|
20595
|
+
res.json(subscription);
|
|
20596
|
+
return;
|
|
20597
|
+
} catch (error2) {
|
|
20598
|
+
next(error2);
|
|
20599
|
+
}
|
|
20600
|
+
}
|
|
19947
20601
|
async function getSubscriptions(req, res, next) {
|
|
19948
20602
|
const status = req.query.status ?? "";
|
|
19949
20603
|
const search = req.query.search ?? "";
|
|
19950
20604
|
const page = Number(req.query.page) ?? 1;
|
|
19951
|
-
const validation =
|
|
19952
|
-
status:
|
|
19953
|
-
search:
|
|
19954
|
-
page:
|
|
20605
|
+
const validation = Joi17.object({
|
|
20606
|
+
status: Joi17.string().required(),
|
|
20607
|
+
search: Joi17.string().optional().allow("", null),
|
|
20608
|
+
page: Joi17.number().required()
|
|
19955
20609
|
});
|
|
19956
20610
|
const { error } = validation.validate({ status, search, page });
|
|
19957
20611
|
if (error) {
|
|
19958
|
-
next(new
|
|
20612
|
+
next(new BadRequestError34(error.message));
|
|
19959
20613
|
}
|
|
19960
20614
|
try {
|
|
19961
20615
|
const subscriptions = await _getSubscriptions({ status, search, page });
|
|
@@ -19967,10 +20621,10 @@ function useSubscriptionController() {
|
|
|
19967
20621
|
}
|
|
19968
20622
|
async function getSubscriptionStatus(req, res, next) {
|
|
19969
20623
|
const id = req.params.id;
|
|
19970
|
-
const validation =
|
|
20624
|
+
const validation = Joi17.string().required();
|
|
19971
20625
|
const { error } = validation.validate(id);
|
|
19972
20626
|
if (error) {
|
|
19973
|
-
next(new
|
|
20627
|
+
next(new BadRequestError34(error.message));
|
|
19974
20628
|
}
|
|
19975
20629
|
try {
|
|
19976
20630
|
const status = await _getStatusByUser(id);
|
|
@@ -19980,37 +20634,17 @@ function useSubscriptionController() {
|
|
|
19980
20634
|
next(error2);
|
|
19981
20635
|
}
|
|
19982
20636
|
}
|
|
20637
|
+
const { schema: subscriptionSchema } = useSubscriptionSchema();
|
|
19983
20638
|
async function createAffiliateSubscription(req, res, next) {
|
|
19984
|
-
const
|
|
19985
|
-
|
|
19986
|
-
const
|
|
19987
|
-
const currency = req.body.currency ?? "PHP";
|
|
19988
|
-
const user = req.headers["user"];
|
|
19989
|
-
const validation = Joi13.object({
|
|
19990
|
-
user: Joi13.string().required(),
|
|
19991
|
-
amount: Joi13.number().required(),
|
|
19992
|
-
customer_id: Joi13.string().required(),
|
|
19993
|
-
payment_method_id: Joi13.string().required(),
|
|
19994
|
-
currency: Joi13.string().optional().allow("", null)
|
|
19995
|
-
});
|
|
19996
|
-
const { error } = validation.validate({
|
|
19997
|
-
user,
|
|
19998
|
-
amount,
|
|
19999
|
-
customer_id,
|
|
20000
|
-
payment_method_id,
|
|
20001
|
-
currency
|
|
20002
|
-
});
|
|
20639
|
+
const value = req.body;
|
|
20640
|
+
value.user = req.headers["user"];
|
|
20641
|
+
const { error } = subscriptionSchema.validate(value);
|
|
20003
20642
|
if (error) {
|
|
20004
|
-
next(new
|
|
20643
|
+
next(new BadRequestError34(error.message));
|
|
20005
20644
|
return;
|
|
20006
20645
|
}
|
|
20007
20646
|
try {
|
|
20008
|
-
const id = await _createAffiliateSubscription(
|
|
20009
|
-
user,
|
|
20010
|
-
amount,
|
|
20011
|
-
customer_id,
|
|
20012
|
-
payment_method_id
|
|
20013
|
-
});
|
|
20647
|
+
const id = await _createAffiliateSubscription(value);
|
|
20014
20648
|
res.json({ message: "Successfully added subscription.", id });
|
|
20015
20649
|
return;
|
|
20016
20650
|
} catch (error2) {
|
|
@@ -20019,18 +20653,17 @@ function useSubscriptionController() {
|
|
|
20019
20653
|
return;
|
|
20020
20654
|
}
|
|
20021
20655
|
}
|
|
20022
|
-
const { schema: subscriptionSchema } = useSubscriptionSchema();
|
|
20023
20656
|
async function createOrgSubscription(req, res, next) {
|
|
20024
20657
|
const value = req.body;
|
|
20025
20658
|
value.user = req.headers["user"];
|
|
20026
20659
|
const { error } = subscriptionSchema.validate(value);
|
|
20027
20660
|
if (error) {
|
|
20028
|
-
next(new
|
|
20661
|
+
next(new BadRequestError34(error.message));
|
|
20029
20662
|
return;
|
|
20030
20663
|
}
|
|
20031
20664
|
try {
|
|
20032
|
-
const
|
|
20033
|
-
res.json(
|
|
20665
|
+
const _res = await _createOrgSubscription(value);
|
|
20666
|
+
res.json(_res);
|
|
20034
20667
|
return;
|
|
20035
20668
|
} catch (error2) {
|
|
20036
20669
|
next(error2);
|
|
@@ -20040,6 +20673,8 @@ function useSubscriptionController() {
|
|
|
20040
20673
|
return {
|
|
20041
20674
|
add,
|
|
20042
20675
|
getByUserId,
|
|
20676
|
+
getByOrgId,
|
|
20677
|
+
getByAffiliateUserId,
|
|
20043
20678
|
getSubscriptions,
|
|
20044
20679
|
getSubscriptionStatus,
|
|
20045
20680
|
createAffiliateSubscription,
|
|
@@ -20048,7 +20683,7 @@ function useSubscriptionController() {
|
|
|
20048
20683
|
}
|
|
20049
20684
|
|
|
20050
20685
|
// src/services/payment-method.service.ts
|
|
20051
|
-
import { BadRequestError as
|
|
20686
|
+
import { BadRequestError as BadRequestError35, useAtlas as useAtlas21 } from "@goweekdays/utils";
|
|
20052
20687
|
function usePaymentMethodService() {
|
|
20053
20688
|
const { createCustomer, linkPaymentMethodEWallet, linkPaymentMethodCard } = useXenditService();
|
|
20054
20689
|
const { getUserById } = useUserRepo();
|
|
@@ -20101,7 +20736,7 @@ function usePaymentMethodService() {
|
|
|
20101
20736
|
failure_return_url = "",
|
|
20102
20737
|
cancel_return_url = ""
|
|
20103
20738
|
} = {}) {
|
|
20104
|
-
const session =
|
|
20739
|
+
const session = useAtlas21.getClient()?.startSession();
|
|
20105
20740
|
session?.startTransaction();
|
|
20106
20741
|
try {
|
|
20107
20742
|
const customerData = {
|
|
@@ -20111,10 +20746,10 @@ function usePaymentMethodService() {
|
|
|
20111
20746
|
if (user) {
|
|
20112
20747
|
const _user = await getUserById(user);
|
|
20113
20748
|
if (!_user) {
|
|
20114
|
-
throw new
|
|
20749
|
+
throw new BadRequestError35("User not found.");
|
|
20115
20750
|
}
|
|
20116
20751
|
if (!_user._id) {
|
|
20117
|
-
throw new
|
|
20752
|
+
throw new BadRequestError35("Invalid user ID.");
|
|
20118
20753
|
}
|
|
20119
20754
|
customerData.email = _user.email;
|
|
20120
20755
|
customerData.given_names = _user.firstName;
|
|
@@ -20123,7 +20758,7 @@ function usePaymentMethodService() {
|
|
|
20123
20758
|
if (org) {
|
|
20124
20759
|
const _org = await getOrgById(org);
|
|
20125
20760
|
if (!_org) {
|
|
20126
|
-
throw new
|
|
20761
|
+
throw new BadRequestError35("Organization not found.");
|
|
20127
20762
|
}
|
|
20128
20763
|
customerData.email = _org.email;
|
|
20129
20764
|
customerData.given_names = _org.name;
|
|
@@ -20137,20 +20772,6 @@ function usePaymentMethodService() {
|
|
|
20137
20772
|
failure_return_url,
|
|
20138
20773
|
cancel_return_url
|
|
20139
20774
|
});
|
|
20140
|
-
await add(
|
|
20141
|
-
{
|
|
20142
|
-
user,
|
|
20143
|
-
org,
|
|
20144
|
-
type,
|
|
20145
|
-
status: "active",
|
|
20146
|
-
paymentId: paymentMethod.id,
|
|
20147
|
-
customerId: customer.id,
|
|
20148
|
-
name: paymentMethod.type,
|
|
20149
|
-
number: mobile_number
|
|
20150
|
-
},
|
|
20151
|
-
session
|
|
20152
|
-
);
|
|
20153
|
-
await session?.commitTransaction();
|
|
20154
20775
|
return {
|
|
20155
20776
|
paymentMethod: paymentMethod.id,
|
|
20156
20777
|
customer: customer.id,
|
|
@@ -20175,15 +20796,15 @@ function usePaymentMethodService() {
|
|
|
20175
20796
|
cardholder_name = "",
|
|
20176
20797
|
currency = "PHP"
|
|
20177
20798
|
} = {}) {
|
|
20178
|
-
const session =
|
|
20799
|
+
const session = useAtlas21.getClient()?.startSession();
|
|
20179
20800
|
session?.startTransaction();
|
|
20180
20801
|
try {
|
|
20181
20802
|
const _user = await getUserById(user);
|
|
20182
20803
|
if (!_user) {
|
|
20183
|
-
throw new
|
|
20804
|
+
throw new BadRequestError35("User not found.");
|
|
20184
20805
|
}
|
|
20185
20806
|
if (!_user._id) {
|
|
20186
|
-
throw new
|
|
20807
|
+
throw new BadRequestError35("Invalid user ID.");
|
|
20187
20808
|
}
|
|
20188
20809
|
const result = await linkPaymentMethodCard({
|
|
20189
20810
|
card_number,
|
|
@@ -20223,8 +20844,8 @@ function usePaymentMethodService() {
|
|
|
20223
20844
|
}
|
|
20224
20845
|
|
|
20225
20846
|
// src/controllers/payment-method.controller.ts
|
|
20226
|
-
import
|
|
20227
|
-
import { BadRequestError as
|
|
20847
|
+
import Joi18 from "joi";
|
|
20848
|
+
import { BadRequestError as BadRequestError36 } from "@goweekdays/utils";
|
|
20228
20849
|
function usePaymentMethodController() {
|
|
20229
20850
|
const { linkEWallet: _linkEWallet, linkCard: _linkCard } = usePaymentMethodService();
|
|
20230
20851
|
async function linkEWallet(req, res, next) {
|
|
@@ -20235,14 +20856,14 @@ function usePaymentMethodController() {
|
|
|
20235
20856
|
const success_return_url = req.body.success_return_url ?? "";
|
|
20236
20857
|
const failure_return_url = req.body.failure_return_url ?? "";
|
|
20237
20858
|
const cancel_return_url = req.body.cancel_return_url ?? "";
|
|
20238
|
-
const validation =
|
|
20239
|
-
user:
|
|
20240
|
-
org:
|
|
20241
|
-
mobile_number:
|
|
20242
|
-
type:
|
|
20243
|
-
success_return_url:
|
|
20244
|
-
failure_return_url:
|
|
20245
|
-
cancel_return_url:
|
|
20859
|
+
const validation = Joi18.object({
|
|
20860
|
+
user: Joi18.string().hex().optional().allow("", null),
|
|
20861
|
+
org: Joi18.string().hex().optional().allow("", null),
|
|
20862
|
+
mobile_number: Joi18.string().required(),
|
|
20863
|
+
type: Joi18.string().valid("GCASH", "PAYMAYA").required(),
|
|
20864
|
+
success_return_url: Joi18.string().uri().required(),
|
|
20865
|
+
failure_return_url: Joi18.string().uri().required(),
|
|
20866
|
+
cancel_return_url: Joi18.string().uri().required()
|
|
20246
20867
|
}).custom((value, helpers) => {
|
|
20247
20868
|
if (!value.user && !value.org) {
|
|
20248
20869
|
return helpers.error("any.invalid", {
|
|
@@ -20261,7 +20882,7 @@ function usePaymentMethodController() {
|
|
|
20261
20882
|
cancel_return_url
|
|
20262
20883
|
});
|
|
20263
20884
|
if (error) {
|
|
20264
|
-
next(new
|
|
20885
|
+
next(new BadRequestError36(error.message));
|
|
20265
20886
|
}
|
|
20266
20887
|
try {
|
|
20267
20888
|
const result = await _linkEWallet({
|
|
@@ -20290,17 +20911,17 @@ function usePaymentMethodController() {
|
|
|
20290
20911
|
const failure_return_url = req.body.failure_return_url ?? "";
|
|
20291
20912
|
const cardType = req.body.cardType ?? "";
|
|
20292
20913
|
const user = req.headers["user"] ?? "";
|
|
20293
|
-
const validation =
|
|
20294
|
-
cardNumber:
|
|
20295
|
-
expiryMonth:
|
|
20296
|
-
expiryYear:
|
|
20297
|
-
cvv:
|
|
20298
|
-
cardholderName:
|
|
20299
|
-
currency:
|
|
20300
|
-
success_return_url:
|
|
20301
|
-
failure_return_url:
|
|
20302
|
-
cardType:
|
|
20303
|
-
user:
|
|
20914
|
+
const validation = Joi18.object({
|
|
20915
|
+
cardNumber: Joi18.string().required(),
|
|
20916
|
+
expiryMonth: Joi18.string().required(),
|
|
20917
|
+
expiryYear: Joi18.string().required(),
|
|
20918
|
+
cvv: Joi18.string().required(),
|
|
20919
|
+
cardholderName: Joi18.string().required(),
|
|
20920
|
+
currency: Joi18.string().optional().allow("", null),
|
|
20921
|
+
success_return_url: Joi18.string().uri().required(),
|
|
20922
|
+
failure_return_url: Joi18.string().uri().required(),
|
|
20923
|
+
cardType: Joi18.string().required(),
|
|
20924
|
+
user: Joi18.string().hex().required()
|
|
20304
20925
|
});
|
|
20305
20926
|
const { error } = validation.validate({
|
|
20306
20927
|
user,
|
|
@@ -20315,7 +20936,7 @@ function usePaymentMethodController() {
|
|
|
20315
20936
|
failure_return_url
|
|
20316
20937
|
});
|
|
20317
20938
|
if (error) {
|
|
20318
|
-
next(new
|
|
20939
|
+
next(new BadRequestError36(error.message));
|
|
20319
20940
|
}
|
|
20320
20941
|
try {
|
|
20321
20942
|
const result = await _linkCard({
|
|
@@ -20339,12 +20960,12 @@ function usePaymentMethodController() {
|
|
|
20339
20960
|
const { getByUser: _getByUser, getByOrg: _getByOrg } = usePaymentMethodRepo();
|
|
20340
20961
|
async function getByUser(req, res, next) {
|
|
20341
20962
|
const user = req.params.user ?? "";
|
|
20342
|
-
const validation =
|
|
20343
|
-
user:
|
|
20963
|
+
const validation = Joi18.object({
|
|
20964
|
+
user: Joi18.string().hex().required()
|
|
20344
20965
|
});
|
|
20345
20966
|
const { error } = validation.validate({ user });
|
|
20346
20967
|
if (error) {
|
|
20347
|
-
next(new
|
|
20968
|
+
next(new BadRequestError36(error.message));
|
|
20348
20969
|
}
|
|
20349
20970
|
try {
|
|
20350
20971
|
const result = await _getByUser(user);
|
|
@@ -20355,12 +20976,12 @@ function usePaymentMethodController() {
|
|
|
20355
20976
|
}
|
|
20356
20977
|
async function getByOrg(req, res, next) {
|
|
20357
20978
|
const org = req.params.org ?? "";
|
|
20358
|
-
const validation =
|
|
20359
|
-
org:
|
|
20979
|
+
const validation = Joi18.object({
|
|
20980
|
+
org: Joi18.string().hex().required()
|
|
20360
20981
|
});
|
|
20361
20982
|
const { error } = validation.validate({ org });
|
|
20362
20983
|
if (error) {
|
|
20363
|
-
next(new
|
|
20984
|
+
next(new BadRequestError36(error.message));
|
|
20364
20985
|
}
|
|
20365
20986
|
try {
|
|
20366
20987
|
const result = await _getByOrg(org);
|
|
@@ -20378,27 +20999,27 @@ function usePaymentMethodController() {
|
|
|
20378
20999
|
}
|
|
20379
21000
|
|
|
20380
21001
|
// src/controllers/address.controller.ts
|
|
20381
|
-
import { BadRequestError as
|
|
20382
|
-
import
|
|
21002
|
+
import { BadRequestError as BadRequestError37, NotFoundError as NotFoundError4 } from "@goweekdays/utils";
|
|
21003
|
+
import Joi19 from "joi";
|
|
20383
21004
|
function useAddressController() {
|
|
20384
21005
|
const { add: _add, getByUserId: _getByUserId } = useAddressRepo();
|
|
20385
21006
|
async function add(req, res, next) {
|
|
20386
21007
|
const value = req.body;
|
|
20387
|
-
const validation =
|
|
20388
|
-
type:
|
|
20389
|
-
user:
|
|
20390
|
-
org:
|
|
20391
|
-
country:
|
|
20392
|
-
address:
|
|
20393
|
-
continuedAddress:
|
|
20394
|
-
city:
|
|
20395
|
-
province:
|
|
20396
|
-
postalCode:
|
|
20397
|
-
taxId:
|
|
21008
|
+
const validation = Joi19.object({
|
|
21009
|
+
type: Joi19.string().required(),
|
|
21010
|
+
user: Joi19.string().hex().optional().allow("", null),
|
|
21011
|
+
org: Joi19.string().hex().optional().allow("", null),
|
|
21012
|
+
country: Joi19.string().required(),
|
|
21013
|
+
address: Joi19.string().required(),
|
|
21014
|
+
continuedAddress: Joi19.string().optional().allow("", null),
|
|
21015
|
+
city: Joi19.string().required(),
|
|
21016
|
+
province: Joi19.string().required(),
|
|
21017
|
+
postalCode: Joi19.string().required(),
|
|
21018
|
+
taxId: Joi19.string().optional().allow("", null)
|
|
20398
21019
|
});
|
|
20399
21020
|
const { error } = validation.validate(value);
|
|
20400
21021
|
if (error) {
|
|
20401
|
-
next(new
|
|
21022
|
+
next(new BadRequestError37(error.message));
|
|
20402
21023
|
}
|
|
20403
21024
|
try {
|
|
20404
21025
|
const value2 = req.body;
|
|
@@ -20411,10 +21032,10 @@ function useAddressController() {
|
|
|
20411
21032
|
}
|
|
20412
21033
|
async function getByUserId(req, res, next) {
|
|
20413
21034
|
const user = req.params.user;
|
|
20414
|
-
const validation =
|
|
21035
|
+
const validation = Joi19.string().hex().required();
|
|
20415
21036
|
const { error } = validation.validate(user);
|
|
20416
21037
|
if (error) {
|
|
20417
|
-
next(new
|
|
21038
|
+
next(new BadRequestError37(error.message));
|
|
20418
21039
|
}
|
|
20419
21040
|
try {
|
|
20420
21041
|
const address = await _getByUserId(user);
|
|
@@ -20435,14 +21056,14 @@ function useAddressController() {
|
|
|
20435
21056
|
}
|
|
20436
21057
|
|
|
20437
21058
|
// src/services/organization.service.ts
|
|
20438
|
-
import { NotFoundError as NotFoundError5, useAtlas as
|
|
21059
|
+
import { NotFoundError as NotFoundError5, useAtlas as useAtlas22 } from "@goweekdays/utils";
|
|
20439
21060
|
function useOrgService() {
|
|
20440
21061
|
const { add: addOrg } = useOrgRepo();
|
|
20441
21062
|
const { addRole } = useRoleRepo();
|
|
20442
21063
|
const { add: addMember } = useMemberRepo();
|
|
20443
21064
|
const { getUserById } = useUserRepo();
|
|
20444
21065
|
async function createOrg({ user = "", name = "", description = "" } = {}) {
|
|
20445
|
-
const session =
|
|
21066
|
+
const session = useAtlas22.getClient()?.startSession();
|
|
20446
21067
|
session?.startTransaction();
|
|
20447
21068
|
try {
|
|
20448
21069
|
const _user = await getUserById(user);
|
|
@@ -20490,25 +21111,25 @@ function useOrgService() {
|
|
|
20490
21111
|
}
|
|
20491
21112
|
|
|
20492
21113
|
// src/controllers/organization.controller.ts
|
|
20493
|
-
import { BadRequestError as
|
|
20494
|
-
import
|
|
21114
|
+
import { BadRequestError as BadRequestError38 } from "@goweekdays/utils";
|
|
21115
|
+
import Joi20 from "joi";
|
|
20495
21116
|
function useOrgController() {
|
|
20496
21117
|
const { createOrg: _createOrg } = useOrgService();
|
|
20497
21118
|
const { getByUserId: _getByUserId } = useMemberRepo();
|
|
20498
21119
|
const { getByName: _getByName } = useOrgRepo();
|
|
20499
21120
|
async function createOrg(req, res, next) {
|
|
20500
21121
|
const value = req.body;
|
|
20501
|
-
const validation =
|
|
20502
|
-
name:
|
|
20503
|
-
type:
|
|
20504
|
-
email:
|
|
20505
|
-
contact:
|
|
20506
|
-
description:
|
|
20507
|
-
user:
|
|
21122
|
+
const validation = Joi20.object({
|
|
21123
|
+
name: Joi20.string().required(),
|
|
21124
|
+
type: Joi20.string().required(),
|
|
21125
|
+
email: Joi20.string().email().required(),
|
|
21126
|
+
contact: Joi20.string().required(),
|
|
21127
|
+
description: Joi20.string().required(),
|
|
21128
|
+
user: Joi20.string().hex().required()
|
|
20508
21129
|
});
|
|
20509
21130
|
const { error } = validation.validate(value);
|
|
20510
21131
|
if (error) {
|
|
20511
|
-
next(new
|
|
21132
|
+
next(new BadRequestError38(error.message));
|
|
20512
21133
|
return;
|
|
20513
21134
|
}
|
|
20514
21135
|
try {
|
|
@@ -20526,23 +21147,23 @@ function useOrgController() {
|
|
|
20526
21147
|
const user = req.headers["user"];
|
|
20527
21148
|
const isPageNumber = isFinite(page);
|
|
20528
21149
|
if (!isPageNumber) {
|
|
20529
|
-
next(new
|
|
21150
|
+
next(new BadRequestError38("Invalid page number."));
|
|
20530
21151
|
return;
|
|
20531
21152
|
}
|
|
20532
21153
|
const isLimitNumber = isFinite(limit);
|
|
20533
21154
|
if (!isLimitNumber) {
|
|
20534
|
-
next(new
|
|
21155
|
+
next(new BadRequestError38("Invalid limit number."));
|
|
20535
21156
|
return;
|
|
20536
21157
|
}
|
|
20537
|
-
const validation =
|
|
20538
|
-
user:
|
|
20539
|
-
page:
|
|
20540
|
-
limit:
|
|
20541
|
-
search:
|
|
21158
|
+
const validation = Joi20.object({
|
|
21159
|
+
user: Joi20.string().hex().required(),
|
|
21160
|
+
page: Joi20.number().min(1).optional().allow("", null),
|
|
21161
|
+
limit: Joi20.number().min(1).optional().allow("", null),
|
|
21162
|
+
search: Joi20.string().optional().allow("", null)
|
|
20542
21163
|
});
|
|
20543
21164
|
const { error } = validation.validate({ user, page, limit, search });
|
|
20544
21165
|
if (error) {
|
|
20545
|
-
next(new
|
|
21166
|
+
next(new BadRequestError38(error.message));
|
|
20546
21167
|
return;
|
|
20547
21168
|
}
|
|
20548
21169
|
try {
|
|
@@ -20555,12 +21176,12 @@ function useOrgController() {
|
|
|
20555
21176
|
}
|
|
20556
21177
|
async function getByName(req, res, next) {
|
|
20557
21178
|
const name = req.params.name;
|
|
20558
|
-
const validation =
|
|
20559
|
-
name:
|
|
21179
|
+
const validation = Joi20.object({
|
|
21180
|
+
name: Joi20.string().required()
|
|
20560
21181
|
});
|
|
20561
21182
|
const { error } = validation.validate({ name });
|
|
20562
21183
|
if (error) {
|
|
20563
|
-
next(new
|
|
21184
|
+
next(new BadRequestError38(error.message));
|
|
20564
21185
|
return;
|
|
20565
21186
|
}
|
|
20566
21187
|
try {
|
|
@@ -20577,6 +21198,276 @@ function useOrgController() {
|
|
|
20577
21198
|
getByName
|
|
20578
21199
|
};
|
|
20579
21200
|
}
|
|
21201
|
+
|
|
21202
|
+
// src/validations/promo-code.schema.ts
|
|
21203
|
+
import Joi21 from "joi";
|
|
21204
|
+
var promoTypeSchema = Joi21.string().valid("tiered", "fixed").required();
|
|
21205
|
+
var promoTierSchema = Joi21.object({
|
|
21206
|
+
min: Joi21.number().integer().min(1).required(),
|
|
21207
|
+
max: Joi21.number().integer().min(Joi21.ref("min")).allow(0).required(),
|
|
21208
|
+
price: Joi21.number().positive().required()
|
|
21209
|
+
});
|
|
21210
|
+
var schema2 = Joi21.object({
|
|
21211
|
+
code: Joi21.string().trim().uppercase().required(),
|
|
21212
|
+
description: Joi21.string().trim().optional(),
|
|
21213
|
+
type: promoTypeSchema,
|
|
21214
|
+
tiers: Joi21.alternatives().conditional("type", {
|
|
21215
|
+
is: "tiered",
|
|
21216
|
+
then: Joi21.array().items(promoTierSchema).min(1).required(),
|
|
21217
|
+
otherwise: Joi21.forbidden()
|
|
21218
|
+
}),
|
|
21219
|
+
fixed_rate: Joi21.alternatives().conditional("type", {
|
|
21220
|
+
is: "fixed",
|
|
21221
|
+
then: Joi21.number().required().min(0),
|
|
21222
|
+
otherwise: Joi21.number().integer().allow(0)
|
|
21223
|
+
}),
|
|
21224
|
+
expiresAt: Joi21.string().optional().allow(null, "")
|
|
21225
|
+
});
|
|
21226
|
+
|
|
21227
|
+
// src/models/promo-code.model.ts
|
|
21228
|
+
import { BadRequestError as BadRequestError39 } from "@goweekdays/utils";
|
|
21229
|
+
function MPromoCode(data) {
|
|
21230
|
+
const { error } = schema2.validate(data);
|
|
21231
|
+
if (error) {
|
|
21232
|
+
throw new BadRequestError39(error.message);
|
|
21233
|
+
}
|
|
21234
|
+
return {
|
|
21235
|
+
_id: data._id,
|
|
21236
|
+
code: data.code,
|
|
21237
|
+
description: data.description ?? "",
|
|
21238
|
+
type: data.type,
|
|
21239
|
+
tiers: data.tiers ?? [],
|
|
21240
|
+
fixed_rate: data.fixed_rate ?? 0,
|
|
21241
|
+
appliesTo: data.appliesTo ?? "",
|
|
21242
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21243
|
+
expiresAt: data.expiresAt ?? "",
|
|
21244
|
+
assignedTo: data.assignedTo,
|
|
21245
|
+
status: data.status ?? "active"
|
|
21246
|
+
};
|
|
21247
|
+
}
|
|
21248
|
+
|
|
21249
|
+
// src/repositories/promo-code.repository.ts
|
|
21250
|
+
import {
|
|
21251
|
+
BadRequestError as BadRequestError40,
|
|
21252
|
+
InternalServerError as InternalServerError22,
|
|
21253
|
+
logger as logger16,
|
|
21254
|
+
paginate as paginate12,
|
|
21255
|
+
useAtlas as useAtlas23
|
|
21256
|
+
} from "@goweekdays/utils";
|
|
21257
|
+
import Joi22 from "joi";
|
|
21258
|
+
function usePromoCodeRepo() {
|
|
21259
|
+
const db = useAtlas23.getDb();
|
|
21260
|
+
if (!db) {
|
|
21261
|
+
throw new InternalServerError22("Unable to connect to server.");
|
|
21262
|
+
}
|
|
21263
|
+
const collection = db.collection("promo-codes");
|
|
21264
|
+
function createIndex() {
|
|
21265
|
+
try {
|
|
21266
|
+
collection.createIndexes([{ key: { code: 1 } }, { key: { type: 1 } }]);
|
|
21267
|
+
} catch (error) {
|
|
21268
|
+
throw new Error("Failed to create index for promo code.");
|
|
21269
|
+
}
|
|
21270
|
+
}
|
|
21271
|
+
function createUniqueIndex() {
|
|
21272
|
+
try {
|
|
21273
|
+
collection.createIndexes([{ key: { code: 1, type: 1 }, unique: true }]);
|
|
21274
|
+
} catch (error) {
|
|
21275
|
+
throw new Error("Failed to create unique index for promo code.");
|
|
21276
|
+
}
|
|
21277
|
+
}
|
|
21278
|
+
function add(value) {
|
|
21279
|
+
try {
|
|
21280
|
+
value = MPromoCode(value);
|
|
21281
|
+
collection.insertOne(value);
|
|
21282
|
+
} catch (error) {
|
|
21283
|
+
logger16.log({ level: "error", message: `${error}` });
|
|
21284
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
21285
|
+
if (isDuplicated) {
|
|
21286
|
+
throw new BadRequestError40("Promo code already exists.");
|
|
21287
|
+
}
|
|
21288
|
+
throw new InternalServerError22("Internal server error.");
|
|
21289
|
+
}
|
|
21290
|
+
}
|
|
21291
|
+
function getByCode(code) {
|
|
21292
|
+
const schema3 = Joi22.object({
|
|
21293
|
+
code: Joi22.string().trim().required()
|
|
21294
|
+
});
|
|
21295
|
+
const { error } = schema3.validate({ code });
|
|
21296
|
+
if (error) {
|
|
21297
|
+
throw new BadRequestError40(error.message);
|
|
21298
|
+
}
|
|
21299
|
+
try {
|
|
21300
|
+
return collection.findOne({ code });
|
|
21301
|
+
} catch (error2) {
|
|
21302
|
+
throw new InternalServerError22("Internal server error.");
|
|
21303
|
+
}
|
|
21304
|
+
}
|
|
21305
|
+
async function getPromoCodes({
|
|
21306
|
+
search = "",
|
|
21307
|
+
page = 1,
|
|
21308
|
+
limit = 10,
|
|
21309
|
+
sort = {},
|
|
21310
|
+
status = "active",
|
|
21311
|
+
type = ""
|
|
21312
|
+
} = {}) {
|
|
21313
|
+
page = page > 0 ? page - 1 : 0;
|
|
21314
|
+
const query = { status };
|
|
21315
|
+
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
21316
|
+
if (search) {
|
|
21317
|
+
query.$text = { $search: search };
|
|
21318
|
+
}
|
|
21319
|
+
if (type) {
|
|
21320
|
+
query.type = type;
|
|
21321
|
+
}
|
|
21322
|
+
try {
|
|
21323
|
+
const items = await collection.aggregate([
|
|
21324
|
+
{ $match: query },
|
|
21325
|
+
{ $sort: sort },
|
|
21326
|
+
{ $skip: page * limit },
|
|
21327
|
+
{ $limit: limit }
|
|
21328
|
+
]).toArray();
|
|
21329
|
+
const length = await collection.countDocuments(query);
|
|
21330
|
+
return paginate12(items, page, limit, length);
|
|
21331
|
+
} catch (error) {
|
|
21332
|
+
logger16.log({ level: "error", message: `${error}` });
|
|
21333
|
+
throw new InternalServerError22("Internal server error.");
|
|
21334
|
+
}
|
|
21335
|
+
}
|
|
21336
|
+
return {
|
|
21337
|
+
createIndex,
|
|
21338
|
+
createUniqueIndex,
|
|
21339
|
+
add,
|
|
21340
|
+
getByCode,
|
|
21341
|
+
getPromoCodes
|
|
21342
|
+
};
|
|
21343
|
+
}
|
|
21344
|
+
|
|
21345
|
+
// src/controllers/promo-code.controller.ts
|
|
21346
|
+
import {
|
|
21347
|
+
AppError as AppError10,
|
|
21348
|
+
BadRequestError as BadRequestError41,
|
|
21349
|
+
InternalServerError as InternalServerError23
|
|
21350
|
+
} from "@goweekdays/utils";
|
|
21351
|
+
import Joi23 from "joi";
|
|
21352
|
+
function usePromoCodeController() {
|
|
21353
|
+
const {
|
|
21354
|
+
add: _add,
|
|
21355
|
+
getByCode: _getByCode,
|
|
21356
|
+
getPromoCodes: _getPromoCodes
|
|
21357
|
+
} = usePromoCodeRepo();
|
|
21358
|
+
async function add(req, res, next) {
|
|
21359
|
+
const data = req.body;
|
|
21360
|
+
const { error } = schema2.validate(data);
|
|
21361
|
+
if (error) {
|
|
21362
|
+
next(new BadRequestError41(error.message));
|
|
21363
|
+
return;
|
|
21364
|
+
}
|
|
21365
|
+
try {
|
|
21366
|
+
_add(data);
|
|
21367
|
+
res.json({ message: "Successfully added promo code." });
|
|
21368
|
+
return;
|
|
21369
|
+
} catch (error2) {
|
|
21370
|
+
if (error2 instanceof AppError10) {
|
|
21371
|
+
next(error2);
|
|
21372
|
+
} else {
|
|
21373
|
+
next(new InternalServerError23(error2));
|
|
21374
|
+
}
|
|
21375
|
+
}
|
|
21376
|
+
}
|
|
21377
|
+
async function getByCode(req, res, next) {
|
|
21378
|
+
const code = req.params.code;
|
|
21379
|
+
const validation = Joi23.string().required();
|
|
21380
|
+
const { error } = validation.validate(code);
|
|
21381
|
+
if (error) {
|
|
21382
|
+
next(new BadRequestError41(error.message));
|
|
21383
|
+
return;
|
|
21384
|
+
}
|
|
21385
|
+
try {
|
|
21386
|
+
const promoCode = _getByCode(code);
|
|
21387
|
+
res.json({ promoCode });
|
|
21388
|
+
return;
|
|
21389
|
+
} catch (error2) {
|
|
21390
|
+
if (error2 instanceof AppError10) {
|
|
21391
|
+
next(error2);
|
|
21392
|
+
} else {
|
|
21393
|
+
next(new InternalServerError23(error2));
|
|
21394
|
+
}
|
|
21395
|
+
}
|
|
21396
|
+
}
|
|
21397
|
+
async function getPromoCodes(req, res, next) {
|
|
21398
|
+
const page = req.query.page;
|
|
21399
|
+
const limit = req.query.limit;
|
|
21400
|
+
const search = req.query.search;
|
|
21401
|
+
const status = req.query.status;
|
|
21402
|
+
const validation = Joi23.object({
|
|
21403
|
+
page: Joi23.number().required(),
|
|
21404
|
+
limit: Joi23.number().required()
|
|
21405
|
+
});
|
|
21406
|
+
const { error } = validation.validate({ page, limit });
|
|
21407
|
+
if (error) {
|
|
21408
|
+
next(new BadRequestError41(error.message));
|
|
21409
|
+
}
|
|
21410
|
+
try {
|
|
21411
|
+
const promoCodes = _getPromoCodes({
|
|
21412
|
+
page: parseInt(page),
|
|
21413
|
+
limit: parseInt(limit),
|
|
21414
|
+
search,
|
|
21415
|
+
status
|
|
21416
|
+
});
|
|
21417
|
+
res.json(promoCodes);
|
|
21418
|
+
return;
|
|
21419
|
+
} catch (error2) {
|
|
21420
|
+
next(error2);
|
|
21421
|
+
}
|
|
21422
|
+
}
|
|
21423
|
+
return {
|
|
21424
|
+
add,
|
|
21425
|
+
getByCode,
|
|
21426
|
+
getPromoCodes
|
|
21427
|
+
};
|
|
21428
|
+
}
|
|
21429
|
+
|
|
21430
|
+
// src/controllers/order.controller.ts
|
|
21431
|
+
import { BadRequestError as BadRequestError42 } from "@goweekdays/utils";
|
|
21432
|
+
import Joi24 from "joi";
|
|
21433
|
+
function useOrderController() {
|
|
21434
|
+
const { getOrders: _getOrders } = useOrderRepo();
|
|
21435
|
+
async function getOrders(req, res, next) {
|
|
21436
|
+
const page = req.query.page ?? 1;
|
|
21437
|
+
const limit = req.query.limit ?? 10;
|
|
21438
|
+
const search = req.query.search ?? "";
|
|
21439
|
+
const status = req.query.status ?? "succeeded";
|
|
21440
|
+
const id = req.query.id ?? "";
|
|
21441
|
+
const validation = Joi24.object({
|
|
21442
|
+
page: Joi24.number().required(),
|
|
21443
|
+
limit: Joi24.number().required().min(10),
|
|
21444
|
+
search: Joi24.string().optional().allow("", null),
|
|
21445
|
+
status: Joi24.string().optional().allow("", null),
|
|
21446
|
+
id: Joi24.string().hex().optional().allow("", null)
|
|
21447
|
+
});
|
|
21448
|
+
const { error } = validation.validate({ page, limit, search, status, id });
|
|
21449
|
+
if (error) {
|
|
21450
|
+
next(new BadRequestError42(error.message));
|
|
21451
|
+
return;
|
|
21452
|
+
}
|
|
21453
|
+
try {
|
|
21454
|
+
const orders = await _getOrders({
|
|
21455
|
+
page: parseInt(page),
|
|
21456
|
+
limit: parseInt(limit),
|
|
21457
|
+
search,
|
|
21458
|
+
status,
|
|
21459
|
+
id
|
|
21460
|
+
});
|
|
21461
|
+
res.json(orders);
|
|
21462
|
+
return;
|
|
21463
|
+
} catch (error2) {
|
|
21464
|
+
next(error2);
|
|
21465
|
+
}
|
|
21466
|
+
}
|
|
21467
|
+
return {
|
|
21468
|
+
getOrders
|
|
21469
|
+
};
|
|
21470
|
+
}
|
|
20580
21471
|
export {
|
|
20581
21472
|
ACCESS_TOKEN_EXPIRY,
|
|
20582
21473
|
ACCESS_TOKEN_SECRET,
|
|
@@ -20599,8 +21490,10 @@ export {
|
|
|
20599
21490
|
MMember,
|
|
20600
21491
|
MONGO_DB,
|
|
20601
21492
|
MONGO_URI,
|
|
21493
|
+
MOrder,
|
|
20602
21494
|
MOrg,
|
|
20603
21495
|
MPaymentMethod,
|
|
21496
|
+
MPromoCode,
|
|
20604
21497
|
MRole,
|
|
20605
21498
|
MSubscription,
|
|
20606
21499
|
MToken,
|
|
@@ -20628,6 +21521,7 @@ export {
|
|
|
20628
21521
|
XENDIT_BASE_URL,
|
|
20629
21522
|
XENDIT_SECRET_KEY,
|
|
20630
21523
|
isDev,
|
|
21524
|
+
schema2 as schema,
|
|
20631
21525
|
useAddressController,
|
|
20632
21526
|
useAddressRepo,
|
|
20633
21527
|
useAuthController,
|
|
@@ -20645,12 +21539,16 @@ export {
|
|
|
20645
21539
|
useFileRepo,
|
|
20646
21540
|
useFileService,
|
|
20647
21541
|
useMemberRepo,
|
|
21542
|
+
useOrderController,
|
|
21543
|
+
useOrderRepo,
|
|
20648
21544
|
useOrgController,
|
|
20649
21545
|
useOrgRepo,
|
|
20650
21546
|
useOrgService,
|
|
20651
21547
|
usePaymentMethodController,
|
|
20652
21548
|
usePaymentMethodRepo,
|
|
20653
21549
|
usePaymentMethodService,
|
|
21550
|
+
usePromoCodeController,
|
|
21551
|
+
usePromoCodeRepo,
|
|
20654
21552
|
useRoleController,
|
|
20655
21553
|
useRoleRepo,
|
|
20656
21554
|
useRoleService,
|