Completed
Push — master ( 742841...f87a52 )
by Roy
03:04
created

WC_Stripe_Apple_Pay::build_line_items()   C

Complexity

Conditions 10
Paths 160

Size

Total Lines 68
Code Lines 43

Duplication

Lines 14
Ratio 20.59 %

Importance

Changes 0
Metric Value
dl 14
loc 68
rs 5.7142
c 0
b 0
f 0
cc 10
eloc 43
nc 160
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * WC_Stripe_Apple_Pay class.
8
 *
9
 * @extends WC_Gateway_Stripe
10
 */
11
class WC_Stripe_Apple_Pay extends WC_Gateway_Stripe {
12
	/**
13
	 * This Instance.
14
	 *
15
	 * @var
16
	 */
17
	private static $_this;
18
19
	/**
20
	 * Statement Description
21
	 *
22
	 * @var
23
	 */
24
	public $statement_descriptor;
25
26
	/**
27
	 * Check if we capture the transaction immediately.
28
	 *
29
	 * @var bool
30
	 */
31
	public $capture;
32
33
	/**
34
	 * Do we accept Apple Pay?
35
	 *
36
	 * @var bool
37
	 */
38
	public $apple_pay;
39
40
	/**
41
	 * Apple Pay button style.
42
	 *
43
	 * @var bool
44
	 */
45
	public $apple_pay_button;
46
47
	/**
48
	 * Apple Pay button language.
49
	 *
50
	 * @var bool
51
	 */
52
	public $apple_pay_button_lang;
53
54
	/**
55
	 * Is test mode active?
56
	 *
57
	 * @var bool
58
	 */
59
	public $testmode;
60
61
	/**
62
	 * Logging enabled?
63
	 *
64
	 * @var bool
65
	 */
66
	public $logging;
67
68
	/**
69
	 * Should we store the users credit cards?
70
	 *
71
	 * @var bool
72
	 */
73
	public $saved_cards;
74
75
	/**
76
	 * Publishable key credentials.
77
	 *
78
	 * @var bool
79
	 */
80
	public $publishable_key;
81
82
	/**
83
	 * Is shipping enabled?
84
	 *
85
	 * @var bool
86
	 */
87
	public $is_shipping_enabled;
88
89
	/**
90
	 * Constructor.
91
	 *
92
	 * @access public
93
	 * @since 3.1.0
94
	 * @version 3.1.0
95
	 */
96
	public function __construct() {
97
		self::$_this = $this;
98
99
		$gateway_settings = get_option( 'woocommerce_stripe_settings', '' );
100
101
		$this->statement_descriptor = ! empty( $gateway_settings['statement_descriptor'] ) ? $gateway_settings['statement_descriptor'] : wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
102
103
		// If both site title and statement descriptor is not set. Fallback.
104
		if ( empty( $this->statement_descriptor ) ) {
105
			$this->statement_descriptor = $_SERVER['SERVER_NAME'];
106
		}
107
108
		$this->enabled               = ( ! empty( $gateway_settings['enabled'] ) && 'yes' === $gateway_settings['enabled'] ) ? true : false;
109
		$this->testmode              = ( ! empty( $gateway_settings['testmode'] ) && 'yes' === $gateway_settings['testmode'] ) ? true : false;
110
		$this->capture               = ( ! empty( $gateway_settings['capture'] ) && 'yes' === $gateway_settings['capture'] ) ? true : false;
111
		$this->saved_cards           = ( ! empty( $gateway_settings['saved_cards'] ) && 'yes' === $gateway_settings['saved_cards'] ) ? true : false;
112
		$this->apple_pay             = ( ! empty( $gateway_settings['apple_pay'] ) && 'yes' === $gateway_settings['apple_pay'] ) ? true : false;
113
		$this->apple_pay_button      = ! empty( $gateway_settings['apple_pay_button'] ) ? $gateway_settings['apple_pay_button'] : 'black';
114
		$this->apple_pay_button_lang = ! empty( $gateway_settings['apple_pay_button_lang'] ) ? $gateway_settings['apple_pay_button_lang'] : 'en';
115
		$this->logging               = ( ! empty( $gateway_settings['logging'] ) && 'yes' === $gateway_settings['logging'] ) ? true : false;
116
		$this->publishable_key       = ! empty( $gateway_settings['publishable_key'] ) ? $gateway_settings['publishable_key'] : '';
117
		$this->is_shipping_enabled   = $this->is_shipping_enabled();
118
119
		if ( $this->testmode ) {
120
			$this->publishable_key = ! empty( $gateway_settings['test_publishable_key'] ) ? $gateway_settings['test_publishable_key'] : '';
121
		}
122
123
		$this->init();
124
	}
125
126
	public static function instance() {
127
		return self::$_this;
128
	}
129
130
	/**
131
	 * Initialize.
132
	 *
133
	 * @access public
134
	 * @since 3.1.0
135
	 * @version 3.1.4
136
	 */
137
	public function init() {
138
		// If Apple Pay is not enabled no need to proceed further.
139
		if ( ! $this->apple_pay ) {
140
			return;
141
		}
142
143
		add_action( 'wp_enqueue_scripts', array( $this, 'cart_scripts' ) );
144
		add_action( 'wp_enqueue_scripts', array( $this, 'single_scripts' ) );
145
146
		/**
147
		 * In order to display the Apple Pay button in the correct position,
148
		 * a new hook was added to WooCommerce 3.0. In older versions of WooCommerce,
149
		 * CSS is used to position the button.
150
		 */
151
		if ( version_compare( WC_VERSION, '3.0.0', '<' ) ) {
152
			add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'display_apple_pay_button' ), 1 );
153
		} else {
154
			add_action( 'woocommerce_after_add_to_cart_quantity', array( $this, 'display_apple_pay_button' ), 1 );
155
		}
156
157
		add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_apple_pay_button' ), 1 );
158
		add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_apple_pay_separator_html' ), 2 );
159
		add_action( 'woocommerce_checkout_before_customer_details', array( $this, 'display_apple_pay_button' ), 1 );
160
		add_action( 'woocommerce_checkout_before_customer_details', array( $this, 'display_apple_pay_separator_html' ), 2 );
161
		add_action( 'wc_ajax_wc_stripe_log_apple_pay_errors', array( $this, 'log_apple_pay_errors' ) );
162
		add_action( 'wc_ajax_wc_stripe_apple_pay', array( $this, 'process_apple_pay' ) );
163
		add_action( 'wc_ajax_wc_stripe_generate_apple_pay_cart', array( $this, 'generate_apple_pay_cart' ) );
164
		add_action( 'wc_ajax_wc_stripe_apple_pay_clear_cart', array( $this, 'clear_cart' ) );
165
		add_action( 'wc_ajax_wc_stripe_generate_apple_pay_single', array( $this, 'generate_apple_pay_single' ) );
166
		add_action( 'wc_ajax_wc_stripe_apple_pay_get_shipping_methods', array( $this, 'get_shipping_methods' ) );
167
		add_action( 'wc_ajax_wc_stripe_apple_pay_update_shipping_method', array( $this, 'update_shipping_method' ) );
168
		add_filter( 'woocommerce_gateway_title', array( $this, 'filter_gateway_title' ), 10, 2 );
169
		add_filter( 'woocommerce_validate_postcode', array( $this, 'postal_code_validation' ), 10, 3 );
170
	}
171
172
	/**
173
	 * Filters the gateway title to reflect Apple Pay.
174
	 *
175
	 */
176
	public function filter_gateway_title( $title, $id ) {
177
		global $post;
178
179
		if ( ! is_object( $post ) ) {
180
			return $title;
181
		}
182
183
		$method_title = get_post_meta( $post->ID, '_payment_method_title', true );
184
185
		if ( 'stripe' === $id && ! empty( $method_title ) && 'Apple Pay (Stripe)' === $method_title ) {
186
			return $method_title;
187
		}
188
189
		return $title;
190
	}
191
192
	/**
193
	 * Log errors coming from Apple Pay.
194
	 *
195
	 * @since 3.1.4
196
	 * @version 3.1.4
197
	 */
198
	public function log_apple_pay_errors() {
199
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_nonce' ) && ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_cart_nonce' ) ) {
200
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
201
		}
202
203
		$errors = wc_clean( stripslashes( $_POST['errors'] ) );
204
205
		$this->log( $errors );
206
207
		exit;
208
	}
209
210
	/**
211
	 * Removes postal code validation from WC.
212
	 *
213
	 * @since 3.1.4
214
	 * @version 3.1.4
215
	 */
216
	public function postal_code_validation( $valid, $postcode, $country ) {
217
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
218
219
		if ( ! $this->apple_pay || ! isset( $gateways['stripe'] ) ) {
220
			return $valid;
221
		}
222
223
		/**
224
		 * Currently Apple Pay truncates postal codes from UK and Canada to first 3 characters
225
		 * when passing it back from the shippingcontactselected object. This causes WC to invalidate
226
		 * the order and not let it go through. The remedy for now is just to remove this validation.
227
		 */
228
		if ( 'GB' === $country || 'CA' === $country ) {
229
			return true;
230
		}
231
232
		return $valid;
233
	}
234
235
	/**
236
	 * Checks if shipping is enabled for the store.
237
	 *
238
	 * @since 3.1.6
239
	 * @version 3.1.6
240
	 * @return bool
241
	 */
242
	public function is_shipping_enabled() {
243
		$shipping_enabled = get_option( 'woocommerce_ship_to_countries', '' );
244
245
		if ( 'disabled' === $shipping_enabled ) {
246
			return false;
247
		}
248
249
		return true;
250
	}
251
252
	/**
253
	 * Enqueue JS scripts and styles for single product page.
254
	 *
255
	 * @since 3.1.0
256
	 * @version 3.1.4
257
	 */
258
	public function single_scripts() {
259
		if ( ! is_single() ) {
260
			return;
261
		}
262
263
		global $post;
264
265
		$product = wc_get_product( $post->ID );
266
267 View Code Duplication
		if ( ! is_object( $product ) || ! in_array( ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ), $this->supported_product_types() ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
268
			return;
269
		}
270
271
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
272
273
		wp_enqueue_style( 'stripe_apple_pay', plugins_url( 'assets/css/stripe-apple-pay.css', WC_STRIPE_MAIN_FILE ), array(), WC_STRIPE_VERSION );
274
275
		wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true );
276
		wp_enqueue_script( 'woocommerce_stripe_apple_pay_single', plugins_url( 'assets/js/stripe-apple-pay-single' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array( 'stripe' ), WC_STRIPE_VERSION, true );
277
278
		$stripe_params = array(
279
			'key'                                           => $this->publishable_key,
280
			'currency_code'                                 => get_woocommerce_currency(),
281
			'country_code'                                  => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
282
			'label'                                         => $this->statement_descriptor,
283
			'ajaxurl'                                       => WC_AJAX::get_endpoint( '%%endpoint%%' ),
284
			'stripe_apple_pay_nonce'                        => wp_create_nonce( '_wc_stripe_apple_pay_nonce' ),
285
			'stripe_apple_pay_cart_nonce'                   => wp_create_nonce( '_wc_stripe_apple_pay_cart_nonce' ),
286
			'stripe_apple_pay_get_shipping_methods_nonce'   => wp_create_nonce( '_wc_stripe_apple_pay_get_shipping_methods_nonce' ),
287
			'stripe_apple_pay_update_shipping_method_nonce' => wp_create_nonce( '_wc_stripe_apple_pay_update_shipping_method_nonce' ),
288
			'needs_shipping'                                => ( $product->needs_shipping() && $this->is_shipping_enabled ) ? 'yes' : 'no',
289
			'i18n'                                          => array(
290
				'sub_total' => __( 'Sub-Total', 'woocommerce-gateway-stripe' ),
291
			),
292
		);
293
294
		wp_localize_script( 'woocommerce_stripe_apple_pay_single', 'wc_stripe_apple_pay_single_params', apply_filters( 'wc_stripe_apple_pay_single_params', $stripe_params ) );
295
	}
296
297
	/**
298
	 * Enqueue JS scripts and styles for the cart/checkout.
299
	 *
300
	 * @since 3.1.0
301
	 * @version 3.1.0
302
	 */
303
	public function cart_scripts() {
304
		if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) ) {
305
			return;
306
		}
307
308
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
309
310
		wp_enqueue_style( 'stripe_apple_pay', plugins_url( 'assets/css/stripe-apple-pay.css', WC_STRIPE_MAIN_FILE ), array(), WC_STRIPE_VERSION );
311
312
		wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true );
313
		wp_enqueue_script( 'woocommerce_stripe_apple_pay', plugins_url( 'assets/js/stripe-apple-pay' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array( 'stripe' ), WC_STRIPE_VERSION, true );
314
315
		$stripe_params = array(
316
			'key'                                           => $this->publishable_key,
317
			'currency_code'                                 => get_woocommerce_currency(),
318
			'country_code'                                  => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
319
			'label'                                         => $this->statement_descriptor,
320
			'ajaxurl'                                       => WC_AJAX::get_endpoint( '%%endpoint%%' ),
321
			'stripe_apple_pay_nonce'                        => wp_create_nonce( '_wc_stripe_apple_pay_nonce' ),
322
			'stripe_apple_pay_cart_nonce'                   => wp_create_nonce( '_wc_stripe_apple_pay_cart_nonce' ),
323
			'stripe_apple_pay_get_shipping_methods_nonce'   => wp_create_nonce( '_wc_stripe_apple_pay_get_shipping_methods_nonce' ),
324
			'stripe_apple_pay_update_shipping_method_nonce' => wp_create_nonce( '_wc_stripe_apple_pay_update_shipping_method_nonce' ),
325
			'needs_shipping'                                => ( WC()->cart->needs_shipping() && $this->is_shipping_enabled ) ? 'yes' : 'no',
326
			'is_cart_page'                                  => is_cart() ? 'yes' : 'no',
327
		);
328
329
		wp_localize_script( 'woocommerce_stripe_apple_pay', 'wc_stripe_apple_pay_params', apply_filters( 'wc_stripe_apple_pay_params', $stripe_params ) );
330
	}
331
332
	/**
333
	 * Checks to make sure product type is supported by Apple Pay.
334
	 *
335
	 * @since 3.1.0
336
	 * @version 3.1.0
337
	 * @return array
338
	 */
339
	public function supported_product_types() {
340
		return apply_filters( 'wc_stripe_apple_pay_supported_types', array(
341
			'simple',
342
			'variable',
343
			'variation',
344
		) );
345
	}
346
347
	/**
348
	 * Checks the cart to see if all items are allowed to use
349
	 * Apple Pay.
350
	 *
351
	 * @since 3.1.4
352
	 * @version 3.1.4
353
	 * @return bool
354
	 */
355
	public function allowed_items_in_cart() {
356
		foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
357
			$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
358
359 View Code Duplication
			if ( ! in_array( ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $_product->product_type : $_product->get_type() ), $this->supported_product_types() ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
360
				return false;
361
			}
362
		}
363
364
		return true;
365
	}
366
367
	/**
368
	 * Display Apple Pay button on the cart page
369
	 *
370
	 * @since 3.1.0
371
	 * @version 3.1.0
372
	 */
373
	public function display_apple_pay_button() {
374
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
375
376
		/**
377
		 * In order for the Apple Pay button to show on product detail page,
378
		 * Apple Pay must be enabled and Stripe gateway must be enabled.
379
		 */
380
		if ( ! $this->apple_pay || ! isset( $gateways['stripe'] ) ) {
381
			$this->log( 'Apple Pay not enabled or Stripe is not an available gateway ( Apple Pay button disabled )' );
382
			return;
383
		}
384
385
		if ( is_single() ) {
386
			global $post;
387
388
			$product = wc_get_product( $post->ID );
389
390 View Code Duplication
			if ( ! is_object( $product ) || ! in_array( ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ), $this->supported_product_types() ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391
				return;
392
			}
393
		} else {
394
			if ( ! $this->allowed_items_in_cart() ) {
395
				$this->log( 'Items in the cart has unsupported product type ( Apple Pay button disabled )' );
396
				return;
397
			}
398
		}
399
400
		?>
401
		<div class="apple-pay-button-wrapper">
402
			<button class="apple-pay-button" lang="<?php echo esc_attr( $this->apple_pay_button_lang ); ?>" style="-webkit-appearance: -apple-pay-button; -apple-pay-button-type: buy; -apple-pay-button-style: <?php echo esc_attr( $this->apple_pay_button ); ?>;" alt="<?php esc_attr_e( 'Buy with Apple Pay', 'woocommerce-gateway-stripe' ); ?>"></button>
403
		</div>
404
		<?php
405
	}
406
407
	/**
408
	 * Display Apple Pay button on the cart page
409
	 *
410
	 * @since 3.1.0
411
	 * @version 3.1.0
412
	 */
413
	public function display_apple_pay_separator_html() {
414
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
415
416
		/**
417
		 * In order for the Apple Pay button to show on cart page,
418
		 * Apple Pay must be enabled and Stripe gateway must be enabled.
419
		 */
420
		if ( ! $this->apple_pay || ! isset( $gateways['stripe'] ) ) {
421
			$this->log( 'Apple Pay not enabled or Stripe is not an available gateway ( Apple Pay button disabled )' );
422
			return;
423
		}
424
425
		if ( ! $this->allowed_items_in_cart() ) {
426
			$this->log( 'Items in the cart has unsupported product type ( Apple Pay button disabled )' );
427
			return;
428
		}
429
		?>
430
		<p class="apple-pay-button-checkout-separator">- <?php esc_html_e( 'Or', 'woocommerce-gateway-stripe' ); ?> -</p>
431
		<?php
432
	}
433
434
	/**
435
	 * Generates the Apple Pay single cart.
436
	 *
437
	 * @since 3.1.0
438
	 * @version 3.1.0
439
	 */
440
	public function generate_apple_pay_single() {
441
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_cart_nonce' ) ) {
442
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
443
		}
444
445
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
446
			define( 'WOOCOMMERCE_CART', true );
447
		}
448
449
		WC()->shipping->reset_shipping();
450
451
		global $post;
452
453
		$product = wc_get_product( $post->ID );
454
		$qty     = ! isset( $_POST['qty'] ) ? 1 : absint( $_POST['qty'] );
455
456
		/**
457
		 * If this page is single product page, we need to simulate
458
		 * adding the product to the cart taken account if it is a
459
		 * simple or variable product.
460
		 */
461
		if ( is_single() ) {
462
			// First empty the cart to prevent wrong calculation.
463
			WC()->cart->empty_cart();
464
465
			if ( 'variable' === ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ) && isset( $_POST['attributes'] ) ) {
466
				$attributes = array_map( 'wc_clean', $_POST['attributes'] );
467
468
				if ( version_compare( WC_VERSION, '3.0.0', '<' ) ) {
469
					$variation_id = $product->get_matching_variation( $attributes );
470
				} else {
471
					$data_store = WC_Data_Store::load( 'product' );
472
					$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
473
				}
474
475
				WC()->cart->add_to_cart( $product->get_id(), $qty, $variation_id, $attributes );
476
			}
477
478
			if ( 'simple' === ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ) ) {
479
				WC()->cart->add_to_cart( $product->get_id(), $qty );
480
			}
481
		}
482
483
		WC()->cart->calculate_totals();
484
485
		wp_send_json( array( 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
486
	}
487
488
	/**
489
	 * Generates the Apple Pay cart.
490
	 *
491
	 * @since 3.1.0
492
	 * @version 3.1.0
493
	 */
494
	public function generate_apple_pay_cart() {
495
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_cart_nonce' ) ) {
496
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
497
		}
498
499
		wp_send_json( array( 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
500
	}
501
502
	/**
503
	 * Clears Apple Pay cart.
504
	 *
505
	 * @since 3.1.4
506
	 * @version 3.1.4
507
	 */
508
	public function clear_cart() {
509
		WC()->cart->empty_cart();
510
		exit;
511
	}
512
513
	/**
514
	 * Calculate and set shipping method.
515
	 *
516
	 * @since 3.1.0
517
	 * @version 3.1.0
518
	 * @param array $address
519
	 */
520
	public function calculate_shipping( $address = array() ) {
521
		$country  = strtoupper( $address['countryCode'] );
522
		$state    = strtoupper( $address['administrativeArea'] );
523
		$postcode = $address['postalCode'];
524
		$city     = $address['locality'];
525
526
		WC()->shipping->reset_shipping();
527
528
		if ( $postcode && ! WC_Validation::is_postcode( $postcode, $country ) ) {
529
			throw new Exception( __( 'Please enter a valid postcode/ZIP.', 'woocommerce-gateway-stripe' ) );
530
		} elseif ( $postcode ) {
531
			$postcode = wc_format_postcode( $postcode, $country );
532
		}
533
534 View Code Duplication
		if ( $country ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
535
			WC()->customer->set_location( $country, $state, $postcode, $city );
536
			WC()->customer->set_shipping_location( $country, $state, $postcode, $city );
537
		} else {
538
			WC()->customer->set_to_base();
539
			WC()->customer->set_shipping_to_base();
540
		}
541
542
		version_compare( WC_VERSION, '3.0', '<' ) ? WC()->customer->calculated_shipping( true ) : WC()->customer->set_calculated_shipping( true );
543
544
		WC()->customer->save();
545
546
		/**
547
		 * Set the shipping package.
548
		 *
549
		 * Note that address lines are not provided at this point
550
		 * because Apple Pay does not supply that until after
551
		 * authentication via passcode or Touch ID. We will need to
552
		 * capture this information when we process the payment.
553
		 */
554
555
		$packages = array();
556
557
		$packages[0]['contents']                 = WC()->cart->get_cart();
558
		$packages[0]['contents_cost']            = 0;
559
		$packages[0]['applied_coupons']          = WC()->cart->applied_coupons;
560
		$packages[0]['user']['ID']               = get_current_user_id();
561
		$packages[0]['destination']['country']   = $country;
562
		$packages[0]['destination']['state']     = $state;
563
		$packages[0]['destination']['postcode']  = $postcode;
564
		$packages[0]['destination']['city']      = $city;
565
566 View Code Duplication
		foreach ( WC()->cart->get_cart() as $item ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
567
			if ( $item['data']->needs_shipping() ) {
568
				if ( isset( $item['line_total'] ) ) {
569
					$packages[0]['contents_cost'] += $item['line_total'];
570
				}
571
			}
572
		}
573
574
		$packages = apply_filters( 'woocommerce_cart_shipping_packages', $packages );
575
576
		WC()->shipping->calculate_shipping( $packages );
577
	}
578
579
	/**
580
	 * Gets shipping for Apple Pay Payment sheet.
581
	 *
582
	 * @since 3.1.0
583
	 * @version 3.1.0
584
	 */
585
	public function get_shipping_methods() {
586
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_get_shipping_methods_nonce' ) ) {
587
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
588
		}
589
590
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
591
			define( 'WOOCOMMERCE_CART', true );
592
		}
593
594
		try {
595
			$address = array_map( 'wc_clean', $_POST['address'] );
596
597
			$this->calculate_shipping( $address );
598
599
			// Set the shipping options.
600
			$currency = get_woocommerce_currency();
601
			$data     = array();
602
603
			$packages = WC()->shipping->get_packages();
604
605
			if ( ! empty( $packages ) && WC()->customer->has_calculated_shipping() ) {
606
				foreach ( $packages as $package_key => $package ) {
607
					if ( empty( $package['rates'] ) ) {
608
						throw new Exception( __( 'Unable to find shipping method for address.', 'woocommerce-gateway-stripe' ) );
609
					}
610
611 View Code Duplication
					foreach ( $package['rates'] as $key => $rate ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
612
						$data[] = array(
613
							'id'       => $rate->id,
614
							'label'    => $rate->label,
615
							'amount'   => array(
616
								'currency' => $currency,
617
								'value'    => $rate->cost,
618
							),
619
							'selected' => false,
620
						);
621
					}
622
				}
623
624
				// Auto select the first shipping method.
625
				WC()->session->set( 'chosen_shipping_methods', array( $data[0]['id'] ) );
626
627
				WC()->cart->calculate_totals();
628
629
				wp_send_json( array( 'success' => 'true', 'shipping_methods' => $this->build_shipping_methods( $data ), 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
630
			} else {
631
				throw new Exception( __( 'Unable to find shipping method for address.', 'woocommerce-gateway-stripe' ) );
632
			}
633
		} catch ( Exception $e ) {
634
			wp_send_json( array( 'success' => 'false', 'shipping_methods' => array(), 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
635
		}
636
	}
637
638
	/**
639
	 * Updates shipping method on cart session.
640
	 *
641
	 * @since 3.1.0
642
	 * @version 3.1.0
643
	 */
644
	public function update_shipping_method() {
645
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
646
			define( 'WOOCOMMERCE_CART', true );
647
		}
648
649
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_update_shipping_method_nonce' ) ) {
650
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
651
		}
652
653
		$selected_shipping_method = array_map( 'wc_clean', $_POST['selected_shipping_method'] );
654
655
		WC()->session->set( 'chosen_shipping_methods', array( $selected_shipping_method['identifier'] ) );
656
657
		WC()->cart->calculate_totals();
658
659
		// Send back the new cart total.
660
		$currency  = get_woocommerce_currency();
661
		$tax_total = max( 0, round( WC()->cart->tax_total + WC()->cart->shipping_tax_total, WC()->cart->dp ) );
662
		$data      = array(
663
			'total' => WC()->cart->total,
664
		);
665
666
		// Include fees and taxes as displayItems.
667 View Code Duplication
		foreach ( WC()->cart->fees as $key => $fee ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
668
			$data['items'][] = array(
669
				'label'  => $fee->name,
670
				'amount' => array(
671
					'currency' => $currency,
672
					'value'    => $fee->amount,
673
				),
674
			);
675
		}
676 View Code Duplication
		if ( 0 < $tax_total ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
677
			$data['items'][] = array(
678
				'label'  => __( 'Tax', 'woocommerce-gateway-stripe' ),
679
				'amount' => array(
680
					'currency' => $currency,
681
					'value'    => $tax_total,
682
				),
683
			);
684
		}
685
686
		wp_send_json( array( 'success' => 'true', 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
687
	}
688
689
	/**
690
	 * Handles the Apple Pay processing via AJAX
691
	 *
692
	 * @access public
693
	 * @since 3.1.0
694
	 * @version 3.1.0
695
	 */
696
	public function process_apple_pay() {
697
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_nonce' ) ) {
698
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
699
		}
700
701
		try {
702
			$result = array_map( 'wc_clean', $_POST['result'] );
703
704
			$order = $this->create_order( $result );
705
706
			$order_id = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->id : $order->get_id();
707
708
			// Handle payment.
709
			if ( $order->get_total() > 0 ) {
710
711 View Code Duplication
				if ( $order->get_total() * 100 < WC_Stripe::get_minimum_amount() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
712
					return new WP_Error( 'stripe_error', sprintf( __( 'Sorry, the minimum allowed order total is %1$s to use this payment method.', 'woocommerce-gateway-stripe' ), wc_price( WC_Stripe::get_minimum_amount() / 100 ) ) );
713
				}
714
715
				$this->log( "Info: Begin processing payment for order {$order_id} for the amount of {$order->get_total()}" );
716
717
				// Make the request.
718
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $result['token']['id'] ) );
719
720
				if ( is_wp_error( $response ) ) {
721
					$localized_messages = $this->get_localized_messages();
722
723
					throw new Exception( ( isset( $localized_messages[ $response->get_error_code() ] ) ? $localized_messages[ $response->get_error_code() ] : $response->get_error_message() ) );
724
				}
725
726
				// Process valid response.
727
				$this->process_response( $response, $order );
728
			} else {
729
				$order->payment_complete();
730
			}
731
732
			// Remove cart.
733
			WC()->cart->empty_cart();
734
735
			update_post_meta( $order_id, '_customer_user', get_current_user_id() );
736
			update_post_meta( $order_id, '_payment_method_title', 'Apple Pay (Stripe)' );
737
738
			// Return thank you page redirect.
739
			wp_send_json( array(
740
				'success'  => 'true',
741
				'redirect' => $this->get_return_url( $order ),
742
			) );
743
744
		} catch ( Exception $e ) {
745
			WC()->session->set( 'refresh_totals', true );
746
			$this->log( sprintf( __( 'Error: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) );
747
748
			if ( is_object( $order ) && isset( $order_id ) && $order->has_status( array( 'pending', 'failed' ) ) ) {
0 ignored issues
show
Bug introduced by
The variable $order does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
749
				$this->send_failed_order_email( $order_id );
750
			}
751
752
			wp_send_json( array( 'success' => 'false', 'msg' => $e->getMessage() ) );
753
		}
754
	}
755
756
	/**
757
	 * Generate the request for the payment.
758
	 * @param  WC_Order $order
759
	 * @param string $source token
760
	 * @return array()
0 ignored issues
show
Documentation introduced by
The doc-type array() could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
761
	 */
762
	protected function generate_payment_request( $order, $source ) {
763
		$post_data                = array();
764
		$post_data['currency']    = strtolower( version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->get_order_currency() : $order->get_currency() );
765
		$post_data['amount']      = $this->get_stripe_amount( $order->get_total(), $post_data['currency'] );
766
		$post_data['description'] = sprintf( __( '%1$s - Order %2$s', 'woocommerce-gateway-stripe' ), $this->statement_descriptor, $order->get_order_number() );
767
		$post_data['capture']     = $this->capture ? 'true' : 'false';
768
769
		$billing_email      = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->billing_email : $order->get_billing_email();
770
771
		if ( ! empty( $billing_email ) && apply_filters( 'wc_stripe_send_stripe_receipt', false ) ) {
772
			$post_data['receipt_email'] = $billing_email;
773
		}
774
775
		$post_data['expand[]']    = 'balance_transaction';
776
		$post_data['source']      = $source;
777
778
		/**
779
		 * Filter the return value of the WC_Payment_Gateway_CC::generate_payment_request.
780
		 *
781
		 * @since 3.1.0
782
		 * @param array $post_data
783
		 * @param WC_Order $order
784
		 * @param object $source
785
		 */
786
		return apply_filters( 'wc_stripe_generate_payment_request', $post_data, $order );
787
	}
788
789
	/**
790
	 * Builds the shippings methods to pass to Apple Pay.
791
	 *
792
	 * @since 3.1.0
793
	 * @version 3.1.0
794
	 */
795
	public function build_shipping_methods( $shipping_methods ) {
796
		if ( empty( $shipping_methods ) ) {
797
			return array();
798
		}
799
800
		$shipping = array();
801
802
		foreach ( $shipping_methods as $method ) {
803
			$shipping[] = array(
804
				'label'      => $method['label'],
805
				'detail'     => '',
806
				'amount'     => $method['amount']['value'],
807
				'identifier' => $method['id'],
808
			);
809
		}
810
811
		return $shipping;
812
	}
813
814
	/**
815
	 * Builds the line items to pass to Apple Pay.
816
	 *
817
	 * @since 3.1.0
818
	 * @version 3.1.0
819
	 */
820
	public function build_line_items() {
821
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
822
			define( 'WOOCOMMERCE_CART', true );
823
		}
824
825
		$decimals = apply_filters( 'wc_stripe_apple_pay_decimals', 2 );
826
		
827
		$items    = array();
828
		$subtotal = 0;
829
830
		foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
831
			$amount         = wc_format_decimal( $cart_item['line_subtotal'], $decimals );
832
			$subtotal       += $cart_item['line_subtotal'];
833
			$quantity_label = 1 < $cart_item['quantity'] ? ' (x' . $cart_item['quantity'] . ')' : '';
834
835
			$product_name = version_compare( WC_VERSION, '3.0', '<' ) ? $cart_item['data']->post->post_title : $cart_item['data']->get_name();
836
837
			$item = array(
838
				'type'   => 'final',
839
				'label'  => $product_name . $quantity_label,
840
				'amount' => wc_format_decimal( $amount, $decimals ),
841
			);
842
843
			$items[] = $item;
844
		}
845
846
		// Default show only subtotal instead of itemization.
847
		if ( apply_filters( 'wc_stripe_apple_pay_disable_itemization', true ) ) {
848
			$items = array();
849
			$items[] = array(
850
				'type'   => 'final',
851
				'label'  => esc_html( __( 'Sub-Total', 'woocommerce-gateway-stripe' ) ),
852
				'amount' => wc_format_decimal( $subtotal, $decimals ),
853
			);
854
		}
855
856
		$discounts   = wc_format_decimal( WC()->cart->get_cart_discount_total(), $decimals );
857
		$tax         = wc_format_decimal( WC()->cart->tax_total + WC()->cart->shipping_tax_total, $decimals );
858
		$shipping    = wc_format_decimal( WC()->cart->shipping_total, $decimals );
859
		$item_total  = wc_format_decimal( WC()->cart->cart_contents_total, $decimals ) + $discounts;
860
		$order_total = wc_format_decimal( $item_total + $tax + $shipping, $decimals );
0 ignored issues
show
Unused Code introduced by
$order_total is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
861
862 View Code Duplication
		if ( wc_tax_enabled() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
863
			$items[] = array(
864
				'type'   => 'final',
865
				'label'  => esc_html( __( 'Tax', 'woocommerce-gateway-stripe' ) ),
866
				'amount' => $tax,
867
			);
868
		}
869
870
		if ( WC()->cart->needs_shipping() && $this->is_shipping_enabled ) {
871
			$items[] = array(
872
				'type'   => 'final',
873
				'label'  => esc_html( __( 'Shipping', 'woocommerce-gateway-stripe' ) ),
874
				'amount' => $shipping,
875
			);
876
		}
877
878 View Code Duplication
		if ( WC()->cart->has_discount() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
879
			$items[] = array(
880
				'type'   => 'final',
881
				'label'  => esc_html( __( 'Discount', 'woocommerce-gateway-stripe' ) ),
882
				'amount' => '-' . $discounts,
883
			);
884
		}
885
886
		return $items;
887
	}
888
889
	/**
890
	 * Create order programatically.
891
	 *
892
	 * @since 3.1.0
893
	 * @version 3.1.0
894
	 * @param array $data
895
	 * @return object $order
896
	 */
897
	public function create_order( $data = array() ) {
898
		if ( empty( $data ) ) {
899
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 520 ) );
900
		}
901
902
		$order = wc_create_order();
903
		$order_id = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->id : $order->get_id();
904
905
		if ( is_wp_error( $order ) ) {
906
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 520 ) );
907
		} elseif ( false === $order ) {
908
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 521 ) );
909
		} else {
910
			do_action( 'woocommerce_new_order', $order_id );
911
		}
912
913
		// Store the line items to the new/resumed order
914
		foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
915
			$item_id = $order->add_product(
916
				$values['data'],
917
				$values['quantity'],
918
				array(
919
					'variation' => $values['variation'],
920
					'totals'    => array(
921
						'subtotal'     => $values['line_subtotal'],
922
						'subtotal_tax' => $values['line_subtotal_tax'],
923
						'total'        => $values['line_total'],
924
						'tax'          => $values['line_tax'],
925
						'tax_data'     => $values['line_tax_data'], // Since 2.2
926
					),
927
				)
928
			);
929
930
			if ( ! $item_id ) {
931
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 525 ) );
932
			}
933
934
			// Allow plugins to add order item meta
935 View Code Duplication
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
936
				do_action( 'woocommerce_add_order_item_meta', $item_id, $values, $cart_item_key );
937
			} else {
938
				do_action( 'woocommerce_new_order_item', $item_id, wc_get_product( $item_id ), $order->get_id() );
939
			}
940
		}
941
942
		// Store fees
943
		foreach ( WC()->cart->get_fees() as $fee_key => $fee ) {
944
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
945
				$item_id = $order->add_fee( $fee );
946
			} else {
947
				$item = new WC_Order_Item_Fee();
948
				$item->set_props( array(
949
					'name'      => $fee->name,
950
					'tax_class' => $fee->taxable ? $fee->tax_class : 0,
951
					'total'     => $fee->amount,
952
					'total_tax' => $fee->tax,
953
					'taxes'     => array(
954
						'total' => $fee->tax_data,
955
					),
956
					'order_id'  => $order->get_id(),
957
				) );
958
				$item_id = $item->save();
959
				$order->add_item( $item );
960
			}
961
962
			if ( ! $item_id ) {
963
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 526 ) );
964
			}
965
966
			// Allow plugins to add order item meta to fees
967 View Code Duplication
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
968
				do_action( 'woocommerce_add_order_fee_meta', $order_id, $item_id, $fee, $fee_key );
969
			} else {
970
				do_action( 'woocommerce_new_order_item', $item_id, $fee, $order->get_id() );
971
			}
972
		}
973
974
		// Store tax rows
975
		foreach ( array_keys( WC()->cart->taxes + WC()->cart->shipping_taxes ) as $tax_rate_id ) {
976
			$tax_amount = WC()->cart->get_tax_amount( $tax_rate_id );
977
			$shipping_tax_amount = WC()->cart->get_shipping_tax_amount( $tax_rate_id );
978
979
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
980
				$item_id = $order->add_tax( $tax_rate_id, $tax_amount, $shipping_tax_amount );
981
			} else {
982
				$item = new WC_Order_Item_Tax();
983
				$item->set_props( array(
984
					'rate_id'            => $tax_rate_id,
985
					'tax_total'          => $tax_amount,
986
					'shipping_tax_total' => $shipping_tax_amount,
987
				) );
988
				$item->set_rate( $tax_rate_id );
989
				$item->set_order_id( $order->get_id() );
990
				$item_id = $item->save();
991
				$order->add_item( $item );
992
			}
993
994
			if ( $tax_rate_id && ! $item_id && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) {
995
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 528 ) );
996
			}
997
		}
998
999
		// Store coupons
1000
		$discount = WC()->cart->get_coupon_discount_amount( $code );
0 ignored issues
show
Bug introduced by
The variable $code seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1001
		$discount_tax = WC()->cart->get_coupon_discount_tax_amount( $code );
0 ignored issues
show
Bug introduced by
The variable $code seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1002
1003
		foreach ( WC()->cart->get_coupons() as $code => $coupon ) {
1004
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
1005
				$coupon_id = $order->add_coupon( $code, $discount, $discount_tax );
1006
			} else {
1007
				$item = new WC_Order_Item_Coupon();
1008
				$item->set_props( array(
1009
					'code'         => $code,
1010
					'discount'     => $discount,
1011
					'discount_tax' => $discount_tax,
1012
					'order_id'     => $order->get_id(),
1013
				) );
1014
				$coupon_id = $item->save();
1015
				$order->add_item( $item );
1016
			}
1017
1018
			if ( ! $coupon_id ) {
1019
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 529 ) );
1020
			}
1021
		}
1022
1023
		// Billing address
1024
		$billing_address = array();
1025
		if ( ! empty( $data['token']['card'] ) ) {
1026
			// Name from Stripe is a full name string.
1027
			$name                          = explode( ' ', $data['token']['card']['name'] );
1028
			$lastname                      = array_pop( $name );
1029
			$firstname                     = implode( ' ', $name );
1030
			$billing_address['first_name'] = $firstname;
1031
			$billing_address['last_name']  = $lastname;
1032
			$billing_address['email']      = $data['shippingContact']['emailAddress'];
1033
			$billing_address['phone']      = $data['shippingContact']['phoneNumber'];
1034
			$billing_address['country']    = $data['token']['card']['country'];
1035
			$billing_address['address_1']  = $data['token']['card']['address_line1'];
1036
			$billing_address['address_2']  = $data['token']['card']['address_line2'];
1037
			$billing_address['city']       = $data['token']['card']['address_city'];
1038
			$billing_address['state']      = $data['token']['card']['address_state'];
1039
			$billing_address['postcode']   = $data['token']['card']['address_zip'];
1040
		}
1041
1042
		// Shipping address.
1043
		$shipping_address = array();
1044
		if ( WC()->cart->needs_shipping() && $this->is_shipping_enabled && ! empty( $data['shippingContact'] ) ) {
1045
			$shipping_address['first_name'] = $data['shippingContact']['givenName'];
1046
			$shipping_address['last_name']  = $data['shippingContact']['familyName'];
1047
			$shipping_address['email']      = $data['shippingContact']['emailAddress'];
1048
			$shipping_address['phone']      = $data['shippingContact']['phoneNumber'];
1049
			$shipping_address['country']    = $data['shippingContact']['countryCode'];
1050
			$shipping_address['address_1']  = $data['shippingContact']['addressLines'][0];
1051
			$shipping_address['address_2']  = $data['shippingContact']['addressLines'][1];
1052
			$shipping_address['city']       = $data['shippingContact']['locality'];
1053
			$shipping_address['state']      = $data['shippingContact']['administrativeArea'];
1054
			$shipping_address['postcode']   = $data['shippingContact']['postalCode'];
1055
		} elseif ( ! empty( $data['shippingContact'] ) ) {
1056
			$shipping_address['first_name'] = $firstname;
0 ignored issues
show
Bug introduced by
The variable $firstname does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1057
			$shipping_address['last_name']  = $lastname;
0 ignored issues
show
Bug introduced by
The variable $lastname does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1058
			$shipping_address['email']      = $data['shippingContact']['emailAddress'];
1059
			$shipping_address['phone']      = $data['shippingContact']['phoneNumber'];
1060
			$shipping_address['country']    = $data['token']['card']['country'];
1061
			$shipping_address['address_1']  = $data['token']['card']['address_line1'];
1062
			$shipping_address['address_2']  = $data['token']['card']['address_line2'];
1063
			$shipping_address['city']       = $data['token']['card']['address_city'];
1064
			$shipping_address['state']      = $data['token']['card']['address_state'];
1065
			$shipping_address['postcode']   = $data['token']['card']['address_zip'];
1066
		}
1067
1068
		$order->set_address( $billing_address, 'billing' );
1069
		$order->set_address( $shipping_address, 'shipping' );
1070
1071
		WC()->shipping->calculate_shipping( WC()->cart->get_shipping_packages() );
1072
1073
		// Get the rate object selected by user.
1074
		foreach ( WC()->shipping->get_packages() as $package_key => $package ) {
1075
			foreach ( $package['rates'] as $key => $rate ) {
1076
				// Loop through user chosen shipping methods.
1077
				foreach ( WC()->session->get( 'chosen_shipping_methods' ) as $method ) {
1078
					if ( $method === $key ) {
1079
						if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
1080
							$order->add_shipping( $rate );
1081
						} else {
1082
							$item = new WC_Order_Item_Shipping();
1083
							$item->set_props( array(
1084
								'method_title' => $rate->label,
1085
								'method_id'    => $rate->id,
1086
								'total'        => wc_format_decimal( $rate->cost ),
1087
								'taxes'        => $rate->taxes,
1088
								'order_id'     => $order->get_id(),
1089
							) );
1090
							foreach ( $rate->get_meta_data() as $key => $value ) {
1091
								$item->add_meta_data( $key, $value, true );
1092
							}
1093
							$item->save();
1094
							$order->add_item( $item );
1095
						}
1096
					}
1097
				}
1098
			}
1099
		}
1100
1101
		$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
1102
		$order->set_payment_method( $available_gateways['stripe'] );
1103
		
1104
		if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
1105
			$order->set_total( WC()->cart->shipping_total, 'shipping' );
1106
			$order->set_total( WC()->cart->get_cart_discount_total(), 'cart_discount' );
1107
			$order->set_total( WC()->cart->get_cart_discount_tax_total(), 'cart_discount_tax' );
1108
			$order->set_total( WC()->cart->tax_total, 'tax' );
1109
			$order->set_total( WC()->cart->shipping_tax_total, 'shipping_tax' );
1110
			$order->set_total( WC()->cart->total );
1111
		} else {
1112
			$order->set_shipping_total( WC()->cart->shipping_total );
1113
			$order->set_discount_total( WC()->cart->get_cart_discount_total() );
1114
			$order->set_discount_tax( WC()->cart->get_cart_discount_tax_total() );
1115
			$order->set_cart_tax( WC()->cart->tax_total );
1116
			$order->set_shipping_tax( WC()->cart->shipping_tax_total );
1117
			$order->set_total( WC()->cart->total );
1118
			$order->save();
1119
		}
1120
1121
		// If we got here, the order was created without problems!
1122
		wc_transaction_query( 'commit' );
1123
1124
		return $order;
1125
	}
1126
1127
	/**
1128
	 * Logs
1129
	 *
1130
	 * @since 3.1.0
1131
	 * @version 3.1.0
1132
	 *
1133
	 * @param string $message
1134
	 */
1135
	public function log( $message ) {
1136
		if ( $this->logging ) {
1137
			WC_Stripe::log( 'Apple Pay: ' . $message );
1138
		}
1139
	}
1140
}
1141
1142
new WC_Stripe_Apple_Pay();
1143