swissChili | f0cbdc3 | 2023-01-05 17:21:38 -0500 | [diff] [blame] | 1 | /* global wc_stripe_payment_request_params, Stripe */ |
| 2 | jQuery( 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 | } ); |