blob: 3b4fc54d230c3cfcf0432d01061f3b7678b8badc [file] [log] [blame]
swissChilif0cbdc32023-01-05 17:21:38 -05001/* global wc_stripe_payment_request_params, Stripe */
2jQuery( function( $ ) {
3 'use strict';
4
5 var stripe = Stripe( wc_stripe_payment_request_params.stripe.key, {
6 locale: wc_stripe_payment_request_params.stripe.locale
7 } ),
8 paymentRequestType;
9
10 /**
11 * Object to handle Stripe payment forms.
12 */
13 var wc_stripe_payment_request = {
14 /**
15 * Get WC AJAX endpoint URL.
16 *
17 * @param {String} endpoint Endpoint.
18 * @return {String}
19 */
20 getAjaxURL: function( endpoint ) {
21 return wc_stripe_payment_request_params.ajax_url
22 .toString()
23 .replace( '%%endpoint%%', 'wc_stripe_' + endpoint );
24 },
25
26 getCartDetails: function() {
27 var data = {
28 security: wc_stripe_payment_request_params.nonce.payment
29 };
30
31 $.ajax( {
32 type: 'POST',
33 data: data,
34 url: wc_stripe_payment_request.getAjaxURL( 'get_cart_details' ),
35 success: function( response ) {
36 wc_stripe_payment_request.startPaymentRequest( response );
37 }
38 } );
39 },
40
41 getAttributes: function() {
42 var select = $( '.variations_form' ).find( '.variations select' ),
43 data = {},
44 count = 0,
45 chosen = 0;
46
47 select.each( function() {
48 var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
49 var value = $( this ).val() || '';
50
51 if ( value.length > 0 ) {
52 chosen ++;
53 }
54
55 count ++;
56 data[ attribute_name ] = value;
57 });
58
59 return {
60 'count' : count,
61 'chosenCount': chosen,
62 'data' : data
63 };
64 },
65
66 processSource: function( source, paymentRequestType ) {
67 var data = wc_stripe_payment_request.getOrderData( source, paymentRequestType );
68
69 return $.ajax( {
70 type: 'POST',
71 data: data,
72 dataType: 'json',
73 url: wc_stripe_payment_request.getAjaxURL( 'create_order' )
74 } );
75 },
76
77 /**
78 * Get order data.
79 *
80 * @since 3.1.0
81 * @version 4.0.0
82 * @param {PaymentResponse} source Payment Response instance.
83 *
84 * @return {Object}
85 */
86 getOrderData: function( evt, paymentRequestType ) {
87 var source = evt.source;
88 var email = source.owner.email;
89 var phone = source.owner.phone;
90 var billing = source.owner.address;
91 var name = source.owner.name;
92 var shipping = evt.shippingAddress;
93 var data = {
94 _wpnonce: wc_stripe_payment_request_params.nonce.checkout,
95 billing_first_name: null !== name ? name.split( ' ' ).slice( 0, 1 ).join( ' ' ) : '',
96 billing_last_name: null !== name ? name.split( ' ' ).slice( 1 ).join( ' ' ) : '',
97 billing_company: '',
98 billing_email: null !== email ? email : evt.payerEmail,
99 billing_phone: null !== phone ? phone : evt.payerPhone && evt.payerPhone.replace( '/[() -]/g', '' ),
100 billing_country: null !== billing ? billing.country : '',
101 billing_address_1: null !== billing ? billing.line1 : '',
102 billing_address_2: null !== billing ? billing.line2 : '',
103 billing_city: null !== billing ? billing.city : '',
104 billing_state: null !== billing ? billing.state : '',
105 billing_postcode: null !== billing ? billing.postal_code : '',
106 shipping_first_name: '',
107 shipping_last_name: '',
108 shipping_company: '',
109 shipping_country: '',
110 shipping_address_1: '',
111 shipping_address_2: '',
112 shipping_city: '',
113 shipping_state: '',
114 shipping_postcode: '',
115 shipping_method: [ null === evt.shippingOption ? null : evt.shippingOption.id ],
116 order_comments: '',
117 payment_method: 'stripe',
118 ship_to_different_address: 1,
119 terms: 1,
120 stripe_source: source.id,
121 payment_request_type: paymentRequestType
122 };
123
124 if ( shipping ) {
125 data.shipping_first_name = shipping.recipient.split( ' ' ).slice( 0, 1 ).join( ' ' );
126 data.shipping_last_name = shipping.recipient.split( ' ' ).slice( 1 ).join( ' ' );
127 data.shipping_company = shipping.organization;
128 data.shipping_country = shipping.country;
129 data.shipping_address_1 = typeof shipping.addressLine[0] === 'undefined' ? '' : shipping.addressLine[0];
130 data.shipping_address_2 = typeof shipping.addressLine[1] === 'undefined' ? '' : shipping.addressLine[1];
131 data.shipping_city = shipping.city;
132 data.shipping_state = shipping.region;
133 data.shipping_postcode = shipping.postalCode;
134 }
135
136 return data;
137 },
138
139 /**
140 * Generate error message HTML.
141 *
142 * @since 3.1.0
143 * @version 4.0.0
144 * @param {String} message Error message.
145 * @return {Object}
146 */
147 getErrorMessageHTML: function( message ) {
148 return $( '<div class="woocommerce-error" />' ).text( message );
149 },
150
151 /**
152 * Display error messages.
153 *
154 * @since 4.8.0
155 * @param {Object} message DOM object with error message to display.
156 */
157 displayErrorMessage: function( message ) {
158 $( '.woocommerce-error' ).remove();
159
160 if ( wc_stripe_payment_request_params.is_product_page ) {
161 var element = $( '.product' ).first();
162 element.before( message );
163
164 $( 'html, body' ).animate({
165 scrollTop: element.prev( '.woocommerce-error' ).offset().top
166 }, 600 );
167 } else {
168 var $form = $( '.shop_table.cart' ).closest( 'form' );
169 $form.before( message );
170 $( 'html, body' ).animate({
171 scrollTop: $form.prev( '.woocommerce-error' ).offset().top
172 }, 600 );
173 }
174 },
175
176 /**
177 * Abort payment and display error messages.
178 *
179 * @since 3.1.0
180 * @version 4.8.0
181 * @param {PaymentResponse} payment Payment response instance.
182 * @param {Object} message DOM object with error message to display.
183 */
184 abortPayment: function( payment, message ) {
185 payment.complete( 'fail' );
186 wc_stripe_payment_request.displayErrorMessage( message );
187 },
188
189 /**
190 * Complete payment.
191 *
192 * @since 3.1.0
193 * @version 4.0.0
194 * @param {PaymentResponse} payment Payment response instance.
195 * @param {String} url Order thank you page URL.
196 */
197 completePayment: function( payment, url ) {
198 wc_stripe_payment_request.block();
199
200 payment.complete( 'success' );
201
202 // Success, then redirect to the Thank You page.
203 window.location = url;
204 },
205
206 block: function() {
207 $.blockUI( {
208 message: null,
209 overlayCSS: {
210 background: '#fff',
211 opacity: 0.6
212 }
213 } );
214 },
215
216 /**
217 * Update shipping options.
218 *
219 * @param {Object} details Payment details.
220 * @param {PaymentAddress} address Shipping address.
221 */
222 updateShippingOptions: function( details, address ) {
223 var data = {
224 security: wc_stripe_payment_request_params.nonce.shipping,
225 country: address.country,
226 state: address.region,
227 postcode: address.postalCode,
228 city: address.city,
229 address: typeof address.addressLine[0] === 'undefined' ? '' : address.addressLine[0],
230 address_2: typeof address.addressLine[1] === 'undefined' ? '' : address.addressLine[1],
231 payment_request_type: paymentRequestType,
232 is_product_page: wc_stripe_payment_request_params.is_product_page,
233 };
234
235 return $.ajax( {
236 type: 'POST',
237 data: data,
238 url: wc_stripe_payment_request.getAjaxURL( 'get_shipping_options' )
239 } );
240 },
241
242 /**
243 * Updates the shipping price and the total based on the shipping option.
244 *
245 * @param {Object} details The line items and shipping options.
246 * @param {String} shippingOption User's preferred shipping option to use for shipping price calculations.
247 */
248 updateShippingDetails: function( details, shippingOption ) {
249 var data = {
250 security: wc_stripe_payment_request_params.nonce.update_shipping,
251 shipping_method: [ shippingOption.id ],
252 payment_request_type: paymentRequestType,
253 is_product_page: wc_stripe_payment_request_params.is_product_page,
254 };
255
256 return $.ajax( {
257 type: 'POST',
258 data: data,
259 url: wc_stripe_payment_request.getAjaxURL( 'update_shipping_method' )
260 } );
261 },
262
263 /**
264 * Adds the item to the cart and return cart details.
265 *
266 */
267 addToCart: function() {
268 var product_id = $( '.single_add_to_cart_button' ).val();
269
270 // Check if product is a variable product.
271 if ( $( '.single_variation_wrap' ).length ) {
272 product_id = $( '.single_variation_wrap' ).find( 'input[name="product_id"]' ).val();
273 }
274
275 var data = {
276 security: wc_stripe_payment_request_params.nonce.add_to_cart,
277 product_id: product_id,
278 qty: $( '.quantity .qty' ).val(),
279 attributes: $( '.variations_form' ).length ? wc_stripe_payment_request.getAttributes().data : []
280 };
281
282 // add addons data to the POST body
283 var formData = $( 'form.cart' ).serializeArray();
284 $.each( formData, function( i, field ) {
285 if ( /^addon-/.test( field.name ) ) {
286 if ( /\[\]$/.test( field.name ) ) {
287 var fieldName = field.name.substring( 0, field.name.length - 2);
288 if ( data[ fieldName ] ) {
289 data[ fieldName ].push( field.value );
290 } else {
291 data[ fieldName ] = [ field.value ];
292 }
293 } else {
294 data[ field.name ] = field.value;
295 }
296 }
297 } );
298
299 return $.ajax( {
300 type: 'POST',
301 data: data,
302 url: wc_stripe_payment_request.getAjaxURL( 'add_to_cart' )
303 } );
304 },
305
306 clearCart: function() {
307 var data = {
308 'security': wc_stripe_payment_request_params.nonce.clear_cart
309 };
310
311 return $.ajax( {
312 type: 'POST',
313 data: data,
314 url: wc_stripe_payment_request.getAjaxURL( 'clear_cart' ),
315 success: function( response ) {}
316 } );
317 },
318
319 getRequestOptionsFromLocal: function() {
320 return {
321 total: wc_stripe_payment_request_params.product.total,
322 currency: wc_stripe_payment_request_params.checkout.currency_code,
323 country: wc_stripe_payment_request_params.checkout.country_code,
324 requestPayerName: true,
325 requestPayerEmail: true,
326 requestPayerPhone: wc_stripe_payment_request_params.checkout.needs_payer_phone,
327 requestShipping: wc_stripe_payment_request_params.product.requestShipping,
328 displayItems: wc_stripe_payment_request_params.product.displayItems
329 };
330 },
331
332 /**
333 * Starts the payment request
334 *
335 * @since 4.0.0
336 * @version 4.8.0
337 */
338 startPaymentRequest: function( cart ) {
339 var paymentDetails,
340 options;
341
342 if ( wc_stripe_payment_request_params.is_product_page ) {
343 options = wc_stripe_payment_request.getRequestOptionsFromLocal();
344
345 paymentDetails = options;
346 } else {
347 options = {
348 total: cart.order_data.total,
349 currency: cart.order_data.currency,
350 country: cart.order_data.country_code,
351 requestPayerName: true,
352 requestPayerEmail: true,
353 requestPayerPhone: wc_stripe_payment_request_params.checkout.needs_payer_phone,
354 requestShipping: cart.shipping_required ? true : false,
355 displayItems: cart.order_data.displayItems
356 };
357
358 paymentDetails = cart.order_data;
359 }
360
361 // Puerto Rico (PR) is the only US territory/possession that's supported by Stripe.
362 // Since it's considered a US state by Stripe, we need to do some special mapping.
363 if ( 'PR' === options.country ) {
364 options.country = 'US';
365 }
366
367 // Handle errors thrown by Stripe, so we don't break the product page
368 try {
369 var paymentRequest = stripe.paymentRequest( options );
370
371 var elements = stripe.elements( { locale: wc_stripe_payment_request_params.button.locale } );
372 var prButton = wc_stripe_payment_request.createPaymentRequestButton( elements, paymentRequest );
373
374 // Check the availability of the Payment Request API first.
375 paymentRequest.canMakePayment().then( function( result ) {
376 if ( ! result ) {
377 return;
378 }
379 if ( result.applePay ) {
380 paymentRequestType = 'apple_pay';
381 } else if ( result.googlePay ) {
382 paymentRequestType = 'google_pay';
383 } else {
384 paymentRequestType = 'payment_request_api';
385 }
386
387 wc_stripe_payment_request.attachPaymentRequestButtonEventListeners( prButton, paymentRequest );
388 wc_stripe_payment_request.showPaymentRequestButton( prButton );
389 } );
390
391 // Possible statuses success, fail, invalid_payer_name, invalid_payer_email, invalid_payer_phone, invalid_shipping_address.
392 paymentRequest.on( 'shippingaddresschange', function( evt ) {
393 $.when( wc_stripe_payment_request.updateShippingOptions( paymentDetails, evt.shippingAddress ) ).then( function( response ) {
394 evt.updateWith( { status: response.result, shippingOptions: response.shipping_options, total: response.total, displayItems: response.displayItems } );
395 } );
396 } );
397
398 paymentRequest.on( 'shippingoptionchange', function( evt ) {
399 $.when( wc_stripe_payment_request.updateShippingDetails( paymentDetails, evt.shippingOption ) ).then( function( response ) {
400 if ( 'success' === response.result ) {
401 evt.updateWith( { status: 'success', total: response.total, displayItems: response.displayItems } );
402 }
403
404 if ( 'fail' === response.result ) {
405 evt.updateWith( { status: 'fail' } );
406 }
407 } );
408 } );
409
410 paymentRequest.on( 'source', function( evt ) {
411 // Check if we allow prepaid cards.
412 if ( 'no' === wc_stripe_payment_request_params.stripe.allow_prepaid_card && 'prepaid' === evt.source.card.funding ) {
413 wc_stripe_payment_request.abortPayment( evt, wc_stripe_payment_request.getErrorMessageHTML( wc_stripe_payment_request_params.i18n.no_prepaid_card ) );
414 } else {
415 $.when( wc_stripe_payment_request.processSource( evt, paymentRequestType ) ).then( function( response ) {
416 if ( 'success' === response.result ) {
417 wc_stripe_payment_request.completePayment( evt, response.redirect );
418 } else {
419 wc_stripe_payment_request.abortPayment( evt, response.messages );
420 }
421 } );
422 }
423 } );
424 } catch( e ) {
425 // Leave for troubleshooting
426 console.error( e );
427 }
428 },
429
430 getSelectedProductData: function() {
431 var product_id = $( '.single_add_to_cart_button' ).val();
432
433 // Check if product is a variable product.
434 if ( $( '.single_variation_wrap' ).length ) {
435 product_id = $( '.single_variation_wrap' ).find( 'input[name="product_id"]' ).val();
436 }
437
438 var addons = $( '#product-addons-total' ).data('price_data') || [];
439 var addon_value = addons.reduce( function ( sum, addon ) { return sum + addon.cost; }, 0 );
440
441 var data = {
442 security: wc_stripe_payment_request_params.nonce.get_selected_product_data,
443 product_id: product_id,
444 qty: $( '.quantity .qty' ).val(),
445 attributes: $( '.variations_form' ).length ? wc_stripe_payment_request.getAttributes().data : [],
446 addon_value: addon_value,
447 };
448
449 return $.ajax( {
450 type: 'POST',
451 data: data,
452 url: wc_stripe_payment_request.getAjaxURL( 'get_selected_product_data' )
453 } );
454 },
455
456 /**
457 * Creates a wrapper around a function that ensures a function can not
458 * called in rappid succesion. The function can only be executed once and then agin after
459 * the wait time has expired. Even if the wrapper is called multiple times, the wrapped
460 * function only excecutes once and then blocks until the wait time expires.
461 *
462 * @param {int} wait Milliseconds wait for the next time a function can be executed.
463 * @param {function} func The function to be wrapped.
464 * @param {bool} immediate Overriding the wait time, will force the function to fire everytime.
465 *
466 * @return {function} A wrapped function with execution limited by the wait time.
467 */
468 debounce: function( wait, func, immediate ) {
469 var timeout;
470 return function() {
471 var context = this, args = arguments;
472 var later = function() {
473 timeout = null;
474 if (!immediate) func.apply(context, args);
475 };
476 var callNow = immediate && !timeout;
477 clearTimeout(timeout);
478 timeout = setTimeout(later, wait);
479 if (callNow) func.apply(context, args);
480 };
481 },
482
483 /**
484 * Creates stripe paymentRequest element or connects to custom button
485 *
486 * @param {object} elements Stripe elements instance.
487 * @param {object} paymentRequest Stripe paymentRequest object.
488 *
489 * @return {object} Stripe paymentRequest element or custom button jQuery element.
490 */
491 createPaymentRequestButton: function( elements, paymentRequest ) {
492 var button;
493 if ( wc_stripe_payment_request_params.button.is_custom ) {
494 button = $( wc_stripe_payment_request_params.button.css_selector );
495 if ( button.length ) {
496 // We fallback to default paymentRequest button if no custom button is found in the UI.
497 // Add flag to be sure that created button is custom button rather than fallback element.
498 button.data( 'isCustom', true );
499 return button;
500 }
501 }
502
503 if ( wc_stripe_payment_request_params.button.is_branded ) {
504 if ( wc_stripe_payment_request.shouldUseGooglePayBrand() ) {
505 button = wc_stripe_payment_request.createGooglePayButton();
506 // Add flag to be sure that created button is branded rather than fallback element.
507 button.data( 'isBranded', true );
508 return button;
509 } else {
510 // Not implemented branded buttons default to Stripe's button
511 // Apple Pay buttons can also fall back to Stripe's button, as it's already branded
512 // Set button type to default or buy, depending on branded type, to avoid issues with Stripe
513 wc_stripe_payment_request_params.button.type = 'long' === wc_stripe_payment_request_params.button.branded_type ? 'buy' : 'default';
514 }
515 }
516
517 return elements.create( 'paymentRequestButton', {
518 paymentRequest: paymentRequest,
519 style: {
520 paymentRequestButton: {
521 type: wc_stripe_payment_request_params.button.type,
522 theme: wc_stripe_payment_request_params.button.theme,
523 height: wc_stripe_payment_request_params.button.height + 'px',
524 },
525 },
526 } );
527 },
528
529 /**
530 * Checks if button is custom payment request button.
531 *
532 * @param {object} prButton Stripe paymentRequest element or custom jQuery element.
533 *
534 * @return {boolean} True when prButton is custom button jQuery element.
535 */
536 isCustomPaymentRequestButton: function ( prButton ) {
537 return prButton && 'function' === typeof prButton.data && prButton.data( 'isCustom' );
538 },
539
540 isBrandedPaymentRequestButton: function ( prButton ) {
541 return prButton && 'function' === typeof prButton.data && prButton.data( 'isBranded' );
542 },
543
544 shouldUseGooglePayBrand: function () {
545 var ua = window.navigator.userAgent.toLowerCase();
546 var isChrome = /chrome/.test( ua ) && ! /edge|edg|opr|brave\//.test( ua ) && 'Google Inc.' === window.navigator.vendor;
547 // newer versions of Brave do not have the userAgent string
548 var isBrave = isChrome && window.navigator.brave;
549 return isChrome && ! isBrave;
550 },
551
552 createGooglePayButton: function () {
553 var allowedThemes = [ 'dark', 'light', 'light-outline' ];
554 var allowedTypes = [ 'short', 'long' ];
555
556 var theme = wc_stripe_payment_request_params.button.theme;
557 var type = wc_stripe_payment_request_params.button.branded_type;
558 var locale = wc_stripe_payment_request_params.button.locale;
559 var height = wc_stripe_payment_request_params.button.height;
560 theme = allowedThemes.includes( theme ) ? theme : 'light';
561 var gpaySvgTheme = 'dark' === theme ? 'dark' : 'light';
562 type = allowedTypes.includes( type ) ? type : 'long';
563
564 var button = $( '<button type="button" id="wc-stripe-branded-button" aria-label="Google Pay" class="gpay-button"></button>' );
565 button.css( 'height', height + 'px' );
566 button.addClass( theme + ' ' + type );
567 if ( 'long' === type ) {
568 var url = 'https://www.gstatic.com/instantbuy/svg/' + gpaySvgTheme + '/' + locale + '.svg';
569 var fallbackUrl = 'https://www.gstatic.com/instantbuy/svg/' + gpaySvgTheme + '/en.svg';
570 // Check if locale GPay button exists, default to en if not
571 setBackgroundImageWithFallback( button, url, fallbackUrl );
572 }
573
574 return button;
575 },
576
577 attachPaymentRequestButtonEventListeners: function( prButton, paymentRequest ) {
578 // First, mark the body so we know a payment request button was used.
579 // This way error handling can any display errors in the most appropriate place.
580 prButton.on( 'click', function ( evt ) {
581 $( 'body' ).addClass( 'woocommerce-stripe-prb-clicked' );
582 });
583
584 // Then, attach specific handling for selected pages and button types
585 if ( wc_stripe_payment_request_params.is_product_page ) {
586 wc_stripe_payment_request.attachProductPageEventListeners( prButton, paymentRequest );
587 } else {
588 wc_stripe_payment_request.attachCartPageEventListeners( prButton, paymentRequest );
589 }
590 },
591
592 attachProductPageEventListeners: function( prButton, paymentRequest ) {
593 var paymentRequestError = [];
594 var addToCartButton = $( '.single_add_to_cart_button' );
595
596 prButton.on( 'click', function ( evt ) {
597 // If login is required for checkout, display redirect confirmation dialog.
598 if ( wc_stripe_payment_request_params.login_confirmation ) {
599 evt.preventDefault();
600 displayLoginConfirmation( paymentRequestType );
601 return;
602 }
603
604 // First check if product can be added to cart.
605 if ( addToCartButton.is( '.disabled' ) ) {
606 evt.preventDefault(); // Prevent showing payment request modal.
607 if ( addToCartButton.is( '.wc-variation-is-unavailable' ) ) {
608 window.alert( wc_add_to_cart_variation_params.i18n_unavailable_text );
609 } else if ( addToCartButton.is( '.wc-variation-selection-needed' ) ) {
610 window.alert( wc_add_to_cart_variation_params.i18n_make_a_selection_text );
611 }
612 return;
613 }
614
615 if ( 0 < paymentRequestError.length ) {
616 evt.preventDefault();
617 window.alert( paymentRequestError );
618 return;
619 }
620
621 wc_stripe_payment_request.addToCart();
622
623 if ( wc_stripe_payment_request.isCustomPaymentRequestButton( prButton ) || wc_stripe_payment_request.isBrandedPaymentRequestButton( prButton ) ) {
624 evt.preventDefault();
625 paymentRequest.show();
626 }
627 });
628
629 $( document.body ).on( 'wc_stripe_unblock_payment_request_button wc_stripe_enable_payment_request_button', function () {
630 wc_stripe_payment_request.unblockPaymentRequestButton();
631 } );
632
633 $( document.body ).on( 'wc_stripe_block_payment_request_button', function () {
634 wc_stripe_payment_request.blockPaymentRequestButton( 'wc_request_button_is_blocked' );
635 } );
636
637 $( document.body ).on( 'wc_stripe_disable_payment_request_button', function () {
638 wc_stripe_payment_request.blockPaymentRequestButton( 'wc_request_button_is_disabled' );
639 } );
640
641 $( document.body ).on( 'woocommerce_variation_has_changed', function () {
642 $( document.body ).trigger( 'wc_stripe_block_payment_request_button' );
643
644 $.when( wc_stripe_payment_request.getSelectedProductData() ).then( function ( response ) {
645 $.when(
646 paymentRequest.update( {
647 total: response.total,
648 displayItems: response.displayItems,
649 } )
650 ).then( function () {
651 $( document.body ).trigger( 'wc_stripe_unblock_payment_request_button' );
652 } );
653 });
654 } );
655
656 // Block the payment request button as soon as an "input" event is fired, to avoid sync issues
657 // when the customer clicks on the button before the debounced event is processed.
658 $( '.quantity' ).on( 'input', '.qty', function() {
659 $( document.body ).trigger( 'wc_stripe_block_payment_request_button' );
660 } );
661
662 $( '.quantity' ).on( 'input', '.qty', wc_stripe_payment_request.debounce( 250, function() {
663 $( document.body ).trigger( 'wc_stripe_block_payment_request_button' );
664 paymentRequestError = [];
665
666 $.when( wc_stripe_payment_request.getSelectedProductData() ).then( function ( response ) {
667 if ( response.error ) {
668 paymentRequestError = [ response.error ];
669 $( document.body ).trigger( 'wc_stripe_unblock_payment_request_button' );
670 } else {
671 $.when(
672 paymentRequest.update( {
673 total: response.total,
674 displayItems: response.displayItems,
675 } )
676 ).then( function () {
677 $( document.body ).trigger( 'wc_stripe_unblock_payment_request_button' );
678 });
679 }
680 } );
681 }));
682
683 if ( $('.variations_form').length ) {
684 $( '.variations_form' ).on( 'found_variation.wc-variation-form', function ( evt, variation ) {
685 if ( variation.is_in_stock ) {
686 wc_stripe_payment_request.unhidePaymentRequestButton();
687 } else {
688 wc_stripe_payment_request.hidePaymentRequestButton();
689 }
690 } );
691 }
692 },
693
694 attachCartPageEventListeners: function ( prButton, paymentRequest ) {
695 prButton.on( 'click', function ( evt ) {
696 // If login is required for checkout, display redirect confirmation dialog.
697 if ( wc_stripe_payment_request_params.login_confirmation ) {
698 evt.preventDefault();
699 displayLoginConfirmation( paymentRequestType );
700 return;
701 }
702
703 if (
704 wc_stripe_payment_request.isCustomPaymentRequestButton(
705 prButton
706 ) ||
707 wc_stripe_payment_request.isBrandedPaymentRequestButton(
708 prButton
709 )
710 ) {
711 evt.preventDefault();
712 paymentRequest.show();
713 }
714 } );
715 },
716
717 showPaymentRequestButton: function( prButton ) {
718 if ( wc_stripe_payment_request.isCustomPaymentRequestButton( prButton ) ) {
719 prButton.addClass( 'is-active' );
720 $( '#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator' ).show();
721 } else if ( wc_stripe_payment_request.isBrandedPaymentRequestButton( prButton ) ) {
722 $( '#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator' ).show();
723 $( '#wc-stripe-payment-request-button' ).html( prButton );
724 } else if ( $( '#wc-stripe-payment-request-button' ).length ) {
725 $( '#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator' ).show();
726 prButton.mount( '#wc-stripe-payment-request-button' );
727 }
728 },
729
730 hidePaymentRequestButton: function () {
731 $( '#wc-stripe-payment-request-wrapper, #wc-stripe-payment-request-button-separator' ).hide();
732 },
733
734 unhidePaymentRequestButton: function () {
735 const stripe_wrapper = $( '#wc-stripe-payment-request-wrapper' );
736 const stripe_separator = $( '#wc-stripe-payment-request-button-separator' );
737 // If either element is hidden, ensure both show.
738 if ( stripe_wrapper.is(':hidden') || stripe_separator.is(':hidden') ) {
739 stripe_wrapper.show();
740 stripe_separator.show();
741 }
742 },
743
744 blockPaymentRequestButton: function( cssClassname ) {
745 // check if element isn't already blocked before calling block() to avoid blinking overlay issues
746 // blockUI.isBlocked is either undefined or 0 when element is not blocked
747 if ( $( '#wc-stripe-payment-request-button' ).data( 'blockUI.isBlocked' ) ) {
748 return;
749 }
750
751 $( '#wc-stripe-payment-request-button' )
752 .addClass( cssClassname )
753 .block( { message: null } );
754 },
755
756 unblockPaymentRequestButton: function() {
757 $( '#wc-stripe-payment-request-button' )
758 .removeClass( ['wc_request_button_is_blocked', 'wc_request_button_is_disabled'] )
759 .unblock();
760 },
761
762 /**
763 * Initialize event handlers and UI state
764 *
765 * @since 4.0.0
766 * @version 4.0.0
767 */
768 init: function() {
769 if ( wc_stripe_payment_request_params.is_product_page ) {
770 wc_stripe_payment_request.startPaymentRequest( '' );
771 } else {
772 wc_stripe_payment_request.getCartDetails();
773 }
774
775 },
776 };
777
778 wc_stripe_payment_request.init();
779
780 // We need to refresh payment request data when total is updated.
781 $( document.body ).on( 'updated_cart_totals', function() {
782 wc_stripe_payment_request.init();
783 } );
784
785 // We need to refresh payment request data when total is updated.
786 $( document.body ).on( 'updated_checkout', function() {
787 wc_stripe_payment_request.init();
788 } );
789
790 function setBackgroundImageWithFallback( element, background, fallback ) {
791 element.css( 'background-image', 'url(' + background + ')' );
792 // Need to use an img element to avoid CORS issues
793 var testImg = document.createElement("img");
794 testImg.onerror = function () {
795 element.css( 'background-image', 'url(' + fallback + ')' );
796 }
797 testImg.src = background;
798 }
799
800 // TODO: Replace this by `client/blocks/payment-request/login-confirmation.js`
801 // when we start using webpack to build this file.
802 function displayLoginConfirmation( paymentRequestType ) {
803 if ( ! wc_stripe_payment_request_params.login_confirmation ) {
804 return;
805 }
806
807 var message = wc_stripe_payment_request_params.login_confirmation.message;
808
809 // Replace dialog text with specific payment request type "Apple Pay" or "Google Pay".
810 if ( 'payment_request_api' !== paymentRequestType ) {
811 message = message.replace(
812 /\*\*.*?\*\*/,
813 'apple_pay' === paymentRequestType ? 'Apple Pay' : 'Google Pay'
814 );
815 }
816
817 // Remove asterisks from string.
818 message = message.replace( /\*\*/g, '' );
819
820 if ( confirm( message ) ) {
821 // Redirect to my account page.
822 window.location.href = wc_stripe_payment_request_params.login_confirmation.redirect_url;
823 }
824 }
825} );