Completed
Push — master ( c36b5b...e0f554 )
by Roy
02:41
created

WC_Stripe_Apple_Pay::update_shipping_method()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 44
Code Lines 25

Duplication

Lines 18
Ratio 40.91 %

Importance

Changes 0
Metric Value
dl 18
loc 44
rs 8.439
c 0
b 0
f 0
cc 5
eloc 25
nc 16
nop 0
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
	 * Gateway.
21
	 *
22
	 * @var
23
	 */
24
	private $_gateway;
0 ignored issues
show
Unused Code introduced by
The property $_gateway is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
25
26
	/**
27
	 * Statement Description
28
	 *
29
	 * @var
30
	 */
31
	public $statement_descriptor;
32
33
	/**
34
	 * Gateway settings.
35
	 *
36
	 * @var
37
	 */
38
	private $_gateway_settings;
39
40
	/**
41
	 * Constructor.
42
	 *
43
	 * @access public
44
	 * @since 3.1.0
45
	 * @version 3.1.0
46
	 */
47
	public function __construct() {
48
		self::$_this = $this;
49
50
		$this->_gateway_settings = get_option( 'woocommerce_stripe_settings', '' );
51
52
		$this->statement_descriptor = ! empty( $this->_gateway_settings['statement_descriptor'] ) ? $this->_gateway_settings['statement_descriptor'] : wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
53
54
		$this->init();
55
	}
56
57
	public function instance() {
58
		return self::$_this;
59
	}
60
61
	/**
62
	 * Initialize.
63
	 *
64
	 * @access public
65
	 * @since 3.1.0
66
	 * @version 3.1.4
67
	 */
68
	public function init() {
69
		// If Apple Pay is not enabled no need to proceed further.
70
		if ( 'yes' !== $this->_gateway_settings['apple_pay'] ) {
71
			return;
72
		}
73
74
		add_action( 'wp_enqueue_scripts', array( $this, 'cart_scripts' ) );
75
		add_action( 'wp_enqueue_scripts', array( $this, 'single_scripts' ) );
76
77
		/**
78
		 * In order to display the Apple Pay button in the correct position,
79
		 * a new hook was added to WooCommerce 3.0. In older versions of WooCommerce,
80
		 * CSS is used to position the button.
81
		 */
82
		if ( version_compare( WC_VERSION, '3.0.0', '<' ) ) {
83
			add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'display_apple_pay_button' ), 1 );
84
		} else {
85
			add_action( 'woocommerce_after_add_to_cart_quantity', array( $this, 'display_apple_pay_button' ), 1 );
86
		}
87
88
		add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_apple_pay_button' ), 1 );
89
		add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_apple_pay_separator_html' ), 2 );
90
		add_action( 'woocommerce_checkout_before_customer_details', array( $this, 'display_apple_pay_button' ), 1 );
91
		add_action( 'woocommerce_checkout_before_customer_details', array( $this, 'display_apple_pay_separator_html' ), 2 );
92
		add_action( 'wc_ajax_wc_stripe_apple_pay', array( $this, 'process_apple_pay' ) );
93
		add_action( 'wc_ajax_wc_stripe_generate_apple_pay_cart', array( $this, 'generate_apple_pay_cart' ) );
94
		add_action( 'wc_ajax_wc_stripe_apple_pay_clear_cart', array( $this, 'clear_cart' ) );
95
		add_action( 'wc_ajax_wc_stripe_generate_apple_pay_single', array( $this, 'generate_apple_pay_single' ) );
96
		add_action( 'wc_ajax_wc_stripe_apple_pay_get_shipping_methods', array( $this, 'get_shipping_methods' ) );
97
		add_action( 'wc_ajax_wc_stripe_apple_pay_update_shipping_method', array( $this, 'update_shipping_method' ) );
98
		add_filter( 'woocommerce_gateway_title', array( $this, 'filter_gateway_title' ), 10, 2 );
99
		add_filter( 'woocommerce_validate_postcode', array( $this, 'postal_code_validation' ), 10, 3 );
100
	}
101
102
	/**
103
	 * Filters the gateway title to reflect Apple Pay.
104
	 *
105
	 */
106
	public function filter_gateway_title( $title, $id ) {
107
		global $post;
108
109
		if ( ! is_object( $post ) ) {
110
			return $title;
111
		}
112
113
		$method_title = get_post_meta( $post->ID, '_payment_method_title', true );
114
115
		if ( 'stripe' === $id && ! empty( $method_title ) ) {
116
			return $method_title;
117
		}
118
119
		return $title;
120
	}
121
122
	/**
123
	 * Removes postal code validation from WC.
124
	 *
125
	 * @since 3.1.4
126
	 * @version 3.1.4
127
	 */
128
	public function postal_code_validation( $valid, $postcode, $country ) {
129
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
130
131
		if (
132
			'yes' !== $this->_gateway_settings['apple_pay']
133
			|| ! isset( $gateways['stripe'] )
134
		) {
135
			return $valid;
136
		}
137
138
		/**
139
		 * Currently Apple Pay truncates postal codes from UK and Canada to first 3 characters
140
		 * when passing it back from the shippingcontactselected object. This causes WC to invalidate
141
		 * the order and not let it go through. The remedy for now is just to remove this validation.
142
		 */
143
		if ( 'GB' === $country || 'CA' === $country ) {
144
			return true;
145
		}
146
147
		return $valid;
148
	}
149
150
	/**
151
	 * Enqueue JS scripts and styles for single product page.
152
	 *
153
	 * @since 3.1.0
154
	 * @version 3.1.4
155
	 */
156
	public function single_scripts() {
157
		if ( ! is_single() ) {
158
			return;
159
		}
160
161
		global $post;
162
163
		$product = wc_get_product( $post->ID );
164
165 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...
166
			return;
167
		}
168
169
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
170
171
		wp_enqueue_style( 'stripe_apple_pay', plugins_url( 'assets/css/stripe-apple-pay.css', WC_STRIPE_MAIN_FILE ), array(), WC_STRIPE_VERSION );
172
173
		wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true );
174
		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 );
175
176
		$publishable_key = 'yes' === $this->_gateway_settings['testmode'] ? $this->_gateway_settings['test_publishable_key'] : $this->_gateway_settings['publishable_key'];
177
178
		$stripe_params = array(
179
			'key'                                           => $publishable_key,
180
			'currency_code'                                 => get_woocommerce_currency(),
181
			'country_code'                                  => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
182
			'label'                                         => $this->statement_descriptor,
183
			'ajaxurl'                                       => WC_AJAX::get_endpoint( '%%endpoint%%' ),
184
			'stripe_apple_pay_nonce'                        => wp_create_nonce( '_wc_stripe_apple_pay_nonce' ),
185
			'stripe_apple_pay_cart_nonce'                   => wp_create_nonce( '_wc_stripe_apple_pay_cart_nonce' ),
186
			'stripe_apple_pay_get_shipping_methods_nonce'   => wp_create_nonce( '_wc_stripe_apple_pay_get_shipping_methods_nonce' ),
187
			'stripe_apple_pay_update_shipping_method_nonce' => wp_create_nonce( '_wc_stripe_apple_pay_update_shipping_method_nonce' ),
188
			'needs_shipping'                                => $product->needs_shipping() ? 'yes' : 'no',
189
			'i18n'                                          => array(
190
				'sub_total' => __( 'Sub-Total', 'woocommerce-gateway-stripe' ),
191
			),
192
		);
193
194
		wp_localize_script( 'woocommerce_stripe_apple_pay_single', 'wc_stripe_apple_pay_single_params', apply_filters( 'wc_stripe_apple_pay_single_params', $stripe_params ) );
195
	}
196
197
	/**
198
	 * Enqueue JS scripts and styles for the cart/checkout.
199
	 *
200
	 * @since 3.1.0
201
	 * @version 3.1.0
202
	 */
203
	public function cart_scripts() {
204
		if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) ) {
205
			return;
206
		}
207
208
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
209
210
		wp_enqueue_style( 'stripe_apple_pay', plugins_url( 'assets/css/stripe-apple-pay.css', WC_STRIPE_MAIN_FILE ), array(), WC_STRIPE_VERSION );
211
212
		wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true );
213
		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 );
214
215
		$publishable_key = 'yes' === $this->_gateway_settings['testmode'] ? $this->_gateway_settings['test_publishable_key'] : $this->_gateway_settings['publishable_key'];
216
217
		$stripe_params = array(
218
			'key'                                           => $publishable_key,
219
			'currency_code'                                 => get_woocommerce_currency(),
220
			'country_code'                                  => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
221
			'label'                                         => $this->statement_descriptor,
222
			'ajaxurl'                                       => WC_AJAX::get_endpoint( '%%endpoint%%' ),
223
			'stripe_apple_pay_nonce'                        => wp_create_nonce( '_wc_stripe_apple_pay_nonce' ),
224
			'stripe_apple_pay_cart_nonce'                   => wp_create_nonce( '_wc_stripe_apple_pay_cart_nonce' ),
225
			'stripe_apple_pay_get_shipping_methods_nonce'   => wp_create_nonce( '_wc_stripe_apple_pay_get_shipping_methods_nonce' ),
226
			'stripe_apple_pay_update_shipping_method_nonce' => wp_create_nonce( '_wc_stripe_apple_pay_update_shipping_method_nonce' ),
227
			'needs_shipping'                                => WC()->cart->needs_shipping() ? 'yes' : 'no',
228
			'is_cart_page'                                  => is_cart() ? 'yes' : 'no',
229
		);
230
231
		wp_localize_script( 'woocommerce_stripe_apple_pay', 'wc_stripe_apple_pay_params', apply_filters( 'wc_stripe_apple_pay_params', $stripe_params ) );
232
	}
233
234
	/**
235
	 * Checks to make sure product type is supported by Apple Pay.
236
	 *
237
	 */
238
	public function supported_product_types() {
239
		return array(
240
			'simple',
241
			'variable',
242
		);
243
	}
244
245
	/**
246
	 * Display Apple Pay button on the cart page
247
	 *
248
	 * @since 3.1.0
249
	 * @version 3.1.0
250
	 */
251
	public function display_apple_pay_button() {
252
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
253
254
		/**
255
		 * In order for the Apple Pay button to show on product detail page,
256
		 * Apple Pay must be enabled and Stripe gateway must be enabled.
257
		 */
258
		if (
259
			'yes' !== $this->_gateway_settings['apple_pay']
260
			|| ! isset( $gateways['stripe'] )
261
		) {
262
			return;
263
		}
264
265
		if ( is_single() ) {
266
			global $post;
267
268
			$product = wc_get_product( $post->ID );
269
270 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...
271
				return;
272
			}
273
		}
274
275
		$apple_pay_button = ! empty( $this->_gateway_settings['apple_pay_button'] ) ? $this->_gateway_settings['apple_pay_button'] : 'black';
276
		$button_lang      = ! empty( $this->_gateway_settings['apple_pay_button_lang'] ) ? strtolower( $this->_gateway_settings['apple_pay_button_lang'] ) : 'en';
277
		?>
278
		<div class="apple-pay-button-wrapper">
279
			<button class="apple-pay-button" lang="<?php echo esc_attr( $button_lang ); ?>" style="-webkit-appearance: -apple-pay-button; -apple-pay-button-type: buy; -apple-pay-button-style: <?php echo esc_attr( $apple_pay_button ); ?>;"></button>
280
		</div>
281
		<?php
282
	}
283
284
	/**
285
	 * Display Apple Pay button on the cart page
286
	 *
287
	 * @since 3.1.0
288
	 * @version 3.1.0
289
	 */
290
	public function display_apple_pay_separator_html() {
291
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
292
293
		/**
294
		 * In order for the Apple Pay button to show on cart page,
295
		 * Apple Pay must be enabled and Stripe gateway must be enabled.
296
		 */
297
		if (
298
			'yes' !== $this->_gateway_settings['apple_pay']
299
			|| ! isset( $gateways['stripe'] )
300
		) {
301
			return;
302
		}
303
304
		if ( is_single() ) {
305
			global $post;
306
307
			$product = wc_get_product( $post->ID );
308
309 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...
310
				return;
311
			}
312
		}
313
		?>
314
		<p class="apple-pay-button-checkout-separator">- <?php esc_html_e( 'Or', 'woocommerce-gateway-stripe' ); ?> -</p>
315
		<?php
316
	}
317
318
	/**
319
	 * Generates the Apple Pay single cart.
320
	 *
321
	 * @since 3.1.0
322
	 * @version 3.1.0
323
	 */
324
	public function generate_apple_pay_single() {
325
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_cart_nonce' ) ) {
326
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
327
		}
328
329
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
330
			define( 'WOOCOMMERCE_CART', true );
331
		}
332
333
		WC()->shipping->reset_shipping();
334
335
		global $post;
336
337
		$product = wc_get_product( $post->ID );
338
		$qty     = ! isset( $_POST['qty'] ) ? 1 : absint( $_POST['qty'] );
339
340
		/**
341
		 * If this page is single product page, we need to simulate
342
		 * adding the product to the cart taken account if it is a
343
		 * simple or variable product.
344
		 */
345
		if ( is_single() ) {
346
			// First empty the cart to prevent wrong calculation.
347
			WC()->cart->empty_cart();
348
349
			if ( 'variable' === ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ) && isset( $_POST['attributes'] ) ) {
350
				$attributes = array_map( 'wc_clean', $_POST['attributes'] );
351
352
				if ( version_compare( WC_VERSION, '3.0.0', '<' ) ) {
353
					$variation_id = $product->get_matching_variation( $attributes );
354
				} else {
355
					$data_store = WC_Data_Store::load( 'product' );
356
					$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
357
				}
358
359
				WC()->cart->add_to_cart( $product->get_id(), $qty, $variation_id, $attributes );
360
			}
361
362
			if ( 'simple' === ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ) ) {
363
				WC()->cart->add_to_cart( $product->get_id(), $qty );
364
			}
365
		}
366
367
		WC()->cart->calculate_totals();
368
369
		wp_send_json( array( 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
370
	}
371
372
	/**
373
	 * Generates the Apple Pay cart.
374
	 *
375
	 * @since 3.1.0
376
	 * @version 3.1.0
377
	 */
378
	public function generate_apple_pay_cart() {
379
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_cart_nonce' ) ) {
380
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
381
		}
382
383
		wp_send_json( array( 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
384
	}
385
386
	/**
387
	 * Clears Apple Pay cart.
388
	 *
389
	 * @since 3.1.4
390
	 * @version 3.1.4
391
	 */
392
	public function clear_cart() {
393
		WC()->cart->empty_cart();
394
		exit;
395
	}
396
397
	/**
398
	 * Calculate and set shipping method.
399
	 *
400
	 * @since 3.1.0
401
	 * @version 3.1.0
402
	 * @param array $address
403
	 */
404
	public function calculate_shipping( $address = array() ) {
405
		$country  = strtoupper( $address['countryCode'] );
406
		$state    = strtoupper( $address['administrativeArea'] );
407
		$postcode = $address['postalCode'];
408
		$city     = $address['locality'];
409
410
		WC()->shipping->reset_shipping();
411
412
		if ( $postcode && ! WC_Validation::is_postcode( $postcode, $country ) ) {
413
			throw new Exception( __( 'Please enter a valid postcode/ZIP.', 'woocommerce-gateway-stripe' ) );
414
		} elseif ( $postcode ) {
415
			$postcode = wc_format_postcode( $postcode, $country );
416
		}
417
418 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...
419
			WC()->customer->set_location( $country, $state, $postcode, $city );
420
			WC()->customer->set_shipping_location( $country, $state, $postcode, $city );
421
		} else {
422
			WC()->customer->set_to_base();
423
			WC()->customer->set_shipping_to_base();
424
		}
425
426
		WC()->customer->calculated_shipping( true );
427
428
		/**
429
		 * Set the shipping package.
430
		 *
431
		 * Note that address lines are not provided at this point
432
		 * because Apple Pay does not supply that until after
433
		 * authentication via passcode or Touch ID. We will need to
434
		 * capture this information when we process the payment.
435
		 */
436
437
		$packages = array();
438
439
		$packages[0]['contents']                 = WC()->cart->get_cart();
440
		$packages[0]['contents_cost']            = 0;
441
		$packages[0]['applied_coupons']          = WC()->cart->applied_coupons;
442
		$packages[0]['user']['ID']               = get_current_user_id();
443
		$packages[0]['destination']['country']   = $country;
444
		$packages[0]['destination']['state']     = $state;
445
		$packages[0]['destination']['postcode']  = $postcode;
446
		$packages[0]['destination']['city']      = $city;
447
448 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...
449
			if ( $item['data']->needs_shipping() ) {
450
				if ( isset( $item['line_total'] ) ) {
451
					$packages[0]['contents_cost'] += $item['line_total'];
452
				}
453
			}
454
		}
455
456
		$packages = apply_filters( 'woocommerce_cart_shipping_packages', $packages );
457
458
		WC()->shipping->calculate_shipping( $packages );
459
	}
460
461
	/**
462
	 * Gets shipping for Apple Pay Payment sheet.
463
	 *
464
	 * @since 3.1.0
465
	 * @version 3.1.0
466
	 */
467
	public function get_shipping_methods() {
468
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_get_shipping_methods_nonce' ) ) {
469
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
470
		}
471
472
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
473
			define( 'WOOCOMMERCE_CART', true );
474
		}
475
476
		try {
477
			$address = array_map( 'wc_clean', $_POST['address'] );
478
479
			$this->calculate_shipping( $address );
480
481
			// Set the shipping options.
482
			$currency = get_woocommerce_currency();
483
			$data     = array();
484
485
			$packages = WC()->shipping->get_packages();
486
487
			if ( ! empty( $packages ) && WC()->customer->has_calculated_shipping() ) {
488
				foreach ( $packages as $package_key => $package ) {
489
					if ( empty( $package['rates'] ) ) {
490
						throw new Exception( __( 'Unable to find shipping method for address.', 'woocommerce-gateway-stripe' ) );
491
					}
492
493
					foreach ( $package['rates'] as $key => $rate ) {
494
						$data[] = array(
495
							'id'       => $rate->id,
496
							'label'    => $rate->label,
497
							'amount'   => array(
498
								'currency' => $currency,
499
								'value'    => $rate->cost,
500
							),
501
							'selected' => false,
502
						);
503
					}
504
				}
505
506
				// Auto select the first shipping method.
507
				WC()->session->set( 'chosen_shipping_methods', array( $data[0]['id'] ) );
508
509
				WC()->cart->calculate_totals();
510
511
				wp_send_json( array( 'success' => 'true', 'shipping_methods' => $this->build_shipping_methods( $data ), 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
512
			} else {
513
				throw new Exception( __( 'Unable to find shipping method for address.', 'woocommerce-gateway-stripe' ) );
514
			}
515
		} catch ( Exception $e ) {
516
			wp_send_json( array( 'success' => 'false', 'shipping_methods' => array(), 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
517
		}
518
	}
519
520
	/**
521
	 * Updates shipping method on cart session.
522
	 *
523
	 * @since 3.1.0
524
	 * @version 3.1.0
525
	 */
526
	public function update_shipping_method() {
527
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
528
			define( 'WOOCOMMERCE_CART', true );
529
		}
530
531
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_update_shipping_method_nonce' ) ) {
532
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
533
		}
534
535
		$selected_shipping_method = array_map( 'wc_clean', $_POST['selected_shipping_method'] );
536
537
		WC()->session->set( 'chosen_shipping_methods', array( $selected_shipping_method['identifier'] ) );
538
539
		WC()->cart->calculate_totals();
540
541
		// Send back the new cart total.
542
		$currency  = get_woocommerce_currency();
543
		$tax_total = max( 0, round( WC()->cart->tax_total + WC()->cart->shipping_tax_total, WC()->cart->dp ) );
544
		$data      = array(
545
			'total' => WC()->cart->total,
546
		);
547
548
		// Include fees and taxes as displayItems.
549 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...
550
			$data['items'][] = array(
551
				'label'  => $fee->name,
552
				'amount' => array(
553
					'currency' => $currency,
554
					'value'    => $fee->amount,
555
				),
556
			);
557
		}
558 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...
559
			$data['items'][] = array(
560
				'label'  => __( 'Tax', 'woocommerce-gateway-stripe' ),
561
				'amount' => array(
562
					'currency' => $currency,
563
					'value'    => $tax_total,
564
				),
565
			);
566
		}
567
568
		wp_send_json( array( 'success' => 'true', 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
569
	}
570
571
	/**
572
	 * Handles the Apple Pay processing via AJAX
573
	 *
574
	 * @access public
575
	 * @since 3.1.0
576
	 * @version 3.1.0
577
	 */
578
	public function process_apple_pay() {
579
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_nonce' ) ) {
580
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
581
		}
582
583
		try {
584
			$result = array_map( 'wc_clean', $_POST['result'] );
585
586
			$order = $this->create_order( $result );
587
588
			$order_id = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->id : $order->get_id();
589
590
			// Handle payment.
591
			if ( $order->get_total() > 0 ) {
592
593 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...
594
					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 ) ) );
595
				}
596
597
				WC_Stripe::log( "Info: Begin processing payment for order {$order_id} for the amount of {$order->get_total()}" );
598
599
				// Make the request.
600
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $result['token']['id'] ) );
601
602
				if ( is_wp_error( $response ) ) {
603
					$localized_messages = $this->get_localized_messages();
604
605
					throw new Exception( ( isset( $localized_messages[ $response->get_error_code() ] ) ? $localized_messages[ $response->get_error_code() ] : $response->get_error_message() ) );
606
				}
607
608
				// Process valid response.
609
				$this->process_response( $response, $order );
610
			} else {
611
				$order->payment_complete();
612
			}
613
614
			// Remove cart.
615
			WC()->cart->empty_cart();
616
617
			update_post_meta( $order_id, '_customer_user', get_current_user_id() );
618
			update_post_meta( $order_id, '_payment_method_title', __( 'Apple Pay (Stripe)', 'woocommerce-gateway-stripe' ) );
619
620
			// Return thank you page redirect.
621
			wp_send_json( array(
622
				'success'  => 'true',
623
				'redirect' => $this->get_return_url( $order ),
624
			) );
625
626
		} catch ( Exception $e ) {
627
			WC()->session->set( 'refresh_totals', true );
628
			WC_Stripe::log( sprintf( __( 'Error: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) );
629
630
			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...
631
				$this->send_failed_order_email( $order_id );
632
			}
633
634
			wp_send_json( array( 'success' => 'false', 'msg' => $e->getMessage() ) );
635
		}
636
	}
637
638
	/**
639
	 * Generate the request for the payment.
640
	 * @param  WC_Order $order
641
	 * @param string $source token
642
	 * @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...
643
	 */
644
	protected function generate_payment_request( $order, $source ) {
645
		$post_data                = array();
646
		$post_data['currency']    = strtolower( version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->get_order_currency() : $order->get_currency() );
647
		$post_data['amount']      = $this->get_stripe_amount( $order->get_total(), $post_data['currency'] );
648
		$post_data['description'] = sprintf( __( '%1$s - Order %2$s', 'woocommerce-gateway-stripe' ), $this->statement_descriptor, $order->get_order_number() );
649
		$post_data['capture']     = 'yes' === $this->_gateway_settings['capture'] ? 'true' : 'false';
650
651
		$billing_email      = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->billing_email : $order->get_billing_email();
652
653
		if ( ! empty( $billing_email ) && apply_filters( 'wc_stripe_send_stripe_receipt', false ) ) {
654
			$post_data['receipt_email'] = $billing_email;
655
		}
656
657
		$post_data['expand[]']    = 'balance_transaction';
658
		$post_data['source']      = $source;
659
660
		/**
661
		 * Filter the return value of the WC_Payment_Gateway_CC::generate_payment_request.
662
		 *
663
		 * @since 3.1.0
664
		 * @param array $post_data
665
		 * @param WC_Order $order
666
		 * @param object $source
667
		 */
668
		return apply_filters( 'wc_stripe_generate_payment_request', $post_data, $order );
669
	}
670
671
	/**
672
	 * Builds the shippings methods to pass to Apple Pay.
673
	 *
674
	 * @since 3.1.0
675
	 * @version 3.1.0
676
	 */
677
	public function build_shipping_methods( $shipping_methods ) {
678
		if ( empty( $shipping_methods ) ) {
679
			return array();
680
		}
681
682
		$shipping = array();
683
684
		foreach ( $shipping_methods as $method ) {
685
			$shipping[] = array(
686
				'label'      => $method['label'],
687
				'detail'     => '',
688
				'amount'     => $method['amount']['value'],
689
				'identifier' => $method['id'],
690
			);
691
		}
692
693
		return $shipping;
694
	}
695
696
	/**
697
	 * Builds the line items to pass to Apple Pay.
698
	 *
699
	 * @since 3.1.0
700
	 * @version 3.1.0
701
	 */
702
	public function build_line_items() {
703
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
704
			define( 'WOOCOMMERCE_CART', true );
705
		}
706
707
		$decimals = apply_filters( 'wc_stripe_apple_pay_decimals', 2 );
708
		
709
		$items    = array();
710
		$subtotal = 0;
711
712
		foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
713
			$amount         = wc_format_decimal( $values['line_subtotal'], $decimals );
714
			$subtotal       += $values['line_subtotal'];
715
			$quantity_label = 1 < $values['quantity'] ? ' (x' . $values['quantity'] . ')' : '';
716
717
			$item = array(
718
				'type'   => 'final',
719
				'label'  => $values['data']->post->post_title . $quantity_label,
720
				'amount' => wc_format_decimal( $amount, $decimals ),
721
			);
722
723
			$items[] = $item;
724
		}
725
726
		// Default show only subtotal instead of itemization.
727
		if ( apply_filters( 'wc_stripe_apple_pay_disable_itemization', true ) ) {
728
			$items = array();
729
			$items[] = array(
730
				'type'   => 'final',
731
				'label'  => __( 'Sub-Total', 'woocommerce-gateway-stripe' ),
732
				'amount' => wc_format_decimal( $subtotal, $decimals ),
733
			);
734
		}
735
736
		$discounts   = wc_format_decimal( WC()->cart->get_cart_discount_total(), $decimals );
737
		$tax         = wc_format_decimal( WC()->cart->tax_total + WC()->cart->shipping_tax_total, $decimals );
738
		$shipping    = wc_format_decimal( WC()->cart->shipping_total, $decimals );
739
		$item_total  = wc_format_decimal( WC()->cart->cart_contents_total, $decimals ) + $discounts;
740
		$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...
741
742
		if ( wc_tax_enabled() ) {
743
			$items[] = array(
744
				'type'   => 'final',
745
				'label'  => __( 'Tax', 'woocommerce-gateway-stripe' ),
746
				'amount' => $tax,
747
			);
748
		}
749
750 View Code Duplication
		if ( WC()->cart->needs_shipping() ) {
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...
751
			$items[] = array(
752
				'type'   => 'final',
753
				'label'  => __( 'Shipping', 'woocommerce-gateway-stripe' ),
754
				'amount' => $shipping,
755
			);
756
		}
757
758 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...
759
			$items[] = array(
760
				'type'   => 'final',
761
				'label'  => __( 'Discount', 'woocommerce-gateway-stripe' ),
762
				'amount' => '-' . $discounts,
763
			);
764
		}
765
766
		return $items;
767
	}
768
769
	/**
770
	 * Create order programatically.
771
	 *
772
	 * @since 3.1.0
773
	 * @version 3.1.0
774
	 * @param array $data
775
	 * @return object $order
776
	 */
777
	public function create_order( $data = array() ) {
778
		if ( empty( $data ) ) {
779
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 520 ) );
780
		}
781
782
		$order = wc_create_order();
783
		$order_id = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->id : $order->get_id();
784
785
		if ( is_wp_error( $order ) ) {
786
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 520 ) );
787
		} elseif ( false === $order ) {
788
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 521 ) );
789
		} else {
790
			do_action( 'woocommerce_new_order', $order_id );
791
		}
792
793
		// Store the line items to the new/resumed order
794
		foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
795
			$item_id = $order->add_product(
796
				$values['data'],
797
				$values['quantity'],
798
				array(
799
					'variation' => $values['variation'],
800
					'totals'    => array(
801
						'subtotal'     => $values['line_subtotal'],
802
						'subtotal_tax' => $values['line_subtotal_tax'],
803
						'total'        => $values['line_total'],
804
						'tax'          => $values['line_tax'],
805
						'tax_data'     => $values['line_tax_data'], // Since 2.2
806
					),
807
				)
808
			);
809
810
			if ( ! $item_id ) {
811
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 525 ) );
812
			}
813
814
			// Allow plugins to add order item meta
815 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...
816
				do_action( 'woocommerce_add_order_item_meta', $item_id, $values, $cart_item_key );
817
			} else {
818
				do_action( 'woocommerce_new_order_item', $item_id, wc_get_product( $item_id ), $order->get_id() );
819
			}
820
		}
821
822
		// Store fees
823
		foreach ( WC()->cart->get_fees() as $fee_key => $fee ) {
824
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
825
				$item_id = $order->add_fee( $fee );
826
			} else {
827
				$item = new WC_Order_Item_Fee();
828
				$item->set_props( array(
829
					'name'      => $fee->name,
830
					'tax_class' => $fee->taxable ? $fee->tax_class : 0,
831
					'total'     => $fee->amount,
832
					'total_tax' => $fee->tax,
833
					'taxes'     => array(
834
						'total' => $fee->tax_data,
835
					),
836
					'order_id'  => $order->get_id(),
837
				) );
838
				$item_id = $item->save();
839
				$order->add_item( $item );
840
			}
841
842
			if ( ! $item_id ) {
843
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 526 ) );
844
			}
845
846
			// Allow plugins to add order item meta to fees
847 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...
848
				do_action( 'woocommerce_add_order_fee_meta', $order_id, $item_id, $fee, $fee_key );
849
			} else {
850
				do_action( 'woocommerce_new_order_item', $item_id, $fee, $order->get_id() );
851
			}
852
		}
853
854
		// Store tax rows
855
		foreach ( array_keys( WC()->cart->taxes + WC()->cart->shipping_taxes ) as $tax_rate_id ) {
856
			$tax_amount = WC()->cart->get_tax_amount( $tax_rate_id );
857
			$shipping_tax_amount = WC()->cart->get_shipping_tax_amount( $tax_rate_id );
858
859
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
860
				$item_id = $order->add_tax( $tax_rate_id, $tax_amount, $shipping_tax_amount );
861
			} else {
862
				$item = new WC_Order_Item_Tax();
863
				$item->set_props( array(
864
					'rate_id'            => $tax_rate_id,
865
					'tax_total'          => $tax_amount,
866
					'shipping_tax_total' => $shipping_tax_amount,
867
				) );
868
				$item->set_rate( $tax_rate_id );
869
				$item->set_order_id( $order->get_id() );
870
				$item_id = $item->save();
871
				$order->add_item( $item );
872
			}
873
874
			if ( $tax_rate_id && ! $item_id && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) {
875
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 528 ) );
876
			}
877
		}
878
879
		// Store coupons
880
		$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...
881
		$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...
882
883
		foreach ( WC()->cart->get_coupons() as $code => $coupon ) {
884
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
885
				$coupon_id = $order->add_coupon( $code, $discount, $discount_tax );
886
			} else {
887
				$item = new WC_Order_Item_Coupon();
888
				$item->set_props( array(
889
					'code'         => $code,
890
					'discount'     => $discount,
891
					'discount_tax' => $discount_tax,
892
					'order_id'     => $order->get_id(),
893
				) );
894
				$coupon_id = $item->save();
895
				$order->add_item( $item );
896
			}
897
898
			if ( ! $coupon_id ) {
899
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 529 ) );
900
			}
901
		}
902
903
		// Billing address
904
		$billing_address = array();
905
		if ( ! empty( $data['token']['card'] ) ) {
906
			// Name from Stripe is a full name string.
907
			$name                          = explode( ' ', $data['token']['card']['name'] );
908
			$lastname                      = array_pop( $name );
909
			$firstname                     = implode( ' ', $name );
910
			$billing_address['first_name'] = $firstname;
911
			$billing_address['last_name']  = $lastname;
912
			$billing_address['email']      = $data['shippingContact']['emailAddress'];
913
			$billing_address['phone']      = $data['shippingContact']['phoneNumber'];
914
			$billing_address['country']    = $data['token']['card']['country'];
915
			$billing_address['address_1']  = $data['token']['card']['address_line1'];
916
			$billing_address['address_2']  = $data['token']['card']['address_line2'];
917
			$billing_address['city']       = $data['token']['card']['address_city'];
918
			$billing_address['state']      = $data['token']['card']['address_state'];
919
			$billing_address['postcode']   = $data['token']['card']['address_zip'];
920
		}
921
922
		// Shipping address.
923
		$shipping_address = array();
924
		if ( WC()->cart->needs_shipping() && ! empty( $data['shippingContact'] ) ) {
925
			$shipping_address['first_name'] = $data['shippingContact']['givenName'];
926
			$shipping_address['last_name']  = $data['shippingContact']['familyName'];
927
			$shipping_address['email']      = $data['shippingContact']['emailAddress'];
928
			$shipping_address['phone']      = $data['shippingContact']['phoneNumber'];
929
			$shipping_address['country']    = $data['shippingContact']['countryCode'];
930
			$shipping_address['address_1']  = $data['shippingContact']['addressLines'][0];
931
			$shipping_address['address_2']  = $data['shippingContact']['addressLines'][1];
932
			$shipping_address['city']       = $data['shippingContact']['locality'];
933
			$shipping_address['state']      = $data['shippingContact']['administrativeArea'];
934
			$shipping_address['postcode']   = $data['shippingContact']['postalCode'];
935
		} elseif ( ! empty( $data['shippingContact'] ) ) {
936
			$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...
937
			$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...
938
			$shipping_address['email']      = $data['shippingContact']['emailAddress'];
939
			$shipping_address['phone']      = $data['shippingContact']['phoneNumber'];
940
			$shipping_address['country']    = $data['token']['card']['country'];
941
			$shipping_address['address_1']  = $data['token']['card']['address_line1'];
942
			$shipping_address['address_2']  = $data['token']['card']['address_line2'];
943
			$shipping_address['city']       = $data['token']['card']['address_city'];
944
			$shipping_address['state']      = $data['token']['card']['address_state'];
945
			$shipping_address['postcode']   = $data['token']['card']['address_zip'];
946
		}
947
948
		$order->set_address( $billing_address, 'billing' );
949
		$order->set_address( $shipping_address, 'shipping' );
950
951
		WC()->shipping->calculate_shipping( WC()->cart->get_shipping_packages() );
952
953
		// Get the rate object selected by user.
954
		foreach ( WC()->shipping->get_packages() as $package_key => $package ) {
955
			foreach ( $package['rates'] as $key => $rate ) {
956
				// Loop through user chosen shipping methods.
957
				foreach ( WC()->session->get( 'chosen_shipping_methods' ) as $method ) {
958
					if ( $method === $key ) {
959
						if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
960
							$order->add_shipping( $rate );
961
						} else {
962
							$item = new WC_Order_Item_Shipping();
963
							$item->set_props( array(
964
								'method_title' => $rate->label,
965
								'method_id'    => $rate->id,
966
								'total'        => wc_format_decimal( $rate->cost ),
967
								'taxes'        => $rate->taxes,
968
								'order_id'     => $order->get_id(),
969
							) );
970
							foreach ( $rate->get_meta_data() as $key => $value ) {
971
								$item->add_meta_data( $key, $value, true );
972
							}
973
							$item->save();
974
							$order->add_item( $item );
975
						}
976
					}
977
				}
978
			}
979
		}
980
981
		$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
982
		$order->set_payment_method( $available_gateways['stripe'] );
983
		$order->set_total( WC()->cart->shipping_total, 'shipping' );
984
		$order->set_total( WC()->cart->get_cart_discount_total(), 'cart_discount' );
985
		$order->set_total( WC()->cart->get_cart_discount_tax_total(), 'cart_discount_tax' );
986
		$order->set_total( WC()->cart->tax_total, 'tax' );
987
		$order->set_total( WC()->cart->shipping_tax_total, 'shipping_tax' );
988
		$order->set_total( WC()->cart->total );
989
990
		// If we got here, the order was created without problems!
991
		wc_transaction_query( 'commit' );
992
993
		return $order;
994
	}
995
}
996
997
new WC_Stripe_Apple_Pay();
998