Completed
Push — master ( 5f4a71...b9b3e9 )
by Roy
02:19
created

WC_Stripe_Apple_Pay::single_scripts()   C

Complexity

Conditions 7
Paths 18

Size

Total Lines 37
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 37
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 24
nc 18
nop 0
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * WC_Stripe_Apple_Pay class.
8
 *
9
 * @extends WC_Gateway_Stripe
10
 */
11
class WC_Stripe_Apple_Pay extends WC_Gateway_Stripe {
12
	/**
13
	 * This Instance.
14
	 *
15
	 * @var
16
	 */
17
	private static $_this;
18
19
	/**
20
	 * Gateway.
21
	 *
22
	 * @var
23
	 */
24
	private $_gateway;
0 ignored issues
show
Unused Code introduced by
The property $_gateway is not used and could be removed.

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

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