@viur/shop-components 0.11.0 → 0.13.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/package.json +1 -1
- package/src/Shop.vue +2 -1
- package/src/ShopOrderStepper.vue +1 -1
- package/src/components/DiscountInput.vue +10 -9
- package/src/components/PaymentProviderUnzer.vue +293 -232
- package/src/composables/address.js +25 -0
- package/src/composables/cart.js +9 -3
- package/src/shop.js +2 -1
- package/src/translations/de.js +3 -0
- package/src/translations/en.js +2 -0
- package/src/translations/fr.js +3 -0
package/package.json
CHANGED
package/src/Shop.vue
CHANGED
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
readytoship: {{ shopStore.state.order?.['is_rts'] }}<br>
|
|
53
53
|
paid: {{ shopStore.state.order?.['is_paid'] }}<br><br>
|
|
54
54
|
|
|
55
|
-
OrderObject: {{shopStore.state.order}}
|
|
55
|
+
OrderObject: {{shopStore.state.order}}<br><br>
|
|
56
|
+
activeDiscounts: {{ shopStore.state.discounts }}
|
|
56
57
|
|
|
57
58
|
</sl-details>
|
|
58
59
|
</template>
|
package/src/ShopOrderStepper.vue
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
{{ state.alert.msg }}
|
|
3
2
|
<shop-alert
|
|
4
3
|
v-if="state.alert.show"
|
|
5
4
|
:variant="state.alert.variant"
|
|
6
5
|
@onHide="state.alert = {}"
|
|
7
|
-
duration="
|
|
6
|
+
duration="5000"
|
|
8
7
|
>
|
|
9
8
|
<template #alertMsg>
|
|
10
9
|
{{ state.alert.msg }}
|
|
11
10
|
</template>
|
|
12
11
|
</shop-alert>
|
|
13
12
|
|
|
14
|
-
<template v-if="shopStore.state.
|
|
13
|
+
<template v-if="shopStore.state.discounts">
|
|
14
|
+
<template v-for="discount in shopStore.state.discounts">
|
|
15
15
|
<div class="viur-shop-discount-view">
|
|
16
|
-
<span>Code: {{
|
|
17
|
-
<sl-button size="small" outline variant="danger" @click="removeDiscountAction" :loading="state.loading">
|
|
16
|
+
<span>Code: {{ discount.dest.name }}</span>
|
|
17
|
+
<sl-button size="small" outline variant="danger" @click="removeDiscountAction(discount.dest.key)" :loading="state.loading">
|
|
18
18
|
<sl-icon name="x-lg" slot="prefix"></sl-icon>
|
|
19
19
|
|
|
20
20
|
</sl-button>
|
|
21
21
|
</div>
|
|
22
|
+
</template>
|
|
22
23
|
</template>
|
|
23
|
-
<sl-button-group
|
|
24
|
+
<sl-button-group>
|
|
24
25
|
<sl-input
|
|
25
26
|
class="viur-shop-discount-input"
|
|
26
27
|
:placeholder="$t('viur.shop.add_discount_placeholder')"
|
|
@@ -63,12 +64,12 @@ function addDiscountAction() {
|
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
function removeDiscountAction(){
|
|
67
|
+
function removeDiscountAction(key){
|
|
67
68
|
state.loading=true
|
|
68
|
-
removeDiscount()
|
|
69
|
+
removeDiscount(key)
|
|
69
70
|
.then(async (resp) => {
|
|
70
71
|
state.loading=false
|
|
71
|
-
state.alert.msg =
|
|
72
|
+
state.alert.msg = $t('viur.shop.discount_removed');
|
|
72
73
|
state.alert.show = true;
|
|
73
74
|
state.alert.variant = "success";
|
|
74
75
|
})
|
|
@@ -1,213 +1,264 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
2
|
+
<div class="loading-wrapper" v-if="state.loading">
|
|
3
|
+
<sl-spinner class="loading"></sl-spinner>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="loading-wrapper" v-if="PaymentCheckIsActive">
|
|
7
|
+
<sl-spinner class="loading"></sl-spinner>
|
|
8
|
+
{{ $t('messages.wait_for_payment') }}
|
|
9
|
+
</div>
|
|
10
|
+
<div class="form-wrapper">
|
|
11
|
+
<sl-alert :open="state.hasError" variant="danger">{{ state.errorMessage }}</sl-alert>
|
|
12
|
+
<form class="unzerUI form" novalidate>
|
|
13
|
+
<template v-if="shopStore.state.order?.['payment_provider'] === 'unzer-card'">
|
|
14
|
+
<div class="field">
|
|
15
|
+
<div id="card-element-id-number" class="unzerInput">
|
|
16
|
+
<!-- Card number UI Element is inserted here. -->
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="two fields">
|
|
20
|
+
<div class="field ten wide">
|
|
21
|
+
<div id="card-element-id-expiry" class="unzerInput">
|
|
22
|
+
<!-- Card expiry date UI Element is inserted here. -->
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="field six wide">
|
|
26
|
+
<div id="card-element-id-cvc" class="unzerInput">
|
|
27
|
+
<!-- Card CVC UI Element is inserted here. -->
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<template v-else-if="shopStore.state.order?.['payment_provider'] === 'unzer-paypal'">
|
|
34
|
+
<sl-alert open variant="danger">{{ $t('viur.shop.paypal_client_popup_info') }}</sl-alert>
|
|
35
|
+
<div id="paypal-element" class="field"></div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<template v-else-if="shopStore.state.order?.['payment_provider'] === 'unzer-ideal'">
|
|
39
|
+
<div id="ideal-element" class="field"></div>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<template v-else-if="shopStore.state.order?.['payment_provider'] === 'unzer-googlepay'">
|
|
43
|
+
<div id="googlepay-element" class="field"></div>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<template v-else-if="shopStore.state.order?.['payment_provider'] === 'unzer-paylater_invoice'">
|
|
47
|
+
<p v-html="$t('viur.shop.missing_birthdate', shopStore.state.order.billing_address.dest)"/>
|
|
48
|
+
<sl-input
|
|
49
|
+
slot="left"
|
|
50
|
+
type="date"
|
|
51
|
+
min="1900-01-01"
|
|
52
|
+
:max="new Date().toISOString().slice(0, 10)"
|
|
53
|
+
:placeholder="$t('viur.shop.birthdate')"
|
|
54
|
+
:label="$t('viur.shop.birthdate')"
|
|
55
|
+
:value="state.birthdate"
|
|
56
|
+
@sl-change="birthdateChange"
|
|
57
|
+
:disabled="state.loading"
|
|
58
|
+
></sl-input>
|
|
59
|
+
<div id="paylater-invoice-element" class="field"></div>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<p
|
|
63
|
+
v-if="!!shopStore.state?.paymentProviderData?.redirectUrl"
|
|
64
|
+
v-html="$t('viur.shop.payment_link', {url: shopStore.state.paymentProviderData.redirectUrl})"
|
|
65
|
+
/>
|
|
66
|
+
</form>
|
|
67
|
+
|
|
68
|
+
<button
|
|
69
|
+
v-if="shopStore.state.order?.['payment_provider'] !== 'unzer-googlepay'"
|
|
70
|
+
:disabled="state.loading || state.birthdateIsInvalid"
|
|
71
|
+
class="unzerUI primary button fluid"
|
|
72
|
+
@click="submitFormToUnzer"
|
|
73
|
+
>{{ $t('viur.shop.pay') }}
|
|
74
|
+
</button>
|
|
75
|
+
<sl-button :disabled="state.loading" variant="danger" @click="cancelPayment">
|
|
76
|
+
{{ $t('actions.cancel') }}
|
|
77
|
+
</sl-button>
|
|
78
|
+
</div>
|
|
62
79
|
</template>
|
|
63
80
|
|
|
64
81
|
<script setup>
|
|
65
82
|
import {Request} from '@viur/vue-utils';
|
|
83
|
+
import {HTTPError} from '@viur/vue-utils/utils/request.js';
|
|
66
84
|
import {useIntervalFn} from '@vueuse/core';
|
|
67
85
|
import {computed, onBeforeMount, reactive} from 'vue';
|
|
86
|
+
import {useAddress} from '../composables/address.js';
|
|
68
87
|
import {useOrder} from '../composables/order';
|
|
69
88
|
import {useViurShopStore} from '../shop';
|
|
70
89
|
|
|
71
90
|
const shopStore = useViurShopStore();
|
|
72
91
|
const {fetchOrder} = useOrder();
|
|
92
|
+
const {saveBirthdate} = useAddress();
|
|
73
93
|
|
|
74
94
|
const emits = defineEmits(['cancel']);
|
|
75
95
|
|
|
76
96
|
const {pause: PaymentCheckPause, resume: PaymentCheckResume, isActive: PaymentCheckIsActive} = useIntervalFn(() => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
console.debug('checking ...');
|
|
98
|
+
|
|
99
|
+
fetchOrder(shopStore.state.orderKey).then(() => {
|
|
100
|
+
if (shopStore.state.order?.['is_paid']) {
|
|
101
|
+
console.debug('Order is paid');
|
|
102
|
+
shopStore.navigateToTab('complete');
|
|
103
|
+
PaymentCheckPause();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
86
106
|
|
|
87
107
|
}, 2000, {immediate: false});
|
|
88
108
|
|
|
89
109
|
|
|
90
110
|
const state = reactive({
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
111
|
+
unzer: computed(() => {
|
|
112
|
+
if (!shopStore.state.paymentProviderData) return null;
|
|
113
|
+
return new unzer(shopStore.state.paymentProviderData['public_key'], {locale: shopStore.state.locale});
|
|
114
|
+
}),
|
|
115
|
+
paymentHandler: {},
|
|
116
|
+
loading: false,
|
|
117
|
+
hasError: false,
|
|
118
|
+
errorMessage: null,
|
|
119
|
+
waitPayment: false,
|
|
120
|
+
birthdate: null,
|
|
121
|
+
birthdateIsInvalid: false,
|
|
100
122
|
});
|
|
101
123
|
|
|
102
124
|
/**
|
|
103
125
|
* Initialize the payment type creation with Unzer-UI components
|
|
104
126
|
*/
|
|
105
127
|
function initUnzerForm() {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
128
|
+
//Unzer field definition
|
|
129
|
+
if (shopStore.state.order?.['payment_provider'] === 'unzer-card') {
|
|
130
|
+
const card = state.unzer.Card();
|
|
131
|
+
// Rendering input field card number
|
|
132
|
+
card.create('number', {
|
|
133
|
+
containerId: 'card-element-id-number',
|
|
134
|
+
onlyIframe: false,
|
|
135
|
+
});
|
|
136
|
+
// Rendering input field card expiry
|
|
137
|
+
card.create('expiry', {
|
|
138
|
+
containerId: 'card-element-id-expiry',
|
|
139
|
+
onlyIframe: false,
|
|
140
|
+
});
|
|
141
|
+
// Rendering input field card cvc
|
|
142
|
+
card.create('cvc', {
|
|
143
|
+
containerId: 'card-element-id-cvc',
|
|
144
|
+
onlyIframe: false,
|
|
145
|
+
});
|
|
146
|
+
state.paymentHandler['unzer-card'] = card;
|
|
147
|
+
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-paypal') {
|
|
148
|
+
// Creating a PayPal instance
|
|
149
|
+
const paypal = state.unzer.Paypal();
|
|
150
|
+
// Rendering input field
|
|
151
|
+
//paypal.create('email', {
|
|
152
|
+
// containerId: 'paypal-element',
|
|
153
|
+
//});
|
|
154
|
+
state.paymentHandler['unzer-paypal'] = paypal;
|
|
155
|
+
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-sofort') {
|
|
156
|
+
const sofort = state.unzer.Sofort();
|
|
157
|
+
state.paymentHandler['unzer-sofort'] = sofort;
|
|
158
|
+
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-ideal') {
|
|
159
|
+
const ideal = state.unzer.Ideal();
|
|
160
|
+
ideal.create('ideal', {
|
|
161
|
+
containerId: 'ideal-element',
|
|
162
|
+
});
|
|
163
|
+
state.paymentHandler['unzer-ideal'] = ideal;
|
|
164
|
+
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-bancontact') {
|
|
165
|
+
const bancontact = state.unzer.Bancontact();
|
|
166
|
+
state.paymentHandler['unzer-bancontact'] = bancontact;
|
|
167
|
+
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-googlepay') {
|
|
168
|
+
const googlepayScriptPromise = new Promise((resolve, reject) => {
|
|
169
|
+
const googlepayScript = document.createElement('script');
|
|
170
|
+
googlepayScript.setAttribute('src', 'https://pay.google.com/gp/p/js/pay.js');
|
|
171
|
+
googlepayScript.onload = () => {
|
|
172
|
+
resolve(googlepayScript);
|
|
173
|
+
};
|
|
174
|
+
googlepayScript.onerror = reject;
|
|
175
|
+
document.head.appendChild(googlepayScript);
|
|
176
|
+
});
|
|
177
|
+
const googlepay = state.unzer.Googlepay();
|
|
178
|
+
state.paymentHandler['unzer-googlepay'] = googlepay;
|
|
179
|
+
const paymentDataRequestObject = googlepay.initPaymentDataRequestObject({
|
|
180
|
+
gatewayMerchantId: shopStore.state.paymentProviderData.channel,
|
|
181
|
+
merchantInfo: {
|
|
182
|
+
merchantId: shopStore.state.paymentProviderData.merchant_id,
|
|
183
|
+
merchantName: shopStore.state.paymentProviderData.merchant_name,
|
|
184
|
+
},
|
|
185
|
+
transactionInfo: {
|
|
186
|
+
countryCode: shopStore.state.order.billing_address.dest['country'].toUpperCase(),
|
|
187
|
+
currencyCode: 'EUR',
|
|
188
|
+
totalPrice: shopStore.state.order.total.toString(),
|
|
189
|
+
totalPriceStatus: 'ESTIMATED', // backend should have the last words
|
|
190
|
+
// totalPriceStatus: 'FINAL',
|
|
191
|
+
// checkoutOption: 'COMPLETE_IMMEDIATE_PURCHASE',
|
|
192
|
+
},
|
|
193
|
+
// buttonColor: 'white',
|
|
194
|
+
allowedCardNetworks: shopStore.state.paymentProviderData.brands,
|
|
195
|
+
allowCreditCards: shopStore.state.paymentProviderData.allow_credit_cards,
|
|
196
|
+
allowPrepaidCards: shopStore.state.paymentProviderData.allow_prepaid_cards,
|
|
197
|
+
|
|
198
|
+
onPaymentAuthorizedCallback: (paymentData) => {
|
|
199
|
+
PaymentCheckPause();
|
|
200
|
+
state.loading = true;
|
|
201
|
+
state.hasError = false;
|
|
202
|
+
|
|
203
|
+
return googlepay.createResource(paymentData)
|
|
204
|
+
.then(result => {
|
|
205
|
+
console.debug(result);
|
|
206
|
+
saveType(result.id);
|
|
207
|
+
return {status: 'success'}; // Tell Google Pay we could handle it
|
|
208
|
+
})
|
|
209
|
+
.catch(function (error) {
|
|
210
|
+
paymentError(error);
|
|
211
|
+
return { // Tell Google Pay we could NOT handle it
|
|
212
|
+
status: 'error',
|
|
213
|
+
message: error.customerMessage || error.message || error || 'Unexpected error',
|
|
151
214
|
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
onPaymentAuthorizedCallback: (paymentData) => {
|
|
177
|
-
PaymentCheckPause();
|
|
178
|
-
state.loading = true;
|
|
179
|
-
state.hasError = false;
|
|
180
|
-
|
|
181
|
-
return googlepay.createResource(paymentData)
|
|
182
|
-
.then(result => {
|
|
183
|
-
console.debug(result);
|
|
184
|
-
saveType(result.id);
|
|
185
|
-
return {status: 'success'}; // Tell Google Pay we could handle it
|
|
186
|
-
})
|
|
187
|
-
.catch(function (error) {
|
|
188
|
-
paymentError(error);
|
|
189
|
-
return { // Tell Google Pay we could NOT handle it
|
|
190
|
-
status: 'error',
|
|
191
|
-
message: error.customerMessage || error.message || error || 'Unexpected error',
|
|
192
|
-
};
|
|
193
|
-
});
|
|
194
|
-
},
|
|
195
|
-
});
|
|
196
|
-
console.debug('unzer-googlepay', googlepay, paymentDataRequestObject);
|
|
197
|
-
// TODO: How can we catch >Uncaught (in promise) AbortError: User closed the Payment Request UI.< ?
|
|
198
|
-
googlepayScriptPromise.then(() => {
|
|
199
|
-
// gpay script has been loaded, we can start the payment
|
|
200
|
-
googlepay.create(
|
|
201
|
-
{containerId: 'googlepay-element'},
|
|
202
|
-
paymentDataRequestObject,
|
|
203
|
-
);
|
|
204
|
-
});
|
|
205
|
-
} else {
|
|
206
|
-
console.warn(`Unknown payment provider: ${shopStore.state.order?.['payment_provider']}`);
|
|
207
|
-
}
|
|
208
|
-
state.loading = false;
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
console.debug('unzer-googlepay', googlepay, paymentDataRequestObject);
|
|
219
|
+
// TODO: How can we catch >Uncaught (in promise) AbortError: User closed the Payment Request UI.< ?
|
|
220
|
+
googlepayScriptPromise.then(() => {
|
|
221
|
+
// gpay script has been loaded, we can start the payment
|
|
222
|
+
googlepay.create(
|
|
223
|
+
{containerId: 'googlepay-element'},
|
|
224
|
+
paymentDataRequestObject,
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-paylater_invoice') {
|
|
228
|
+
state.birthdateIsInvalid = true; // no value --> invalid
|
|
229
|
+
const paylaterInvoice = state.unzer.PaylaterInvoice();
|
|
230
|
+
paylaterInvoice.create({
|
|
231
|
+
containerId: 'paylater-invoice-element',
|
|
232
|
+
customerType: 'B2C', // or B2B
|
|
233
|
+
});
|
|
234
|
+
state.paymentHandler['unzer-paylater_invoice'] = paylaterInvoice;
|
|
235
|
+
} else {
|
|
236
|
+
console.warn(`Unknown payment provider: ${shopStore.state.order?.['payment_provider']}`);
|
|
237
|
+
}
|
|
238
|
+
state.loading = false;
|
|
209
239
|
}
|
|
210
240
|
|
|
241
|
+
function birthdateChange(event) {
|
|
242
|
+
console.debug('birthdateChange', arguments);
|
|
243
|
+
if (!event.target.value || !event.target.checkValidity()) {
|
|
244
|
+
// state.birthdate = null;
|
|
245
|
+
state.birthdateIsInvalid = true;
|
|
246
|
+
} else {
|
|
247
|
+
state.birthdate = event.target.value;
|
|
248
|
+
state.birthdateIsInvalid = false;
|
|
249
|
+
state.loading = true;
|
|
250
|
+
saveBirthdate(shopStore.state.order.billing_address.dest['key'], event.target.value)
|
|
251
|
+
.then(result => {
|
|
252
|
+
console.debug(result);
|
|
253
|
+
})
|
|
254
|
+
.catch(paymentError)
|
|
255
|
+
.finally(() => {
|
|
256
|
+
state.loading = false;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
211
262
|
/**
|
|
212
263
|
* Handle an error
|
|
213
264
|
*
|
|
@@ -215,10 +266,17 @@ function initUnzerForm() {
|
|
|
215
266
|
* with a translated (and detailed) description for the customer
|
|
216
267
|
*/
|
|
217
268
|
function paymentError(error) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
269
|
+
console.error(error);
|
|
270
|
+
state.loading = false;
|
|
271
|
+
state.hasError = true;
|
|
272
|
+
if (error && error.constructor.name === HTTPError.name && error.response.headers.get('x-viur-shop-error')) {
|
|
273
|
+
error.response.json().then(res => {
|
|
274
|
+
console.error(res.errors);
|
|
275
|
+
state.errorMessage = res.errors.map(err => err.customer_message || err.message).join(', ');
|
|
276
|
+
});
|
|
277
|
+
} else {
|
|
221
278
|
state.errorMessage = error.customerMessage || error.message || error || 'Error';
|
|
279
|
+
}
|
|
222
280
|
}
|
|
223
281
|
|
|
224
282
|
/**
|
|
@@ -227,13 +285,15 @@ function paymentError(error) {
|
|
|
227
285
|
* This function is used by all unzer pyment types, except Google Pay.
|
|
228
286
|
*/
|
|
229
287
|
function submitFormToUnzer() {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
288
|
+
PaymentCheckPause();
|
|
289
|
+
state.loading = true;
|
|
290
|
+
state.hasError = false;
|
|
291
|
+
state.paymentHandler[shopStore.state.order?.['payment_provider']].createResource()
|
|
292
|
+
.then((result) => {
|
|
293
|
+
saveType(result.id);
|
|
294
|
+
})
|
|
295
|
+
.catch((error) => {
|
|
296
|
+
paymentError(error);
|
|
237
297
|
});
|
|
238
298
|
}
|
|
239
299
|
|
|
@@ -245,73 +305,74 @@ function submitFormToUnzer() {
|
|
|
245
305
|
* @param typeId The type-id of the created Type, e.g. ``s-crd-abc123def456``
|
|
246
306
|
*/
|
|
247
307
|
function saveType(typeId) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
}).catch(error => {
|
|
269
|
-
paymentError(error);
|
|
308
|
+
const paymenttarget = shopStore.state.order?.['payment_provider'].split('-').slice(1).join('-');
|
|
309
|
+
Request.post(`${shopStore.state.shopUrl}/pp_unzer_${paymenttarget}/save_type`, {
|
|
310
|
+
dataObj: {
|
|
311
|
+
order_key: shopStore.state.orderKey,
|
|
312
|
+
type_id: typeId,
|
|
313
|
+
},
|
|
314
|
+
}).then(async (resp) => {
|
|
315
|
+
shopStore.state.order = await resp.json();
|
|
316
|
+
shopStore.checkoutOrder().then((resp) => {
|
|
317
|
+
state.loading = false;
|
|
318
|
+
state.hasError = false;
|
|
319
|
+
if (shopStore.state.paymentProviderData?.redirectUrl) {
|
|
320
|
+
state.waitPayment = true;
|
|
321
|
+
window.open(shopStore.state.paymentProviderData.redirectUrl, '_blank', 'popup');
|
|
322
|
+
PaymentCheckResume();
|
|
323
|
+
}
|
|
324
|
+
}).catch(async (error) => {
|
|
325
|
+
paymentError(error);
|
|
270
326
|
});
|
|
327
|
+
|
|
328
|
+
}).catch(error => {
|
|
329
|
+
paymentError(error);
|
|
330
|
+
});
|
|
271
331
|
}
|
|
272
332
|
|
|
273
333
|
|
|
274
334
|
function cancelPayment() {
|
|
275
|
-
|
|
276
|
-
|
|
335
|
+
PaymentCheckPause();
|
|
336
|
+
emits('cancel');
|
|
277
337
|
}
|
|
278
338
|
|
|
279
339
|
onBeforeMount(() => {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
340
|
+
state.loading = true;
|
|
341
|
+
if (!shopStore.state.paymentProviderData) {
|
|
342
|
+
shopStore.checkout().then(() => {
|
|
343
|
+
initUnzerForm();
|
|
344
|
+
fetchOrder(shopStore.state.orderKey); // refresh order after checkout_start freeze
|
|
345
|
+
}).catch((error) => {
|
|
346
|
+
console.log(error);
|
|
347
|
+
});
|
|
348
|
+
} else {
|
|
349
|
+
initUnzerForm();
|
|
350
|
+
}
|
|
290
351
|
});
|
|
291
352
|
|
|
292
353
|
</script>
|
|
293
354
|
|
|
294
355
|
<style scoped>
|
|
295
356
|
.loading-wrapper {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
357
|
+
display: flex;
|
|
358
|
+
flex-direction: column;
|
|
359
|
+
align-items: center;
|
|
299
360
|
}
|
|
300
361
|
|
|
301
362
|
.form-wrapper {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
363
|
+
display: flex;
|
|
364
|
+
flex-direction: column;
|
|
365
|
+
gap: 20px;
|
|
305
366
|
}
|
|
306
367
|
|
|
307
368
|
.loading {
|
|
308
|
-
|
|
369
|
+
font-size: 3rem;
|
|
309
370
|
}
|
|
310
371
|
</style>
|
|
311
372
|
|
|
312
373
|
<style>
|
|
313
374
|
/* global style to overwrite UnzerCSS */
|
|
314
375
|
.unzerUI.primary.button, .unzerUI.primary.buttons .button {
|
|
315
|
-
|
|
376
|
+
background-color: var(--ignt-color-primary) !important;
|
|
316
377
|
}
|
|
317
378
|
</style>
|
|
@@ -4,6 +4,7 @@ import {useCart} from '../composables/cart';
|
|
|
4
4
|
import {useOrder} from '../composables/order';
|
|
5
5
|
import {useViurShopStore} from '../shop';
|
|
6
6
|
import {useShipping} from './shipping.js';
|
|
7
|
+
import {Request} from '@viur/vue-utils';
|
|
7
8
|
|
|
8
9
|
export const useAddress = defineStore('useAddressStore', () => {
|
|
9
10
|
const shopStore = useViurShopStore();
|
|
@@ -110,10 +111,34 @@ export const useAddress = defineStore('useAddressStore', () => {
|
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
function saveBirthdate(addressKey, birthdate) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
Request.securePost(
|
|
117
|
+
`/json/${shopStore.state.moduleName}/address/edit/${addressKey}`, {
|
|
118
|
+
dataObj: {
|
|
119
|
+
'@order': shopStore.state.orderKey,
|
|
120
|
+
birthdate,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
.then(async (res) => {
|
|
125
|
+
const data = await res.json();
|
|
126
|
+
if (data['action'] === 'editSuccess') {
|
|
127
|
+
resolve(data);
|
|
128
|
+
} else {
|
|
129
|
+
reject(`Update failed`);
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.catch(reject);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
113
137
|
return {
|
|
114
138
|
state,
|
|
115
139
|
saveForm,
|
|
116
140
|
updateAddresses,
|
|
117
141
|
saveAddresses,
|
|
142
|
+
saveBirthdate,
|
|
118
143
|
};
|
|
119
144
|
});
|
package/src/composables/cart.js
CHANGED
|
@@ -48,6 +48,7 @@ export function useCart() {
|
|
|
48
48
|
shopStore.state.cartReady = true;
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
+
shopStore.state.discounts = {}
|
|
51
52
|
return fetchCartRoot().then(() => {
|
|
52
53
|
if (!shopStore.state.cartRoot?.["key"]) return 0;
|
|
53
54
|
fetchCartItems(shopStore.state.cartRoot["key"]).then(() => { // TODO: duplicate code
|
|
@@ -59,10 +60,12 @@ export function useCart() {
|
|
|
59
60
|
|
|
60
61
|
function fetchCartRoot(){
|
|
61
62
|
// fetch list of Rootnodes and saves the first one
|
|
62
|
-
|
|
63
63
|
return Request.get(`${shopStore.state.shopUrl}/cart/listRootNodes`).then(async (resp)=>{
|
|
64
64
|
let data = await resp.clone().json()
|
|
65
65
|
shopStore.state.cartRoot = data.filter(i=>i['cart_type']==='basket')?.[0] ? data.filter(i=>i['cart_type']==='basket')[0]:[]
|
|
66
|
+
if (shopStore.state.cartRoot.discount){
|
|
67
|
+
shopStore.state.discounts = {[shopStore.state.cartRoot.discount.dest.key]:shopStore.state.cartRoot.discount}
|
|
68
|
+
}
|
|
66
69
|
return resp
|
|
67
70
|
})
|
|
68
71
|
}
|
|
@@ -82,6 +85,9 @@ export function useCart() {
|
|
|
82
85
|
if (item["skel_type"]==="leaf"){
|
|
83
86
|
currentLeafs.push(item)
|
|
84
87
|
}else{
|
|
88
|
+
if(item.discount){
|
|
89
|
+
shopStore.state.discounts[item.discount.dest.key] = item.discount
|
|
90
|
+
}
|
|
85
91
|
await fetchCartItems(item['key'])
|
|
86
92
|
}
|
|
87
93
|
}
|
|
@@ -168,11 +174,11 @@ export function useCart() {
|
|
|
168
174
|
});
|
|
169
175
|
}
|
|
170
176
|
|
|
171
|
-
function removeDiscount() {
|
|
177
|
+
function removeDiscount(key) {
|
|
172
178
|
return new Promise((resolve, reject) => {
|
|
173
179
|
Request.securePost(`${shopStore.state.shopApiUrl}/discount_remove`, {
|
|
174
180
|
dataObj: {
|
|
175
|
-
discount_key:
|
|
181
|
+
discount_key: key,
|
|
176
182
|
},
|
|
177
183
|
})
|
|
178
184
|
.then(async (resp) => {
|
package/src/shop.js
CHANGED
|
@@ -122,7 +122,8 @@ export const useViurShopStore = defineStore("viurshopStore", () => {
|
|
|
122
122
|
|
|
123
123
|
//checkout
|
|
124
124
|
paymentProviderData:null,
|
|
125
|
-
UserDataLayout: shallowRef(AddressFormLayout)
|
|
125
|
+
UserDataLayout: shallowRef(AddressFormLayout),
|
|
126
|
+
discounts:{}
|
|
126
127
|
})
|
|
127
128
|
|
|
128
129
|
function addTab({name, component, displayname, iconname, iconlibrary,active})
|
package/src/translations/de.js
CHANGED
|
@@ -43,6 +43,7 @@ export default {
|
|
|
43
43
|
'summary_total': 'Gesamtbetrag',
|
|
44
44
|
'summary_vat': 'inkl. {percentage} MwSt.',
|
|
45
45
|
'discount_not_found': 'Dieser Rabattcode existiert nicht.',
|
|
46
|
+
'discount_not_found': 'Rabattcode entfernt.',
|
|
46
47
|
'discount_not_active': 'Dieser Rabattcode ist nicht aktiv.',
|
|
47
48
|
'discount_not_available': 'Dieser Rabattcode ist für dieses Land nicht verfügbar.',
|
|
48
49
|
'discount_already_used': 'Dieser Rabattcode wurde bereits benutzt.',
|
|
@@ -58,6 +59,8 @@ export default {
|
|
|
58
59
|
'order_step_complete': 'Bestellung Abgeschlossen',
|
|
59
60
|
'paypal_client_popup_info': 'Bitte prüfen Sie, ob Ihr Browser das PayPal-Popup zulässt.',
|
|
60
61
|
'payment_link': 'Ihr Browser öffnet kein Popup? Dann klicken Sie bitte <a href="{url}" target="_blank">hier</a>.',
|
|
62
|
+
'birthdate': 'Geburtsdatum',
|
|
63
|
+
'missing_birthdate': 'Bei der ausgewählten Bezahlmethode benötigen wir zur Rechnungsadresse noch das Geburtsdatum von <i>{firstname} {lastname}</i>.',
|
|
61
64
|
},
|
|
62
65
|
},
|
|
63
66
|
messages: {
|
package/src/translations/en.js
CHANGED
|
@@ -58,6 +58,8 @@ export default {
|
|
|
58
58
|
'order_step_complete': 'Order complete',
|
|
59
59
|
'paypal_client_popup_info': 'Please make sure your browser allows the PayPal popup.',
|
|
60
60
|
'payment_link': 'Your browser does not open a popup? Then please click <a href="{url}" target="_blank">here</a>.',
|
|
61
|
+
'birthdate': 'date of birth',
|
|
62
|
+
'missing_birthdate': 'For the selected payment method, we require the date of birth of <i>{firstname} {lastname}</i> in addition to the billing address.',
|
|
61
63
|
},
|
|
62
64
|
},
|
|
63
65
|
messages: {
|
package/src/translations/fr.js
CHANGED
|
@@ -43,6 +43,7 @@ export default {
|
|
|
43
43
|
'summary_total': 'Montant TTC',
|
|
44
44
|
'summary_vat': 'TVA incluse ({percentage})',
|
|
45
45
|
'discount_not_found': 'Ce code promo n’existe pas.',
|
|
46
|
+
'discount_removed': 'Code promo supprimé.',
|
|
46
47
|
'discount_not_active': 'Ce code promo n’est pas actif.',
|
|
47
48
|
'discount_not_available': 'Ce code promo n’est pas valable pour ce pays.',
|
|
48
49
|
'discount_already_used': 'Ce code promo a déjà été utilisé.',
|
|
@@ -58,6 +59,8 @@ export default {
|
|
|
58
59
|
'order_step_complete': 'Valider la commande',
|
|
59
60
|
'paypal_client_popup_info':"Assurez-vous que votre navigateur autorise la fenêtre contextuelle PayPal.",
|
|
60
61
|
'payment_link': 'Votre navigateur n\'ouvre pas de popup ? Alors cliquez <a href="{url}" target="_blank">ici</a>.',
|
|
62
|
+
'birthdate': 'date de naissance',
|
|
63
|
+
'missing_birthdate': 'Pour le mode de paiement sélectionné, nous avons besoin, en plus de l\'adresse de facturation, de la date de naissance de <i>{firstname} {lastname}</i>.',
|
|
61
64
|
},
|
|
62
65
|
},
|
|
63
66
|
messages: {
|