@viur/shop-components 0.13.1 → 0.13.2-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
CHANGED
|
@@ -81,11 +81,19 @@
|
|
|
81
81
|
</sl-dialog>
|
|
82
82
|
</template>
|
|
83
83
|
|
|
84
|
+
paypal?
|
|
85
|
+
<template v-if="shopStore.state.order?.['payment_provider'] !== null && shopStore.state.order?.['payment_provider'].startsWith('paypal')">
|
|
86
|
+
<sl-dialog v-if="state.paymentPopup" :label="$t('viur.shop.order_pay')" :open="state.paymentPopup" @sl-after-hide="state.paymentPopup=false">
|
|
87
|
+
<payment-provider-paypal @cancel="paymentCanceled"></payment-provider-paypal>
|
|
88
|
+
</sl-dialog>
|
|
89
|
+
</template>
|
|
90
|
+
|
|
84
91
|
<slot name="template_confirm">
|
|
85
92
|
</slot>
|
|
86
93
|
</template>
|
|
87
94
|
<script setup>
|
|
88
95
|
import {computed, onBeforeMount, reactive, watch, useTemplateRef} from 'vue'
|
|
96
|
+
import PaymentProviderPaypal from '../components/PaymentProviderPaypal.vue';
|
|
89
97
|
import { useViurShopStore } from '../shop';
|
|
90
98
|
import boneUtils from '@viur/vue-utils/bones/utils'
|
|
91
99
|
import {Request} from '@viur/vue-utils'
|
|
@@ -144,7 +152,7 @@ async function startCheckout(){
|
|
|
144
152
|
}
|
|
145
153
|
}
|
|
146
154
|
state.paymentPopup=true
|
|
147
|
-
shopStore.
|
|
155
|
+
shopStore.checkoutStart();
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
//close popup if payment successfull
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
PAYPAL HERE
|
|
3
|
+
|
|
4
|
+
<div class="loading-wrapper" v-if="state.loading">
|
|
5
|
+
<sl-spinner class="loading"></sl-spinner>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="loading-wrapper" v-if="PaymentCheckIsActive">
|
|
9
|
+
<sl-spinner class="loading"></sl-spinner>
|
|
10
|
+
{{ $t('messages.wait_for_payment') }}
|
|
11
|
+
</div>
|
|
12
|
+
<div class="form-wrapper">
|
|
13
|
+
<sl-alert :open="state.hasError" variant="danger">{{ state.errorMessage }}</sl-alert>
|
|
14
|
+
|
|
15
|
+
<div id="paypal-button-container"></div>
|
|
16
|
+
|
|
17
|
+
<sl-button :disabled="state.loading" variant="danger" @click="cancelPayment">
|
|
18
|
+
{{ $t('actions.cancel') }}
|
|
19
|
+
</sl-button>
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup>
|
|
24
|
+
import {Request} from '@viur/vue-utils';
|
|
25
|
+
import {HTTPError} from '@viur/vue-utils/utils/request.js';
|
|
26
|
+
import {useIntervalFn} from '@vueuse/core';
|
|
27
|
+
import {onBeforeMount, reactive} from 'vue';
|
|
28
|
+
import {useAddress} from '../composables/address.js';
|
|
29
|
+
import {useOrder} from '../composables/order';
|
|
30
|
+
import {useViurShopStore} from '../shop';
|
|
31
|
+
|
|
32
|
+
const shopStore = useViurShopStore();
|
|
33
|
+
const {fetchOrder} = useOrder();
|
|
34
|
+
const {saveBirthdate} = useAddress();
|
|
35
|
+
|
|
36
|
+
const emits = defineEmits(['cancel']);
|
|
37
|
+
|
|
38
|
+
const {pause: PaymentCheckPause, resume: PaymentCheckResume, isActive: PaymentCheckIsActive} = useIntervalFn(() => {
|
|
39
|
+
console.debug('checking ...');
|
|
40
|
+
|
|
41
|
+
fetchOrder(shopStore.state.orderKey).then(() => {
|
|
42
|
+
if (shopStore.state.order?.['is_paid']) {
|
|
43
|
+
console.debug('Order is paid');
|
|
44
|
+
shopStore.navigateToTab('complete');
|
|
45
|
+
PaymentCheckPause();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
}, 2000, {immediate: false});
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
const state = reactive({
|
|
53
|
+
paymentHandler: {},
|
|
54
|
+
loading: false,
|
|
55
|
+
hasError: false,
|
|
56
|
+
errorMessage: null,
|
|
57
|
+
waitPayment: false,
|
|
58
|
+
order_id: null,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Initialize the payment type creation with Unzer-UI components
|
|
63
|
+
*/
|
|
64
|
+
function initPaypalForm() {
|
|
65
|
+
if (shopStore.state.order?.['payment_provider'] !== 'paypal_checkout') {
|
|
66
|
+
console.error(`PayPal Checkout does not work with ${shopStore.state.order?.['payment_provider']}.`);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const client_id = shopStore.state.paymentProviderData.public_key;
|
|
70
|
+
|
|
71
|
+
const scriptConfig = {
|
|
72
|
+
// https://developer.paypal.com/sdk/js/configuration/#configure-and-customize-your-integration
|
|
73
|
+
'client-id': shopStore.state.paymentProviderData.public_key,
|
|
74
|
+
'buyer-country': shopStore.state.order.billing_address.dest['country'].toUpperCase() ?? 'DE',
|
|
75
|
+
'currency': 'EUR',
|
|
76
|
+
'components': 'buttons,applepay',
|
|
77
|
+
'enable-funding': 'venmo,paylater,card,sofort,sepa,giropay',
|
|
78
|
+
'debug': shopStore.state.debug,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const paypalScriptPromise = new Promise((resolve, reject) => {
|
|
82
|
+
const paypalScript = document.createElement('script');
|
|
83
|
+
paypalScript.setAttribute('src', `https://www.paypal.com/sdk/js?${new URLSearchParams(scriptConfig).toString()}`);
|
|
84
|
+
paypalScript.onload = () => {
|
|
85
|
+
resolve(paypalScript);
|
|
86
|
+
};
|
|
87
|
+
paypalScript.onerror = reject;
|
|
88
|
+
document.head.appendChild(paypalScript);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
paypalScriptPromise.then(() => {
|
|
93
|
+
console.debug('paypalScriptPromise resolved - READY');
|
|
94
|
+
console.debug(shopStore.state.paymentProviderData);
|
|
95
|
+
|
|
96
|
+
const paypalButtons = window.paypal.Buttons({
|
|
97
|
+
style: {
|
|
98
|
+
shape: 'rect',
|
|
99
|
+
layout: 'vertical',
|
|
100
|
+
color: 'gold',
|
|
101
|
+
label: 'paypal',
|
|
102
|
+
},
|
|
103
|
+
message: {
|
|
104
|
+
amount: 100,
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
async createOrder() {
|
|
108
|
+
console.debug('createOrder');
|
|
109
|
+
try {
|
|
110
|
+
let resp = await shopStore.checkoutOrder();//.then((resp) => {
|
|
111
|
+
console.debug(resp);
|
|
112
|
+
|
|
113
|
+
// const orderData = await resp.json();
|
|
114
|
+
const orderData = await resp.payment;
|
|
115
|
+
// const orderData = await response.json();
|
|
116
|
+
|
|
117
|
+
// let orderData = shopStore.state.paymentProviderData;
|
|
118
|
+
console.debug(orderData);
|
|
119
|
+
|
|
120
|
+
if (orderData.id) {
|
|
121
|
+
state.order_id = orderData.id;
|
|
122
|
+
return orderData.id;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const errorDetail = orderData?.details?.[0];
|
|
126
|
+
const errorMessage = errorDetail
|
|
127
|
+
? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
|
|
128
|
+
: JSON.stringify(orderData);
|
|
129
|
+
|
|
130
|
+
throw new Error(errorMessage);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(error);
|
|
133
|
+
// resultMessage(`Could not initiate PayPal Checkout...<br><br>${error}`);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
async onApprove(data, actions) {
|
|
138
|
+
console.debug('onApprove', data, actions);
|
|
139
|
+
state.loading = true;
|
|
140
|
+
|
|
141
|
+
// buyer approved the payment, send it to the backend to capture it and mark as paid
|
|
142
|
+
const paymenttarget = shopStore.state.order?.['payment_provider'].replace(/-/g, '_');
|
|
143
|
+
Request.post(`${shopStore.state.shopUrl}/pp_${paymenttarget}/capture_order`, {
|
|
144
|
+
dataObj: {
|
|
145
|
+
order_key: shopStore.state.orderKey,
|
|
146
|
+
order_id: state.order_id,
|
|
147
|
+
},
|
|
148
|
+
}).then(async (resp) => {
|
|
149
|
+
console.debug('capture_order', resp);
|
|
150
|
+
const data = await resp.json();
|
|
151
|
+
const orderData = data.payment;
|
|
152
|
+
console.debug(orderData);
|
|
153
|
+
|
|
154
|
+
// Three cases to handle:
|
|
155
|
+
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
|
|
156
|
+
// (2) Other non-recoverable errors -> Show a failure message
|
|
157
|
+
// (3) Successful transaction -> Show confirmation or thank you message
|
|
158
|
+
const errorDetail = orderData?.details?.[0];
|
|
159
|
+
|
|
160
|
+
if (errorDetail?.issue === 'INSTRUMENT_DECLINED') {
|
|
161
|
+
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
|
|
162
|
+
// recoverable state, per
|
|
163
|
+
// https://developer.paypal.com/docs/checkout/standard/customize/handle-funding-failures/
|
|
164
|
+
return actions.restart();
|
|
165
|
+
} else if (errorDetail) {
|
|
166
|
+
// (2) Other non-recoverable errors -> Show a failure message
|
|
167
|
+
paymentError(`${errorDetail.description} (${orderData.debug_id})`);
|
|
168
|
+
} else if (!orderData.purchase_units) {
|
|
169
|
+
console.error(`No purchase_units`);
|
|
170
|
+
paymentError(`Missing purchase_units`);
|
|
171
|
+
} else {
|
|
172
|
+
// (3) Successful transaction -> Show confirmation or thank you message
|
|
173
|
+
// Or go to another URL: actions.redirect('thank_you.html');
|
|
174
|
+
const transaction =
|
|
175
|
+
orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
|
|
176
|
+
orderData?.purchase_units?.[0]?.payment?.authorizations?.[0];
|
|
177
|
+
console.debug('Capture result', orderData, JSON.stringify(orderData, null, 2));
|
|
178
|
+
|
|
179
|
+
state.loading = false;
|
|
180
|
+
state.hasError = false;
|
|
181
|
+
state.waitPayment = true;
|
|
182
|
+
PaymentCheckResume();
|
|
183
|
+
}
|
|
184
|
+
}).catch(error => {
|
|
185
|
+
paymentError(error);
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
onError: (err) => {
|
|
190
|
+
console.error(err);
|
|
191
|
+
paymentError(err);
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
appSwitchWhenAvailable: true,
|
|
195
|
+
});
|
|
196
|
+
paypalButtons.render('#paypal-button-container');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
state.loading = false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Handle an error
|
|
205
|
+
*
|
|
206
|
+
* @param error The error. In best cases an object from unzer
|
|
207
|
+
* with a translated (and detailed) description for the customer
|
|
208
|
+
*/
|
|
209
|
+
function paymentError(error) {
|
|
210
|
+
console.error(error);
|
|
211
|
+
state.loading = false;
|
|
212
|
+
state.hasError = true;
|
|
213
|
+
if (error && error.constructor.name === HTTPError.name && error.response.headers.get('x-viur-shop-error')) {
|
|
214
|
+
error.response.json().then(res => {
|
|
215
|
+
console.error(res.errors);
|
|
216
|
+
state.errorMessage = res.errors.map(err => err.customer_message || err.message).join(', ');
|
|
217
|
+
});
|
|
218
|
+
} else {
|
|
219
|
+
state.errorMessage = error.customerMessage || error.message || error || 'Error';
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function cancelPayment() {
|
|
224
|
+
PaymentCheckPause();
|
|
225
|
+
emits('cancel');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
onBeforeMount(() => {
|
|
229
|
+
console.debug('mounting', shopStore.state.paymentProviderData);
|
|
230
|
+
state.loading = true;
|
|
231
|
+
if (!shopStore.state.paymentProviderData) {
|
|
232
|
+
shopStore.checkoutStart().then(() => {
|
|
233
|
+
initPaypalForm();
|
|
234
|
+
fetchOrder(shopStore.state.orderKey); // refresh order after checkout_start freeze
|
|
235
|
+
}).catch((error) => {
|
|
236
|
+
console.log(error);
|
|
237
|
+
});
|
|
238
|
+
} else {
|
|
239
|
+
initPaypalForm();
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
</script>
|
|
244
|
+
|
|
245
|
+
<style scoped>
|
|
246
|
+
.loading-wrapper {
|
|
247
|
+
display: flex;
|
|
248
|
+
flex-direction: column;
|
|
249
|
+
align-items: center;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.form-wrapper {
|
|
253
|
+
display: flex;
|
|
254
|
+
flex-direction: column;
|
|
255
|
+
gap: 20px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.loading {
|
|
259
|
+
font-size: 3rem;
|
|
260
|
+
}
|
|
261
|
+
</style>
|
|
@@ -339,7 +339,7 @@ function cancelPayment() {
|
|
|
339
339
|
onBeforeMount(() => {
|
|
340
340
|
state.loading = true;
|
|
341
341
|
if (!shopStore.state.paymentProviderData) {
|
|
342
|
-
shopStore.
|
|
342
|
+
shopStore.checkoutStart().then(() => {
|
|
343
343
|
initUnzerForm();
|
|
344
344
|
fetchOrder(shopStore.state.orderKey); // refresh order after checkout_start freeze
|
|
345
345
|
}).catch((error) => {
|
package/src/shop.js
CHANGED
|
@@ -212,7 +212,7 @@ export const useViurShopStore = defineStore("viurshopStore", () => {
|
|
|
212
212
|
fetchPaymentMeta()
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
function
|
|
215
|
+
function checkoutStart(){
|
|
216
216
|
//request Payment
|
|
217
217
|
return new Promise((resolve,reject)=>{
|
|
218
218
|
Request.post(`${state.shopUrl}/order/checkout_start`,{dataObj:{
|
|
@@ -300,7 +300,7 @@ export const useViurShopStore = defineStore("viurshopStore", () => {
|
|
|
300
300
|
navigateToPrevious,
|
|
301
301
|
tabValidation,
|
|
302
302
|
fetchMetaData,
|
|
303
|
-
|
|
303
|
+
checkoutStart,
|
|
304
304
|
checkoutOrder,
|
|
305
305
|
addTab,
|
|
306
306
|
removeTab
|