@goweekdays/layer-common 0.0.11 → 0.0.13

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,17 @@
1
1
  # @goweekdays/layer-common
2
2
 
3
+ ## 0.0.13
4
+
5
+ ### Patch Changes
6
+
7
+ - c6c7076: Add invoice payment
8
+
9
+ ## 0.0.12
10
+
11
+ ### Patch Changes
12
+
13
+ - 72ba6c2: Remove client plugin
14
+
3
15
  ## 0.0.11
4
16
 
5
17
  ### Patch Changes
@@ -1,27 +1,18 @@
1
1
  <template>
2
2
  <v-row no-gutters>
3
3
  <v-col cols="12">
4
- <slot name="header">
4
+ <v-item-group
5
+ v-model.number="selectedPaymentMethod"
6
+ selected-class="bg-cyan-accent-1"
7
+ mandatory
8
+ >
5
9
  <v-row no-gutters>
6
- <v-col cols="12" class="text-h6 font-weight-bold">
7
- Payment Method
10
+ <v-col cols="12">
11
+ <InputLabel title="E-Wallet" />
8
12
  </v-col>
9
- </v-row>
10
- </slot>
11
- </v-col>
12
13
 
13
- <v-col cols="12" class="mt-2">
14
- <v-row no-gutters>
15
- <v-col cols="12">
16
- <v-item-group
17
- v-model.number="selectedPaymentMethod"
18
- selected-class="bg-cyan-accent-1"
19
- mandatory
20
- >
14
+ <v-col cols="12" class="mt-2">
21
15
  <v-row>
22
- <v-col cols="12">
23
- <InputLabel title="E-Wallet" />
24
- </v-col>
25
16
  <template v-for="eWalletItem in props.supportedEwallets">
26
17
  <v-col cols="12" lg="3" md="4" sm="4">
27
18
  <v-item v-slot="{ toggle, selectedClass }">
@@ -37,11 +28,15 @@
37
28
  </v-item>
38
29
  </v-col>
39
30
  </template>
31
+ </v-row>
32
+ </v-col>
40
33
 
41
- <v-col cols="12">
42
- <InputLabel title="Direct Debit" />
43
- </v-col>
34
+ <v-col cols="12" class="mt-6">
35
+ <InputLabel title="Direct Debit" />
36
+ </v-col>
44
37
 
38
+ <v-col cols="12" class="mt-2">
39
+ <v-row>
45
40
  <template
46
41
  v-for="supportedDirectDebitItem in props.supportedDirectDebit"
47
42
  >
@@ -64,9 +59,9 @@
64
59
  </v-col>
65
60
  </template>
66
61
  </v-row>
67
- </v-item-group>
68
- </v-col>
69
- </v-row>
62
+ </v-col>
63
+ </v-row>
64
+ </v-item-group>
70
65
  </v-col>
71
66
  </v-row>
72
67
  </template>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <v-row no-gutters justify="end" align="center">
3
+ <span class="mr-2 text-caption text-fontgray">
4
+ {{ props.pageRange }}
5
+ </span>
6
+ <div class="arrow-navigation">
7
+ <v-btn
8
+ icon="mdi-chevron-left"
9
+ variant="text"
10
+ density="comfortable"
11
+ :disabled="page <= 1"
12
+ @click="decrement"
13
+ />
14
+ <v-btn
15
+ icon="mdi-chevron-right"
16
+ variant="text"
17
+ density="comfortable"
18
+ :disabled="page >= props.pages"
19
+ @click="increment"
20
+ />
21
+ </div>
22
+ </v-row>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ const page = defineModel({ type: Number, default: 0 });
27
+
28
+ const props = defineProps({
29
+ pages: {
30
+ type: Number,
31
+ required: true,
32
+ default: 0,
33
+ },
34
+ pageRange: {
35
+ type: String,
36
+ required: true,
37
+ default: "-- - -- of --",
38
+ },
39
+ });
40
+
41
+ const emit = defineEmits(["refresh", "update:pagination"]);
42
+
43
+ function increment() {
44
+ page.value++;
45
+ emit("update:pagination", page.value);
46
+ }
47
+
48
+ function decrement() {
49
+ page.value--;
50
+ emit("update:pagination", page.value);
51
+ }
52
+ </script>
@@ -43,10 +43,11 @@
43
43
  <slot name="extension"></slot>
44
44
  </template>
45
45
  </v-toolbar>
46
+ <v-divider></v-divider>
46
47
 
47
48
  <v-data-table
48
49
  v-model="selected"
49
- v-bind="$attrs"
50
+ v-bind="attrs"
50
51
  :headers="props.headers"
51
52
  :items="props.items"
52
53
  item-value="_id"
@@ -66,6 +67,7 @@
66
67
  </template>
67
68
 
68
69
  <script setup lang="ts">
70
+ const attrs = useAttrs();
69
71
  const selected = defineModel({
70
72
  type: Array as PropType<Array<string>>,
71
73
  default: () => [],
@@ -32,10 +32,38 @@ export default function useAddress() {
32
32
  });
33
33
  }
34
34
 
35
+ function updateById({
36
+ id = "",
37
+ country = "",
38
+ address = "",
39
+ continuedAddress = "",
40
+ city = "",
41
+ province = "",
42
+ postalCode = "",
43
+ taxId = "",
44
+ } = {}) {
45
+ return useNuxtApp().$api(`/api/addresses/details/${id}`, {
46
+ method: "PUT",
47
+ body: {
48
+ country,
49
+ address,
50
+ continuedAddress,
51
+ city,
52
+ province,
53
+ postalCode,
54
+ taxId,
55
+ },
56
+ });
57
+ }
58
+
35
59
  function getByUserId(user = "") {
36
60
  return useNuxtApp().$api<TAddress>(`/api/addresses/user/${user}`);
37
61
  }
38
62
 
63
+ function getByOrgId(id = "") {
64
+ return useNuxtApp().$api<TAddress>(`/api/addresses/org/${id}`);
65
+ }
66
+
39
67
  const _address = useState("address", (): TAddress => {
40
68
  return {
41
69
  type: "",
@@ -103,5 +131,7 @@ export default function useAddress() {
103
131
  getByUserId,
104
132
  address: _address,
105
133
  completeAddress,
134
+ getByOrgId,
135
+ updateById,
106
136
  };
107
137
  }
@@ -1,5 +1,9 @@
1
1
  export default function useInvoice() {
2
2
  function getBySubscriptionId({ search = "", id = "", page = 1 } = {}) {
3
+ if (!id) {
4
+ throw new Error("Subscription ID is required");
5
+ }
6
+
3
7
  return useNuxtApp().$api<Record<string, any>>(
4
8
  `/api/invoices/subscription/${id}`,
5
9
  {
@@ -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,7 @@ 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}`
79
78
  );
80
79
  }
81
80
 
@@ -86,14 +85,6 @@ export default function usePaymentMethod() {
86
85
  const cardholderName = useState("cardholderName", () => "");
87
86
  const selectedPaymentMethod = useState("selectedPaymentMethod", () => "");
88
87
 
89
- function reset() {
90
- eWalletNumber.value = "";
91
- cardNumber.value = "";
92
- cardExpiration.value = "";
93
- cardSecurityCode.value = "";
94
- cardholderName.value = "";
95
- }
96
-
97
88
  function linkOnly(value: Record<string, any>) {
98
89
  return useNuxtApp().$api<Record<string, any>>(
99
90
  "/api/payment-methods/link-only",
@@ -160,6 +151,142 @@ export default function usePaymentMethod() {
160
151
  ...supportedDirectDebit,
161
152
  ];
162
153
 
154
+ const { updatePaymentMethodById } = useSubscription();
155
+
156
+ const linkingOverlay = useState("linkingOverlay", () => false);
157
+ const updatingPaymentMethod = useState("updatingPaymentMethod", () => false);
158
+ const linkingMessage = useState("linkingMessage", () => "");
159
+ const paymentMethod = useState(
160
+ "paymentMethod",
161
+ (): Record<string, any> | null => null
162
+ );
163
+
164
+ function reset() {
165
+ eWalletNumber.value = "";
166
+ cardNumber.value = "";
167
+ cardExpiration.value = "";
168
+ cardSecurityCode.value = "";
169
+ cardholderName.value = "";
170
+ paymentMethod.value = null;
171
+ }
172
+
173
+ async function initLink(value: TLinkParams) {
174
+ const validation = z.object({
175
+ subscriptionId: z.string().min(1),
176
+ paymentMethodType: z.enum(["EWALLET", "DIRECT_DEBIT", "CREDIT_CARD"]),
177
+ paymentMethodChannel: z.string().min(1),
178
+ customerId: z.string().min(1),
179
+ card_number: z.string().optional(),
180
+ expiry_month: z.string().optional(),
181
+ expiry_year: z.string().optional(),
182
+ card_security_code: z.string().optional(),
183
+ cardholder_name: z.string().optional(),
184
+ });
185
+
186
+ const parsed = validation.safeParse(value);
187
+
188
+ if (!parsed.success) {
189
+ throw new Error(
190
+ `Validation failed: ${parsed.error.issues
191
+ .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
192
+ .join(", ")}`
193
+ );
194
+ }
195
+
196
+ linkingOverlay.value = true;
197
+ const success_return_url = `${window.origin}/payment-method-linked`;
198
+ const failure_return_url = `${window.origin}/payment-method-failed-link`;
199
+ const cancel_return_url = `${window.origin}/payment-method-cancel-link`;
200
+
201
+ const payload: Record<string, any> = {
202
+ type: value.paymentMethodType,
203
+ reusability: "MULTIPLE_USE",
204
+ };
205
+
206
+ if (payload.type === "EWALLET") {
207
+ payload.country = "PH";
208
+ payload.ewallet = {
209
+ channel_code: value.paymentMethodChannel,
210
+ channel_properties: {
211
+ success_return_url,
212
+ failure_return_url,
213
+ cancel_return_url,
214
+ },
215
+ };
216
+
217
+ payload.customer_id = value.customerId;
218
+ } else if (payload.type === "DIRECT_DEBIT") {
219
+ payload.direct_debit = {
220
+ channel_code: value.paymentMethodChannel,
221
+ channel_properties: {
222
+ success_return_url,
223
+ failure_return_url,
224
+ cancel_return_url,
225
+ },
226
+ };
227
+ payload.customer_id = value.customerId;
228
+ } else if (payload.type === "CREDIT_CARD") {
229
+ payload.card = {
230
+ currency: "PHP",
231
+ channel_properties: {
232
+ success_return_url,
233
+ failure_return_url,
234
+ skip_three_d_secure: true,
235
+ },
236
+ card_information: {
237
+ card_number: value.card_number,
238
+ expiry_month: value.expiry_month,
239
+ expiry_year: value.expiry_year,
240
+ cvv: value.card_security_code,
241
+ cardholder_name: value.cardholder_name,
242
+ },
243
+ };
244
+ }
245
+
246
+ try {
247
+ const _paymentMethod = await linkOnly(payload);
248
+
249
+ // Open a small popup window
250
+ const popupWidth = 500;
251
+ const popupHeight = 600;
252
+ const left = (screen.width - popupWidth) / 2;
253
+ const top = (screen.height - popupHeight) / 2;
254
+
255
+ const popup = window.open(
256
+ _paymentMethod.actions[0].url,
257
+ "eWalletPopup",
258
+ `width=${popupWidth},height=${popupHeight},top=${top},left=${left},resizable=yes,scrollbars=yes`
259
+ );
260
+
261
+ // Check every 500ms if the popup is closed
262
+ const checkPopupClosed = setInterval(async () => {
263
+ if (!popup || popup.closed) {
264
+ clearInterval(checkPopupClosed);
265
+ console.log(
266
+ "Popup closed. Proceeding with subscription automation..."
267
+ );
268
+ // Call your function to handle subscription
269
+
270
+ const temp = await getById(_paymentMethod.id);
271
+
272
+ if (temp.status === "ACTIVE") {
273
+ await updatePaymentMethodById(
274
+ value.subscriptionId,
275
+ _paymentMethod.id as string
276
+ );
277
+
278
+ paymentMethod.value = await getById(_paymentMethod.id);
279
+ }
280
+
281
+ updatingPaymentMethod.value = false;
282
+ linkingOverlay.value = false;
283
+ }
284
+ }, 500);
285
+ } catch (error: any) {
286
+ linkingMessage.value = error.response._data.message;
287
+ }
288
+ }
289
+
163
290
  return {
164
291
  linkEWallet,
165
292
  linkCard,
@@ -175,5 +302,10 @@ export default function usePaymentMethod() {
175
302
  linkOnly,
176
303
  getById,
177
304
  supportedPaymentMethods,
305
+ initLink,
306
+ linkingOverlay,
307
+ updatingPaymentMethod,
308
+ linkingMessage,
309
+ paymentMethod,
178
310
  };
179
311
  }
@@ -9,6 +9,40 @@ export default function useSubscription() {
9
9
  });
10
10
  }
11
11
 
12
+ function addBillingContactById(id: string, email: string) {
13
+ return useNuxtApp().$api(`/api/subscriptions/billing-contact/${id}`, {
14
+ method: "PUT",
15
+ body: {
16
+ email,
17
+ },
18
+ });
19
+ }
20
+
21
+ function updateBillingContactByAddedAt(
22
+ id: string,
23
+ addedAt: string,
24
+ email: string
25
+ ) {
26
+ return useNuxtApp().$api(
27
+ `/api/subscriptions/billing-contact/${id}/added-at/${addedAt}`,
28
+ {
29
+ method: "PUT",
30
+ body: {
31
+ email,
32
+ },
33
+ }
34
+ );
35
+ }
36
+
37
+ function deleteBillingContactByAddedAt(id: string, addedAt: string) {
38
+ return useNuxtApp().$api(
39
+ `/api/subscriptions/billing-contact/${id}/added-at/${addedAt}`,
40
+ {
41
+ method: "DELETE",
42
+ }
43
+ );
44
+ }
45
+
12
46
  function getById(id: string) {
13
47
  return useNuxtApp().$api<TSubscription>(`/api/subscriptions/id/${id}`);
14
48
  }
@@ -62,6 +96,15 @@ export default function useSubscription() {
62
96
  });
63
97
  }
64
98
 
99
+ function updatePaymentMethodById(id: string, paymentMethodId: string) {
100
+ return useNuxtApp().$api(`/api/subscriptions/payment-method/${id}`, {
101
+ method: "PUT",
102
+ body: {
103
+ paymentMethodId,
104
+ },
105
+ });
106
+ }
107
+
65
108
  async function affSubscriptionStatus() {
66
109
  const { currentUser } = useLocalAuth();
67
110
 
@@ -144,6 +187,15 @@ export default function useSubscription() {
144
187
  );
145
188
  }
146
189
 
190
+ function processSubscriptionPayment(id: string, invoice: string) {
191
+ return useNuxtApp().$api(`/api/subscriptions/payment/id/${id}`, {
192
+ method: "PUT",
193
+ body: {
194
+ invoice,
195
+ },
196
+ });
197
+ }
198
+
147
199
  return {
148
200
  add,
149
201
  getById,
@@ -161,5 +213,10 @@ export default function useSubscription() {
161
213
  updateSeatsById,
162
214
  updatePromoCodeById,
163
215
  updateStatusById,
216
+ updatePaymentMethodById,
217
+ addBillingContactById,
218
+ updateBillingContactByAddedAt,
219
+ deleteBillingContactByAddedAt,
220
+ processSubscriptionPayment,
164
221
  };
165
222
  }
@@ -137,7 +137,10 @@ export default function useUtils() {
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] || "";
@@ -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/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.11",
5
+ "version": "0.0.13",
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>
@@ -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
+ };
@@ -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;
package/plugins/client.ts DELETED
@@ -1,30 +0,0 @@
1
- export default defineNuxtPlugin(async () => {
2
- const cookieConfig = useRuntimeConfig().public.cookieConfig;
3
-
4
- const clientId = useCookie("clientId", cookieConfig).value;
5
-
6
- if (!clientId) {
7
- const res = await fetch("/api/auth/init");
8
- const headers = res.headers.get("accept-ch");
9
-
10
- console.log("headers", headers);
11
-
12
- // const headerValues = [
13
- // headers.get("user-agent"),
14
- // headers.get("sec-ch-ua"),
15
- // headers.get("sec-ch-ua-platform"),
16
- // headers.get("sec-ch-ua-mobile"),
17
- // headers.get("accept-language"),
18
- // ].join("|");
19
-
20
- const buffer = await crypto.subtle.digest(
21
- "SHA-256",
22
- new TextEncoder().encode(headers ?? "")
23
- );
24
- const _clientId = [...new Uint8Array(buffer)]
25
- .map((b) => b.toString(16).padStart(2, "0"))
26
- .join("");
27
-
28
- useCookie("clientId", cookieConfig).value = _clientId;
29
- }
30
- });