@sbc-connect/nuxt-pay 0.1.6 → 0.1.8
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/.env.example +14 -1
- package/CHANGELOG.md +22 -0
- package/README.md +4 -3
- package/app/app.config.ts +2 -0
- package/app/components/Connect/Fee/ExtraFee.vue +12 -0
- package/app/components/Connect/Fee/Widget.vue +197 -0
- package/app/enums/connect-pay-cfs-status.ts +6 -0
- package/app/enums/connect-pay-method.ts +6 -0
- package/app/interfaces/connect-fees.ts +35 -0
- package/app/interfaces/connect-pay-account.ts +21 -0
- package/app/layouts/ConnectPay.vue +19 -0
- package/app/plugins/pay-api.ts +28 -0
- package/app/stores/connect-fee.ts +276 -0
- package/i18n/locales/en-CA.ts +43 -0
- package/i18n/locales/fr-CA.ts +2 -0
- package/modules/pay-assets/index.ts +15 -0
- package/modules/pay-assets/runtime/assets/connect-pay-tw.css +2 -0
- package/nuxt.config.ts +29 -1
- package/package.json +7 -7
- package/app/assets/css/tw-pay.css +0 -2
package/.env.example
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
# Web Urls
|
|
2
2
|
NUXT_PUBLIC_BASE_URL="http://localhost:3000/"
|
|
3
3
|
NUXT_PUBLIC_REGISTRY_HOME_URL="https://dev.bcregistry.gov.bc.ca/"
|
|
4
|
+
NUXT_PUBLIC_AUTH_WEB_URL="https://dev.account.bcregistry.gov.bc.ca/"
|
|
5
|
+
|
|
6
|
+
# API Key
|
|
7
|
+
NUXT_PUBLIC_X_API_KEY=""
|
|
4
8
|
|
|
5
9
|
# API Urls
|
|
6
10
|
# Status API
|
|
7
11
|
NUXT_PUBLIC_STATUS_API_URL="https://status-api-dev.apps.gold.devops.gov.bc.ca"
|
|
8
12
|
NUXT_PUBLIC_STATUS_API_VERSION="/api/v1"
|
|
9
13
|
|
|
14
|
+
# Auth API
|
|
15
|
+
NUXT_PUBLIC_AUTH_API_URL="https://test.api.connect.gov.bc.ca/auth-dev"
|
|
16
|
+
NUXT_PUBLIC_AUTH_API_VERSION="/api/v1"
|
|
17
|
+
|
|
18
|
+
# Pay API
|
|
19
|
+
NUXT_PUBLIC_PAY_API_URL="https://test.api.connect.gov.bc.ca/pay-dev"
|
|
20
|
+
NUXT_PUBLIC_PAY_API_VERSION="/api/v1"
|
|
21
|
+
|
|
10
22
|
# LaunchDarkly
|
|
11
23
|
NUXT_PUBLIC_LD_CLIENT_ID=""
|
|
12
24
|
|
|
13
25
|
# Identity Provider
|
|
14
26
|
NUXT_PUBLIC_IDP_URL="https://dev.loginproxy.gov.bc.ca/auth"
|
|
15
27
|
NUXT_PUBLIC_IDP_REALM="bcregistry"
|
|
16
|
-
NUXT_PUBLIC_IDP_CLIENTID="connect-web"
|
|
28
|
+
NUXT_PUBLIC_IDP_CLIENTID="connect-web"
|
|
29
|
+
NUXT_PUBLIC_SITEMINDER_LOGOUT_URL="https://logontest7.gov.bc.ca/clp-cgi/logoff.cgi"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @sbc-connect/nuxt-pay
|
|
2
2
|
|
|
3
|
+
## 0.1.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#36](https://github.com/bcgov/connect-nuxt/pull/36) [`a9176be`](https://github.com/bcgov/connect-nuxt/commit/a9176be460388cba2df53cdfb49e4fd2dfd3d324) Thanks [@kialj876](https://github.com/kialj876)! - Pay Layer fee options update
|
|
8
|
+
Pay Layer documentation
|
|
9
|
+
bcgov/entity#29337
|
|
10
|
+
|
|
11
|
+
- [#32](https://github.com/bcgov/connect-nuxt/pull/32) [`66d83d1`](https://github.com/bcgov/connect-nuxt/commit/66d83d14b2ec7950057dd39a4d876a8c4096923f) Thanks [@kialj876](https://github.com/kialj876)! - Pay layer cleanup bcgov/entity#29337
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`b2a0458`](https://github.com/bcgov/connect-nuxt/commit/b2a04587d5408d213d463ef6161b701ca597ef86), [`66d83d1`](https://github.com/bcgov/connect-nuxt/commit/66d83d14b2ec7950057dd39a4d876a8c4096923f)]:
|
|
14
|
+
- @sbc-connect/nuxt-auth@0.1.8
|
|
15
|
+
|
|
16
|
+
## 0.1.7
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- [#28](https://github.com/bcgov/connect-nuxt/pull/28) [`68af125`](https://github.com/bcgov/connect-nuxt/commit/68af1259b87846f42010026977411481e53ca8fb) Thanks [@kialj876](https://github.com/kialj876)! - Filling out the pay layer. Ticket bcgov/entity#29337
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`68af125`](https://github.com/bcgov/connect-nuxt/commit/68af1259b87846f42010026977411481e53ca8fb)]:
|
|
23
|
+
- @sbc-connect/nuxt-auth@0.1.7
|
|
24
|
+
|
|
3
25
|
## 0.1.6
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ This package provides the necessary components, composables, and service integra
|
|
|
15
15
|
- Composables for interacting with the payment API.
|
|
16
16
|
- Pre-configured for handling different payment methods (Credit Card, PAD, etc.).
|
|
17
17
|
|
|
18
|
-
For detailed usage and documentation, please see the [Pay Layer Docs](../../../docs/packages/layers/pay/
|
|
18
|
+
For detailed usage and documentation, please see the [Pay Layer Docs](../../../docs/packages/layers/pay/overview.md).
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
@@ -49,8 +49,9 @@ For local development, you will need credentials for the interacting with the Pa
|
|
|
49
49
|
|
|
50
50
|
```
|
|
51
51
|
# .env
|
|
52
|
-
NUXT_PUBLIC_PAY_API_URL="https://
|
|
53
|
-
|
|
52
|
+
NUXT_PUBLIC_PAY_API_URL="https://test.api.connect.gov.bc.ca/pay-dev"
|
|
53
|
+
NUXT_PUBLIC_PAY_API_VERSION="/api/v1"
|
|
54
|
+
NUXT_PUBLIC_X_API_KEY="your-dev-api-key"
|
|
54
55
|
```
|
|
55
56
|
|
|
56
57
|
### Production Environments
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{ description: string, fee: number, showFeeValue: boolean }>()
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div class="flex justify-between pl-9 pr-4 py-3">
|
|
7
|
+
<p class="font-bold">
|
|
8
|
+
{{ description }}
|
|
9
|
+
</p>
|
|
10
|
+
<p>{{ showFeeValue ? `$${fee.toFixed(2)}`: '$ -' }}</p>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { t } = useI18n()
|
|
3
|
+
const {
|
|
4
|
+
feeOptions,
|
|
5
|
+
fees,
|
|
6
|
+
placeholderFeeItem,
|
|
7
|
+
total,
|
|
8
|
+
totalServiceFees,
|
|
9
|
+
totalFutureEffectiveFees,
|
|
10
|
+
totalPriorityFees,
|
|
11
|
+
totalProcessingFees,
|
|
12
|
+
totalGst,
|
|
13
|
+
totalPst,
|
|
14
|
+
userSelectedPaymentMethod,
|
|
15
|
+
allowedPaymentMethods,
|
|
16
|
+
userPaymentAccount,
|
|
17
|
+
allowAlternatePaymentMethod
|
|
18
|
+
} = storeToRefs(useConnectFeeStore())
|
|
19
|
+
|
|
20
|
+
const isPlaceholderActive = ref(false)
|
|
21
|
+
|
|
22
|
+
watch(fees, (v) => {
|
|
23
|
+
if (v && (Object.keys(v).length > 0)) {
|
|
24
|
+
isPlaceholderActive.value = false
|
|
25
|
+
} else {
|
|
26
|
+
isPlaceholderActive.value = true
|
|
27
|
+
}
|
|
28
|
+
}, { immediate: true, deep: true })
|
|
29
|
+
|
|
30
|
+
const feeItems = computed<ConnectFeeItem[]>(() => {
|
|
31
|
+
if (!isPlaceholderActive.value) {
|
|
32
|
+
return Object.values(fees.value)
|
|
33
|
+
}
|
|
34
|
+
return [placeholderFeeItem.value]
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const serviceFee = computed(() => {
|
|
38
|
+
return isPlaceholderActive.value
|
|
39
|
+
? placeholderFeeItem.value.serviceFees
|
|
40
|
+
: totalServiceFees.value
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// folding stuff
|
|
44
|
+
const folded = ref(false)
|
|
45
|
+
|
|
46
|
+
const isFoldable = useMediaQuery('(max-width: 1024px)')
|
|
47
|
+
watch(isFoldable, (val) => {
|
|
48
|
+
if (!val) {
|
|
49
|
+
folded.value = false
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const toggleFolded = () => {
|
|
54
|
+
if (isFoldable.value) {
|
|
55
|
+
folded.value = !folded.value
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const getItemFee = (feeItem: ConnectFeeItem) => {
|
|
60
|
+
if (feeItem.isPlaceholder) {
|
|
61
|
+
return '$ -'
|
|
62
|
+
}
|
|
63
|
+
if (feeItem.waived) {
|
|
64
|
+
return t('connect.label.noFee')
|
|
65
|
+
}
|
|
66
|
+
return `$${(feeItem.filingFees * (feeItem.quantity || 1)).toFixed(2)}`
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<template>
|
|
71
|
+
<div
|
|
72
|
+
data-testid="fee-widget"
|
|
73
|
+
class="z-10 rounded bg-white lg:shadow-sm"
|
|
74
|
+
>
|
|
75
|
+
<UButton
|
|
76
|
+
:tabindex="isFoldable ? 0 : -1"
|
|
77
|
+
:role="isFoldable ? 'button' : 'title'"
|
|
78
|
+
class="flex w-full bg-brand py-2 pl-4 text-lg lg:text-3xl font-bold transition-all"
|
|
79
|
+
:class="[folded ? 'rounded' : 'rounded-b-none rounded-t', isFoldable ? '' : 'pointer-events-none']"
|
|
80
|
+
:aria-label="$t('connect.label.feeSummary')"
|
|
81
|
+
:label="$t('connect.label.feeSummary')"
|
|
82
|
+
@click="toggleFolded"
|
|
83
|
+
>
|
|
84
|
+
<template #trailing>
|
|
85
|
+
<div class="flex grow justify-end pr-1">
|
|
86
|
+
<UIcon
|
|
87
|
+
v-if="isFoldable"
|
|
88
|
+
class="size-7"
|
|
89
|
+
:name="folded ? 'i-mdi-chevron-down' : 'i-mdi-chevron-up'"
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
</UButton>
|
|
94
|
+
<ConnectTransitionCollapse>
|
|
95
|
+
<div v-if="!folded">
|
|
96
|
+
<div class="divide-y divide-line-muted pt-1 text-sm">
|
|
97
|
+
<div
|
|
98
|
+
v-for="feeItem in feeItems"
|
|
99
|
+
:key="feeItem.filingTypeCode"
|
|
100
|
+
data-testid="fee-item"
|
|
101
|
+
class="flex justify-between px-4 py-3"
|
|
102
|
+
>
|
|
103
|
+
<div>
|
|
104
|
+
<p class="flex items-center gap-1 font-bold">
|
|
105
|
+
<span>{{ feeItem.label }}</span>
|
|
106
|
+
</p>
|
|
107
|
+
<p
|
|
108
|
+
v-if="feeItem.quantity !== undefined && feeItem.quantityDesc"
|
|
109
|
+
class="pl-4 text-neutral-toned"
|
|
110
|
+
>
|
|
111
|
+
x {{ feeItem.quantity }} {{ feeItem.quantityDesc }}
|
|
112
|
+
</p>
|
|
113
|
+
</div>
|
|
114
|
+
<p>{{ getItemFee(feeItem) }}</p>
|
|
115
|
+
</div>
|
|
116
|
+
<ConnectFeeExtraFee
|
|
117
|
+
v-if="!!feeOptions.showFutureEffectiveFee || (!!feeOptions.showAllActiveFees && totalFutureEffectiveFees)"
|
|
118
|
+
data-testid="future-effective-fee"
|
|
119
|
+
:description="$t('connect.label.futureEffectiveFee')"
|
|
120
|
+
:fee="totalFutureEffectiveFees"
|
|
121
|
+
show-fee-value
|
|
122
|
+
/>
|
|
123
|
+
<ConnectFeeExtraFee
|
|
124
|
+
v-if="!!feeOptions.showPriorityFee || (!!feeOptions.showAllActiveFees && totalPriorityFees)"
|
|
125
|
+
data-testid="priority-fee"
|
|
126
|
+
:description="$t('connect.label.priorityFee')"
|
|
127
|
+
:fee="totalPriorityFees"
|
|
128
|
+
show-fee-value
|
|
129
|
+
/>
|
|
130
|
+
<ConnectFeeExtraFee
|
|
131
|
+
v-if="!!feeOptions.showProcessingFee || (!!feeOptions.showAllActiveFees && totalProcessingFees)"
|
|
132
|
+
data-testid="processing-fee"
|
|
133
|
+
:description="$t('connect.label.processingFee')"
|
|
134
|
+
:fee="totalProcessingFees"
|
|
135
|
+
show-fee-value
|
|
136
|
+
/>
|
|
137
|
+
<ConnectFeeExtraFee
|
|
138
|
+
v-if="!!feeOptions.showServiceFee || (!!feeOptions.showAllActiveFees && serviceFee)"
|
|
139
|
+
data-testid="service-fee"
|
|
140
|
+
:description="$t('connect.label.serviceFee')"
|
|
141
|
+
:fee="serviceFee"
|
|
142
|
+
show-fee-value
|
|
143
|
+
/>
|
|
144
|
+
<ConnectFeeExtraFee
|
|
145
|
+
v-if="!!feeOptions.showPst || (!!feeOptions.showAllActiveFees && totalPst)"
|
|
146
|
+
data-testid="pst-fee"
|
|
147
|
+
:description="$t('connect.label.pst')"
|
|
148
|
+
:fee="totalPst"
|
|
149
|
+
show-fee-value
|
|
150
|
+
/>
|
|
151
|
+
<ConnectFeeExtraFee
|
|
152
|
+
v-if="!!feeOptions.showGst || (!!feeOptions.showAllActiveFees && totalGst)"
|
|
153
|
+
data-testid="gst-fee"
|
|
154
|
+
:description="$t('connect.label.gst')"
|
|
155
|
+
:fee="totalGst"
|
|
156
|
+
show-fee-value
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
<div
|
|
160
|
+
data-testid="total-fee"
|
|
161
|
+
class="flex flex-row items-end justify-between border-t border-line-muted p-3"
|
|
162
|
+
>
|
|
163
|
+
<p class="mb-1 font-bold">
|
|
164
|
+
{{ $t("connect.label.totalFees") }}
|
|
165
|
+
</p>
|
|
166
|
+
<p class="flex items-end text-sm text-neutral">
|
|
167
|
+
<span class="mb-1">{{ $t("connect.label.cad") }}</span>
|
|
168
|
+
<b class="ml-[5px] flex items-end text-2xl text-black">
|
|
169
|
+
{{ !isPlaceholderActive ? `$${total.toFixed(2)}` : '$ -' }}
|
|
170
|
+
</b>
|
|
171
|
+
</p>
|
|
172
|
+
</div>
|
|
173
|
+
<USelect
|
|
174
|
+
v-if="allowAlternatePaymentMethod && allowedPaymentMethods.length > 1"
|
|
175
|
+
v-model="userSelectedPaymentMethod"
|
|
176
|
+
:items="allowedPaymentMethods"
|
|
177
|
+
variant="none"
|
|
178
|
+
:content="{ sideOffset: -1 }"
|
|
179
|
+
:ui="{
|
|
180
|
+
base: 'rounded-none px-4 py-2 w-full focus-visible:ring-2 focus-visible:ring-primary',
|
|
181
|
+
content: 'rounded-t-none',
|
|
182
|
+
trailingIcon: 'ml-auto',
|
|
183
|
+
itemTrailingIcon: 'hidden',
|
|
184
|
+
}"
|
|
185
|
+
>
|
|
186
|
+
<span class="text-xs text-left">
|
|
187
|
+
{{
|
|
188
|
+
$t(`connect.payMethod.text.${userSelectedPaymentMethod}`, {
|
|
189
|
+
account: userPaymentAccount?.cfsAccount?.bankAccountNumber },
|
|
190
|
+
)
|
|
191
|
+
}}
|
|
192
|
+
</span>
|
|
193
|
+
</USelect>
|
|
194
|
+
</div>
|
|
195
|
+
</ConnectTransitionCollapse>
|
|
196
|
+
</div>
|
|
197
|
+
</template>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ConnectTax {
|
|
2
|
+
gst: number
|
|
3
|
+
pst: number
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ConnectFeeItem {
|
|
7
|
+
filingFees: number
|
|
8
|
+
filingType: string
|
|
9
|
+
filingTypeCode: string
|
|
10
|
+
futureEffectiveFees: number
|
|
11
|
+
priorityFees: number
|
|
12
|
+
processingFees: number
|
|
13
|
+
serviceFees: number
|
|
14
|
+
tax: ConnectTax
|
|
15
|
+
total: number
|
|
16
|
+
label?: string
|
|
17
|
+
isPlaceholder?: boolean
|
|
18
|
+
quantity?: number
|
|
19
|
+
quantityDesc?: string
|
|
20
|
+
waived?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ConnectFeeOptions {
|
|
24
|
+
showAllActiveFees?: boolean
|
|
25
|
+
showFutureEffectiveFee?: boolean
|
|
26
|
+
showPriorityFee?: boolean
|
|
27
|
+
showProcessingFee?: boolean
|
|
28
|
+
showGst?: boolean
|
|
29
|
+
showPst?: boolean
|
|
30
|
+
showServiceFee?: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ConnectFees { // this could have multiple mapped feeItems
|
|
34
|
+
[key: string]: ConnectFeeItem
|
|
35
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface ConnectPayAccount {
|
|
2
|
+
accountId: string
|
|
3
|
+
accountName: string
|
|
4
|
+
billable: boolean
|
|
5
|
+
cfsAccount: {
|
|
6
|
+
bankAccountNumber: string
|
|
7
|
+
bankInstitutionNumber: string
|
|
8
|
+
bankTransitNumber: string
|
|
9
|
+
cfsAccountNumber: string
|
|
10
|
+
cfsPartyNumber: string
|
|
11
|
+
cfsSiteNumber: string
|
|
12
|
+
paymentMethod: ConnectPayMethod
|
|
13
|
+
status: ConnectPayCfsStatus
|
|
14
|
+
}
|
|
15
|
+
credit: number
|
|
16
|
+
id: number
|
|
17
|
+
padTosAcceptedBy: string
|
|
18
|
+
padTosAcceptedDate: string
|
|
19
|
+
paymentMethod: ConnectPayMethod
|
|
20
|
+
version: number
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ConnectLayout>
|
|
3
|
+
<template #header>
|
|
4
|
+
<ConnectHeaderAuth />
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<template #body>
|
|
8
|
+
<div class="flex flex-col lg:flex-row lg:gap-6 grow">
|
|
9
|
+
<div class="grow max-w-full overflow-hidden">
|
|
10
|
+
<slot />
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="shrink-0 lg:w-[300px] lg:static sticky lg:mt-10">
|
|
14
|
+
<ConnectFeeWidget class="sticky lg:top-10" />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
</ConnectLayout>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
2
|
+
const rtc = nuxtApp.$config.public
|
|
3
|
+
const payApiUrl = rtc.payApiUrl + rtc.payApiVersion
|
|
4
|
+
const appName = rtc.appName
|
|
5
|
+
const xApiKey = rtc.xApiKey
|
|
6
|
+
|
|
7
|
+
const api = $fetch.create({
|
|
8
|
+
baseURL: payApiUrl,
|
|
9
|
+
async onRequest({ options }) {
|
|
10
|
+
const auth = useConnectAuth()
|
|
11
|
+
const accountStore = useConnectAccountStore()
|
|
12
|
+
|
|
13
|
+
const token = await auth.getToken()
|
|
14
|
+
const accountId = accountStore.currentAccount.id
|
|
15
|
+
|
|
16
|
+
options.headers.set('Authorization', `Bearer ${token}`)
|
|
17
|
+
options.headers.set('App-Name', appName)
|
|
18
|
+
options.headers.set('X-Apikey', xApiKey)
|
|
19
|
+
options.headers.set('Account-Id', String(accountId))
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
provide: {
|
|
25
|
+
payApi: api
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
export const useConnectFeeStore = defineStore('connect-pay-fee-store', () => {
|
|
2
|
+
const { $payApi } = useNuxtApp()
|
|
3
|
+
const { t } = useI18n()
|
|
4
|
+
const { baseModal } = useConnectModal()
|
|
5
|
+
|
|
6
|
+
const defaultFeeOptions = {
|
|
7
|
+
showAllActiveFees: true,
|
|
8
|
+
showFutureEffectiveFee: false,
|
|
9
|
+
showPriorityFee: false,
|
|
10
|
+
showProcessingFee: false,
|
|
11
|
+
showGst: false,
|
|
12
|
+
showPst: false,
|
|
13
|
+
showServiceFee: false
|
|
14
|
+
}
|
|
15
|
+
const feeOptions = ref<ConnectFeeOptions>(defaultFeeOptions)
|
|
16
|
+
|
|
17
|
+
const fees = ref<ConnectFees>({})
|
|
18
|
+
const feesCached = ref<ConnectFees>({})
|
|
19
|
+
|
|
20
|
+
const defaultPlaceholder = {
|
|
21
|
+
isPlaceholder: true,
|
|
22
|
+
filingFees: 0,
|
|
23
|
+
filingType: 'placeholder',
|
|
24
|
+
filingTypeCode: 'PLACEHOLDER',
|
|
25
|
+
futureEffectiveFees: 0,
|
|
26
|
+
label: '',
|
|
27
|
+
priorityFees: 0,
|
|
28
|
+
processingFees: 0,
|
|
29
|
+
serviceFees: 0,
|
|
30
|
+
tax: {
|
|
31
|
+
gst: 0,
|
|
32
|
+
pst: 0
|
|
33
|
+
},
|
|
34
|
+
total: 0
|
|
35
|
+
}
|
|
36
|
+
const placeholderFeeItem = ref<ConnectFeeItem>(defaultPlaceholder)
|
|
37
|
+
|
|
38
|
+
const initFees = async (
|
|
39
|
+
feeCodes: { code: string, entityType: string, label: string, quantityDesc?: string }[],
|
|
40
|
+
placeholder: { label: string, matchServiceFeeToCode?: string },
|
|
41
|
+
options?: ConnectFeeOptions
|
|
42
|
+
) => {
|
|
43
|
+
// Get all the fee information for each fee code from the pay api
|
|
44
|
+
const feePromises = []
|
|
45
|
+
for (const feeInfo of feeCodes) {
|
|
46
|
+
feePromises.push(getFee(feeInfo.entityType, feeInfo.code))
|
|
47
|
+
}
|
|
48
|
+
const feesResolved = (await Promise.all(feePromises)).filter(fee => !!fee)
|
|
49
|
+
|
|
50
|
+
// Add all fee information for each code to the store
|
|
51
|
+
feesCached.value = feesResolved.reduce((reducedFees, fee) => {
|
|
52
|
+
return {
|
|
53
|
+
...reducedFees,
|
|
54
|
+
[fee.filingTypeCode]: {
|
|
55
|
+
...fee,
|
|
56
|
+
label: (feeCodes.find(feeInfo => feeInfo.code === fee.filingTypeCode))?.label,
|
|
57
|
+
quantityDesc: (feeCodes.find(feeInfo => feeInfo.code === fee.filingTypeCode))?.quantityDesc
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}, feesCached.value)
|
|
61
|
+
|
|
62
|
+
// set the placeholder values
|
|
63
|
+
placeholderFeeItem.value.label = placeholder.label
|
|
64
|
+
if (placeholder.matchServiceFeeToCode) {
|
|
65
|
+
placeholderFeeItem.value.serviceFees = feesCached.value[placeholder.matchServiceFeeToCode]?.serviceFees || 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// set fee options
|
|
69
|
+
if (options) {
|
|
70
|
+
feeOptions.value = {
|
|
71
|
+
...feeOptions.value,
|
|
72
|
+
...options
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const getTotalFromFees = (feeValue: string, isTax = false) => {
|
|
78
|
+
let total = 0
|
|
79
|
+
for (const key of Object.keys(fees.value)) {
|
|
80
|
+
if (fees.value[key]?.waived) {
|
|
81
|
+
// if waived then total value for is 0
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
const quantity = fees.value[key]?.quantity ?? 1
|
|
85
|
+
// @ts-expect-error - string cant index
|
|
86
|
+
if (isTax && fees.value[key].tax[feeValue]) {
|
|
87
|
+
// @ts-expect-error - string cant index
|
|
88
|
+
total += fees.value[key].tax[feeValue] * quantity
|
|
89
|
+
// @ts-expect-error - string cant index
|
|
90
|
+
} else if (fees.value[key][feeValue]) {
|
|
91
|
+
if (feeValue === 'total') {
|
|
92
|
+
// @ts-expect-error - string cant index
|
|
93
|
+
// ignore service fee and processing fee
|
|
94
|
+
// - (otherwise incorrectly adding it for each item instead of once at the end)
|
|
95
|
+
total += (fees.value[key].total - fees.value[key].serviceFees - fees.value[key].processingFees) * quantity
|
|
96
|
+
} else {
|
|
97
|
+
// @ts-expect-error - string cant index
|
|
98
|
+
total += fees.value[key][feeValue] * quantity
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return total
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const getMaxFromFees = (feeValue: string) => {
|
|
106
|
+
let maxFee = 0
|
|
107
|
+
for (const key of Object.keys(fees.value)) {
|
|
108
|
+
// @ts-expect-error - string cant index
|
|
109
|
+
const itemFee = fees.value[key][feeValue]
|
|
110
|
+
if (itemFee && (itemFee > maxFee)) {
|
|
111
|
+
maxFee = itemFee
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return maxFee
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const totalFutureEffectiveFees = computed(() => getTotalFromFees('futureEffectiveFees'))
|
|
118
|
+
const totalPriorityFees = computed(() => getTotalFromFees('priorityFees'))
|
|
119
|
+
const totalProcessingFees = computed(() => getMaxFromFees('processingFees'))
|
|
120
|
+
const totalServiceFees = computed(() => getMaxFromFees('serviceFees'))
|
|
121
|
+
const totalGst = computed(() => getTotalFromFees('gst', true))
|
|
122
|
+
const totalPst = computed(() => getTotalFromFees('pst', true))
|
|
123
|
+
const total = computed(() => getTotalFromFees('total') + totalServiceFees.value + totalProcessingFees.value)
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Fetches the Fee info for the given entity type / fee code combination.
|
|
127
|
+
*
|
|
128
|
+
* @returns {Promise<Fee | undefined>} Fee data or undefined if an error occurs.
|
|
129
|
+
*/
|
|
130
|
+
const getFee = async (
|
|
131
|
+
entityType: string,
|
|
132
|
+
code: string
|
|
133
|
+
): Promise<ConnectFeeItem | undefined> => {
|
|
134
|
+
try {
|
|
135
|
+
const params = {
|
|
136
|
+
priority: true,
|
|
137
|
+
futureEffective: true
|
|
138
|
+
}
|
|
139
|
+
return await $payApi<ConnectFeeItem>(`/fees/${entityType}/${code}`, { params })
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('Error fetching Fee: ', error)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const addReplaceFee = (
|
|
146
|
+
code: string,
|
|
147
|
+
options?: {
|
|
148
|
+
futureEffective?: boolean
|
|
149
|
+
priority?: boolean
|
|
150
|
+
quantity?: number
|
|
151
|
+
quantityDesc?: string
|
|
152
|
+
waived?: boolean
|
|
153
|
+
}
|
|
154
|
+
) => {
|
|
155
|
+
const fee = feesCached.value?.[code]
|
|
156
|
+
if (fee) {
|
|
157
|
+
const futureEffectiveFee = options?.futureEffective ? fee.futureEffectiveFees : 0
|
|
158
|
+
const priorityFee = options?.priority ? fee.priorityFees : 0
|
|
159
|
+
const total = fee.total - (fee.futureEffectiveFees - futureEffectiveFee) - (fee.priorityFees - priorityFee)
|
|
160
|
+
const extraFees = {
|
|
161
|
+
futureEffectiveFees: options?.futureEffective ? fee.futureEffectiveFees : 0,
|
|
162
|
+
priorityFees: options?.priority ? fee.priorityFees : 0,
|
|
163
|
+
total
|
|
164
|
+
}
|
|
165
|
+
fees.value[code] = {
|
|
166
|
+
...fee,
|
|
167
|
+
...extraFees,
|
|
168
|
+
...(options || {})
|
|
169
|
+
}
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
console.error(`Error adding ${code}. Please use initFees to initialize this fee code before adding it.`)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const removeFee = (code: string) => {
|
|
176
|
+
/* eslint-disable-next-line */
|
|
177
|
+
delete fees.value[code]
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// alternate payment option stuff
|
|
181
|
+
const PAD_PENDING_STATES = [ConnectPayCfsStatus.PENDING, ConnectPayCfsStatus.PENDING_PAD_ACTIVATION]
|
|
182
|
+
const userPaymentAccount = shallowRef<ConnectPayAccount>({} as ConnectPayAccount)
|
|
183
|
+
const userSelectedPaymentMethod = ref<ConnectPayMethod>(ConnectPayMethod.DIRECT_PAY)
|
|
184
|
+
const allowAlternatePaymentMethod = ref<boolean>(false)
|
|
185
|
+
const allowedPaymentMethods = ref<{ label: string, value: ConnectPayMethod }[]>([])
|
|
186
|
+
|
|
187
|
+
watch(userSelectedPaymentMethod, () => {
|
|
188
|
+
// if pad in confirmation period then set selected payment to CC
|
|
189
|
+
if (PAD_PENDING_STATES.includes(userPaymentAccount.value?.cfsAccount?.status)) {
|
|
190
|
+
userSelectedPaymentMethod.value = ConnectPayMethod.DIRECT_PAY
|
|
191
|
+
// show modal for user
|
|
192
|
+
baseModal.open({
|
|
193
|
+
title: t('connect.label.padAccountInConfirmationPeriod'),
|
|
194
|
+
description: t('connect.text.padAccountInConfirmationPeriod'),
|
|
195
|
+
dismissible: true,
|
|
196
|
+
buttons: [
|
|
197
|
+
{ label: t('connect.label.close'), variant: 'outline', shouldClose: true }
|
|
198
|
+
]
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const $resetAlternatePayOptions = () => {
|
|
204
|
+
userPaymentAccount.value = {} as ConnectPayAccount
|
|
205
|
+
userSelectedPaymentMethod.value = ConnectPayMethod.DIRECT_PAY
|
|
206
|
+
allowAlternatePaymentMethod.value = false
|
|
207
|
+
allowedPaymentMethods.value = []
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const initAlternatePaymentMethod = async () => {
|
|
211
|
+
$resetAlternatePayOptions()
|
|
212
|
+
const accountId = useConnectAccountStore().currentAccount.id
|
|
213
|
+
try {
|
|
214
|
+
// get payment account
|
|
215
|
+
const res = await $payApi<ConnectPayAccount>(`/accounts/${accountId}`)
|
|
216
|
+
userPaymentAccount.value = res
|
|
217
|
+
|
|
218
|
+
// add options to allowedPaymentMethods
|
|
219
|
+
let defaultMethod = userPaymentAccount.value.paymentMethod
|
|
220
|
+
if (defaultMethod !== undefined) {
|
|
221
|
+
const accountNum = userPaymentAccount.value.cfsAccount?.bankAccountNumber ?? ''
|
|
222
|
+
allowedPaymentMethods.value.push({
|
|
223
|
+
label: t(`connect.payMethod.label.${defaultMethod}`, { account: accountNum }),
|
|
224
|
+
value: defaultMethod
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
// only add direct pay if not default option
|
|
228
|
+
if (defaultMethod !== ConnectPayMethod.DIRECT_PAY) {
|
|
229
|
+
allowedPaymentMethods.value.push({
|
|
230
|
+
label: t(`connect.payMethod.label.${ConnectPayMethod.DIRECT_PAY}`),
|
|
231
|
+
value: ConnectPayMethod.DIRECT_PAY
|
|
232
|
+
})
|
|
233
|
+
// if pad in confirmation period then set default payment to CC
|
|
234
|
+
if (PAD_PENDING_STATES.includes(res.cfsAccount?.status)) {
|
|
235
|
+
defaultMethod = ConnectPayMethod.DIRECT_PAY
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
userSelectedPaymentMethod.value = defaultMethod
|
|
240
|
+
|
|
241
|
+
// only set allowed flag to true if previous steps didnt cause an error
|
|
242
|
+
allowAlternatePaymentMethod.value = true
|
|
243
|
+
} catch (e) {
|
|
244
|
+
logFetchError(e, 'Error initializing user payment account')
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const $reset = () => {
|
|
249
|
+
feeOptions.value = defaultFeeOptions
|
|
250
|
+
fees.value = {}
|
|
251
|
+
placeholderFeeItem.value = defaultPlaceholder
|
|
252
|
+
$resetAlternatePayOptions()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
feeOptions,
|
|
257
|
+
fees,
|
|
258
|
+
placeholderFeeItem,
|
|
259
|
+
totalFutureEffectiveFees,
|
|
260
|
+
totalPriorityFees,
|
|
261
|
+
totalProcessingFees,
|
|
262
|
+
totalGst,
|
|
263
|
+
totalPst,
|
|
264
|
+
totalServiceFees,
|
|
265
|
+
total,
|
|
266
|
+
initFees,
|
|
267
|
+
addReplaceFee,
|
|
268
|
+
removeFee,
|
|
269
|
+
initAlternatePaymentMethod,
|
|
270
|
+
userPaymentAccount,
|
|
271
|
+
userSelectedPaymentMethod,
|
|
272
|
+
allowedPaymentMethods,
|
|
273
|
+
allowAlternatePaymentMethod,
|
|
274
|
+
$reset
|
|
275
|
+
}
|
|
276
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
export default {
|
|
3
|
+
connect: {
|
|
4
|
+
label: {
|
|
5
|
+
cad: 'CAD',
|
|
6
|
+
close: 'Close',
|
|
7
|
+
feeSummary: 'Fee Summary',
|
|
8
|
+
futureEffectiveFee: 'Future Effective Fee',
|
|
9
|
+
gst: 'GST',
|
|
10
|
+
noFee: 'No Fee',
|
|
11
|
+
priorityFee: 'Priority Fee',
|
|
12
|
+
processingFee: 'Processing Fee',
|
|
13
|
+
pst: 'PST',
|
|
14
|
+
serviceFee: 'Service Fee',
|
|
15
|
+
totalFees: 'Total Fees',
|
|
16
|
+
padAccountInConfirmationPeriod: 'PAD Account in Confirmation Period'
|
|
17
|
+
},
|
|
18
|
+
payMethod: {
|
|
19
|
+
// These are used dynamically with the ConnectPayMethod enum
|
|
20
|
+
label: {
|
|
21
|
+
BCOL: 'BC Online Account',
|
|
22
|
+
DIRECT_PAY: 'Credit Card',
|
|
23
|
+
EFT: 'Electronic Funds Transfer',
|
|
24
|
+
EJV: 'Electronic Journal Voucher',
|
|
25
|
+
ONLINE_BANKING: 'Online Banking',
|
|
26
|
+
PAD: 'Pre-authorized Debit (PAD) {account}',
|
|
27
|
+
undefined: 'Default'
|
|
28
|
+
},
|
|
29
|
+
text: {
|
|
30
|
+
BCOL: 'Paying with BC Online Account',
|
|
31
|
+
DIRECT_PAY: 'Paying with Credit Card',
|
|
32
|
+
EFT: 'Paying with Electronic Funds Transfer',
|
|
33
|
+
EJV: 'Paying with Electronic Journal Voucher',
|
|
34
|
+
ONLINE_BANKING: 'Paying with Online Banking',
|
|
35
|
+
PAD: 'Paying with Pre-authorized Debit (PAD) {account}',
|
|
36
|
+
undefined: 'Paying with default method'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
text: {
|
|
40
|
+
padAccountInConfirmationPeriod: 'This account will not be able to perform any PAD transactions until the mandatory (3) day confirmation period has ended. Until then you may continue to pay using credit card.'
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver } from 'nuxt/kit'
|
|
2
|
+
|
|
3
|
+
export default defineNuxtModule({
|
|
4
|
+
meta: {
|
|
5
|
+
name: 'pay-assets',
|
|
6
|
+
configKey: 'payAssets'
|
|
7
|
+
},
|
|
8
|
+
defaults: {},
|
|
9
|
+
async setup(_options, _nuxt) {
|
|
10
|
+
console.info('Setting up **pay** assets module')
|
|
11
|
+
const resolver = createResolver(import.meta.url)
|
|
12
|
+
|
|
13
|
+
_nuxt.options.css.push(resolver.resolve('./runtime/assets/connect-pay-tw.css'))
|
|
14
|
+
}
|
|
15
|
+
})
|
package/nuxt.config.ts
CHANGED
|
@@ -12,9 +12,37 @@ export default defineNuxtConfig({
|
|
|
12
12
|
|
|
13
13
|
extends: ['@sbc-connect/nuxt-auth'],
|
|
14
14
|
|
|
15
|
+
imports: {
|
|
16
|
+
dirs: ['interfaces', 'enums', 'stores']
|
|
17
|
+
},
|
|
18
|
+
|
|
15
19
|
alias: {
|
|
16
20
|
'#pay': resolve('./')
|
|
17
21
|
},
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
i18n: {
|
|
24
|
+
locales: [
|
|
25
|
+
{
|
|
26
|
+
name: 'English',
|
|
27
|
+
code: 'en-CA',
|
|
28
|
+
language: 'en-CA',
|
|
29
|
+
dir: 'ltr',
|
|
30
|
+
file: 'en-CA.ts'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'Français',
|
|
34
|
+
code: 'fr-CA',
|
|
35
|
+
language: 'fr-CA',
|
|
36
|
+
dir: 'ltr',
|
|
37
|
+
file: 'fr-CA.ts'
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
runtimeConfig: {
|
|
43
|
+
public: {
|
|
44
|
+
payApiUrl: '',
|
|
45
|
+
payApiVersion: ''
|
|
46
|
+
}
|
|
47
|
+
}
|
|
20
48
|
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sbc-connect/nuxt-pay",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.8",
|
|
5
5
|
"repository": "github:bcgov/connect-nuxt",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
"nuxt": "^4.0.2",
|
|
13
13
|
"typescript": "^5.8.3",
|
|
14
14
|
"vue-tsc": "^3.0.4",
|
|
15
|
-
"@sbc-connect/
|
|
16
|
-
"@sbc-connect/
|
|
17
|
-
"@sbc-connect/vitest-config": "0.0.
|
|
15
|
+
"@sbc-connect/playwright-config": "0.0.6",
|
|
16
|
+
"@sbc-connect/eslint-config": "0.0.6",
|
|
17
|
+
"@sbc-connect/vitest-config": "0.0.6"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@sbc-connect/nuxt-auth": "0.1.
|
|
20
|
+
"@sbc-connect/nuxt-auth": "0.1.8"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"preinstall": "npx only-allow pnpm",
|
|
24
|
-
"dev": "nuxi dev .playground",
|
|
24
|
+
"dev": "nuxi dev .playground --dotenv ../.env",
|
|
25
25
|
"build": "nuxt build .playground",
|
|
26
26
|
"build:test": "pnpm generate && npx serve .playground/.output/public",
|
|
27
|
-
"generate": "nuxt generate .playground",
|
|
27
|
+
"generate": "nuxt generate .playground --dotenv ../.env",
|
|
28
28
|
"preview": "npx serve .playground/.output/public",
|
|
29
29
|
"lint": "eslint .",
|
|
30
30
|
"lint:fix": "eslint --fix .",
|