PluginProbe
PayPlug for WooCommerce (Official) / 3.1.0
PayPlug for WooCommerce (Official) v3.1.0
3.1.0 3.0.0 2.18.0 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1.0 1.10.0 1.10.1 1.2.1 1.2.10 1.2.11 1.2.2 1.2.3 1.2.4 1.2.5 All 102 releases
payplug / resources / js / frontend / wc-payplug-apple_pay-blocks.js

wc-payplug-apple_pay-blocks.js in PayPlug for WooCommerce (Official) 3.1.0, at resources/js/frontend/wc-payplug-apple_pay-blocks.js

345 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __ } from '@wordpress/i18n';
2 import { decodeEntities } from '@wordpress/html-entities';
3 import { getSetting } from '@woocommerce/settings';
4 import { registerPaymentMethod, registerExpressPaymentMethod } from '@woocommerce/blocks-registry';
5 import { useEffect, useRef } from 'react';
6 import { apple_pay_update_payment, getPayment } from './helper/wc-payplug-apple_pay-requests';
7 import ApplePayCart from './wc-payplug-apple_pay_cart-blocks';
8
9 const settings = getSetting( 'apple_pay_data', {} );
10 const defaultLabel = __('Gateway method title', 'payplug');
11 const label = decodeEntities( settings?.title ) || defaultLabel;
12
13 const Content = (props) => {
14
15 const { eventRegistration, emitResponse } = props;
16 const { onPaymentSetup, onCheckoutSuccess} = eventRegistration;
17 // A plain local variable would be reset on every re-render, but it's read from
18 // async callbacks/DOM handlers that can fire well after a re-render has happened.
19 const sessionRef = useRef(null);
20
21 useEffect(() => {
22 const element = jQuery("form .wp-block-woocommerce-checkout-actions-block .wc-block-components-button");
23 // Safari requires ApplePaySession to be constructed synchronously from a user gesture,
24 // so it's created here on the raw click rather than inside the async onPaymentSetup
25 // callback below. We don't preventDefault(): checkout's own field validation and order
26 // sync must run normally first, exactly like every other payment method in the list.
27 const onPlaceOrderClick = () => {
28 apple_pay.CreateSession();
29 apple_pay.CancelOrder();
30 };
31 element.on("click", onPlaceOrderClick);
32 return () => {
33 element.off("click", onPlaceOrderClick);
34 };
35 // CreateSession() reads props.billing.cartTotal, so the handler must be re-bound whenever
36 // it changes to avoid using a stale value from the first render.
37 },[props.billing.cartTotal.value]);
38
39
40
41 useEffect(() => {
42 const handlePaymentProcessing = async () => {
43
44 // Order-pay always has a real, existing order from the start (the one being
45 // repaid), so the payment intent can be created right away.
46 if (settings.is_order_pay) {
47 try {
48 const response = await getPayment(props, settings.order_pay_id);
49
50 if (!response || response.success === false) {
51 const errorMessage = (response && response.data && response.data.message)
52 || (response && typeof response.data === 'string' ? response.data : null)
53 || __('Payment processing failed. Please retry.', 'payplug');
54
55 return {
56 type: emitResponse.responseTypes.ERROR,
57 message: errorMessage,
58 messageContext: emitResponse.noticeContexts.PAYMENTS,
59 };
60 }
61
62 apple_pay.BeginSession(response);
63
64 return {
65 type: emitResponse.responseTypes.SUCCESS
66 }
67 } catch (error) {
68 const serverData = error && error.responseJSON && error.responseJSON.data;
69 const serverMessage = (serverData && serverData.message)
70 || (typeof serverData === 'string' ? serverData : null);
71
72 return {
73 type: emitResponse.responseTypes.ERROR,
74 message: serverMessage || __('Payment processing failed. Please retry.', 'payplug'),
75 messageContext: emitResponse.noticeContexts.PAYMENTS,
76 };
77 }
78 }
79
80 // Regular checkout: WooCommerce doesn't create the real order until the place-order
81 // submission that follows this step (order creation is deferred until then since
82 // WooCommerce 10.8). The Apple Pay session was already constructed on click; the
83 // merchant session/payment intent for it can only be created once that real order
84 // exists, so that happens server-side in process_payment() and comes back here via
85 // payment_details on the onCheckoutSuccess event below - nothing more to do yet.
86 return {
87 type: emitResponse.responseTypes.SUCCESS
88 };
89 }
90
91 const unsubscribeAfterProcessing = onPaymentSetup(handlePaymentProcessing);
92 return () => { unsubscribeAfterProcessing(); };
93
94 }, [
95 onPaymentSetup,
96 emitResponse.noticeContexts.PAYMENTS,
97 emitResponse.responseTypes.ERROR,
98 emitResponse.responseTypes.SUCCESS
99 ]);
100
101 useEffect(() => {
102 const handleCheckoutSuccess = ({ orderId, processingResponse }) => {
103
104 // Order-pay's session.begin()/onvalidatemerchant were already wired up in
105 // BeginSession() above, during onPaymentSetup - just wait for authorization.
106 if (!settings.is_order_pay) {
107 sessionRef.current.order_id = orderId;
108 apple_pay.BeginSessionFromPaymentDetails(processingResponse?.paymentDetails || {}, processingResponse?.redirectUrl);
109 }
110
111 return new Promise((resolve) => {
112 sessionRef.current.onpaymentauthorized = async event => {
113 const data = {
114 'action': 'applepay_update_payment',
115 'post_type': 'POST',
116 'payment_id': sessionRef.current.payment_id,
117 'payment_token': event.payment.token,
118 'order_id': sessionRef.current.order_id
119 };
120
121 try {
122 const res = await apple_pay_update_payment(data);
123
124 if (res.success !== true) {
125 sessionRef.current.completePayment({"status": ApplePaySession.STATUS_FAILURE});
126 resolve({
127 type: emitResponse.responseTypes.ERROR,
128 message: __('Payment processing failed. Please retry.', 'payplug'),
129 messageContext: emitResponse.noticeContexts.PAYMENTS,
130 });
131 return;
132 }
133
134 sessionRef.current.completePayment({"status": ApplePaySession.STATUS_SUCCESS});
135 resolve({
136 type: emitResponse.responseTypes.SUCCESS,
137 redirectUrl: sessionRef.current.return_url,
138 });
139 } catch (error) {
140 sessionRef.current.completePayment({"status": ApplePaySession.STATUS_FAILURE});
141 resolve({
142 type: emitResponse.responseTypes.ERROR,
143 message: __('Payment processing failed. Please retry.', 'payplug'),
144 messageContext: emitResponse.noticeContexts.PAYMENTS,
145 });
146 }
147 };
148 });
149 }
150 const unsubscribeAfterProcessing = onCheckoutSuccess(handleCheckoutSuccess);
151 return () => { unsubscribeAfterProcessing(); };
152
153 }, [
154 onCheckoutSuccess
155 ]);
156
157 let apple_pay = {
158 CreateSession: function () {
159 const request = {
160 "countryCode": settings.payplug_countryCode,
161 "currencyCode": settings.payplug_currencyCode,
162 "merchantCapabilities": [
163 "supports3DS"
164 ],
165 "supportedNetworks": [
166 "visa",
167 "masterCard"
168 ],
169 "supportedTypes": [
170 "debit",
171 "credit"
172 ],
173 "total": {
174 "label": "Apple Pay",
175 "type": "final",
176 "amount": props.billing.cartTotal.value/100
177 },
178 'applicationData': btoa(JSON.stringify({
179 'apple_pay_domain': settings.payplug_apple_pay_domain
180 }))
181 }
182
183 sessionRef.current = new ApplePaySession(3, request)
184 },
185 CancelOrder: function () {
186 sessionRef.current.oncancel = event => {
187 window.location = sessionRef.current.cancel_url
188 }
189 },
190 BeginSession: function (response) {
191 const session = sessionRef.current;
192 session.payment_id = response.data.payment_id;
193 session.order_id = settings.order_pay_id;
194 session.cancel_url = response.data.cancel;
195 session.return_url = response.data.redirect;
196 apple_pay.MerchantValidated(session, response.data.merchant_session)
197 session.begin()
198 },
199 // Counterpart to BeginSession() for regular checkout, where the merchant session/payment
200 // intent only becomes available once the real order exists (see the onCheckoutSuccess
201 // comment above) - payment_details values are always strings, so merchant_session is
202 // JSON-encoded server-side and needs to be parsed back into an object here.
203 BeginSessionFromPaymentDetails: function (paymentDetails, fallbackRedirectUrl) {
204 const session = sessionRef.current;
205 session.payment_id = paymentDetails.payment_id;
206 session.cancel_url = paymentDetails.cancel_url;
207 session.return_url = paymentDetails.return_url || fallbackRedirectUrl;
208
209 let merchantSession = null;
210 try {
211 merchantSession = JSON.parse(paymentDetails.merchant_session);
212 } catch (error) {
213 merchantSession = null;
214 }
215
216 apple_pay.MerchantValidated(session, merchantSession)
217 session.begin()
218 },
219 MerchantValidated: function(session, merchant_session) {
220 session.onvalidatemerchant = async event => {
221 try {
222 session.completeMerchantValidation(merchant_session)
223 } catch (err) {
224 apple_pay.CancelOrder()
225 }
226 }
227 }
228 }
229
230 return (
231 <></>
232 )
233
234 };
235 /**
236 * Label component
237 *
238 */
239 const Label = () => {
240 return (
241 <span style={{ width: '100%' }}>
242 {label}
243 <Icon />
244 </span>
245 )
246 }
247
248 const Icon = () => {
249 return (
250 <img src={settings?.icon.src} alt={settings?.icon.icon_alt} className="payplug-payment-icon" style={{float: 'right'}}/>
251 )
252 }
253
254 /**
255 * Payplug payment method config object.
256 */
257 const ApplePay = {
258 name: "apple_pay",
259 label: <Label />,
260 content: <Content />,
261 edit: <Content />,
262 canMakePayment: () => true,
263 ariaLabel: label,
264 supports: {
265 features: settings.supports
266 },
267 };
268
269 /**
270 *
271 * @param props
272 * @returns {JSX.Element}
273 * @Content for express payment method
274 */
275 const ExpressContent = (props) => {
276 return (
277 <>
278 <div id="apple-pay-button-wrapper">
279 <apple-pay-button
280 id="apple-pay-button"
281 buttonstyle="black"
282 type="pay"
283 locale={settings?.payplug_locale}
284 ></apple-pay-button>
285 </div>
286 <ApplePayCart {...props} />
287 </>
288 );
289 };
290
291 const ExpressApplePay = {
292 name: "apple_pay",
293 content: <ExpressContent/>,
294 edit: <ExpressContent/>,
295 canMakePayment: (data) => {
296
297 if (!settings?.is_cart) {
298 return false;
299 }
300
301 settings.payplug_apple_pay_shipping_required = data.cartNeedsShipping;
302 settings.total_amount = data.cartTotals.total_price;
303
304 if (!data.cartNeedsShipping) {
305 return true;
306 }
307
308 // On a fresh checkout page load, no address has been entered yet, so shipping
309 // rates/selection may not exist at all: Apple Pay's own sheet collects the address
310 // and lets the merchant offer shipping methods dynamically from there
311 // (see ApplePayCart's onshippingmethodselected), so don't block the button on it.
312 if (!data.selectedShippingMethods || !data.selectedShippingMethods[0]) {
313 return true;
314 }
315
316 let selectedShippingMethod = data.selectedShippingMethods[0];
317
318 settings.payplug_carriers.forEach(function(item){
319
320 if (item.identifier === selectedShippingMethod){
321 item.selected = true;
322 }
323
324 })
325
326 let payplug_authorized_carriers = settings?.payplug_authorized_carriers;
327 let selected_shipping = data.selectedShippingMethods[0].split(":");
328 let authorized = false;
329
330 payplug_authorized_carriers.forEach(function(item){
331 if (selected_shipping[0] === item){
332 authorized = true
333 }
334 });
335
336 return authorized;
337 },
338 paymentMethodId: "apple_pay"
339
340 };
341
342 registerExpressPaymentMethod( ExpressApplePay );
343
344 registerPaymentMethod( ApplePay );
345