@goweekdays/core 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @goweekdays/core
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 794d115: Add paid invoice webhook
8
+
3
9
  ## 1.0.0
4
10
 
5
11
  ### Major Changes
package/dist/index.d.ts CHANGED
@@ -1995,10 +1995,37 @@ declare function useInvoiceRepo(): {
1995
1995
  } | undefined;
1996
1996
  createdAt?: Date | undefined;
1997
1997
  } | null>;
1998
+ getByTransactionId: (id: string) => Promise<{
1999
+ type: "organization-subscription" | "affiliate-subscription" | "one-time-payment" | "other";
2000
+ status: "pending" | "cancelled" | "paid" | "overdue";
2001
+ amount: number;
2002
+ invoiceNumber: string;
2003
+ dueDate: Date;
2004
+ items: {
2005
+ description: string;
2006
+ unitPrice: number;
2007
+ quantity: number;
2008
+ total: number;
2009
+ seats?: number | undefined;
2010
+ }[];
2011
+ _id?: ObjectId | undefined;
2012
+ updatedAt?: Date | undefined;
2013
+ metadata?: {
2014
+ description?: string | undefined;
2015
+ userId?: string | ObjectId | undefined;
2016
+ orgId?: string | ObjectId | undefined;
2017
+ transactionId?: string | undefined;
2018
+ currency?: string | undefined;
2019
+ subscriptionId?: string | ObjectId | undefined;
2020
+ billingCycle?: "monthly" | "yearly" | "quarterly" | undefined;
2021
+ } | undefined;
2022
+ createdAt?: Date | undefined;
2023
+ } | null>;
1998
2024
  };
1999
2025
 
2000
2026
  declare function useInvoiceService(): {
2001
2027
  processOverDueInvoices: (BATCH_SIZE?: number, MAX_RETRIES?: number) => Promise<void>;
2028
+ paypalPaidInvoiceWebhookHandler: (invoiceId: string, status: string) => Promise<"Payment processed successfully." | undefined>;
2002
2029
  };
2003
2030
 
2004
2031
  declare function useInvoiceController(): {
package/dist/index.js CHANGED
@@ -21101,6 +21101,15 @@ function useInvoiceRepo() {
21101
21101
  throw new import_utils63.InternalServerError("Failed to get invoice by invoice number.");
21102
21102
  }
21103
21103
  }
21104
+ async function getByTransactionId(id) {
21105
+ try {
21106
+ return await collection.findOne({
21107
+ "metadata.transactionId": id
21108
+ });
21109
+ } catch (error) {
21110
+ throw new import_utils63.InternalServerError("Failed to get invoice by transaction ID.");
21111
+ }
21112
+ }
21104
21113
  async function getBySubscriptionId({ page, search, limit, sort, id } = {}) {
21105
21114
  try {
21106
21115
  try {
@@ -21183,7 +21192,8 @@ function useInvoiceRepo() {
21183
21192
  updateStatusByInvoiceNumber,
21184
21193
  getOverdueInvoices,
21185
21194
  getBySubscriptionId,
21186
- getByNumber
21195
+ getByNumber,
21196
+ getByTransactionId
21187
21197
  };
21188
21198
  }
21189
21199
 
@@ -24781,16 +24791,18 @@ function useOrderController() {
24781
24791
  // src/services/invoice.service.ts
24782
24792
  var import_utils84 = require("@goweekdays/utils");
24783
24793
  function useInvoiceService() {
24784
- const { getOverdueInvoices, updateStatusByInvoiceNumber, getByNumber } = useInvoiceRepo();
24794
+ const {
24795
+ getOverdueInvoices,
24796
+ updateStatusByInvoiceNumber,
24797
+ getByTransactionId
24798
+ } = useInvoiceRepo();
24785
24799
  const {
24786
24800
  getById,
24787
24801
  processSuccessfulPayment,
24788
24802
  updateMaxSeatsById,
24789
- markSubscriptionAsFailed,
24790
24803
  updateStatusById
24791
24804
  } = useSubscriptionRepo();
24792
- const { pay, getPaymentMethodById } = useXenditService();
24793
- const { add: addPayment, getByInvoiceId } = usePaymentRepo();
24805
+ const { add: addPayment } = usePaymentRepo();
24794
24806
  const { getInvoiceById } = usePaypalService();
24795
24807
  async function processOverDueInvoices(BATCH_SIZE = 100, MAX_RETRIES = 7) {
24796
24808
  while (true) {
@@ -24933,8 +24945,95 @@ function useInvoiceService() {
24933
24945
  });
24934
24946
  }
24935
24947
  }
24948
+ async function paypalPaidInvoiceWebhookHandler(invoiceId, status) {
24949
+ if (!invoiceId) {
24950
+ throw new import_utils84.BadRequestError("Invoice ID is required.");
24951
+ }
24952
+ if (!status) {
24953
+ throw new import_utils84.BadRequestError("Status is required.");
24954
+ }
24955
+ const session = import_utils84.useAtlas.getClient()?.startSession();
24956
+ if (!session) {
24957
+ import_utils84.logger.log({
24958
+ level: "error",
24959
+ message: "Failed to start session."
24960
+ });
24961
+ return;
24962
+ }
24963
+ try {
24964
+ const invoice = await getByTransactionId(invoiceId);
24965
+ if (!invoice) {
24966
+ throw new import_utils84.NotFoundError("Invoice not found.");
24967
+ }
24968
+ if (!invoice.metadata?.subscriptionId) {
24969
+ throw new import_utils84.BadRequestError(
24970
+ "Subscription ID is missing in invoice metadata."
24971
+ );
24972
+ }
24973
+ session?.startTransaction();
24974
+ const subscription = await getById(invoice.metadata.subscriptionId);
24975
+ if (!subscription) {
24976
+ throw new import_utils84.NotFoundError("Subscription not found.");
24977
+ }
24978
+ if (!subscription._id) {
24979
+ throw new import_utils84.BadRequestError("Subscription ID is missing.");
24980
+ }
24981
+ if (subscription.status !== "active") {
24982
+ await updateStatusById(subscription._id.toString(), "active", session);
24983
+ }
24984
+ await updateStatusByInvoiceNumber(invoice.invoiceNumber, "paid", session);
24985
+ await processSuccessfulPayment(
24986
+ {
24987
+ _id: subscription._id,
24988
+ nextBillingDate: subscription.nextBillingDate
24989
+ },
24990
+ session
24991
+ );
24992
+ const paymentData = {
24993
+ invoiceId: invoice.invoiceNumber,
24994
+ amount: invoice.amount,
24995
+ status: "completed",
24996
+ paymentMethod: "PAYPAL",
24997
+ metadata: {
24998
+ userId: String(subscription.user || ""),
24999
+ orgId: String(subscription.org || ""),
25000
+ currency: subscription.currency,
25001
+ payment: invoice.metadata.transactionId,
25002
+ subscriptionId: subscription._id?.toString()
25003
+ }
25004
+ };
25005
+ if (!paymentData.metadata.userId) {
25006
+ delete paymentData.metadata.userId;
25007
+ }
25008
+ if (!paymentData.metadata.orgId) {
25009
+ delete paymentData.metadata.orgId;
25010
+ }
25011
+ await addPayment(paymentData, session);
25012
+ if (!subscription.paidSeats) {
25013
+ throw new Error("Subscription paid seats is missing.");
25014
+ }
25015
+ await updateMaxSeatsById(
25016
+ {
25017
+ _id: subscription._id.toString(),
25018
+ seats: subscription.paidSeats
25019
+ },
25020
+ session
25021
+ );
25022
+ await session.commitTransaction();
25023
+ return "Payment processed successfully.";
25024
+ } catch (error) {
25025
+ session?.abortTransaction();
25026
+ import_utils84.logger.log({
25027
+ level: "error",
25028
+ message: String(error)
25029
+ });
25030
+ } finally {
25031
+ session?.endSession();
25032
+ }
25033
+ }
24936
25034
  return {
24937
- processOverDueInvoices
25035
+ processOverDueInvoices,
25036
+ paypalPaidInvoiceWebhookHandler
24938
25037
  };
24939
25038
  }
24940
25039
 
@@ -25025,6 +25124,28 @@ function useInvoiceController() {
25025
25124
  next(error2);
25026
25125
  }
25027
25126
  }
25127
+ const { paypalPaidInvoiceWebhookHandler } = useInvoiceService();
25128
+ async function paidInvoiceWebhookHandler(req, res, next) {
25129
+ const invoiceId = req.body.resource.id ?? "";
25130
+ const status = req.body.resource.status ?? "";
25131
+ const validation = import_joi28.default.object({
25132
+ invoiceId: import_joi28.default.string().required(),
25133
+ status: import_joi28.default.string().valid("PAID").required()
25134
+ });
25135
+ const { error } = validation.validate({ invoiceId, status });
25136
+ if (error) {
25137
+ next(new import_utils85.BadRequestError(error.message));
25138
+ return;
25139
+ }
25140
+ try {
25141
+ await paypalPaidInvoiceWebhookHandler(invoiceId, status);
25142
+ res.status(200).json({ message: "Webhook processed successfully." });
25143
+ return;
25144
+ } catch (error2) {
25145
+ next(error2);
25146
+ return;
25147
+ }
25148
+ }
25028
25149
  return {
25029
25150
  getBySubscriptionId,
25030
25151
  getByNumber,