Completed
Push — master ( 6d8164...bf7249 )
by Roy
03:26
created

assets/js/stripe-payment-request.js   F

Complexity

Total Complexity 74
Complexity/F 1.95

Size

Lines of Code 497
Function Count 38

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 0
nc 4194304
dl 0
loc 497
rs 3.931
c 3
b 0
f 0
wmc 74
mnd 5
bc 60
fnc 38
bpm 1.5789
cpm 1.9473
noi 3

How to fix   Complexity   

Complexity

Complex classes like assets/js/stripe-payment-request.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
7
	/**
8
	 * Object to handle Stripe payment forms.
9
	 */
10
	var wc_stripe_payment_request = {
11
		/**
12
		 * Get WC AJAX endpoint URL.
13
		 *
14
		 * @param  {String} endpoint Endpoint.
15
		 * @return {String}
16
		 */
17
		getAjaxURL: function( endpoint ) {
18
			return wc_stripe_payment_request_params.ajax_url
19
				.toString()
20
				.replace( '%%endpoint%%', 'wc_stripe_' + endpoint );
21
		},
22
23
		getCartDetails: function( paymentRequest ) {
24
			var data = {
25
				security: wc_stripe_payment_request_params.nonce.payment
26
			};
27
28
			$.ajax({
29
				type:    'POST',
30
				data:    data,
31
				url:     wc_stripe_payment_request.getAjaxURL( 'get_cart_details' ),
32
				success: function( response ) {
33
					paymentRequest.update({
34
						total: response.order_data.total
35
					});
36
				}
37
			});
38
		},
39
40
		getAttributes: function() {
41
			var select = $( '.variations_form' ).find( '.variations select' ),
42
				data   = {},
43
				count  = 0,
44
				chosen = 0;
45
46
			select.each( function() {
47
				var attribute_name = $( this ).data( 'attribute_name' ) || $( this ).attr( 'name' );
48
				var value          = $( this ).val() || '';
49
50
				if ( value.length > 0 ) {
51
					chosen ++;
52
				}
53
54
				count ++;
55
				data[ attribute_name ] = value;
56
			});
57
58
			return {
59
				'count'      : count,
60
				'chosenCount': chosen,
61
				'data'       : data
62
			};			
63
		},
64
65
		processSource: function( source, paymentRequestType ) {
66
			var data = wc_stripe_payment_request.getOrderData( source, paymentRequestType );
67
68
			return $.ajax({
69
				type:    'POST',
70
				data:    data,
71
				dataType: 'json',
72
				url:     wc_stripe_payment_request.getAjaxURL( 'create_order' )
73
			});
74
		},
75
76
		/**
77
		 * Get order data.
78
		 *
79
		 * @since 3.1.0
80
		 * @version 4.0.0
81
		 * @param {PaymentResponse} source Payment Response instance.
0 ignored issues
show
Documentation introduced by
The parameter source does not exist. Did you maybe forget to remove this comment?
Loading history...
82
		 *
83
		 * @return {Object}
84
		 */
85
		getOrderData: function( evt, paymentRequestType ) {
86
			var source   = evt.source;
87
			var email    = source.owner.email;
88
			var phone    = source.owner.phone;
89
			var billing  = source.owner.address;
90
			var name     = source.owner.name;
91
			var shipping = evt.shippingAddress;
92
			var data     = {
93
				_wpnonce:                  wc_stripe_payment_request_params.nonce.checkout,
94
				billing_first_name:        null !== name ? name.split( ' ' ).slice( 0, 1 ).join( ' ' ) : '',
95
				billing_last_name:         null !== name ? name.split( ' ' ).slice( 1 ).join( ' ' ) : '',
96
				billing_company:           '',
97
				billing_email:             null !== email   ? email : evt.payerEmail,
98
				billing_phone:             null !== phone   ? phone : evt.payerPhone.replace( '/[() -]/g', '' ),
99
				billing_country:           null !== billing ? billing.country : '',
100
				billing_address_1:         null !== billing ? billing.line1 : '',
101
				billing_address_2:         null !== billing ? billing.line2 : '',
102
				billing_city:              null !== billing ? billing.city : '',
103
				billing_state:             null !== billing ? billing.state : '',
104
				billing_postcode:          null !== billing ? billing.postal_code : '',
105
				shipping_first_name:       '',
106
				shipping_last_name:        '',
107
				shipping_company:          '',
108
				shipping_country:          '',
109
				shipping_address_1:        '',
110
				shipping_address_2:        '',
111
				shipping_city:             '',
112
				shipping_state:            '',
113
				shipping_postcode:         '',
114
				shipping_method:           [ null === evt.shippingOption ? null : evt.shippingOption.id ],
115
				order_comments:            '',
116
				payment_method:            'stripe',
117
				ship_to_different_address: 1,
118
				terms:                     1,
119
				stripe_source:             JSON.stringify( source ),
120
				payment_request_type:      paymentRequestType
121
			};
122
123
			if ( shipping ) {
124
				data.shipping_first_name = shipping.recipient.split( ' ' ).slice( 0, 1 ).join( ' ' );
125
				data.shipping_last_name  = shipping.recipient.split( ' ' ).slice( 1 ).join( ' ' );
126
				data.shipping_company    = shipping.organization;
127
				data.shipping_country    = shipping.country;
128
				data.shipping_address_1  = typeof shipping.addressLine[0] === 'undefined' ? '' : shipping.addressLine[0];
129
				data.shipping_address_2  = typeof shipping.addressLine[1] === 'undefined' ? '' : shipping.addressLine[1];
130
				data.shipping_city       = shipping.city;
131
				data.shipping_state      = shipping.region;
132
				data.shipping_postcode   = shipping.postalCode;
133
			}
134
135
			return data;
136
		},
137
138
		/**
139
		 * Generate error message HTML.
140
		 *
141
		 * @since 3.1.0
142
		 * @version 4.0.0
143
		 * @param  {String} message Error message.
144
		 * @return {Object}
145
		 */
146
		getErrorMessageHTML: function( message ) {
147
			return $( '<div class="woocommerce-error" />' ).text( message );
148
		},
149
150
		/**
151
		 * Abort payment and display error messages.
152
		 *
153
		 * @since 3.1.0
154
		 * @version 4.0.0
155
		 * @param {PaymentResponse} payment Payment response instance.
156
		 * @param {String}          message Error message to display.
157
		 */
158
		abortPayment: function( payment, message ) {
159
			payment.complete( 'fail' );
160
161
			$( '.woocommerce-error' ).remove();
162
163
			if ( wc_stripe_payment_request_params.is_product_page ) {
164
				var element = $( '.product' );
165
166
				element.before( message );
167
168
				$( 'html, body' ).animate({
169
					scrollTop: element.prev( '.woocommerce-error' ).offset().top
170
				}, 600 );
171
			} else {
172
				var $form = $( '.shop_table.cart' ).closest( 'form' );
173
174
				$form.before( message );
175
176
				$( 'html, body' ).animate({
177
					scrollTop: $form.prev( '.woocommerce-error' ).offset().top
178
				}, 600 );
179
			}
180
		},
181
182
		/**
183
		 * Complete payment.
184
		 *
185
		 * @since 3.1.0
186
		 * @version 4.0.0
187
		 * @param {PaymentResponse} payment Payment response instance.
188
		 * @param {String}          url     Order thank you page URL.
189
		 */
190
		completePayment: function( payment, url ) {
191
			wc_stripe_payment_request.block();
192
193
			payment.complete( 'success' );
194
195
			// Success, then redirect to the Thank You page.
196
			window.location = url;
197
		},
198
199
		block: function() {
200
			$.blockUI({
201
				message: null,
202
				overlayCSS: {
203
					background: '#fff',
204
					opacity: 0.6
205
				}
206
			});
207
		},
208
209
		/**
210
		 * Update shipping options.
211
		 *
212
		 * @param {Object}         details Payment details.
213
		 * @param {PaymentAddress} address Shipping address.
214
		 */
215
		updateShippingOptions: function( details, address ) {
216
			var data = {
217
				security:  wc_stripe_payment_request_params.nonce.shipping,
218
				country:   address.country,
219
				state:     address.region,
220
				postcode:  address.postalCode,
221
				city:      address.city,
222
				address:   typeof address.addressLine[0] === 'undefined' ? '' : address.addressLine[0],
223
				address_2: typeof address.addressLine[1] === 'undefined' ? '' : address.addressLine[1]
224
			};
225
226
			return $.ajax({
227
				type:    'POST',
228
				data:    data,
229
				url:     wc_stripe_payment_request.getAjaxURL( 'get_shipping_options' )
230
			});
231
		},
232
233
		/**
234
		 * Updates the shipping price and the total based on the shipping option.
235
		 *
236
		 * @param {Object}   details        The line items and shipping options.
237
		 * @param {String}   shippingOption User's preferred shipping option to use for shipping price calculations.
238
		 */
239
		updateShippingDetails: function( details, shippingOption ) {
240
			var data = {
241
				security: wc_stripe_payment_request_params.nonce.update_shipping,
242
				shipping_method: [ shippingOption.id ]
243
			};
244
245
			return $.ajax({
246
				type: 'POST',
247
				data: data,
248
				url:  wc_stripe_payment_request.getAjaxURL( 'update_shipping_method' )
249
			});
250
		},
251
252
		/**
253
		 * Adds the item to the cart and return cart details.
254
		 *
255
		 */
256
		addToCart: function() {
257
			var product_id = $( '.single_add_to_cart_button' ).val();
258
259
			// Check if product is a variable product.
260
			if ( $( '.single_variation_wrap' ).length ) {
261
				product_id = $( '.single_variation_wrap' ).find( 'input[name="product_id"]' ).val();
262
			}
263
264
			var data = {
265
				security: wc_stripe_payment_request_params.nonce.add_to_cart,
266
				product_id: product_id,
267
				qty: $( '.quantity .qty' ).val(),
268
				attributes: $( '.variations_form' ).length ? wc_stripe_payment_request.getAttributes().data : []
269
			};
270
271
			return $.ajax({
272
				type: 'POST',
273
				data: data,
274
				url:  wc_stripe_payment_request.getAjaxURL( 'add_to_cart' )
275
			});
276
		},
277
278
		clearCart: function() {
279
			var data = {
280
					'security': wc_stripe_payment_request_params.nonce.clear_cart
281
				};
282
283
			return $.ajax({
284
				type:    'POST',
285
				data:    data,
286
				url:     wc_stripe_payment_request.getAjaxURL( 'clear_cart' ),
287
				success: function( response ) {}
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
288
			});
289
		},
290
291
		getRequestOptionsFromLocal: function() {
292
			return {
293
				total: wc_stripe_payment_request_params.product.total,
294
				currency: wc_stripe_payment_request_params.checkout.currency_code,
295
				country: wc_stripe_payment_request_params.checkout.country_code,
296
				requestPayerName: true,
297
				requestPayerEmail: true,
298
				requestPayerPhone: true,
299
				requestShipping: wc_stripe_payment_request_params.product.requestShipping,
300
				displayItems: wc_stripe_payment_request_params.product.displayItems
301
			};
302
		},
303
304
		/**
305
		 * Starts the payment request
306
		 *
307
		 * @since 4.0.0
308
		 * @version 4.0.0
309
		 */
310
		startPaymentRequest: function( cart ) {
311
			var paymentDetails,
312
				options;
313
314
			if ( wc_stripe_payment_request_params.is_product_page ) {
315
				options = wc_stripe_payment_request.getRequestOptionsFromLocal();
316
317
				paymentDetails = options;
318
			} else {
319
				options = {
320
					total: cart.order_data.total,
321
					currency: cart.order_data.currency,
322
					country: cart.order_data.country_code,
323
					requestPayerName: true,
324
					requestPayerEmail: true,
325
					requestPayerPhone: true,
326
					requestShipping: cart.shipping_required ? true : false,
327
					displayItems: cart.order_data.displayItems
328
				};
329
330
				paymentDetails = cart.order_data;
331
			}
332
333
			var paymentRequest     = stripe.paymentRequest( options );
334
			var paymentRequestType = '';
335
			
336
337
			var elements = stripe.elements({ locale: wc_stripe_payment_request_params.button.locale });
338
			var prButton = elements.create( 'paymentRequestButton', {
339
				paymentRequest: paymentRequest,
340
				style: {
341
					paymentRequestButton: {
342
						type: wc_stripe_payment_request_params.button.type,
343
						theme: wc_stripe_payment_request_params.button.theme,
344
						height: wc_stripe_payment_request_params.button.height + 'px'
345
					},
346
				}
347
			});
348
349
			// Check the availability of the Payment Request API first.
350
			paymentRequest.canMakePayment().then( function( result ) {
351
				if ( result ) {
352
					paymentRequestType = result.applePay ? 'apple_pay' : 'payment_request_api';
353
354
					if ( wc_stripe_payment_request_params.is_product_page ) {
355
						var addToCartButton = $( '.single_add_to_cart_button' );
356
357
						prButton.on( 'click', function( e ) {
358
							// First check if product can be added to cart.
359
							if ( addToCartButton.is( '.disabled' ) ) {
360
								e.preventDefault();
361
								if ( addToCartButton.is( '.wc-variation-is-unavailable' ) ) {
362
									window.alert( wc_add_to_cart_variation_params.i18n_unavailable_text );
0 ignored issues
show
Bug introduced by
The variable wc_add_to_cart_variation_params seems to be never declared. If this is a global, consider adding a /** global: wc_add_to_cart_variation_params */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
363
								} else if ( addToCartButton.is( '.wc-variation-selection-needed' ) ) {
364
									window.alert( wc_add_to_cart_variation_params.i18n_make_a_selection_text );
365
								}
366
							} else {
367
								wc_stripe_payment_request.addToCart();
368
							}
369
						});
370
371
						$( document.body ).on( 'woocommerce_variation_has_changed', function() {
372
							$( '#wc-stripe-payment-request-button' ).block({ message: null });
373
374
							$.when( wc_stripe_payment_request.getSelectedProductData() ).then( function( response ) {
375
								$.when( paymentRequest.update({
376
									total: response.total,
377
									displayItems: response.displayItems
378
								}) ).then( function() {
379
									$( '#wc-stripe-payment-request-button' ).unblock();
380
								});
381
							});
382
						});
383
384
						$( '.quantity' ).on( 'change', '.qty', function() {
385
							$( '#wc-stripe-payment-request-button' ).block({ message: null });
386
387
							$.when( wc_stripe_payment_request.getSelectedProductData() ).then( function( response ) {
388
								$.when( paymentRequest.update({
389
									total: response.total,
390
									displayItems: response.displayItems
391
								}) ).then( function() {
392
									$( '#wc-stripe-payment-request-button' ).unblock();
393
								});
394
							});
395
						});
396
					}
397
398
					if ( $( '#wc-stripe-payment-request-button' ).length ) {
399
						prButton.mount( '#wc-stripe-payment-request-button' );
400
						$( '#wc-stripe-payment-request-button-separator' ).show();
401
					}
402
				} else {
403
					$( '#wc-stripe-payment-request-button' ).hide();
404
					$( '#wc-stripe-payment-request-button-separator' ).hide();
405
				}
406
			});
407
408
			// Possible statuses success, fail, invalid_payer_name, invalid_payer_email, invalid_payer_phone, invalid_shipping_address.
409
			paymentRequest.on( 'shippingaddresschange', function( evt ) {
410
				$.when( wc_stripe_payment_request.updateShippingOptions( paymentDetails, evt.shippingAddress ) ).then( function( response ) {
411
					evt.updateWith( { status: response.result, shippingOptions: response.shipping_options, total: response.total, displayItems: response.displayItems } );
412
				});
413
			});
414
415
			paymentRequest.on( 'shippingoptionchange', function( evt ) {
416
				$.when( wc_stripe_payment_request.updateShippingDetails( paymentDetails, evt.shippingOption ) ).then( function( response ) {
417
					if ( 'success' === response.result ) {
418
						evt.updateWith( { status: 'success', total: response.total, displayItems: response.displayItems } );
419
					}
420
421
					if ( 'fail' === response.result ) {
422
						evt.updateWith( { status: 'fail' } );
423
					}
424
				});												
425
			});
426
427
			paymentRequest.on( 'source', function( evt ) {
428
				// Check if we allow prepaid cards.
429
				if ( 'no' === wc_stripe_payment_request_params.stripe.allow_prepaid_card && 'prepaid' === evt.source.card.funding ) {
430
					wc_stripe_payment_request.abortPayment( evt, wc_stripe_payment_request.getErrorMessageHTML( wc_stripe_payment_request_params.i18n.no_prepaid_card ) );
431
				} else {
432
					$.when( wc_stripe_payment_request.processSource( evt, paymentRequestType ) ).then( function( response ) {
433
						if ( 'success' === response.result ) {
434
							wc_stripe_payment_request.completePayment( evt, response.redirect );
435
						} else {
436
							wc_stripe_payment_request.abortPayment( evt, response.messages );
437
						}
438
					});
439
				}
440
			});
441
		},
442
443
		getSelectedProductData: function() {
444
			var product_id = $( '.single_add_to_cart_button' ).val();
445
446
			// Check if product is a variable product.
447
			if ( $( '.single_variation_wrap' ).length ) {
448
				product_id = $( '.single_variation_wrap' ).find( 'input[name="product_id"]' ).val();
449
			}
450
451
			var data = {
452
				security: wc_stripe_payment_request_params.nonce.get_selected_product_data,
453
				product_id: product_id,
454
				qty: $( '.quantity .qty' ).val(),
455
				attributes: $( '.variations_form' ).length ? wc_stripe_payment_request.getAttributes().data : []
456
			};
457
458
			return $.ajax({
459
				type: 'POST',
460
				data: data,
461
				url:  wc_stripe_payment_request.getAjaxURL( 'get_selected_product_data' )
462
			});
463
		},
464
465
		/**
466
		 * Initialize event handlers and UI state
467
		 *
468
		 * @since 4.0.0
469
		 * @version 4.0.0
470
		 */
471
		init: function() {
472
			var data = {
473
				security: wc_stripe_payment_request_params.nonce.payment
474
			};
475
476
			$.ajax({
477
				type:    'POST',
478
				data:    data,
479
				url:     wc_stripe_payment_request.getAjaxURL( 'get_cart_details' ),
480
				success: function( response ) {
481
					wc_stripe_payment_request.startPaymentRequest( response );
482
				}
483
			});
484
		},
485
	};
486
487
	wc_stripe_payment_request.init();
488
489
	// We need to refresh payment request data when total is updated.
490
	$( document.body ).on( 'updated_cart_totals', function() {
491
		wc_stripe_payment_request.init();
492
	});
493
494
	// We need to refresh payment request data when total is updated.
495
	$( document.body ).on( 'updated_checkout', function() {
496
		wc_stripe_payment_request.init();
497
	});
498
});
499