1/** 2 * Initialize Stripe Connect payment gateway 3 * @param {jQuery} $ - jQuery instance 4 */ 5async function pms_stripe_maybe_load_gateway( $ ) { 6 7 if( !( $('#stripe-pk').length > 0 ) ){ 8 return false 9 } 10 11 var stripe_pk = $( '#stripe-pk' ).val() 12 13 //compatibility with PB conditional logic. if there are multiple subscription plans fields and the first one is hidden then it won't have a value attribute because of conditional logic 14 if( typeof stripe_pk == 'undefined' || stripe_pk == '' ) 15 stripe_pk = $('#stripe-pk').attr('conditional-value') 16 17 if( typeof stripe_pk == 'undefined' ) 18 return false 19 20 if ( typeof pms.stripe_connected_account == 'undefined' || pms.stripe_connected_account == '' ){ 21 console.log( 'Before you can accept payments, you need to connect your Stripe Account by going to Dashboard -> Paid Member Subscriptions -> Settings -> Payments.' ) 22 return false 23 } 24 25 var elements = false 26 var elements_setup_intent = false 27 var update_elements_setup_intent = false 28 var stripe = false 29 30 var $payment_element = '' 31 var $elements_instance_slug = '' 32 33 var cardIsEmpty = true 34 var recreating_payment_element = false 35 var email_change_debounce_timer = null 36 37 var subscription_plan_selector = 'input[name=subscription_plans]' 38 var paygate_selector = 'input.pms_pay_gate' 39 40 var stripe_appearance = {} 41 42 if( pms.pms_elements_appearance_api ) 43 stripe_appearance = pms.pms_elements_appearance_api 44 45 var StripeData = { 46 stripeAccount: pms.stripe_connected_account 47 } 48 49 if( pms.stripe_locale ) 50 StripeData.locale = pms.stripe_locale 51 52 stripe = Stripe( stripe_pk, StripeData ) 53 54 var stripe_payment_intent_options = { 55 mode : 'payment', 56 currency : pms.currency, 57 amount : 1099, 58 paymentMethodCreation: 'manual', 59 appearance : stripe_appearance, 60 } 61 62 var stripe_setup_intent_options = { 63 mode : 'setup', 64 currency : pms.currency, 65 paymentMethodCreation: 'manual', 66 appearance : stripe_appearance, 67 } 68 69 if( pms.off_session_payments && pms.off_session_payments == 1 ){ 70 stripe_payment_intent_options.setupFutureUsage = 'off_session' 71 stripe_setup_intent_options.setupFutureUsage = 'off_session' 72 } 73 74 if( pms.pms_customer_session ){ 75 stripe_payment_intent_options.customerSessionClientSecret = pms.pms_customer_session 76 stripe_setup_intent_options.customerSessionClientSecret = pms.pms_customer_session 77 } 78 79 elements = stripe.elements( stripe_payment_intent_options ) 80 elements_setup_intent = stripe.elements( stripe_setup_intent_options ) 81 82 stripeConnectInit() 83 84 // Validate currency for currently selected subscription plan if MC add-on
84is active 85 if ( pms.pms_mc_addon_active ) 86 await pms_stripe_validate_sdk_checkout_currency( $( $pms_checked_subscription ) ); 87 88 // Declare reCaptcha callback as already executed 89 if( typeof pms_initialize_recaptcha_v3 == 'function' ){ 90 window.pmsRecaptchaCallbackExecuted = true 91 92 jQuery('.pms-form').off('submit', pms_initialize_recaptcha_v3 ); 93 } 94 95 if( typeof wppbInitializeRecaptchaV3 == 'function' ){ 96 window.wppbRecaptchaCallbackExecuted = true 97 } 98 99 // Update Stripe Payment Intent on subscription plan change 100 $(document).on('click', subscription_plan_selector, async function ( event ) { 101 102 if ( pms.pms_mc_addon_active ){ 103 await pms_stripe_validate_sdk_checkout_currency( $(this) ); 104 } 105 106 stripeConnectInit() 107 108 }) 109 110 /** 111 * Resolves the Stripe setup_future_usage for a plan input from its recurring config and the customer's renewal choice 112 * 113 * - 'off_session' when the plan recurs (always-recurring, opt-in with the auto-renew box ticked, or the site default forcing recurring) 114 * - null when it does not recur, undefined when the input carries no recurring config so callers leave the current value untouched 115 * - shared so the primary plan and add-on items (e.g. Order Bumps) derive setup_future_usage from one rule and stay in agreement with the server-side PaymentIntent 116 * 117 */ 118 function pms_stripe_connect_setup_future_usage_for_input( $input ) { 119 120 let recurring = $input.data( 'recurring' ) 121 122 if( recurring == 0 || recurring == 1 ){ 123 124 let default_recurring = $('input[type="hidden"][name="pms_default_recurring"]').val() 125 126 if( default_recurring == 2 ) 127 return 'off_session' 128 else if( default_recurring == 3 ) 129 return null 130 else 131 return $('.pms-subscription-plan-auto-renew input[name="pms_recurring"]').prop('checked') ? 'off_session' : null 132 133 } else if( recurring == 2 ){ 134 return 'off_session' 135 } else if( recurring == 3 ){ 136 return null 137 } 138 139 return undefined 140 141 } 142 143 // expose the rule so add-ons resolve setup_future_usage with the same logic as the primary plan 144 $.pms_stripe_connect_setup_future_usage_for_input = pms_stripe_connect_setup_future_usage_for_input 145 146 // Re-run mode selection when an addon signals a checkout state change (e.g. Order Bumps toggling line items) 147 $(document).on( 'pms_recompute_payment_mode', function() { 148 stripeConnectInit() 149 }) 150 151 $(document).on('click', '.pms-subscription-plan-auto-renew input[name="pms_recurring"]', function ( event ) { 152 153 if( $(this).prop('checked') ){ 154 elements.update( { setupFutureUsage: 'off_session' } ); 155 } else { 156 elements.update( { setupFutureUsage: null } ); 157 } 158 159 }) 160 161 // This was added for Link support. 162 // After the user email is entered, the payment element is destroyed and then recreated using the new parameters. 163 $(document).on('change', '.pms-form .pms-user-email-field input[name="user_email"], .wppb-register-user input[name="email"], .pms-form .pms-billing-details input[name="pms_billing_email"]', function ( event ) { 164 165 // Skip if already recreating 166 if( recreating_payment_element ){ 167 return 168 } 169 170 if( $('input[type=hidden][name=pay_gate]').val() != 'stripe_connect' && $('input[type=radio][name=pay_gate]:checked').val() != 'stripe_connect' ) 171 return 172 173 if( $('input[type=hidden][name=pay_gate]').is(':disabled') || $('input[type=radio][name=pay_gate]:checked').is(':disabled') ) 174 return 175 176 let element = $(this) 177 let email = $(this).val() 178 179 // Clear any pending debounce timer 180 if( email_change_debounce_timer ){ 181 clearTimeout( email_change_debounce_timer ) 182 } 183 184 if( email.length > 0 ){ 185 186 // Disable the input to prevent multiple submissions 187 element.attr('disabled', true) 188 189 pms_stripe_show_spinner() 190 191 email_change_debounce_timer = setTimeout( function(){ 192 193 recreating_payment_element = true 194 195 // Only destroy if element exists 196 if( $payment_element && $payment_element != '' ){ 197 $payment_element.destroy() 198 $payment_element = '' 199 } 200 201 setTimeout( function(){
202 stripeConnectInit() 203 element.attr('disabled', false) 204 recreating_payment_element = false 205 }, 300 ) 206 207 }, 250 ) 208 } 209 210 }) 211 212 // Discount applied 213 $(document).on('pms_discount_success', stripeConnectInit ) 214 $(document).on('pms_discount_error', stripeConnectInit ) 215 216 // Update elements price when taxes are applied or removed 217 $(document).on('pms_tax_applied', function ( event, data ) { 218 219 if( data.total > 0 ){ 220 elements.update( { amount: pms_stripe_convert_amount_to_cents( data.total ) } ); 221 } 222 223 }) 224 225 $(document).on('pms_tax_removed', function ( event ) { 226 227 if( typeof $pms_checked_subscription != 'undefined' ){ 228 229 let price = $pms_checked_subscription.data( 'price' ) 230 231 if( price > 0 ){ 232 elements.update( { amount: pms_stripe_convert_amount_to_cents( price ) } ); 233 } 234 } 235 236 }) 237 238 // Show credit card details on the update payment method form 239 if ( $( 'input[name="pms_update_payment_method"]' ).length > 0 && $( '.pms-paygate-extra-fields-stripe_connect' ).length > 0 ){ 240 $('.pms-paygate-extra-fields-stripe_connect').show() 241 } 242 243 // Paid Member Subscription submit buttons 244 var payment_buttons = 'input[name=pms_register], '; 245 payment_buttons += 'input[name=pms_new_subscription], '; 246 payment_buttons += 'input[name=pms_change_subscription], '; 247 payment_buttons += 'input[name=pms_upgrade_subscription], '; 248 payment_buttons += 'input[name=pms_renew_subscription], '; 249 payment_buttons += 'input[name=pms_confirm_retry_payment_subscription], '; 250 251 // Profile Builder submit buttons 252 payment_buttons += '.wppb-register-user input[name=register]'; 253 254 // WPPB Recaptcha 255 $(document).on( 'wppb_invisible_recaptcha_success', stripeConnectPaymentGatewayHandler ) 256 257 $(document).on('submit', '.pms-form', async function (e) { 258 259 if( e.target && ( jQuery( e.target ).attr('id') == 'pms_recover_password_form' || jQuery( e.target ).attr('id') == 'pms_new_password_form' || jQuery( e.target ).attr('id') == 'pms_login' ) ) 260 return 261 262 var target_button = $('input[type="submit"], button[type="submit"]', $(this)).not('#pms-apply-discount').not('input[name="pms_redirect_back"]') 263 264 // Email Confirmation using PB form 265 var form = $(this).closest( 'form' ) 266 267 if( typeof form != 'undefined' && form && form.length > 0 && form.hasClass( 'pms-ec-register-form' ) ){ 268 269 stripeConnectPaymentGatewayHandler(e, target_button) 270 271 // Skip if the Go Back button was pressed 272 } else if ( !e.originalEvent || !e.originalEvent.submitter || $(e.originalEvent.submitter).attr('name') != 'pms_redirect_back' ) { 273 274 if ( $(e.originalEvent.submitter).attr('name') == 'pms_update_payment_method' ) 275 stripeConnectUpdatePaymentMethod(e, target_button) 276 else 277 stripeConnectPaymentGatewayHandler(e, target_button) 278 279 } 280 281 }) 282 283 $(document).on('submit', '.wppb-register-user', function (e) { 284 285 if ( ! ( $( '.wppb-recaptcha .wppb-recaptcha-element', $(e.currentTarget) ).hasClass( 'wppb-invisible-recaptcha' ) ) ) { 286 287 var target_button = $('input[type="submit"], button[type="submit"]', $(this)).not('#pms-apply-discount').not('input[name="pms_redirect_back"]') 288 289 stripeConnectPaymentGatewayHandler(e, target_button) 290 291 } 292 293 }) 294 295 async function stripeConnectPaymentGatewayHandler( e, target_button = false ){ 296 297 if( $('input[type=hidden][name=pay_gate]').val() != 'stripe_connect' && $('input[type=radio][name=pay_gate]:checked').val() != 'stripe_connect' ) 298 return 299 300 if( $('input[type=hidden][name=pay_gate]').is(':disabled') || $('input[type=radio][name=pay_gate]:checked').is(':disabled') ) 301 return 302 303 e.preventDefault() 304 305 $.pms_form_remove_errors() 306 307 var current_button = $(this) 308 309 // Current submit button can't be determined from `this` context in case of the Invisible reCaptcha handler 310 if( e.type == 'wppb_invisible_recaptcha_success' || e.type == 'wppb_v3_recaptcha_success' || e.type == 'pms_v3_recaptcha_success' ){ 311 312 // target_button is supplied to the handler starting with version 3.5.0 of Profile Builder, we use this for backwards compatibility 313 current_button = target_button == false ? $( 'input[type="submit"]', $( '.wppb-recaptcha-element' ).closest( 'form' ) ) : $( target_button ) 314 315 } else if ( e.type == 'submit' ){ 316 317 if( target_button != false ) 318 current_button = $( target_button ) 319 320 } 321 322 // Disable the button 323 current_button.attr( 'disabled', true ) 324 325 return pms_stripe_maybe_validate_recaptcha( current_button, e ).then( async function( recaptcha_response ){ 326 327 let target_elements = elements 328 329 if( $.pms_checkout_is_setup_intents() ) 330 target_elements = elements_setup_intent 331 332 // let add-ons escalate setup_future_usage to off_session before the ConfirmationToken is built (e.g. a recurring Order Bump under a non-recurring primary), so the token matches the server-side PaymentIntent 333 // - escalate-only: it never downgrades the value the primary plan already required, it only forces off_session when an add-on needs the payment method stored for future renewals 334 if( $(document).triggerHandler( 'pms_filter_stripe_connect_setup_future_usage', [ null, current_button.closest( 'form' ) ] ) === 'off_session' ) 335 target_elements.update( { setupFutureUsage: 'off_session' } ) 336 337 const {error: submitError} = await target_elements.submit() 338 339 if (submitError) { 340 let message = '' 341 342 if( submitError.message && submitError.message != '' ){ 343 message = submitError.message 344 } else { 345 message = 'An error occurred while processing your payment. Please try again.' 346 } 347 348 $.pms_stripe_add_credit_card_error( message ) 349 $.pms_form_scrollTo( '#pms-paygates-wrapper', current_button ) 350 return false 351 } 352 353 // Create the ConfirmationToken using the details collected by the Payment Element 354 const {error, confirmationToken}
354 = await stripe.createConfirmationToken({ 355 elements : target_elements, 356 }); 357 358 if (error) { 359 360 let message = '' 361 362 if( error.message && error.message != '' ){ 363 message = error.message 364 } else { 365 message = 'An error occurred while processing your payment. Please try again.' 366 } 367 368 $.pms_stripe_add_credit_card_error( message ) 369 $.pms_form_scrollTo( '#pms-paygates-wrapper', current_button ) 370 return false 371 } 372 373 return pms_stripe_process_checkout( current_button, confirmationToken.id ).then( async function( response ){ 374 375 // Handle validation errors 376 if( response.success == false && ( typeof response.data != 'undefined' || typeof response.wppb_errors != 'undefined' || typeof response.pms_errors != 'undefined' ) ){ 377 pms_stripe_handle_validation_errors( response, current_button ) 378 379 return false 380 } else if( response.success == false && typeof response.type != 'undefined' && response.type == 'use_stripe_sdk' ){ 381 382 let intent 383 384 if( $.pms_checkout_is_setup_intents() ){ 385 var { error, setupIntent } = await stripe.handleNextAction({ 386 clientSecret: response.client_secret 387 }); 388 389 intent = setupIntent 390 391 } else { 392 var { error, paymentIntent } = await stripe.handleNextAction({ 393 clientSecret: response.client_secret 394 }); 395 396 intent = paymentIntent 397 } 398 399 if( error && error.payment_intent ){ 400 intent = error.payment_intent 401 } else if ( error && error.setup_intent ){ 402 intent = error.setup_intent 403 } 404 405 // Process the payment on the server 406 const server_response = await pms_stripe_process_payment( intent, response, current_button ) 407 408 if ( typeof server_response.redirect_url != 'undefined' && server_response.redirect_url ){ 409 window.location.replace( server_response.redirect_url ) 410 411 return true 412 } 413 414 return false 415 416 } 417 418 419 // Redirect to the URL if the response contains a redirect URL 420 if ( typeof response.redirect_url != 'undefined' && response.redirect_url ){ 421 window.location.replace( response.redirect_url ) 422 return true 423 } 424 425 console.log( '[PMS Stripe] Something unexpected happened. Response: ' + response ) 426 427 return false; 428 429 }) 430 431 }) 432 } 433 434 async function pms_stripe_process_checkout( current_button, confirmationToken ){ 435 436 // grab all data from the form 437 var data = await $.pms_form_get_data( current_button, true ) 438 439 if( confirmationToken ){ 440 data['stripe_confirmation_token'] = confirmationToken 441 } 442 443 if( data == false ) 444 return 445 446 // expand array values into separate appends so multi-value fields reach the wire as repeated entries 447 // - FormData.append stringifies array values via comma-join, collapsing them into a single scalar 448 var form_data = new FormData() 449 450 for (var key in data) { 451 if ( Array.isArray( data[key] ) ) { 452 data[key].forEach( function ( value ) { 453 form_data.append( key, value ) 454 } ) 455 } else { 456 form_data.append( key, data[key] ) 457 } 458 } 459 460 return fetch( pms.ajax_url, { 461 method : 'post', 462 credentials: 'same-origin', // Required for WordPress cookie authentication 463 body : form_data 464 }).then(function (res) { 465 return res.json() 466 }).catch(error => { 467 console.error('Something went wrong:', error) 468 throw error 469 }) 470 471 } 472 473 async function pms_stripe_process_payment( payment_intent, user_data, target_button ){ 474 475 // update nonce 476 nonce_data = {} 477 nonce_data.action = 'pms_update_nonce' 478 479 // Update nonce 480 const nonce = await fetch(pms.ajax_url, { 481 method: 'POST', 482 credentials: 'same-origin', 483 headers: { 484 'Content-Type': 'application/x-www-form-urlencoded', 485 }, 486 body: new URLSearchParams({ 487 action: 'pms_update_nonce' 488 }) 489 }).then(function (res) { 490 return res.json() 491 }).catch(error => { 492 console.error('Something went wrong:', error) 493 throw error 494 }) 495 496 // get form data 497 var form_data = await $.pms_form_get_data( target_button ) 498 499 form_data.pmstkn_original = form_data.pmstkn 500 form_data.pmstkn = '' 501 502 const data = { 503 ...form_data, 504 action : 'pms_process_payment', 505 user_id : user_data.user_id, 506 payment_id : user_data.payment_id, 507 subscription_id : user_data.subscription_id, 508 subscription_plan_id: user_data.subscription_plan_id, 509 payment_intent : payment_intent.id, 510 current_page : window.location.href, 511 pms_nonce : nonce 512 }; 513 514 // Add subscription price if plans exist 515 if ( data.subscription_plans ) { 516 const priceKey = `subscription_price_${data.subscription_plans}`; 517 518 if ( form_data[priceKey] ) { 519 data[priceKey] = form_data[priceKey]; 520 } 521 } 522 523 // prepare data 524 var request_data = new FormData() 525 526 for (var key in data) { 527 request_data.append(key, data[key]) 528 } 529 530 // Process payment 531 return await fetch(pms.ajax_url, { 532 method : 'POST', 533 credentials: 'same-origin', 534 body : request_data 535 }).then(function (res) { 536 return res.json() 537 }).catch(error => { 538 console.error('Something went wrong:', error) 539 throw error 540 }) 541 542 } 543 544 // Update Payment Method 545 function stripeConnectInitUpdatePaymentMethod(){ 546 547 var $client_secret_setup_intent = $('.pms-form input[name="pms_stripe_connect_setup_intent"]').val() 548 549 if( $client_secret_setup_intent && $client_secret_setup_intent.length > 0 ){ 550 551 update_elements_setup_intent = stripe.elements({ clientSecret: $client_secret_setup_intent, appearance: stripe_appearance }) 552 553 $update_element = update_elements_setup_intent.create( "payment" ) 554 $update_element.mount("#pms-stripe-payment-elements") 555 556 // Show credit card form error messages to the user as they happpen 557 $update_element.addEventListener('change', creditCardErrorsHandler ) 558 559 pms_stripe_hide_spinner() 560 } 561 562 } 563 564 function stripeConnectUpdatePaymentMethod( e, target_button = false ){ 565 566 e.preventDefault() 567 568 $.pms_form_remove_errors() 569 570 var current_button = $(this) 571 572 if ( target_button != false ) 573 current_button = $( target_button ) 574 575 //Disable the button 576 current_button.attr('disabled', true) 577 578 // Add error if credit card was not completed 579 if (cardIsEmpty === true) { 580 $.pms_form_add_validation_errors([{ target: 'credit_card', message: pms.invalid_card_details_error }], current_button) 581 return 582 } 583
584 stripe.confirmSetup({ 585 elements: update_elements_setup_intent, 586 confirmParams: { 587 return_url: pms.stripe_return_url, 588 payment_method_data: { billing_details: pms_stripe_get_billing_details() } 589 }, 590 redirect: 'if_required', 591 }).then(function (result) { 592 593 let token 594 595 if (result.error && result.error.decline_code && result.error.decline_code == 'live_mode_test_card') { 596 let errors = [{ target: 'credit_card', message: result.error.message }] 597 598 $.pms_form_add_validation_errors(errors, current_button) 599 } else if (result.error && result.error.type && result.error.type == 'validation_error') 600 $.pms_form_reset_submit_button(current_button) 601 else { 602 if (result.error && result.error.setup_intent) 603 token = { id: result.error.setup_intent.id } 604 else if (result.setupIntent) 605 token = { id: result.setupIntent.payment_method } 606 else 607 token = '' 608 609 stripeTokenHandler(token, $(current_button).closest('form')) 610 } 611 612 }) 613 614 } 615 616 function stripeConnectInit(){ 617 618 var target_elements_instance = false 619 var target_elements_instance_slug = '' 620 621 // Update Payment Method SetupIntent 622 if ( $('#pms-update-payment-method-form #pms-stripe-payment-elements').length > 0 ){ 623 stripeConnectInitUpdatePaymentMethod(); 624 return false; 625 // SetupIntent 626 } else if ( $.pms_checkout_is_setup_intents() ) { 627 target_elements_instance = elements_setup_intent 628 target_elements_instance_slug = 'setup_intents' 629 // PaymentIntents 630 } else { 631 target_elements_instance = elements 632 target_elements_instance_slug = 'payment_intents' 633 } 634 635 let selected_subscription = jQuery( subscription_plan_selector + '[type=radio]' ).length > 0 ? jQuery( subscription_plan_selector + '[type=radio]:checked' ) : jQuery( subscription_plan_selector + '[type=hidden]' ) 636 637 // Handle Setup Future Usage parameter 638 let primary_setup_future_usage = pms_stripe_connect_setup_future_usage_for_input( selected_subscription ) 639 640 if( typeof primary_setup_future_usage !== 'undefined' ) 641 elements.update( { setupFutureUsage: primary_setup_future_usage } ); 642 643 if( target_elements_instance != false ){ 644 645 if( $payment_element == '' ){ 646 647 if( typeof selected_subscription != 'undefined' && target_elements_instance_slug == 'payment_intents' ){ 648 649 // Use default price if custom currency price is unavailable 650 let price = (selected_subscription.data('mc_price') !== undefined && selected_subscription.data('mc_price') !== null) ? parseFloat( selected_subscription.data('mc_price') ) : parseFloat( selected_subscription.data('price') ); 651 652 // Take into account sign-up fees as well 653 let sign_up_fee = parseFloat( selected_subscription.data('sign_up_fee') ); 654 655 if( !isNaN( sign_up_fee ) && sign_up_fee > 0 ){ 656 price = price + sign_up_fee; 657 } 658 659 if( price > 0 ){ 660 target_elements_instance.update( { amount: pms_stripe_convert_amount_to_cents( price ) } ); 661 } 662 } 663 664 let default_values = pms_stripe_get_default_values() 665 666 $payment_element = target_elements_instance.create( "payment", default_values ) 667 $payment_element.mount("#pms-stripe-payment-elements") 668 669 // Show credit card form error messages to the user as they happpen 670 $payment_element.addEventListener('change', creditCardErrorsHandler ) 671 672 } else { 673 674 // Update the amount of the payment element 675 if( typeof selected_subscription != 'undefined' && target_elements_instance_slug == 'payment_intents' ){ 676 677 // Use default price if custom currency price is unavailable 678 let price = (selected_subscription.data('mc_price') !== undefined && selected_subscription.data('mc_price') !== null) ? parseFloat( selected_subscription.data('mc_price') ) : parseFloat( selected_subscription.data('price') ); 679 680 // Take into account sign-up fees as well 681 let sign_up_fee = parseFloat( selected_subscription.data('sign_up_fee') ); 682 683 if( !isNaN( sign_up_fee ) && sign_up_fee > 0 ){ 684 price = price + sign_up_fee; 685 } 686 687 if( price > 0 ){ 688 target_elements_instance.update( { amount: pms_stripe_convert_amount_to_cents( price ) } ); 689 } 690 } 691 692 let default_values = pms_stripe_get_default_values() 693 694 if( $elements_instance_slug != target_elements_instance_slug ){ 695 696 $payment_element.destroy() 697 698 $payment_element = target_elements_instance.create( "payment", default_values ) 699 $payment_element.mount("#pms-stripe-payment-elements") 700 701 // Show credit card form error messages to the user as they happpen 702 $payment_element.addEventListener('change', creditCardErrorsHandler ) 703 704 } 705 706 } 707 708 $elements_instance_slug = target_elements_instance_slug 709 710 pms_stripe_hide_spinner() 711 712 if( typeof paymentSidebarPosition == 'function' ){ 713 setTimeout( paymentSidebarPosition, 300 ) 714 } 715 716 } 717 718 } 719 720 async function stripeConnectUpdatePaymentIntent(){ 721 722 if( !$client_secret || !( $client_secret.length > 0 ) ) 723 return 724 725 // Don't make this call when a Free Trial subscription is selected since we use the prepared SetupIntent 726 if ( $.pms_checkout_is_setup_intents() || $( '#pms-update-payment-method-form' ).length > 0 ) 727 return 728 729 if( updating_payment_intent ) 730 return 731 732 updating_payment_intent = true 733
734 var submitButton = $('.pms-form .pms-form-submit, .pms-form input[type="submit"], .pms-form button[type="submit"], .wppb-register-user input[type="submit"], .wppb-register-user button[type="submit"]').not('#pms-apply-discount, .login-submit #wp-submit') 735 736 var data = $.pms_form_get_data( submitButton ) 737 738 data.action = 'pms_update_payment_intent_connect' 739 data.pms_nonce = $('#pms-stripe-ajax-update-payment-intent-nonce').val() 740 data.intent_secret = $client_secret 741 742 data.pmstkn_original = data.form_type == 'pms' ? $('.pms-form #pmstkn').val() : 'wppb_register' 743 data.pmstkn = '' 744 745 return await $.post(pms.ajax_url, data, function (response) { 746 747 if( typeof response == 'undefined' || response == '' ){ 748 updating_payment_intent = false 749 return false; 750 } 751 752 response = JSON.parse( response ) 753 754 if ( response.status == 'requires_payment_method' ) { 755 updating_payment_intent = false 756 757 elements.fetchUpdates().then( function(elements_response){ 758 if( typeof paymentSidebarPosition == 'function' ){ 759 setTimeout( paymentSidebarPosition, 300 ) 760 } 761 762 return true; 763 }) 764 } 765 766 updating_payment_intent = false 767 768 return false; 769 770 }) 771 772 } 773 774 function stripeConnectProcessPayment( result, user_data, form_data, target_button ){ 775 776 // update nonce 777 nonce_data = {} 778 nonce_data.action = 'pms_update_nonce' 779 780 $.post(pms.ajax_url, nonce_data, function (response) { 781 782 response = JSON.parse(response) 783 784 data = {} 785 data.action = 'pms_process_payment' 786 data.user_id = user_data.user_id 787 data.payment_id = user_data.payment_id 788 data.subscription_id = user_data.subscription_id 789 data.subscription_plan_id = user_data.subscription_plan_id 790 data.pms_current_subscription = form_data.pms_current_subscription 791 data.current_page = window.location.href 792 data.pms_nonce = response 793 data.form_type = form_data.form_type ? form_data.form_type : '' 794 data.pmstkn_original = form_data.pmstkn ? form_data.pmstkn : '' 795 data.setup_intent = form_data.setup_intent ? form_data.setup_intent : '' 796 data.user_consent_logged_in = form_data.user_consent_logged_in ? form_data.user_consent_logged_in : '' 797 798 if( data.setup_intent == '' ) 799 data.payment_intent = $client_secret_id 800 else 801 data.payment_intent = $client_secret_setup_id 802 803 // to determine actual location for change subscription 804 data.form_action = form_data.form_action ? form_data.form_action : '' 805 806 // for member data 807 data.pay_gate = form_data.pay_gate ? form_data.pay_gate : '' 808 data.subscription_plans = form_data.subscription_plans ? form_data.subscription_plans : '' 809 810 if( data.subscription_plans ) 811 data['subscription_price_' + data.subscription_plans] = form_data['subscription_price_' + data.subscription_plans] 812 813 // custom profile builder form name 814 data.form_name = form_data.form_name ? form_data.form_name : '' 815 816 if( form_data.pms_default_recurring ) 817 data.pms_default_recurring = form_data.pms_default_recurring 818 819 if ( form_data.pms_recurring ) 820 data.pms_recurring = form_data.pms_recurring 821 822 if ( form_data.discount_code ) 823 data.discount_code = form_data.discount_code 824 825 if ( form_data.group_name ) 826 data.group_name = form_data.group_name 827 828 if ( form_data.group_description ) 829 data.group_description = form_data.group_description 830 831 // add billing details 832 if ( form_data.pms_billing_address ) 833 data.pms_billing_address = form_data.pms_billing_address 834 835 if ( form_data.pms_billing_city ) 836 data.pms_billing_city = form_data.pms_billing_city 837 838 if ( form_data.pms_billing_country ) 839 data.pms_billing_country = form_data.pms_billing_country 840 841 if ( form_data.pms_billing_state ) 842 data.pms_billing_state = form_data.pms_billing_state 843 844 if ( form_data.pms_billing_zip ) 845 data.pms_billing_zip = form_data.pms_billing_zip 846 847 if ( form_data.pms_vat_number ) 848 data.pms_vat_number = form_data.pms_vat_number 849 850 if ( form_data.wppb_referer_url ) 851 data.wppb_referer_url = form_data.wppb_referer_url 852 853 $.post(pms.ajax_url, data, function (response) { 854 855 response = JSON.parse(response) 856 857 if( typeof response.redirect_url != 'undefined' && response.redirect_url ) 858 window.location.replace( response.redirect_url ) 859 860 }) 861 862 }) 863 864 } 865 866 /* 867 * Stripe response handler 868 * 869 */ 870 function stripeTokenHandler( token, $form = null ) { 871 872 if( $form === null ) 873 $form = $(payment_buttons).closest('form') 874 875 $form.append( $('<input type="hidden" name="stripe_token" />').val( token.id ) ) 876 877 // We have to append a hidden input to the form to simulate that the submit 878 // button has been clicked to have it to the $_POST 879 var button_name = $form.find('input[type="submit"], button[type="submit"]').not('#pms-apply-discount').not('input[name="pms_redirect_back"]').attr('name') 880 var button_value = $form.find('input[type="submit"], button[type="submit"]').not('#pms-apply-discount').not('input[name="pms_redirect_back"]').val() 881 882 $form.append( $('<input type="hidden" />').val( button_value ).attr('name', button_name ) ) 883 884 $form.get(0).submit() 885 886 } 887 888 function pms_stripe_get_billing_details() { 889 890 var data = {} 891 892 var email = $( '.pms-form input[name="user_email"], .wppb-user-forms input[name="email"]' ).val() 893 894 if( typeof email == 'undefined' || email == '' ) 895 data.email = $( '.pms-form input[name="pms_billing_email"]' ).val() 896 897 if( typeof email != 'undefined' && email != '' ) 898 data.email = email.replace(/\s+/g, '') // remove any whitespace that might be present in the email 899 900 var name = '' 901 902 if( $( '.pms-billing-details input[name="pms_billing_first_name"]' ).length > 0 ) 903 name = name + $( '.pms-billing-details input[name="pms_billing_first_name"]' ).val() + ' ' 904 else if( $( '.pms-form input[name="first_name"], .wppb-user-forms input[name="first_name"]' ).length > 0 ) 905 name = name + $( '.pms-form input[name="first_name"], .wppb-user-forms input[name="first_name"]' ).val() + ' ' 906 907 if( $( '.pms-billing-details input[name="pms_billing_last_name"]' ).length > 0 ) 908 name = name + $( '.pms-billing-details input[name="pms_billing_last_name"]' ).val() 909 else if( $( '.pms-form input[name="last_name"], .wppb-user-forms input[name="last_name"]' ).length > 0 ) 910 name = name + $( '.pms-form input[name="last_name"], .wppb-user-forms input[name="last_name"]' ).val() 911 912 if( name.length > 1 ) 913 data.name = name 914 915 if( $( '.pms-billing-details ').length > 0 ){ 916 917 data.address = { 918 city : $( '.pms-billing-details input[name="pms_billing_city"]' ).val(), 919 country : $( '.pms-billing-details input[name="pms_billing_country"]' ).val(), 920 line1 : $( '.pms-billing-details input[name="pms_billing_address"]' ).val(), 921 postal_code : $( '.pms-billing-details input[name="pms_billing_zip"]' ).val(), 922 state : $( '.pms-billing-details input[name="pms_billing_state"]' ).val() 923 } 924 925 } 926 927 return data 928 929 } 930
931 function creditCardErrorsHandler( event ){ 932 933 if( event.complete == true ) 934 cardIsEmpty = false 935 else 936 cardIsEmpty = true 937 938 if( typeof paymentSidebarPosition == 'function' ){ 939 setTimeout( paymentSidebarPosition, 300 ) 940 } 941 942 } 943 944 function pms_stripe_handle_validation_errors( response, current_button ){ 945 946 var form_type = $('.wppb-register-user .wppb-subscription-plans').length > 0 ? 'wppb' : $('.pms-ec-register-form').length > 0 ? 'pms_email_confirmation' : 'pms' 947 948 // Paid Member Subscription forms 949 if (response.data && ( form_type == 'pms' || form_type == 'pms_email_confirmation' ) ){ 950 $.pms_form_add_validation_errors( response.data, current_button ) 951 // Profile Builder form 952 } else { 953 954 // Add PMS related errors (Billing Fields) 955 // These are added first because the form will scroll to the error and these 956 // are always placed at the end of the WPPB form 957 if( response.pms_errors && response.pms_errors.length > 0 ) 958 $.pms_form_add_validation_errors( response.pms_errors, current_button ) 959 960 // Add WPPB related errors 961 if( typeof response.wppb_errors == 'object' ) 962 $.pms_form_add_wppb_validation_errors( response.wppb_errors, current_button ) 963 964 } 965 966 jQuery(document).trigger( 'pms_checkout_validation_error', response, current_button ) 967 968 } 969 970 async function pms_stripe_maybe_validate_recaptcha( current_button, event = null ){ 971 972 if( typeof pms_initialize_recaptcha_v3 == 'function' ){ 973 974 let form = current_button.closest('form') 975 976 var recaptcha_field = jQuery('.pms-recaptcha', form ) 977 978 if( recaptcha_field.length > 0 ){ 979 980 return await pms_initialize_recaptcha_v3( event, form ) 981 982 } 983 984 } 985 986 let wppb_form = current_button.closest('.wppb-register-user') 987 988 if( wppb_form.length > 0 && wppb_form[0].length > 0 && typeof wppbInitializeRecaptchaV3 == 'function' ){ 989 990 let wppb_recaptcha_field = jQuery('.wppb-recaptcha .wppb-recaptcha-element', wppb_form ) 991 992 if( wppb_recaptcha_field.length > 0 ){ 993 return await wppbInitializeRecaptchaV3( event, wppb_form ) 994 } 995 996 } 997 998 return true 999 1000 } 1001 1002 function pms_stripe_convert_amount_to_cents( amount ){ 1003 1004 let currency = pms.currency 1005 1006 // List of zero-decimal currencies 1007 const zero_decimal_currencies = [ 1008 'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 1009 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF' 1010 ]; 1011 1012 // If currency is not in zero-decimal list, multiply by 100 1013 if ( !zero_decimal_currencies.includes( currency.toUpperCase() ) ) { 1014 amount = amount * 100; 1015 } 1016 1017 // Round to ensure we have a whole number 1018 return Math.round( amount ); 1019 } 1020 1021 /** 1022 * Validate custom currency 1023 * 1024 * @param subscriptionPlan 1025 * @returns {Promise<*>} 1026 */ 1027 async function pms_stripe_validate_sdk_checkout_currency( subscriptionPlan ) { 1028 1029 pms_stripe_show_spinner() 1030 1031 return $.ajax({ 1032 url: pms.ajax_url, 1033 type: 'POST', 1034 data: { 1035 action : 'pms_validate_sdk_currency', 1036 pms_nonce : pms.pms_validate_currency_nonce, 1037 subscription_plan_id: subscriptionPlan.val(), 1038 pms_mc_currency : subscriptionPlan.data('mc_currency'), 1039 subscription_id : subscriptionPlan.closest('form').find('input[name="pms_current_subscription"]').val(), 1040 pay_gate : 'stripe_connect' 1041 }, 1042 dataType: 'json', 1043 }).done( function ( response ) { 1044 1045 if ( response && response.success && response.currency ) 1046 elements.update( { currency: response.currency.toLowerCase() } ) 1047 1048 pms_stripe_hide_spinner() 1049 1050 return; 1051 1052 }).fail( function () { 1053 1054 pms_stripe_hide_spinner() 1055 1056 }); 1057 } 1058 1059 function pms_stripe_hide_spinner(){ 1060 1061 jQuery('#pms-stripe-payment-elements').show() 1062 jQuery( '#pms-stripe-connect .pms-spinner__holder' ).hide() 1063 1064 } 1065 1066 function pms_stripe_show_spinner(){ 1067 1068 jQuery('#pms-stripe-payment-elements').hide() 1069 jQuery( '#pms-stripe-connect .pms-spinner__holder' ).show() 1070 1071 } 1072 1073 function pms_stripe_get_default_values(){ 1074 1075 if( jQuery('body').hasClass( 'logged-in' ) && pms.pms_customer_email ){ 1076 var user_email = pms.pms_customer_email 1077 } else { 1078 var user_email = $( '.pms-form input[name="user_email"], .wppb-register-user input[name="email"]' ).val() 1079 } 1080 1081 let name = '' 1082 1083 if( jQuery('body').hasClass( 'logged-in' ) && pms.pms_customer_name ){ 1084 name = pms.pms_customer_name 1085 } else { 1086 if( $( '.pms-billing-details input[name="pms_billing_first_name"]' ).length > 0 ) 1087 name = name + $( '.pms-billing-details input[name="pms_billing_first_name"]' ).val() + ' ' 1088 else if( $( '.pms-form input[name="first_name"], .wppb-user-forms input[name="first_name"]' ).length > 0 ) 1089 name = name + $( '.pms-form input[name="first_name"], .wppb-user-forms input[name="first_name"]' ).val() + ' ' 1090 1091 if( $( '.pms-billing-details input[name="pms_billing_last_name"]' ).length > 0 ) 1092 name = name + $( '.pms-billing-details input[name="pms_billing_last_name"]' ).val() 1093 else if( $( '.pms-form input[name="last_name"], .wppb-user-forms input[name="last_name"]' ).length > 0 ) 1094 name = name + $( '.pms-form input[name="last_name"], .wppb-user-forms input[name="last_name"]' ).val() 1095 } 1096 1097 if( typeof user_email != 'undefined' && user_email.length > 0 ){ 1098 var default_values = { 1099 defaultValues: { 1100 billingDetails: { 1101 email: user_email, 1102 name : name.length > 0 ? name : null 1103 } 1104 } 1105 } 1106 } else { 1107 var default_values = {} 1108 } 1109 1110 return default_values 1111 1112 } 1113 1114} 1115 1116// Initialize Stripe Connect when document is ready 1117jQuery( function() { 1118 try { 1119 pms_stripe_maybe_load_gateway( jQuery ); 1120 } catch ( err ) { 1121 console.error( '[PMS Stripe] Error during init:', err ); 1122 } 1123}); 1124 1125// Maybe initialize Stripe when Elementor popup is shown 1126jQuery(document).on('elementor/popup/show', function () { 1127 if ( jQuery('.pms-form #pms-stripe-connect', jQuery('.elementor-popup-modal') ).length > 0 ) { 1128 pms_stripe_maybe_load_gateway( jQuery ) 1129 1130 // By default, the regular submit event of the form is not triggered when the button is clicked inside the popup. 1131 // We simulate a submit event to trigger the form submission on the click event. 1132 document.addEventListener('click', function (ev) { 1133 const btn = ev.target.closest('input[type="submit"], button[type="submit"]'); 1134 if (!btn) return; 1135 const form = btn.form || btn.closest('form'); 1136 if (!form) return; 1137 if (!form.classList.contains('pms-form')) return; 1138 1139 ev.preventDefault(); 1140 ev.stopImmediatePropagation(); 1141 1142 const submitEvent = new Event('submit', { bubbles: true, cancelable: true }); 1143 form.dispatchEvent(submitEvent); 1144 }, true); 1145 } 1146}); 1147 1148
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.