PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.32.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.32.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / square / js / frontend.js

frontend.js in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.32.1, at square/js/frontend.js

332 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function() {
2 if ( ! window.frmSquareVars ) {
3 return;
4 }
5
6 const { appId } = frmSquareVars;
7 const { locationId } = frmSquareVars;
8
9 // Track the state of the Square card element
10 let squareCardElementIsComplete = false;
11 let thisForm = null;
12 let running = 0;
13
14 let cardGlobal;
15
16 const buyerTokens = {};
17
18 // Track the state of each field in the card form.
19 // postalCode is not included by default because Square hides it
20 // for cards issued in countries that don't require it (e.g., Australia).
21 const cardFields = {
22 cardNumber: false,
23 expirationDate: false,
24 cvv: false
25 };
26
27 async function initializeCard( payments ) {
28 const cardElement = document.querySelector( '.frm-card-element' );
29 if ( ! cardElement ) {
30 return;
31 }
32
33 const card = await payments.card();
34 const cardStyle = frmSquareVars.style;
35 await card.attach( '.frm-card-element' );
36
37 card.configure( { style: cardStyle } );
38
39 // Track when the postal code field is rendered by Square.
40 // Square hides the postal code for cards issued in certain countries.
41 card.addEventListener( 'focusClassAdded', event => {
42 const { field } = event.detail;
43 if ( field === 'postalCode' ) {
44 cardFields.postalCode = event.detail.currentState.isCompletelyValid;
45 }
46 } );
47
48 // Add event listener to track when the card form is valid
49 card.addEventListener( 'focusClassRemoved', event => {
50 const { field } = event.detail;
51 const value = event.detail.currentState.isCompletelyValid;
52
53 cardFields[ field ] = value;
54
55 // Check if all fields are valid
56 squareCardElementIsComplete = Object.values( cardFields ).every( item => item === true );
57
58 // Update form submit button based on form validity
59 if ( thisForm ) {
60 if ( squareCardElementIsComplete ) {
61 enableSubmit();
62 } else {
63 disableSubmit( thisForm );
64 }
65 }
66 } );
67
68 /**
69 * Enable the submit button when postal code is completed.
70 * This is the best we can do for now as Square does not provide
71 * any way of knowing that the credit card was auto-filled.
72 */
73 card.addEventListener( 'postalCodeChanged', function( event ) {
74 if ( event.detail.currentState.isCompletelyValid ) {
75 cardFields.cardNumber = true;
76 cardFields.expirationDate = true;
77 cardFields.cvv = true;
78 cardFields.postalCode = true;
79 enableSubmit();
80 } else {
81 cardFields.postalCode = false;
82 squareCardElementIsComplete = false;
83 disableSubmit();
84 }
85 } );
86
87 return card;
88 }
89
90 /**
91 * Enable the submit button for the form.
92 */
93 function enableSubmit() {
94 if ( running > 0 ) {
95 return;
96 }
97
98 thisForm.classList.add( 'frm_loading_form' );
99 frmFrontForm.removeSubmitLoading( jQuery( thisForm ), 'enable', 0 );
100
101 // Trigger custom event for other scripts to hook into
102 const event = new CustomEvent( 'frmSquareLiteEnableSubmit', {
103 detail: { form: thisForm }
104 } );
105 document.dispatchEvent( event );
106 }
107
108 /**
109 * Disable submit button for a target form.
110 *
111 * @param {Element} form
112 * @return {void}
113 */
114 function disableSubmit( form ) {
115 jQuery( form ).find( 'input[type="submit"],input[type="button"],button[type="submit"]' ).not( '.frm_prev_page' ).attr( 'disabled', 'disabled' );
116
117 // Trigger custom event for other scripts to hook into
118 const event = new CustomEvent( 'frmSquareLiteDisableSubmit', {
119 detail: { form }
120 } );
121 document.dispatchEvent( event );
122 }
123
124 async function createPayment( event, token, verificationToken ) {
125 const tokenInput = document.createElement( 'input' );
126 tokenInput.type = 'hidden';
127 tokenInput.value = token;
128 tokenInput.setAttribute( 'name', 'square-token' );
129
130 const verificationInput = document.createElement( 'input' );
131 verificationInput.type = 'hidden';
132 verificationInput.value = verificationToken;
133 verificationInput.setAttribute( 'name', 'square-verification-token' );
134
135 // Use the thisForm variable that we set earlier
136 if ( thisForm ) {
137 thisForm.append( tokenInput );
138 thisForm.append( verificationInput );
139
140 if ( typeof frmFrontForm.submitFormManual === 'function' ) {
141 frmFrontForm.submitFormManual( event, thisForm );
142 } else {
143 // Fallback if submitFormManual is not available
144 thisForm.submit();
145 }
146 }
147 }
148
149 async function tokenize( paymentMethod ) {
150 const tokenResult = await paymentMethod.tokenize();
151
152 if ( tokenResult.status === 'OK' ) {
153 return tokenResult.token;
154 }
155
156 let errorMessage = `Tokenization failed with status: ${ tokenResult.status }`;
157 if ( tokenResult.errors ) {
158 errorMessage += ` and errors: ${ JSON.stringify( tokenResult.errors ) }`;
159 }
160
161 throw new Error( errorMessage );
162 }
163
164 // Required in SCA Mandated Regions: Learn more at https://developer.squareup.com/docs/sca-overview
165 async function verifyBuyer( payments, token ) {
166 const formData = new FormData( thisForm );
167 formData.append( 'action', 'frm_verify_buyer' );
168 formData.append( 'nonce', frmSquareVars.nonce );
169
170 // Remove a few fields so form validation does not incorrectly trigger.
171 formData.delete( 'frm_action' );
172 formData.delete( 'form_key' );
173 formData.delete( 'item_key' );
174
175 const response = await fetch( frmSquareVars.ajax, {
176 method: 'POST',
177 body: formData
178 } );
179
180 if ( ! response.ok ) {
181 throw new Error( 'Failed to verify buyer' );
182 }
183
184 const verificationData = await response.json();
185 if ( ! verificationData.success ) {
186 throw new Error( verificationData.data );
187 }
188
189 if ( buyerTokens[ verificationData.data.hash ] ) {
190 // Avoid a second verify buyer request if the verification data has not changed.
191 return buyerTokens[ verificationData.data.hash ];
192 }
193
194 const { verificationDetails } = verificationData.data;
195 const verificationResults = await payments.verifyBuyer( token, verificationDetails );
196
197 buyerTokens[ verificationData.data.hash ] = verificationResults.token;
198
199 return verificationResults.token;
200 }
201
202 /**
203 * Display an error message in the payment form.
204 *
205 * @param {string} errorMessage
206 * @return {void}
207 */
208 function displayPaymentFailure( errorMessage ) {
209 if ( ! thisForm ) {
210 return;
211 }
212
213 const statusContainer = thisForm.querySelector( '.frm-card-errors' );
214 if ( statusContainer ) {
215 statusContainer.textContent = errorMessage;
216 }
217 }
218
219 async function squareInit() {
220 // Find the form containing the Square payment element
221 const cardContainer = document.querySelector( '.frm-card-element' );
222 if ( cardContainer ) {
223 thisForm = cardContainer.closest( 'form' );
224 if ( thisForm ) {
225 // Initially disable the submit button until card is valid
226 disableSubmit( thisForm );
227
228 // Add event listener for form submission
229 thisForm.addEventListener( 'submit', function( event ) {
230 event.preventDefault();
231 event.stopPropagation();
232
233 if ( ! squareCardElementIsComplete ) {
234 const statusContainer = thisForm.querySelector( '.frm-card-errors' );
235 if ( statusContainer ) {
236 statusContainer.textContent = 'Please complete all card details before submitting.';
237 }
238 } else {
239 handlePaymentMethodSubmission( event, cardGlobal );
240 }
241
242 return false;
243 } );
244 }
245 }
246
247 let payments;
248 try {
249 // Square requires HTTPS to work.
250 payments = window.Square.payments( appId, locationId );
251 } catch ( e ) {
252 const statusContainer = document.querySelector( '.frm-card-errors' );
253 statusContainer.classList.add( 'missing-credentials', 'frm_error' );
254 statusContainer.style.visibility = 'visible';
255 statusContainer.textContent = e.message;
256 return;
257 }
258
259 let card;
260 try {
261 card = await initializeCard( payments );
262 } catch ( e ) {
263 console.error( 'Initializing Card failed', e );
264 return;
265 }
266
267 cardGlobal = card;
268
269 /**
270 * @param {Object} $form
271 * @return {boolean} false if there are errors.
272 */
273 function validateFormSubmit( $form ) {
274 const errors = frmFrontForm.validateFormSubmit( $form );
275 const keys = Object.keys( errors );
276
277 if ( 1 === keys.length && errors[ keys[ 0 ] ] === '' ) {
278 // Pop the empty error that gets added by invisible recaptcha.
279 keys.pop();
280 }
281
282 return 0 === keys.length;
283 }
284
285 async function handlePaymentMethodSubmission( event, card ) {
286 try {
287 thisForm.classList.add( 'frm_js_validate' );
288
289 if ( ! validateFormSubmit( thisForm ) ) {
290 return;
291 }
292
293 // Increment running counter and disable the submit button
294 running++;
295 if ( thisForm ) {
296 disableSubmit( thisForm );
297 }
298
299 const token = await tokenize( card );
300 const verificationToken = await verifyBuyer( payments, token );
301 await createPayment( event, token, verificationToken );
302
303 // Decrement running counter after successful payment
304 running--;
305 if ( running === 0 && thisForm ) {
306 enableSubmit();
307 }
308 } catch ( e ) {
309 // Decrement running counter and re-enable submit if appropriate
310 running--;
311 if ( running === 0 && thisForm && squareCardElementIsComplete ) {
312 enableSubmit();
313 }
314 displayPaymentFailure( e.message );
315 }
316 }
317 }
318
319 document.addEventListener( 'DOMContentLoaded', async function() {
320 if ( ! window.Square ) {
321 console.error( 'Square.js failed to load properly' );
322 return;
323 }
324
325 squareInit();
326
327 jQuery( document ).on( 'frmPageChanged', function() {
328 squareInit();
329 } );
330 } );
331 }() );
332