Completed
Push — master ( be4704...08e20e )
by Roy
02:42
created

WC_Stripe_Apple_Pay::single_scripts()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 38
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 38
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 25
nc 10
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
		if ( ! is_object( $product ) || ! in_array( ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ), $this->supported_product_types() ) ) {
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
	 */
311
	public function supported_product_types() {
312
		return array(
313
			'simple',
314
			'variable',
315
		);
316
	}
317
318
	/**
319
	 * Display Apple Pay button on the cart page
320
	 *
321
	 * @since 3.1.0
322
	 * @version 3.1.0
323
	 */
324
	public function display_apple_pay_button() {
325
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
326
327
		/**
328
		 * In order for the Apple Pay button to show on product detail page,
329
		 * Apple Pay must be enabled and Stripe gateway must be enabled.
330
		 */
331
		if ( ! $this->apple_pay || ! isset( $gateways['stripe'] ) ) {
332
			return;
333
		}
334
335 View Code Duplication
		if ( is_single() ) {
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...
336
			global $post;
337
338
			$product = wc_get_product( $post->ID );
339
340
			if ( ! is_object( $product ) || ! in_array( ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ), $this->supported_product_types() ) ) {
341
				return;
342
			}
343
		}
344
345
		?>
346
		<div class="apple-pay-button-wrapper">
347
			<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>
348
		</div>
349
		<?php
350
	}
351
352
	/**
353
	 * Display Apple Pay button on the cart page
354
	 *
355
	 * @since 3.1.0
356
	 * @version 3.1.0
357
	 */
358
	public function display_apple_pay_separator_html() {
359
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
360
361
		/**
362
		 * In order for the Apple Pay button to show on cart page,
363
		 * Apple Pay must be enabled and Stripe gateway must be enabled.
364
		 */
365
		if ( ! $this->apple_pay || ! isset( $gateways['stripe'] ) ) {
366
			return;
367
		}
368
369 View Code Duplication
		if ( is_single() ) {
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...
370
			global $post;
371
372
			$product = wc_get_product( $post->ID );
373
374
			if ( ! is_object( $product ) || ! in_array( ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ), $this->supported_product_types() ) ) {
375
				return;
376
			}
377
		}
378
		?>
379
		<p class="apple-pay-button-checkout-separator">- <?php esc_html_e( 'Or', 'woocommerce-gateway-stripe' ); ?> -</p>
380
		<?php
381
	}
382
383
	/**
384
	 * Generates the Apple Pay single cart.
385
	 *
386
	 * @since 3.1.0
387
	 * @version 3.1.0
388
	 */
389
	public function generate_apple_pay_single() {
390
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_cart_nonce' ) ) {
391
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
392
		}
393
394
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
395
			define( 'WOOCOMMERCE_CART', true );
396
		}
397
398
		WC()->shipping->reset_shipping();
399
400
		global $post;
401
402
		$product = wc_get_product( $post->ID );
403
		$qty     = ! isset( $_POST['qty'] ) ? 1 : absint( $_POST['qty'] );
404
405
		/**
406
		 * If this page is single product page, we need to simulate
407
		 * adding the product to the cart taken account if it is a
408
		 * simple or variable product.
409
		 */
410
		if ( is_single() ) {
411
			// First empty the cart to prevent wrong calculation.
412
			WC()->cart->empty_cart();
413
414
			if ( 'variable' === ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ) && isset( $_POST['attributes'] ) ) {
415
				$attributes = array_map( 'wc_clean', $_POST['attributes'] );
416
417
				if ( version_compare( WC_VERSION, '3.0.0', '<' ) ) {
418
					$variation_id = $product->get_matching_variation( $attributes );
419
				} else {
420
					$data_store = WC_Data_Store::load( 'product' );
421
					$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
422
				}
423
424
				WC()->cart->add_to_cart( $product->get_id(), $qty, $variation_id, $attributes );
425
			}
426
427
			if ( 'simple' === ( version_compare( WC_VERSION, '3.0.0', '<' ) ? $product->product_type : $product->get_type() ) ) {
428
				WC()->cart->add_to_cart( $product->get_id(), $qty );
429
			}
430
		}
431
432
		WC()->cart->calculate_totals();
433
434
		wp_send_json( array( 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
435
	}
436
437
	/**
438
	 * Generates the Apple Pay cart.
439
	 *
440
	 * @since 3.1.0
441
	 * @version 3.1.0
442
	 */
443
	public function generate_apple_pay_cart() {
444
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_cart_nonce' ) ) {
445
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
446
		}
447
448
		wp_send_json( array( 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
449
	}
450
451
	/**
452
	 * Clears Apple Pay cart.
453
	 *
454
	 * @since 3.1.4
455
	 * @version 3.1.4
456
	 */
457
	public function clear_cart() {
458
		WC()->cart->empty_cart();
459
		exit;
460
	}
461
462
	/**
463
	 * Calculate and set shipping method.
464
	 *
465
	 * @since 3.1.0
466
	 * @version 3.1.0
467
	 * @param array $address
468
	 */
469
	public function calculate_shipping( $address = array() ) {
470
		$country  = strtoupper( $address['countryCode'] );
471
		$state    = strtoupper( $address['administrativeArea'] );
472
		$postcode = $address['postalCode'];
473
		$city     = $address['locality'];
474
475
		WC()->shipping->reset_shipping();
476
477
		if ( $postcode && ! WC_Validation::is_postcode( $postcode, $country ) ) {
478
			throw new Exception( __( 'Please enter a valid postcode/ZIP.', 'woocommerce-gateway-stripe' ) );
479
		} elseif ( $postcode ) {
480
			$postcode = wc_format_postcode( $postcode, $country );
481
		}
482
483 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...
484
			WC()->customer->set_location( $country, $state, $postcode, $city );
485
			WC()->customer->set_shipping_location( $country, $state, $postcode, $city );
486
		} else {
487
			WC()->customer->set_to_base();
488
			WC()->customer->set_shipping_to_base();
489
		}
490
491
		WC()->customer->calculated_shipping( true );
492
493
		/**
494
		 * Set the shipping package.
495
		 *
496
		 * Note that address lines are not provided at this point
497
		 * because Apple Pay does not supply that until after
498
		 * authentication via passcode or Touch ID. We will need to
499
		 * capture this information when we process the payment.
500
		 */
501
502
		$packages = array();
503
504
		$packages[0]['contents']                 = WC()->cart->get_cart();
505
		$packages[0]['contents_cost']            = 0;
506
		$packages[0]['applied_coupons']          = WC()->cart->applied_coupons;
507
		$packages[0]['user']['ID']               = get_current_user_id();
508
		$packages[0]['destination']['country']   = $country;
509
		$packages[0]['destination']['state']     = $state;
510
		$packages[0]['destination']['postcode']  = $postcode;
511
		$packages[0]['destination']['city']      = $city;
512
513 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...
514
			if ( $item['data']->needs_shipping() ) {
515
				if ( isset( $item['line_total'] ) ) {
516
					$packages[0]['contents_cost'] += $item['line_total'];
517
				}
518
			}
519
		}
520
521
		$packages = apply_filters( 'woocommerce_cart_shipping_packages', $packages );
522
523
		WC()->shipping->calculate_shipping( $packages );
524
	}
525
526
	/**
527
	 * Gets shipping for Apple Pay Payment sheet.
528
	 *
529
	 * @since 3.1.0
530
	 * @version 3.1.0
531
	 */
532
	public function get_shipping_methods() {
533
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_get_shipping_methods_nonce' ) ) {
534
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
535
		}
536
537
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
538
			define( 'WOOCOMMERCE_CART', true );
539
		}
540
541
		try {
542
			$address = array_map( 'wc_clean', $_POST['address'] );
543
544
			$this->calculate_shipping( $address );
545
546
			// Set the shipping options.
547
			$currency = get_woocommerce_currency();
548
			$data     = array();
549
550
			$packages = WC()->shipping->get_packages();
551
552
			if ( ! empty( $packages ) && WC()->customer->has_calculated_shipping() ) {
553
				foreach ( $packages as $package_key => $package ) {
554
					if ( empty( $package['rates'] ) ) {
555
						throw new Exception( __( 'Unable to find shipping method for address.', 'woocommerce-gateway-stripe' ) );
556
					}
557
558 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...
559
						$data[] = array(
560
							'id'       => $rate->id,
561
							'label'    => $rate->label,
562
							'amount'   => array(
563
								'currency' => $currency,
564
								'value'    => $rate->cost,
565
							),
566
							'selected' => false,
567
						);
568
					}
569
				}
570
571
				// Auto select the first shipping method.
572
				WC()->session->set( 'chosen_shipping_methods', array( $data[0]['id'] ) );
573
574
				WC()->cart->calculate_totals();
575
576
				wp_send_json( array( 'success' => 'true', 'shipping_methods' => $this->build_shipping_methods( $data ), 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
577
			} else {
578
				throw new Exception( __( 'Unable to find shipping method for address.', 'woocommerce-gateway-stripe' ) );
579
			}
580
		} catch ( Exception $e ) {
581
			wp_send_json( array( 'success' => 'false', 'shipping_methods' => array(), 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
582
		}
583
	}
584
585
	/**
586
	 * Updates shipping method on cart session.
587
	 *
588
	 * @since 3.1.0
589
	 * @version 3.1.0
590
	 */
591
	public function update_shipping_method() {
592
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
593
			define( 'WOOCOMMERCE_CART', true );
594
		}
595
596
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_update_shipping_method_nonce' ) ) {
597
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
598
		}
599
600
		$selected_shipping_method = array_map( 'wc_clean', $_POST['selected_shipping_method'] );
601
602
		WC()->session->set( 'chosen_shipping_methods', array( $selected_shipping_method['identifier'] ) );
603
604
		WC()->cart->calculate_totals();
605
606
		// Send back the new cart total.
607
		$currency  = get_woocommerce_currency();
608
		$tax_total = max( 0, round( WC()->cart->tax_total + WC()->cart->shipping_tax_total, WC()->cart->dp ) );
609
		$data      = array(
610
			'total' => WC()->cart->total,
611
		);
612
613
		// Include fees and taxes as displayItems.
614 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...
615
			$data['items'][] = array(
616
				'label'  => $fee->name,
617
				'amount' => array(
618
					'currency' => $currency,
619
					'value'    => $fee->amount,
620
				),
621
			);
622
		}
623 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...
624
			$data['items'][] = array(
625
				'label'  => __( 'Tax', 'woocommerce-gateway-stripe' ),
626
				'amount' => array(
627
					'currency' => $currency,
628
					'value'    => $tax_total,
629
				),
630
			);
631
		}
632
633
		wp_send_json( array( 'success' => 'true', 'line_items' => $this->build_line_items(), 'total' => WC()->cart->total ) );
634
	}
635
636
	/**
637
	 * Handles the Apple Pay processing via AJAX
638
	 *
639
	 * @access public
640
	 * @since 3.1.0
641
	 * @version 3.1.0
642
	 */
643
	public function process_apple_pay() {
644
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_apple_pay_nonce' ) ) {
645
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
646
		}
647
648
		try {
649
			$result = array_map( 'wc_clean', $_POST['result'] );
650
651
			$order = $this->create_order( $result );
652
653
			$order_id = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->id : $order->get_id();
654
655
			// Handle payment.
656
			if ( $order->get_total() > 0 ) {
657
658 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...
659
					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 ) ) );
660
				}
661
662
				$this->log( "Info: Begin processing payment for order {$order_id} for the amount of {$order->get_total()}" );
663
664
				// Make the request.
665
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $result['token']['id'] ) );
666
667
				if ( is_wp_error( $response ) ) {
668
					$localized_messages = $this->get_localized_messages();
669
670
					throw new Exception( ( isset( $localized_messages[ $response->get_error_code() ] ) ? $localized_messages[ $response->get_error_code() ] : $response->get_error_message() ) );
671
				}
672
673
				// Process valid response.
674
				$this->process_response( $response, $order );
675
			} else {
676
				$order->payment_complete();
677
			}
678
679
			// Remove cart.
680
			WC()->cart->empty_cart();
681
682
			update_post_meta( $order_id, '_customer_user', get_current_user_id() );
683
			update_post_meta( $order_id, '_payment_method_title', __( 'Apple Pay (Stripe)', 'woocommerce-gateway-stripe' ) );
684
685
			// Return thank you page redirect.
686
			wp_send_json( array(
687
				'success'  => 'true',
688
				'redirect' => $this->get_return_url( $order ),
689
			) );
690
691
		} catch ( Exception $e ) {
692
			WC()->session->set( 'refresh_totals', true );
693
			$this->log( sprintf( __( 'Error: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) );
694
695
			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...
696
				$this->send_failed_order_email( $order_id );
697
			}
698
699
			wp_send_json( array( 'success' => 'false', 'msg' => $e->getMessage() ) );
700
		}
701
	}
702
703
	/**
704
	 * Generate the request for the payment.
705
	 * @param  WC_Order $order
706
	 * @param string $source token
707
	 * @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...
708
	 */
709
	protected function generate_payment_request( $order, $source ) {
710
		$post_data                = array();
711
		$post_data['currency']    = strtolower( version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->get_order_currency() : $order->get_currency() );
712
		$post_data['amount']      = $this->get_stripe_amount( $order->get_total(), $post_data['currency'] );
713
		$post_data['description'] = sprintf( __( '%1$s - Order %2$s', 'woocommerce-gateway-stripe' ), $this->statement_descriptor, $order->get_order_number() );
714
		$post_data['capture']     = $this->capture ? 'true' : 'false';
715
716
		$billing_email      = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->billing_email : $order->get_billing_email();
717
718
		if ( ! empty( $billing_email ) && apply_filters( 'wc_stripe_send_stripe_receipt', false ) ) {
719
			$post_data['receipt_email'] = $billing_email;
720
		}
721
722
		$post_data['expand[]']    = 'balance_transaction';
723
		$post_data['source']      = $source;
724
725
		/**
726
		 * Filter the return value of the WC_Payment_Gateway_CC::generate_payment_request.
727
		 *
728
		 * @since 3.1.0
729
		 * @param array $post_data
730
		 * @param WC_Order $order
731
		 * @param object $source
732
		 */
733
		return apply_filters( 'wc_stripe_generate_payment_request', $post_data, $order );
734
	}
735
736
	/**
737
	 * Builds the shippings methods to pass to Apple Pay.
738
	 *
739
	 * @since 3.1.0
740
	 * @version 3.1.0
741
	 */
742
	public function build_shipping_methods( $shipping_methods ) {
743
		if ( empty( $shipping_methods ) ) {
744
			return array();
745
		}
746
747
		$shipping = array();
748
749
		foreach ( $shipping_methods as $method ) {
750
			$shipping[] = array(
751
				'label'      => $method['label'],
752
				'detail'     => '',
753
				'amount'     => $method['amount']['value'],
754
				'identifier' => $method['id'],
755
			);
756
		}
757
758
		return $shipping;
759
	}
760
761
	/**
762
	 * Builds the line items to pass to Apple Pay.
763
	 *
764
	 * @since 3.1.0
765
	 * @version 3.1.0
766
	 */
767
	public function build_line_items() {
768
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
769
			define( 'WOOCOMMERCE_CART', true );
770
		}
771
772
		$decimals = apply_filters( 'wc_stripe_apple_pay_decimals', 2 );
773
		
774
		$items    = array();
775
		$subtotal = 0;
776
777
		foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
778
			$amount         = wc_format_decimal( $values['line_subtotal'], $decimals );
779
			$subtotal       += $values['line_subtotal'];
780
			$quantity_label = 1 < $values['quantity'] ? ' (x' . $values['quantity'] . ')' : '';
781
782
			$item = array(
783
				'type'   => 'final',
784
				'label'  => $values['data']->post->post_title . $quantity_label,
785
				'amount' => wc_format_decimal( $amount, $decimals ),
786
			);
787
788
			$items[] = $item;
789
		}
790
791
		// Default show only subtotal instead of itemization.
792
		if ( apply_filters( 'wc_stripe_apple_pay_disable_itemization', true ) ) {
793
			$items = array();
794
			$items[] = array(
795
				'type'   => 'final',
796
				'label'  => esc_html( __( 'Sub-Total', 'woocommerce-gateway-stripe' ) ),
797
				'amount' => wc_format_decimal( $subtotal, $decimals ),
798
			);
799
		}
800
801
		$discounts   = wc_format_decimal( WC()->cart->get_cart_discount_total(), $decimals );
802
		$tax         = wc_format_decimal( WC()->cart->tax_total + WC()->cart->shipping_tax_total, $decimals );
803
		$shipping    = wc_format_decimal( WC()->cart->shipping_total, $decimals );
804
		$item_total  = wc_format_decimal( WC()->cart->cart_contents_total, $decimals ) + $discounts;
805
		$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...
806
807 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...
808
			$items[] = array(
809
				'type'   => 'final',
810
				'label'  => esc_html( __( 'Tax', 'woocommerce-gateway-stripe' ) ),
811
				'amount' => $tax,
812
			);
813
		}
814
815 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...
816
			$items[] = array(
817
				'type'   => 'final',
818
				'label'  => esc_html( __( 'Shipping', 'woocommerce-gateway-stripe' ) ),
819
				'amount' => $shipping,
820
			);
821
		}
822
823 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...
824
			$items[] = array(
825
				'type'   => 'final',
826
				'label'  => esc_html( __( 'Discount', 'woocommerce-gateway-stripe' ) ),
827
				'amount' => '-' . $discounts,
828
			);
829
		}
830
831
		return $items;
832
	}
833
834
	/**
835
	 * Create order programatically.
836
	 *
837
	 * @since 3.1.0
838
	 * @version 3.1.0
839
	 * @param array $data
840
	 * @return object $order
841
	 */
842
	public function create_order( $data = array() ) {
843
		if ( empty( $data ) ) {
844
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 520 ) );
845
		}
846
847
		$order = wc_create_order();
848
		$order_id = version_compare( WC_VERSION, '3.0.0', '<' ) ? $order->id : $order->get_id();
849
850
		if ( is_wp_error( $order ) ) {
851
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 520 ) );
852
		} elseif ( false === $order ) {
853
			throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 521 ) );
854
		} else {
855
			do_action( 'woocommerce_new_order', $order_id );
856
		}
857
858
		// Store the line items to the new/resumed order
859
		foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
860
			$item_id = $order->add_product(
861
				$values['data'],
862
				$values['quantity'],
863
				array(
864
					'variation' => $values['variation'],
865
					'totals'    => array(
866
						'subtotal'     => $values['line_subtotal'],
867
						'subtotal_tax' => $values['line_subtotal_tax'],
868
						'total'        => $values['line_total'],
869
						'tax'          => $values['line_tax'],
870
						'tax_data'     => $values['line_tax_data'], // Since 2.2
871
					),
872
				)
873
			);
874
875
			if ( ! $item_id ) {
876
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 525 ) );
877
			}
878
879
			// Allow plugins to add order item meta
880 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...
881
				do_action( 'woocommerce_add_order_item_meta', $item_id, $values, $cart_item_key );
882
			} else {
883
				do_action( 'woocommerce_new_order_item', $item_id, wc_get_product( $item_id ), $order->get_id() );
884
			}
885
		}
886
887
		// Store fees
888
		foreach ( WC()->cart->get_fees() as $fee_key => $fee ) {
889
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
890
				$item_id = $order->add_fee( $fee );
891
			} else {
892
				$item = new WC_Order_Item_Fee();
893
				$item->set_props( array(
894
					'name'      => $fee->name,
895
					'tax_class' => $fee->taxable ? $fee->tax_class : 0,
896
					'total'     => $fee->amount,
897
					'total_tax' => $fee->tax,
898
					'taxes'     => array(
899
						'total' => $fee->tax_data,
900
					),
901
					'order_id'  => $order->get_id(),
902
				) );
903
				$item_id = $item->save();
904
				$order->add_item( $item );
905
			}
906
907
			if ( ! $item_id ) {
908
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 526 ) );
909
			}
910
911
			// Allow plugins to add order item meta to fees
912 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...
913
				do_action( 'woocommerce_add_order_fee_meta', $order_id, $item_id, $fee, $fee_key );
914
			} else {
915
				do_action( 'woocommerce_new_order_item', $item_id, $fee, $order->get_id() );
916
			}
917
		}
918
919
		// Store tax rows
920
		foreach ( array_keys( WC()->cart->taxes + WC()->cart->shipping_taxes ) as $tax_rate_id ) {
921
			$tax_amount = WC()->cart->get_tax_amount( $tax_rate_id );
922
			$shipping_tax_amount = WC()->cart->get_shipping_tax_amount( $tax_rate_id );
923
924
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
925
				$item_id = $order->add_tax( $tax_rate_id, $tax_amount, $shipping_tax_amount );
926
			} else {
927
				$item = new WC_Order_Item_Tax();
928
				$item->set_props( array(
929
					'rate_id'            => $tax_rate_id,
930
					'tax_total'          => $tax_amount,
931
					'shipping_tax_total' => $shipping_tax_amount,
932
				) );
933
				$item->set_rate( $tax_rate_id );
934
				$item->set_order_id( $order->get_id() );
935
				$item_id = $item->save();
936
				$order->add_item( $item );
937
			}
938
939
			if ( $tax_rate_id && ! $item_id && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) {
940
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 528 ) );
941
			}
942
		}
943
944
		// Store coupons
945
		$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...
946
		$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...
947
948
		foreach ( WC()->cart->get_coupons() as $code => $coupon ) {
949
			if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
950
				$coupon_id = $order->add_coupon( $code, $discount, $discount_tax );
951
			} else {
952
				$item = new WC_Order_Item_Coupon();
953
				$item->set_props( array(
954
					'code'         => $code,
955
					'discount'     => $discount,
956
					'discount_tax' => $discount_tax,
957
					'order_id'     => $order->get_id(),
958
				) );
959
				$coupon_id = $item->save();
960
				$order->add_item( $item );
961
			}
962
963
			if ( ! $coupon_id ) {
964
				throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce-gateway-stripe' ), 529 ) );
965
			}
966
		}
967
968
		// Billing address
969
		$billing_address = array();
970
		if ( ! empty( $data['token']['card'] ) ) {
971
			// Name from Stripe is a full name string.
972
			$name                          = explode( ' ', $data['token']['card']['name'] );
973
			$lastname                      = array_pop( $name );
974
			$firstname                     = implode( ' ', $name );
975
			$billing_address['first_name'] = $firstname;
976
			$billing_address['last_name']  = $lastname;
977
			$billing_address['email']      = $data['shippingContact']['emailAddress'];
978
			$billing_address['phone']      = $data['shippingContact']['phoneNumber'];
979
			$billing_address['country']    = $data['token']['card']['country'];
980
			$billing_address['address_1']  = $data['token']['card']['address_line1'];
981
			$billing_address['address_2']  = $data['token']['card']['address_line2'];
982
			$billing_address['city']       = $data['token']['card']['address_city'];
983
			$billing_address['state']      = $data['token']['card']['address_state'];
984
			$billing_address['postcode']   = $data['token']['card']['address_zip'];
985
		}
986
987
		// Shipping address.
988
		$shipping_address = array();
989
		if ( WC()->cart->needs_shipping() && ! empty( $data['shippingContact'] ) ) {
990
			$shipping_address['first_name'] = $data['shippingContact']['givenName'];
991
			$shipping_address['last_name']  = $data['shippingContact']['familyName'];
992
			$shipping_address['email']      = $data['shippingContact']['emailAddress'];
993
			$shipping_address['phone']      = $data['shippingContact']['phoneNumber'];
994
			$shipping_address['country']    = $data['shippingContact']['countryCode'];
995
			$shipping_address['address_1']  = $data['shippingContact']['addressLines'][0];
996
			$shipping_address['address_2']  = $data['shippingContact']['addressLines'][1];
997
			$shipping_address['city']       = $data['shippingContact']['locality'];
998
			$shipping_address['state']      = $data['shippingContact']['administrativeArea'];
999
			$shipping_address['postcode']   = $data['shippingContact']['postalCode'];
1000
		} elseif ( ! empty( $data['shippingContact'] ) ) {
1001
			$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...
1002
			$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...
1003
			$shipping_address['email']      = $data['shippingContact']['emailAddress'];
1004
			$shipping_address['phone']      = $data['shippingContact']['phoneNumber'];
1005
			$shipping_address['country']    = $data['token']['card']['country'];
1006
			$shipping_address['address_1']  = $data['token']['card']['address_line1'];
1007
			$shipping_address['address_2']  = $data['token']['card']['address_line2'];
1008
			$shipping_address['city']       = $data['token']['card']['address_city'];
1009
			$shipping_address['state']      = $data['token']['card']['address_state'];
1010
			$shipping_address['postcode']   = $data['token']['card']['address_zip'];
1011
		}
1012
1013
		$order->set_address( $billing_address, 'billing' );
1014
		$order->set_address( $shipping_address, 'shipping' );
1015
1016
		WC()->shipping->calculate_shipping( WC()->cart->get_shipping_packages() );
1017
1018
		// Get the rate object selected by user.
1019
		foreach ( WC()->shipping->get_packages() as $package_key => $package ) {
1020
			foreach ( $package['rates'] as $key => $rate ) {
1021
				// Loop through user chosen shipping methods.
1022
				foreach ( WC()->session->get( 'chosen_shipping_methods' ) as $method ) {
1023
					if ( $method === $key ) {
1024
						if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
1025
							$order->add_shipping( $rate );
1026
						} else {
1027
							$item = new WC_Order_Item_Shipping();
1028
							$item->set_props( array(
1029
								'method_title' => $rate->label,
1030
								'method_id'    => $rate->id,
1031
								'total'        => wc_format_decimal( $rate->cost ),
1032
								'taxes'        => $rate->taxes,
1033
								'order_id'     => $order->get_id(),
1034
							) );
1035
							foreach ( $rate->get_meta_data() as $key => $value ) {
1036
								$item->add_meta_data( $key, $value, true );
1037
							}
1038
							$item->save();
1039
							$order->add_item( $item );
1040
						}
1041
					}
1042
				}
1043
			}
1044
		}
1045
1046
		$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
1047
		$order->set_payment_method( $available_gateways['stripe'] );
1048
		$order->set_total( WC()->cart->shipping_total, 'shipping' );
1049
		$order->set_total( WC()->cart->get_cart_discount_total(), 'cart_discount' );
1050
		$order->set_total( WC()->cart->get_cart_discount_tax_total(), 'cart_discount_tax' );
1051
		$order->set_total( WC()->cart->tax_total, 'tax' );
1052
		$order->set_total( WC()->cart->shipping_tax_total, 'shipping_tax' );
1053
		$order->set_total( WC()->cart->total );
1054
1055
		// If we got here, the order was created without problems!
1056
		wc_transaction_query( 'commit' );
1057
1058
		return $order;
1059
	}
1060
1061
	/**
1062
	 * Logs
1063
	 *
1064
	 * @since 3.1.0
1065
	 * @version 3.1.0
1066
	 *
1067
	 * @param string $message
1068
	 */
1069
	public function log( $message ) {
1070
		if ( $this->logging ) {
1071
			WC_Stripe::log( 'Apple Pay: ' . $message );
1072
		}
1073
	}
1074
}
1075
1076
new WC_Stripe_Apple_Pay();
1077