Completed
Push — master ( 08e20e...26bf3a )
by Roy
02:24
created

WC_Stripe_Apple_Pay::allowed_items_in_cart()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 3
Ratio 27.27 %

Importance

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