@viur/shop-components 0.9.4 → 0.10.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/components/PaymentProviderUnzer.vue +242 -147
- package/src/shop.js +1 -0
package/package.json
CHANGED
|
@@ -1,93 +1,108 @@
|
|
|
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
|
-
|
|
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
|
+
<p
|
|
46
|
+
v-if="!!shopStore.state?.paymentProviderData?.redirectUrl"
|
|
47
|
+
v-html="$t('viur.shop.payment_link', {url: shopStore.state.paymentProviderData.redirectUrl})"
|
|
48
|
+
/>
|
|
49
|
+
</form>
|
|
50
|
+
|
|
51
|
+
<button
|
|
52
|
+
v-if="shopStore.state.order?.['payment_provider'] !== 'unzer-googlepay'"
|
|
53
|
+
:disabled="state.loading"
|
|
54
|
+
class="unzerUI primary button fluid"
|
|
55
|
+
@click="submitFormToUnzer"
|
|
56
|
+
>{{ $t('viur.shop.pay') }}
|
|
57
|
+
</button>
|
|
58
|
+
<sl-button :disabled="state.loading" variant="danger" @click="cancelPayment">
|
|
59
|
+
{{ $t('actions.cancel') }}
|
|
60
|
+
</sl-button>
|
|
61
|
+
</div>
|
|
50
62
|
</template>
|
|
51
63
|
|
|
52
64
|
<script setup>
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
57
|
-
import {
|
|
58
|
-
const shopStore = useViurShopStore()
|
|
59
|
-
const {fetchOrder} = useOrder()
|
|
60
|
-
|
|
61
|
-
const emits = defineEmits(['cancel'])
|
|
62
|
-
|
|
63
|
-
const { pause:PaymentCheckPause, resume:PaymentCheckResume, isActive:PaymentCheckIsActive } = useIntervalFn(() => {
|
|
64
|
-
console.debug('checking ...')
|
|
65
|
-
|
|
66
|
-
fetchOrder(shopStore.state.orderKey).then(()=>{
|
|
67
|
-
if (shopStore.state.order?.['is_paid']){
|
|
68
|
-
console.debug('Order is paid')
|
|
69
|
-
shopStore.navigateToTab('complete');
|
|
70
|
-
PaymentCheckPause();
|
|
71
|
-
}
|
|
72
|
-
})
|
|
65
|
+
import {Request} from '@viur/vue-utils';
|
|
66
|
+
import {useIntervalFn} from '@vueuse/core';
|
|
67
|
+
import {computed, onBeforeMount, reactive} from 'vue';
|
|
68
|
+
import {useOrder} from '../composables/order';
|
|
69
|
+
import {useViurShopStore} from '../shop';
|
|
73
70
|
|
|
74
|
-
|
|
71
|
+
const shopStore = useViurShopStore();
|
|
72
|
+
const {fetchOrder} = useOrder();
|
|
73
|
+
|
|
74
|
+
const emits = defineEmits(['cancel']);
|
|
75
|
+
|
|
76
|
+
const {pause: PaymentCheckPause, resume: PaymentCheckResume, isActive: PaymentCheckIsActive} = useIntervalFn(() => {
|
|
77
|
+
console.debug('checking ...');
|
|
78
|
+
|
|
79
|
+
fetchOrder(shopStore.state.orderKey).then(() => {
|
|
80
|
+
if (shopStore.state.order?.['is_paid']) {
|
|
81
|
+
console.debug('Order is paid');
|
|
82
|
+
shopStore.navigateToTab('complete');
|
|
83
|
+
PaymentCheckPause();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
}, 2000, {immediate: false});
|
|
75
88
|
|
|
76
89
|
|
|
77
90
|
const state = reactive({
|
|
78
|
-
unzer:computed(()=>{
|
|
79
|
-
if (!shopStore.state.paymentProviderData) return null
|
|
80
|
-
return new unzer(shopStore.state.paymentProviderData[
|
|
91
|
+
unzer: computed(() => {
|
|
92
|
+
if (!shopStore.state.paymentProviderData) return null;
|
|
93
|
+
return new unzer(shopStore.state.paymentProviderData['public_key'], {locale: shopStore.state.locale});
|
|
81
94
|
}),
|
|
82
|
-
paymentHandler:{},
|
|
83
|
-
loading:false,
|
|
84
|
-
hasError:false,
|
|
85
|
-
errorMessage:
|
|
86
|
-
waitPayment:false
|
|
87
|
-
})
|
|
88
|
-
|
|
95
|
+
paymentHandler: {},
|
|
96
|
+
loading: false,
|
|
97
|
+
hasError: false,
|
|
98
|
+
errorMessage: null,
|
|
99
|
+
waitPayment: false,
|
|
100
|
+
});
|
|
89
101
|
|
|
90
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Initialize the payment type creation with Unzer-UI components
|
|
104
|
+
*/
|
|
105
|
+
function initUnzerForm() {
|
|
91
106
|
//Unzer field definition
|
|
92
107
|
if (shopStore.state.order?.['payment_provider'] === 'unzer-card') {
|
|
93
108
|
const card = state.unzer.Card();
|
|
@@ -106,21 +121,20 @@ function initUnzerForm(){
|
|
|
106
121
|
containerId: 'card-element-id-cvc',
|
|
107
122
|
onlyIframe: false,
|
|
108
123
|
});
|
|
109
|
-
|
|
110
|
-
state.paymentHandler['unzer-card'] = card
|
|
124
|
+
state.paymentHandler['unzer-card'] = card;
|
|
111
125
|
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-paypal') {
|
|
112
126
|
// Creating a PayPal instance
|
|
113
|
-
const paypal = state.unzer.Paypal()
|
|
127
|
+
const paypal = state.unzer.Paypal();
|
|
114
128
|
// Rendering input field
|
|
115
129
|
//paypal.create('email', {
|
|
116
130
|
// containerId: 'paypal-element',
|
|
117
131
|
//});
|
|
118
|
-
state.paymentHandler['unzer-paypal']= paypal;
|
|
132
|
+
state.paymentHandler['unzer-paypal'] = paypal;
|
|
119
133
|
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-sofort') {
|
|
120
|
-
const sofort = state.unzer.Sofort()
|
|
134
|
+
const sofort = state.unzer.Sofort();
|
|
121
135
|
state.paymentHandler['unzer-sofort'] = sofort;
|
|
122
136
|
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-ideal') {
|
|
123
|
-
const ideal = state.unzer.Ideal()
|
|
137
|
+
const ideal = state.unzer.Ideal();
|
|
124
138
|
ideal.create('ideal', {
|
|
125
139
|
containerId: 'ideal-element',
|
|
126
140
|
});
|
|
@@ -128,95 +142,176 @@ function initUnzerForm(){
|
|
|
128
142
|
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-bancontact') {
|
|
129
143
|
const bancontact = state.unzer.Bancontact();
|
|
130
144
|
state.paymentHandler['unzer-bancontact'] = bancontact;
|
|
145
|
+
} else if (shopStore.state.order?.['payment_provider'] === 'unzer-googlepay') {
|
|
146
|
+
const googlepayScriptPromise = new Promise((resolve, reject) => {
|
|
147
|
+
const googlepayScript = document.createElement('script');
|
|
148
|
+
googlepayScript.setAttribute('src', 'https://pay.google.com/gp/p/js/pay.js');
|
|
149
|
+
googlepayScript.onload = () => {
|
|
150
|
+
resolve(googlepayScript);
|
|
151
|
+
};
|
|
152
|
+
googlepayScript.onerror = reject;
|
|
153
|
+
document.head.appendChild(googlepayScript);
|
|
154
|
+
});
|
|
155
|
+
const googlepay = state.unzer.Googlepay();
|
|
156
|
+
state.paymentHandler['unzer-googlepay'] = googlepay;
|
|
157
|
+
const paymentDataRequestObject = googlepay.initPaymentDataRequestObject({
|
|
158
|
+
gatewayMerchantId: shopStore.state.paymentProviderData.channel,
|
|
159
|
+
merchantInfo: {
|
|
160
|
+
merchantId: shopStore.state.paymentProviderData.merchant_id,
|
|
161
|
+
merchantName: shopStore.state.paymentProviderData.merchant_name,
|
|
162
|
+
},
|
|
163
|
+
transactionInfo: {
|
|
164
|
+
countryCode: shopStore.state.order.billing_address.dest['country'].toUpperCase(),
|
|
165
|
+
currencyCode: 'EUR',
|
|
166
|
+
totalPrice: shopStore.state.order.total.toString(),
|
|
167
|
+
totalPriceStatus: 'ESTIMATED', // backend should have the last words
|
|
168
|
+
// totalPriceStatus: 'FINAL',
|
|
169
|
+
// checkoutOption: 'COMPLETE_IMMEDIATE_PURCHASE',
|
|
170
|
+
},
|
|
171
|
+
// buttonColor: 'white',
|
|
172
|
+
allowedCardNetworks: shopStore.state.paymentProviderData.brands,
|
|
173
|
+
allowCreditCards: shopStore.state.paymentProviderData.allow_credit_cards,
|
|
174
|
+
allowPrepaidCards: shopStore.state.paymentProviderData.allow_prepaid_cards,
|
|
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
|
+
});
|
|
131
205
|
} else {
|
|
132
206
|
console.warn(`Unknown payment provider: ${shopStore.state.order?.['payment_provider']}`);
|
|
133
207
|
}
|
|
134
|
-
state.loading = false
|
|
208
|
+
state.loading = false;
|
|
135
209
|
}
|
|
136
210
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Handle an error
|
|
213
|
+
*
|
|
214
|
+
* @param error The error. In best cases an object from unzer
|
|
215
|
+
* with a translated (and detailed) description for the customer
|
|
216
|
+
*/
|
|
217
|
+
function paymentError(error) {
|
|
218
|
+
console.error(error);
|
|
219
|
+
state.loading = false;
|
|
220
|
+
state.hasError = true;
|
|
221
|
+
state.errorMessage = error.customerMessage || error.message || error || 'Error';
|
|
141
222
|
}
|
|
142
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Create the payment Type (ressource) on behalf of the Unzer-UI components.
|
|
226
|
+
*
|
|
227
|
+
* This function is used by all unzer pyment types, except Google Pay.
|
|
228
|
+
*/
|
|
229
|
+
function submitFormToUnzer() {
|
|
230
|
+
PaymentCheckPause();
|
|
231
|
+
state.loading = true;
|
|
232
|
+
state.hasError = false;
|
|
233
|
+
state.paymentHandler[shopStore.state.order?.['payment_provider']].createResource().then((result) => {
|
|
234
|
+
saveType(result.id);
|
|
235
|
+
}).catch((error) => {
|
|
236
|
+
paymentError(error);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
143
239
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
240
|
+
/**
|
|
241
|
+
* Save type-id on the order
|
|
242
|
+
*
|
|
243
|
+
* After the Unzer-UI components have collected the customer data and created the payment Type this function
|
|
244
|
+
* has to be called to send the type-id to the viur-shop backend and store it.
|
|
245
|
+
* @param typeId The type-id of the created Type, e.g. ``s-crd-abc123def456``
|
|
246
|
+
*/
|
|
247
|
+
function saveType(typeId) {
|
|
248
|
+
const paymenttarget = shopStore.state.order?.['payment_provider'].split('-')[1];
|
|
249
|
+
Request.post(`${shopStore.state.shopUrl}/pp_unzer_${paymenttarget}/save_type`, {
|
|
250
|
+
dataObj: {
|
|
152
251
|
order_key: shopStore.state.orderKey,
|
|
153
|
-
type_id:
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
state.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
})
|
|
173
|
-
}).catch((error)=> {
|
|
174
|
-
paymentError(error)
|
|
175
|
-
})
|
|
252
|
+
type_id: typeId,
|
|
253
|
+
},
|
|
254
|
+
}).then(async (resp) => {
|
|
255
|
+
shopStore.state.order = await resp.json();
|
|
256
|
+
shopStore.checkoutOrder().then((resp) => {
|
|
257
|
+
state.loading = false;
|
|
258
|
+
state.hasError = false;
|
|
259
|
+
if (shopStore.state.paymentProviderData?.redirectUrl) {
|
|
260
|
+
state.waitPayment = true;
|
|
261
|
+
window.open(shopStore.state.paymentProviderData.redirectUrl, '_blank', 'popup');
|
|
262
|
+
PaymentCheckResume();
|
|
263
|
+
}
|
|
264
|
+
}).catch(async (error) => {
|
|
265
|
+
paymentError(error);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
}).catch(error => {
|
|
269
|
+
paymentError(error);
|
|
270
|
+
});
|
|
176
271
|
}
|
|
177
272
|
|
|
178
273
|
|
|
179
|
-
function cancelPayment(){
|
|
180
|
-
PaymentCheckPause()
|
|
181
|
-
emits('cancel')
|
|
274
|
+
function cancelPayment() {
|
|
275
|
+
PaymentCheckPause();
|
|
276
|
+
emits('cancel');
|
|
182
277
|
}
|
|
183
278
|
|
|
184
|
-
onBeforeMount(()=>{
|
|
185
|
-
state.loading = true
|
|
186
|
-
if (!shopStore.state.paymentProviderData){
|
|
187
|
-
shopStore.checkout().then(()=>{
|
|
188
|
-
initUnzerForm()
|
|
189
|
-
}).catch((error)=>{
|
|
190
|
-
console.log(error)
|
|
191
|
-
})
|
|
192
|
-
}else{
|
|
193
|
-
|
|
279
|
+
onBeforeMount(() => {
|
|
280
|
+
state.loading = true;
|
|
281
|
+
if (!shopStore.state.paymentProviderData) {
|
|
282
|
+
shopStore.checkout().then(() => {
|
|
283
|
+
initUnzerForm();
|
|
284
|
+
}).catch((error) => {
|
|
285
|
+
console.log(error);
|
|
286
|
+
});
|
|
287
|
+
} else {
|
|
288
|
+
initUnzerForm();
|
|
194
289
|
}
|
|
195
|
-
})
|
|
290
|
+
});
|
|
196
291
|
|
|
197
292
|
</script>
|
|
198
293
|
|
|
199
294
|
<style scoped>
|
|
200
|
-
.loading-wrapper{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
295
|
+
.loading-wrapper {
|
|
296
|
+
display: flex;
|
|
297
|
+
flex-direction: column;
|
|
298
|
+
align-items: center;
|
|
204
299
|
}
|
|
205
300
|
|
|
206
|
-
.form-wrapper{
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
301
|
+
.form-wrapper {
|
|
302
|
+
display: flex;
|
|
303
|
+
flex-direction: column;
|
|
304
|
+
gap: 20px;
|
|
210
305
|
}
|
|
211
306
|
|
|
212
|
-
.loading{
|
|
213
|
-
|
|
307
|
+
.loading {
|
|
308
|
+
font-size: 3rem;
|
|
214
309
|
}
|
|
215
310
|
</style>
|
|
216
311
|
|
|
217
312
|
<style>
|
|
218
313
|
/* global style to overwrite UnzerCSS */
|
|
219
314
|
.unzerUI.primary.button, .unzerUI.primary.buttons .button {
|
|
220
|
-
|
|
315
|
+
background-color: var(--ignt-color-primary) !important;
|
|
221
316
|
}
|
|
222
317
|
</style>
|
package/src/shop.js
CHANGED
|
@@ -11,6 +11,7 @@ export const useViurShopStore = defineStore("viurshopStore", () => {
|
|
|
11
11
|
const state = reactive({
|
|
12
12
|
//shop module name
|
|
13
13
|
showNodes:false,
|
|
14
|
+
locale: 'de-DE',
|
|
14
15
|
language:"de",
|
|
15
16
|
moduleName:'shop',
|
|
16
17
|
hostUrl: computed(()=>(import.meta.env.VITE_API_URL ? import.meta.env.VITE_API_URL : window.location.origin)),
|