1window.twwp_checkout = { 2 $initialized: false, 3 $checkout_form: jQuery('form.checkout'), 4 $block_checkout: false, 5 $paymentNeeded: true, 6 $intentData:null, 7 $paymentClientSecret:null, 8 $intentId:null, 9 $elements:null, 10 11 12 $options: { stripeAccount: twwp_config.stripe_account_id }, 13 init: function() { 14 if(twwp_config.edit_page === 'yes') { 15 return; 16 } 17 if (0 === jQuery('form.checkout').length) { 18 this.$checkout_form = jQuery('form.wc-block-checkout__form'); 19 this.$block_checkout = true; 20 } 21 else { 22 this.$checkout_form = jQuery('form.checkout'); 23 } 24 if ('undefined' != typeof Stripe && jQuery('#twwp-card-element').length) { 25 if (!this.$initialized) { 26 this.stripe = Stripe(twwp_config.stripe_public_key, this.$options); 27 this.initPayment(); 28 29 this.$checkout_form.on('checkout_place_order_tenweb_payments_stripe', function(event, wc_checkout_form) { 30 let $form = jQuery( this ); 31 event.preventDefault(); 32 if(window.twwp_checkout.$paymentNeeded) { 33 window.twwp_checkout.handle10WebPay(event, wc_checkout_form, $form, window.twwp_checkout.$elements); 34 return false; 35 } 36 }); 37 this.$initialized = true; 38 } 39 else if (!jQuery('#twwp-card-element').hasClass('StripeElement')) { 40 if (window.twwp_checkout && window.twwp_checkout.paymentElement && typeof window.twwp_checkout.paymentElement.mount === 'function') { 41 window.twwp_checkout.paymentElement.mount('#twwp-card-element'); 42 } 43 } 44 } 45 }, 46 47 /** 48 * Initializes the Stripe payment process on the webpage. 49 * This function performs the following tasks: 50 * 1. Awaits the completion of the `setPaymentIntent` function to set up the payment intent on the server 51 * and obtain the client secret required for Stripe payment processing. 52 * 2. Defines appearance settings for the Stripe Payment Element to customize its look and feel. 53 * 3. Configures options for the Payment Element, such as which fields to display or hide. 54 * Note: The country field configuration should be adjusted based on actual requirements. 55 * 4. Creates a Stripe Payment Element instance using the client secret and appearance settings. 56 * 5. Mounts the Payment Element to the specified DOM element with the ID 'twwp-card-element'. 57 * 6. Sets up an event listener to handle changes in the Payment Element, displaying error messages if any 58 * occur during user input. 59 */ 60 initPayment: async function() { 61 try { 62 // Asynchronously set the payment intent for the transaction 63 await this.setPaymentIntent(); 64 65 // Define the appearance settings for the Payment Element (these settings are not provided in the code) 66 let variables = {}; 67 if(typeof twwp_config.payment_text_color !== 'undefined') { 68 variables.colorText = twwp_config.payment_text_color; 69 } 70 variables.fontFamily = 'Inter'; 71 const appearance = { 72 theme: 'stripe', 73 variables: variables 74 }; 75 76 // Configure options for the payment element, particularly focusing on fields to display 77 const options = { 78 fields: { 79 billingDetails: { 80 address: { 81 country: 'never', 82 }, 83 } 84 }, 85 wallets: { 86 applePay: 'auto', 87 googlePay: 'auto', 88 }, 89 paymentMethodOrder: [ 90 'card', 91 'googlePay', 92 'applePay', 93 // TODO: Add other payment methods here 94 ], 95 }; 96 // Retrieve the client secret for the payment intent, which is required to authenticate the payment 97 let clientSecret = this.$paymentClientSecret; 98 99 // Initialize Stripe elements with the client secret and appearance settings 100 window.twwp_checkout.$elements = this.stripe.elements({ clientSecret, appearance }); 101 102 // Create the payment element using the Stripe elements instance with specified options 103 window.twwp_checkout.paymentElement = this.$elements.create('payment', options); 104 105 // Mount the payment element to the specified DOM element
106 window.twwp_checkout.paymentElement.mount('#twwp-card-element'); 107 // Add an event listener to handle changes in the payment element and display errors if any 108 window.twwp_checkout.paymentElement.on('change', function(event) { 109 // Get the element where error messages will be displayed 110 var displayError = jQuery('#twwp-card-errors'); 111 112 // If there's an error in the payment element, display the error message 113 if (event.error) { 114 displayError.text(event.error.message); 115 } else { 116 // Clear the error message if no error is present 117 displayError.text(''); 118 } 119 }); 120 }catch (error) { 121 console.error('Error retrieving new clientSecret:', error); 122 } 123 124 125 126 }, 127 getPaymentIntentFromSession: function() { 128 const cookieHash = twwp_config.cookiehash; 129 if (cookieHash) { 130 var paymentIntent = Cookies.get('twwp_payment_intent_' + cookieHash); 131 if (paymentIntent) { 132 paymentIntent = JSON.parse(paymentIntent); 133 const intentId = paymentIntent['intentId']; 134 const clientSecret = paymentIntent['clientSecret']; 135 const cartHash = paymentIntent['cartHash']; 136 return { intentId, clientSecret, cartHash}; 137 } 138 } 139 return {}; 140 }, 141 deletePaymentIntentFromSession: function() { 142 this.$intentData = null; 143 this.$paymentClientSecret = null; 144 this.$intentId = null; 145 const cookieHash = twwp_config.cookiehash; 146 if (cookieHash) { 147 Cookies.remove('twwp_payment_intent_' + cookieHash, {path: twwp_config.cookiepath}); 148 }
149 // TODO: find the best way to generate a new payment intent 150 }, 151 blockForm: function () { 152 if (!this.$block_checkout) { 153 this.$checkout_form.addClass( 'processing' ).block( { 154 message: null, 155 overlayCSS: { 156 background: '#fff', 157 opacity: 0.6 158 } 159 } ); 160 } 161 }, 162 unblockForm: function () { 163 if (!this.$block_checkout) { 164 this.$checkout_form.removeClass( 'processing' ).unblock(); 165 } 166 }, 167 168 /** 169 * Handles payment intent actions such as create, update, and retrieve intent data. 170 * Sends necessary data via POST to the server and processes the response. 171 * 172 * @param {string} action - The action to be performed (e.g., 'create', 'update'). 173 * @param {string} intentId - The ID of the payment intent. 174 * @param {string|null} [order_id=null] - Optional order ID if available. 175 * @returns {Promise<Object>} - A Promise that resolves to an object containing intentId and clientSecret, or an empty object if unsuccessful. 176 */ 177 paymentIntentActions: async function(action, intentId, order_id = null) { 178 let formData = new FormData(); 179 formData.append('action', 'twwp_stripe_create_intent'); 180 formData.append('nonce', twwp_config.ajaxnonce); 181 formData.append('intentAction', action); 182 formData.append('intentId', intentId); 183 if(order_id) { 184 formData.append('order_id', order_id); 185 } 186 // Sends a POST request to the server using the provided form data to either create or update a payment intent. 187 const response = await fetch(twwp_config.ajaxurl, { 188 method: "POST", 189 body: formData, 190 }); 191 192 const { success, data } = await response.json(); 193 // Fetch updates for the PaymentIntent to reflect any changes (e.g., new amount). 194 if(action ==='update' && window.twwp_checkout.$elements){ 195 await window.twwp_checkout.$elements.fetchUpdates(); 196 } 197 if (success === true) { 198 const { intentId, clientSecret } = data; 199 return { intentId, clientSecret }; 200 } 201 return {}; 202 }, 203 204 setPaymentIntent: async function(){ 205 const cartHash = Cookies.get('woocommerce_cart_hash'); // Get the current cart hash from cookies 206 let paymentIntentAction = 'create'; 207 try { 208 // Retrieve payment intent from session 209 this.$intentData = await this.getPaymentIntentFromSession(); 210 211 if (this.$intentData.intentId) { 212 // If an intent ID exists, prepare to update it 213 paymentIntentAction = 'update'; 214 this.$intentId = this.$intentData.intentId; 215 } 216 217 // Check if the cart hash matches; if not, update or create payment intent 218 if (cartHash !== this.$intentData.cartHash) { 219 this.$intentData = await this.paymentIntentActions(paymentIntentAction, this.$intentId); 220 if(paymentIntentAction === 'update' && Object.keys(this.$intentData).length === 0){ 221 this.deletePaymentIntentFromSession(); 222 await this.setPaymentIntent(); 223 } 224 } 225 226 // Store the client secret and intent ID from the payment intent data 227 this.$paymentClientSecret = this.$intentData.clientSecret; 228 this.$intentId = this.$intentData.intentId; 229 }catch (error) { 230 231 } 232 }, 233 234 /** 235 * Asynchronously validates the checkout form by sending a POST request 236 * to the WooCommerce checkout URL with serialized form data. 237 * 238 * @param {jQuery} form - The jQuery object representing the form to be validated. 239 * @returns {Promise} - A promise that resolves with the response if the request is successful, or rejects with an error if the request fails. 240 */ 241 validateCheckoutForm: async function(form, intentId) { 242 // Convert the form element to FormData object for easier manipulation 243 const formData = new FormData(form[0]); 244 const cookieHash = twwp_config.cookiehash; 245 // Append a custom field to indicate the validation action 246 formData.append('twwp_checkout_validation', 'true'); 247 formData.append('intent_id', intentId); 248 formData.append('intent_cookie_id', 'twwp_payment_intent_' + twwp_config.cookiehash); 249 250 // Serialize the FormData object to a query string format for AJAX submission 251 const formDataSerialized = new URLSearchParams(formData).toString(); 252 253 // Return a promise to handle the asynchronous AJAX request 254 return new Promise((resolve, reject) => { 255 // Make an AJAX request to the checkout URL with the serialized form data 256 jQuery.ajax({ 257 type: 'POST', 258 url: wc_checkout_params.checkout_url, 259 data: formDataSerialized, 260 dataType: 'json', 261 }) 262 .done(response => resolve(response)) 263 .fail((jqXHR, textStatus, errorThrown) => reject(errorThrown)); 264 }); 265 }, 266 /** 267 * handle10WebPay - Handles the payment process during checkout using Stripe. 268 * This function validates the checkout form, retrieves or updates the payment intent, 269 * creates a payment method, and confirms the payment with Stripe. It manages form 270 * blocking/unblocking and handles any errors that occur during the process. 271 * 272 * @param {Object} event - The event object from the checkout submission. 273 * @param {Object} wc_checkout_form - The WooCommerce checkout form object. 274 * @param {Object} $form - The jQuery object of the checkout form. 275 */ 276 handle10WebPay: async function(event, wc_checkout_form, $form,elements) { 277 // Block the checkout form to prevent multiple submissions 278 window.twwp_checkout.blockForm(); 279 280 // Validate the checkout form 281 let validate_data = await this.validateCheckoutForm($form, this.$intentId); 282 if(typeof validate_data.order_id !== 'undefined') { 283 this.$intentData = await this.paymentIntentActions('update', this.$intentId, validate_data.order_id); 284 } 285 if (validate_data.result !== "validation_success") { 286 // If validation fails, unblock the form and display an error message 287 window.twwp_checkout.unblockForm(); 288 if (validate_data.messages) { 289 wc_checkout_form.submit_error(validate_data.messages); 290 } 291 return false;
292 } 293 try { 294 if (this.$paymentClientSecret) { 295 // Append intent ID to the form as a hidden input 296 window.twwp_checkout.$checkout_form.find('.twwp_intentId').remove(); // Corrected the usage to find the element before removing 297 window.twwp_checkout.$checkout_form.append(`<input class="twwp_intentId" type='hidden' name='intentId' value='${this.$intentId}'>`); 298 const name = jQuery('#billing_first_name').length && jQuery('#billing_last_name').length ? 299 jQuery('#billing_first_name').val() + ' ' + jQuery('#billing_last_name').val() : undefined; 300 301 const postal_code = jQuery('#billing_postcode').length ? jQuery('#billing_postcode').val() : undefined; 302 const billing_country = jQuery('#billing_country').length ? jQuery('#billing_country').val() : undefined; 303 const billing_phone = jQuery('#billing_phone').length ? jQuery('#billing_phone').val() : undefined; 304 const billing_state = jQuery('#billing_state').length ? jQuery('#billing_state').val() : undefined; 305 const billing_email = jQuery('#billing_email').length ? jQuery('#billing_email').val() : undefined; 306 307// Prepare billing details object 308 const billingDetails = {}; 309 if (name) billingDetails.name = name; 310 if (billing_email) billingDetails.email = billing_email; 311 if (billing_phone) billingDetails.phone = billing_phone; 312 if (billing_state || postal_code || billing_country) { 313 billingDetails.address = {}; 314 if (billing_state) billingDetails.address.state = billing_state; 315 if (postal_code) billingDetails.address.postal_code = postal_code; 316 if (billing_country) billingDetails.address.country = billing_country; 317 } 318 319 const result = await window.twwp_checkout.stripe.confirmPayment({ 320 elements, 321 confirmParams: { 322 payment_method_data: { 323 billing_details: billingDetails 324 } 325 }, 326 redirect: 'if_required', 327 }); 328 if (result.error) { 329 if(result.error.code === 'payment_intent_unexpected_state'){ 330 await this.deletePaymentIntentFromSession(); 331 } 332 window.twwp_checkout.unblockForm(); 333 wc_checkout_form.submit_error(`<div class="woocommerce-error">Payment failed: ${result.error.message}
333.</div>`); 334 } else { 335 // Payment succeeded, unblock the form and submit it to complete the order 336 window.twwp_checkout.$paymentNeeded = false; 337 window.twwp_checkout.unblockForm(); 338 window.twwp_checkout.$checkout_form.trigger('submit'); 339 } 340 } else { 341 // Payment intent not found 342 throw new Error('Payment intent not found.'); 343 } 344 } catch (error) { 345 console.log(error); 346 // Catch and handle any errors 347 window.twwp_checkout.unblockForm(); 348 wc_checkout_form.submit_error(`<div class="woocommerce-error">${error.message}</div>`); 349 } 350 }, 351 resetFields: function(field_ids) { 352 jQuery('#twwp_payengine_card_token').remove(); 353 jQuery('#twwp_payengine_browser_info').remove(); 354 jQuery('#twwp_payengine_3ds_succeeded').remove(); 355 window.twwp_checkout.$performing3ds = false; 356 window.twwp_checkout.$3dsresponse = false; 357 } 358}; 359jQuery(document).ready(function(){ 360 jQuery('body').on('updated_checkout', function () { 361 window.twwp_checkout.init(); 362 }); 363}); 364 365window.addEventListener( 'elementor/frontend/init', () => { 366 // The first one should be a general solution, unfortunately it does not work, so had to use this one. This will work for one widget only. 367 elementorFrontend.hooks.addAction( 'frontend/element_ready/twbb_woocommerce-checkout-page.default', function ( $scope ) { 368 window.twwp_checkout.init(); 369 }); 370} );
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.