@goweekdays/layer-common 0.0.12 → 0.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.
@@ -1,3 +1,5 @@
1
+ import { z } from "zod";
2
+
1
3
  export default function usePaymentMethod() {
2
4
  function linkEWallet({
3
5
  type = "GCASH",
@@ -72,10 +74,13 @@ export default function usePaymentMethod() {
72
74
 
73
75
  function getById(id = "") {
74
76
  return useNuxtApp().$api<Record<string, any>>(
75
- `/api/payment-methods/id/${id}`,
76
- {
77
- method: "GET",
78
- }
77
+ `/api/payment-methods/id/${id}`
78
+ );
79
+ }
80
+
81
+ function getByCustomerId(id = "") {
82
+ return useNuxtApp().$api<Record<string, any>>(
83
+ `/api/payment-methods/customer/${id}`
79
84
  );
80
85
  }
81
86
 
@@ -84,16 +89,10 @@ export default function usePaymentMethod() {
84
89
  const cardExpiration = useState("cardExpiration", () => "");
85
90
  const cardSecurityCode = useState("cardSecurityCode", () => "");
86
91
  const cardholderName = useState("cardholderName", () => "");
92
+ const cardholderMobileNumber = useState("cardholderMobileNumber", () => "");
93
+ const cardholderEmail = useState("cardholderEmail", () => "");
87
94
  const selectedPaymentMethod = useState("selectedPaymentMethod", () => "");
88
95
 
89
- function reset() {
90
- eWalletNumber.value = "";
91
- cardNumber.value = "";
92
- cardExpiration.value = "";
93
- cardSecurityCode.value = "";
94
- cardholderName.value = "";
95
- }
96
-
97
96
  function linkOnly(value: Record<string, any>) {
98
97
  return useNuxtApp().$api<Record<string, any>>(
99
98
  "/api/payment-methods/link-only",
@@ -109,21 +108,25 @@ export default function usePaymentMethod() {
109
108
  channel: "GCASH",
110
109
  logo: "/gcash-logo.svg",
111
110
  type: "EWALLET",
111
+ text: "GCash",
112
112
  },
113
113
  {
114
114
  channel: "PAYMAYA",
115
115
  logo: "/paymaya-logo.svg",
116
116
  type: "EWALLET",
117
+ text: "PayMaya",
117
118
  },
118
119
  {
119
120
  channel: "GRABPAY",
120
121
  logo: "/grabpay-logo.svg",
121
122
  type: "EWALLET",
123
+ text: "GrabPay",
122
124
  },
123
125
  {
124
126
  channel: "SHOPEEPAY",
125
127
  logo: "/shopeepay-logo.svg",
126
128
  type: "EWALLET",
129
+ text: "ShopeePay",
127
130
  },
128
131
  ];
129
132
 
@@ -132,26 +135,31 @@ export default function usePaymentMethod() {
132
135
  channel: "UBP",
133
136
  logo: "/ubp-logo.svg",
134
137
  type: "DIRECT_DEBIT",
138
+ text: "BA_UBP",
135
139
  },
136
140
  {
137
141
  channel: "BDO",
138
142
  logo: "/bdo-logo.svg",
139
143
  type: "DIRECT_DEBIT",
144
+ text: "BDO",
140
145
  },
141
146
  {
142
147
  channel: "BPI",
143
148
  logo: "/bpi-logo.svg",
144
149
  type: "DIRECT_DEBIT",
150
+ text: "BA_BPI",
145
151
  },
146
152
  {
147
153
  channel: "RCBC",
148
154
  logo: "/rcbc-logo.svg",
149
155
  type: "DIRECT_DEBIT",
156
+ text: "RCBC",
150
157
  },
151
158
  {
152
159
  channel: "Chinabank",
153
160
  logo: "/chinabank-logo.svg",
154
161
  type: "DIRECT_DEBIT",
162
+ text: "Chinabank",
155
163
  },
156
164
  ];
157
165
 
@@ -160,6 +168,155 @@ export default function usePaymentMethod() {
160
168
  ...supportedDirectDebit,
161
169
  ];
162
170
 
171
+ const { updatePaymentMethodById } = useSubscription();
172
+
173
+ const linkingOverlay = useState("linkingOverlay", () => false);
174
+ const updatingPaymentMethod = useState("updatingPaymentMethod", () => false);
175
+ const linkingMessage = useState("linkingMessage", () => "");
176
+ const paymentMethod = useState(
177
+ "paymentMethod",
178
+ (): Record<string, any> | null => null
179
+ );
180
+
181
+ function reset() {
182
+ eWalletNumber.value = "";
183
+ cardNumber.value = "";
184
+ cardExpiration.value = "";
185
+ cardSecurityCode.value = "";
186
+ cardholderName.value = "";
187
+ paymentMethod.value = null;
188
+ }
189
+
190
+ async function initLink(value: TLinkParams, callback?: Function) {
191
+ const validation = z.object({
192
+ subscriptionId: z.string().min(1).optional(),
193
+ paymentMethodType: z.enum(["EWALLET", "DIRECT_DEBIT", "CREDIT_CARD"]),
194
+ paymentMethodChannel: z.string().min(1),
195
+ customerId: z.string().min(1),
196
+ card_number: z.string().optional(),
197
+ expiry_month: z.string().optional(),
198
+ expiry_year: z.string().optional(),
199
+ card_security_code: z.string().optional(),
200
+ cardholder_name: z.string().optional(),
201
+ });
202
+
203
+ const parsed = validation.safeParse(value);
204
+
205
+ if (!parsed.success) {
206
+ throw new Error(
207
+ `Validation failed: ${parsed.error.issues
208
+ .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
209
+ .join(", ")}`
210
+ );
211
+ }
212
+
213
+ linkingOverlay.value = true;
214
+ const success_return_url = `${window.origin}/payment-method-linked`;
215
+ const failure_return_url = `${window.origin}/payment-method-failed-link`;
216
+ const cancel_return_url = `${window.origin}/payment-method-cancel-link`;
217
+
218
+ const payload: Record<string, any> = {
219
+ type: value.paymentMethodType,
220
+ reusability: "MULTIPLE_USE",
221
+ };
222
+
223
+ if (payload.type === "EWALLET") {
224
+ payload.country = "PH";
225
+ payload.ewallet = {
226
+ channel_code: value.paymentMethodChannel,
227
+ channel_properties: {
228
+ success_return_url,
229
+ failure_return_url,
230
+ cancel_return_url,
231
+ },
232
+ };
233
+
234
+ payload.customer_id = value.customerId;
235
+ } else if (payload.type === "DIRECT_DEBIT") {
236
+ payload.direct_debit = {
237
+ channel_code: value.paymentMethodChannel,
238
+ channel_properties: {
239
+ success_return_url,
240
+ failure_return_url,
241
+ },
242
+ };
243
+ payload.customer_id = value.customerId;
244
+ } else if (payload.type === "CARD") {
245
+ payload.card = {
246
+ currency: "PHP",
247
+ channel_properties: {
248
+ success_return_url,
249
+ failure_return_url,
250
+ skip_three_d_secure: true,
251
+ },
252
+ card_information: {
253
+ card_number: value.card_number,
254
+ expiry_month: value.expiry_month,
255
+ expiry_year: value.expiry_year,
256
+ cvv: value.card_security_code,
257
+ cardholder_name: value.cardholder_name,
258
+ },
259
+ };
260
+ }
261
+
262
+ try {
263
+ const _paymentMethod = await linkOnly(payload);
264
+
265
+ // Open a small popup window
266
+ const popupWidth = 500;
267
+ const popupHeight = 600;
268
+ const left = (screen.width - popupWidth) / 2;
269
+ const top = (screen.height - popupHeight) / 2;
270
+
271
+ const popup = window.open(
272
+ _paymentMethod.actions[0].url,
273
+ "eWalletPopup",
274
+ `width=${popupWidth},height=${popupHeight},top=${top},left=${left},resizable=yes,scrollbars=yes`
275
+ );
276
+
277
+ // Check every 500ms if the popup is closed
278
+ const checkPopupClosed = setInterval(async () => {
279
+ if (!popup || popup.closed) {
280
+ clearInterval(checkPopupClosed);
281
+ console.log(
282
+ "Popup closed. Proceeding with subscription automation..."
283
+ );
284
+ // Call your function to handle subscription
285
+
286
+ const temp = await getById(_paymentMethod.id);
287
+
288
+ if (temp.status === "ACTIVE" && value.subscriptionId) {
289
+ await updatePaymentMethodById(
290
+ value.subscriptionId,
291
+ _paymentMethod.id as string
292
+ );
293
+
294
+ paymentMethod.value = await getById(_paymentMethod.id);
295
+ }
296
+
297
+ if (callback) {
298
+ callback();
299
+ }
300
+
301
+ updatingPaymentMethod.value = false;
302
+ linkingOverlay.value = false;
303
+ }
304
+ }, 500);
305
+ } catch (error: any) {
306
+ linkingMessage.value = error.response._data.message;
307
+ }
308
+ }
309
+
310
+ function updateStatusById(id: string, status: string) {
311
+ return useNuxtApp().$api<Record<string, any>>(
312
+ `/api/payment-methods/status/${id}`,
313
+ {
314
+ method: "PATCH",
315
+ body: { status },
316
+ }
317
+ );
318
+ }
319
+
163
320
  return {
164
321
  linkEWallet,
165
322
  linkCard,
@@ -170,10 +327,21 @@ export default function usePaymentMethod() {
170
327
  cardExpiration,
171
328
  cardSecurityCode,
172
329
  cardholderName,
330
+ cardholderMobileNumber,
331
+ cardholderEmail,
173
332
  selectedPaymentMethod,
174
333
  reset,
175
334
  linkOnly,
176
335
  getById,
177
336
  supportedPaymentMethods,
337
+ supportedDirectDebit,
338
+ supportedEwallets,
339
+ initLink,
340
+ linkingOverlay,
341
+ updatingPaymentMethod,
342
+ linkingMessage,
343
+ paymentMethod,
344
+ getByCustomerId,
345
+ updateStatusById,
178
346
  };
179
347
  }
@@ -1,4 +1,19 @@
1
1
  export default function useSubscription() {
2
+ function createSubscriptionPlan(
3
+ seats: number,
4
+ trialDays: number,
5
+ productId: string
6
+ ) {
7
+ return useNuxtApp().$api("/api/subscriptions/plan", {
8
+ method: "POST",
9
+ body: {
10
+ seats,
11
+ trialDays,
12
+ productId,
13
+ },
14
+ });
15
+ }
16
+
2
17
  function add(subscriptionId: string, user: string) {
3
18
  return useNuxtApp().$api("/api/subscriptions", {
4
19
  method: "POST",
@@ -9,6 +24,40 @@ export default function useSubscription() {
9
24
  });
10
25
  }
11
26
 
27
+ function addBillingContactById(id: string, email: string) {
28
+ return useNuxtApp().$api(`/api/subscriptions/billing-contact/${id}`, {
29
+ method: "PUT",
30
+ body: {
31
+ email,
32
+ },
33
+ });
34
+ }
35
+
36
+ function updateBillingContactByAddedAt(
37
+ id: string,
38
+ addedAt: string,
39
+ email: string
40
+ ) {
41
+ return useNuxtApp().$api(
42
+ `/api/subscriptions/billing-contact/${id}/added-at/${addedAt}`,
43
+ {
44
+ method: "PUT",
45
+ body: {
46
+ email,
47
+ },
48
+ }
49
+ );
50
+ }
51
+
52
+ function deleteBillingContactByAddedAt(id: string, addedAt: string) {
53
+ return useNuxtApp().$api(
54
+ `/api/subscriptions/billing-contact/${id}/added-at/${addedAt}`,
55
+ {
56
+ method: "DELETE",
57
+ }
58
+ );
59
+ }
60
+
12
61
  function getById(id: string) {
13
62
  return useNuxtApp().$api<TSubscription>(`/api/subscriptions/id/${id}`);
14
63
  }
@@ -62,6 +111,15 @@ export default function useSubscription() {
62
111
  });
63
112
  }
64
113
 
114
+ function updatePaymentMethodById(id: string, paymentMethodId: string) {
115
+ return useNuxtApp().$api(`/api/subscriptions/payment-method/${id}`, {
116
+ method: "PUT",
117
+ body: {
118
+ paymentMethodId,
119
+ },
120
+ });
121
+ }
122
+
65
123
  async function affSubscriptionStatus() {
66
124
  const { currentUser } = useLocalAuth();
67
125
 
@@ -144,6 +202,36 @@ export default function useSubscription() {
144
202
  );
145
203
  }
146
204
 
205
+ function processSubscriptionPayment(id: string, invoice: string) {
206
+ return useNuxtApp().$api(`/api/subscriptions/payment/id/${id}`, {
207
+ method: "PUT",
208
+ body: {
209
+ invoice,
210
+ },
211
+ });
212
+ }
213
+
214
+ function addOrgSubscription(value: {
215
+ user: string;
216
+ transactionId: string;
217
+ promoCode?: string;
218
+ seats: number;
219
+ perSeatPrice: number;
220
+ currency: string;
221
+ org: {
222
+ name: string;
223
+ email: string;
224
+ contact: string;
225
+ busInst: string;
226
+ type: string;
227
+ };
228
+ }) {
229
+ return useNuxtApp().$api("/api/subscriptions/subscribe", {
230
+ method: "POST",
231
+ body: value,
232
+ });
233
+ }
234
+
147
235
  return {
148
236
  add,
149
237
  getById,
@@ -161,5 +249,12 @@ export default function useSubscription() {
161
249
  updateSeatsById,
162
250
  updatePromoCodeById,
163
251
  updateStatusById,
252
+ updatePaymentMethodById,
253
+ addBillingContactById,
254
+ updateBillingContactByAddedAt,
255
+ deleteBillingContactByAddedAt,
256
+ processSubscriptionPayment,
257
+ createSubscriptionPlan,
258
+ addOrgSubscription,
164
259
  };
165
260
  }
@@ -133,11 +133,14 @@ export default function useUtils() {
133
133
  async function getCountries() {
134
134
  try {
135
135
  const countries = await useNuxtApp().$api<Array<Record<string, any>>>(
136
- "https://restcountries.com/v3.1/all",
136
+ "https://restcountries.com/v3.1/all?fields=name,currencies,idd",
137
137
  { method: "GET" }
138
138
  );
139
139
 
140
- const uniqueCountries = new Map();
140
+ const uniqueCountries: Map<
141
+ string,
142
+ { index: string; name: string; code: string }
143
+ > = new Map();
141
144
 
142
145
  countries.forEach((country) => {
143
146
  let suffixes = country.idd?.suffixes?.[0] || "";
@@ -221,6 +224,14 @@ export default function useUtils() {
221
224
  );
222
225
  }
223
226
 
227
+ function extractMonthYear(expiry: string) {
228
+ const [month, year] = expiry.split("/");
229
+ return {
230
+ month: month.padStart(2, "0"),
231
+ year: 2000 + parseInt(year, 10),
232
+ };
233
+ }
234
+
224
235
  return {
225
236
  requiredRule,
226
237
  emailRule,
@@ -242,5 +253,6 @@ export default function useUtils() {
242
253
  computeTieredCost,
243
254
  requireListRule,
244
255
  convertPermissionsToArray,
256
+ extractMonthYear,
245
257
  };
246
258
  }
@@ -11,13 +11,4 @@ export default defineNuxtRouteMiddleware(async () => {
11
11
  // Redirect to login page if no access token
12
12
  return navigateTo({ name: "index" });
13
13
  }
14
-
15
- const { getCurrentUser } = useLocalAuth();
16
-
17
- try {
18
- await getCurrentUser();
19
- } catch (error) {
20
- // Redirect to login page if user authentication fails
21
- return navigateTo({ name: "index" });
22
- }
23
14
  });
package/middleware/org.ts CHANGED
@@ -5,7 +5,7 @@ const hexSchema = z
5
5
  .regex(/^[0-9a-fA-F]{24}$/, "Invalid organization ID");
6
6
 
7
7
  export default defineNuxtRouteMiddleware((to) => {
8
- const { organization } = to.params;
8
+ const organization = (to.params.organization as string) ?? "";
9
9
 
10
10
  if (organization && !hexSchema.safeParse(organization).success) {
11
11
  return navigateTo(
package/nuxt.config.ts CHANGED
@@ -8,6 +8,7 @@ export default defineNuxtConfig({
8
8
 
9
9
  runtimeConfig: {
10
10
  public: {
11
+ XENDIT_PUBLIC_KEY: (process.env.XENDIT_PUBLIC_KEY as string) ?? "",
11
12
  BASE_URL: process.env.BASE_URL ?? "/",
12
13
  cookieConfig: {
13
14
  domain: (process.env.DOMAIN as string) ?? "localhost",
@@ -24,6 +25,7 @@ export default defineNuxtConfig({
24
25
  APP_AFFILIATE: (process.env.APP_AFFILIATE as string) ?? "",
25
26
  APP_INVENTORY: (process.env.APP_INVENTORY as string) ?? "",
26
27
  APP_ASSET: (process.env.APP_ASSET as string) ?? "",
28
+ APP_ACCOUNTING: (process.env.APP_ACCOUNTING as string) ?? "",
27
29
  APP_BOOK_KEEPING: (process.env.APP_BOOK_KEEPING as string) ?? "",
28
30
  },
29
31
  },
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@goweekdays/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "0.0.12",
5
+ "version": "0.1.0",
6
6
  "main": "./nuxt.config.ts",
7
7
  "publishConfig": {
8
8
  "access": "public"
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <v-row no-gutters class="fill-height" justify="center" align-content="center">
3
+ <v-card class="pa-4" elevation="4" color="warning lighten-1">
4
+ <v-card-title class="text-white d-flex align-center">
5
+ <v-icon class="mr-2">mdi-check-circle</v-icon>
6
+ Cancelled Payment Method Link
7
+ </v-card-title>
8
+
9
+ <v-card-actions class="d-flex justify-center">
10
+ <v-btn
11
+ color="white"
12
+ variant="tonal"
13
+ rounded="xl"
14
+ size="large"
15
+ @click="closeWindow"
16
+ >Close</v-btn
17
+ >
18
+ </v-card-actions>
19
+ </v-card>
20
+ </v-row>
21
+ </template>
22
+
23
+ <script setup>
24
+ definePageMeta({
25
+ layout: "plain",
26
+ });
27
+
28
+ const closeWindow = () => {
29
+ window.close();
30
+ };
31
+ </script>
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <v-row no-gutters class="fill-height" justify="center" align-content="center">
3
+ <v-card class="pa-4" elevation="4" color="error lighten-1">
4
+ <v-card-title class="text-white d-flex align-center">
5
+ <v-icon class="mr-2">mdi-check-circle</v-icon>
6
+ Failed Payment Method Link
7
+ </v-card-title>
8
+
9
+ <v-card-actions class="d-flex justify-center">
10
+ <v-btn
11
+ color="white"
12
+ variant="tonal"
13
+ rounded="xl"
14
+ size="large"
15
+ @click="closeWindow"
16
+ >Close</v-btn
17
+ >
18
+ </v-card-actions>
19
+ </v-card>
20
+ </v-row>
21
+ </template>
22
+
23
+ <script setup>
24
+ definePageMeta({
25
+ layout: "plain",
26
+ });
27
+
28
+ const closeWindow = () => {
29
+ window.close();
30
+ };
31
+ </script>
package/plugins/API.ts CHANGED
@@ -11,9 +11,7 @@ export default defineNuxtPlugin(() => {
11
11
  retryDelay: 500,
12
12
  onRequest({ options }) {
13
13
  const accessToken = useCookie("accessToken", cookieConfig).value;
14
- options.headers = accessToken
15
- ? { Authorization: `Bearer ${accessToken}` }
16
- : {};
14
+ options.headers.set("Authorization", `Bearer ${accessToken}`);
17
15
  },
18
16
 
19
17
  async onResponseError({ response }) {
@@ -0,0 +1,28 @@
1
+ declare type TInvoiceItem = {
2
+ itemId: string;
3
+ quantity: number;
4
+ seats?: number;
5
+ total: number;
6
+ };
7
+
8
+ declare type TInvoiceMetadata = {
9
+ userId?: string | ObjectId;
10
+ orgId?: string | ObjectId;
11
+ subscriptionId?: string | ObjectId;
12
+ currency?: string;
13
+ description?: string;
14
+ };
15
+
16
+ declare type TInvoice = {
17
+ _id?: string | ObjectId;
18
+ invoiceNumber: string;
19
+ billingCycle: string;
20
+ status: string;
21
+ createdAt: string;
22
+ updatedAt: string;
23
+ items: TInvoiceItem[];
24
+ metadata?: TInvoiceMetadata;
25
+ amount: number;
26
+ dueDate: string;
27
+ type: string;
28
+ };
package/types/local.d.ts CHANGED
@@ -14,6 +14,7 @@ declare type TNavigationItem = {
14
14
  icon?: string;
15
15
  route?: TNavigationRoute;
16
16
  children?: TNavigationItem[];
17
+ disabled?: boolean;
17
18
  };
18
19
 
19
20
  declare type TKeyValuePair<
@@ -0,0 +1,11 @@
1
+ declare type TLinkParams = {
2
+ subscriptionId?: string;
3
+ paymentMethodType: string;
4
+ paymentMethodChannel: string;
5
+ customerId: string;
6
+ card_number?: string;
7
+ expiry_month?: string;
8
+ expiry_year?: string;
9
+ card_security_code?: string;
10
+ cardholder_name?: string;
11
+ };
@@ -1,3 +1,8 @@
1
+ declare type TBillingRecipient = {
2
+ addedAt?: string;
3
+ email: string;
4
+ };
5
+
1
6
  declare type TSubscription = {
2
7
  _id?: string;
3
8
  user?: string;
@@ -14,6 +19,7 @@ declare type TSubscription = {
14
19
  maxSeats?: number;
15
20
  status?: string;
16
21
  billingCycle: "monthly" | "yearly";
22
+ billingContacts?: TBillingRecipient[];
17
23
  nextBillingDate?: string;
18
24
  lastPaymentStatus?: string;
19
25
  failedAttempts?: number;
@@ -0,0 +1,3 @@
1
+ declare interface Window {
2
+ Xendit: Record<any, any>;
3
+ }