WC_Stripe_Payment_Request::__construct()   F
last analyzed

Complexity

Conditions 15
Paths 640

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
nc 640
nop 0
dl 0
loc 33
rs 2.25
c 0
b 0
f 0

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Stripe Payment Request API
4
 * Adds support for Apple Pay and Chrome Payment Request API buttons.
5
 * Utilizes the Stripe Payment Request Button to support checkout from the product detail and cart pages.
6
 *
7
 * @package WooCommerce_Stripe/Classes/Payment_Request
8
 * @since   4.0.0
9
 */
10
11
if ( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
/**
16
 * WC_Stripe_Payment_Request class.
17
 */
18
class WC_Stripe_Payment_Request {
19
	/**
20
	 * Enabled.
21
	 *
22
	 * @var
23
	 */
24
	public $stripe_settings;
25
26
	/**
27
	 * Total label
28
	 *
29
	 * @var
30
	 */
31
	public $total_label;
32
33
	/**
34
	 * Key
35
	 *
36
	 * @var
37
	 */
38
	public $publishable_key;
39
40
	/**
41
	 * Key
42
	 *
43
	 * @var
44
	 */
45
	public $secret_key;
46
47
	/**
48
	 * Is test mode active?
49
	 *
50
	 * @var bool
51
	 */
52
	public $testmode;
53
54
	/**
55
	 * This Instance.
56
	 *
57
	 * @var
58
	 */
59
	private static $_this;
60
61
	/**
62
	 * Initialize class actions.
63
	 *
64
	 * @since   3.0.0
65
	 * @version 4.0.0
66
	 */
67
	public function __construct() {
68
		self::$_this           = $this;
69
		$this->stripe_settings = get_option( 'woocommerce_stripe_settings', array() );
70
		$this->testmode        = ( ! empty( $this->stripe_settings['testmode'] ) && 'yes' === $this->stripe_settings['testmode'] ) ? true : false;
71
		$this->publishable_key = ! empty( $this->stripe_settings['publishable_key'] ) ? $this->stripe_settings['publishable_key'] : '';
72
		$this->secret_key      = ! empty( $this->stripe_settings['secret_key'] ) ? $this->stripe_settings['secret_key'] : '';
73
		$this->total_label     = ! empty( $this->stripe_settings['statement_descriptor'] ) ? WC_Stripe_Helper::clean_statement_descriptor( $this->stripe_settings['statement_descriptor'] ) : '';
74
75
		if ( $this->testmode ) {
76
			$this->publishable_key = ! empty( $this->stripe_settings['test_publishable_key'] ) ? $this->stripe_settings['test_publishable_key'] : '';
77
			$this->secret_key      = ! empty( $this->stripe_settings['test_secret_key'] ) ? $this->stripe_settings['test_secret_key'] : '';
78
		}
79
80
		$this->total_label = str_replace( "'", '', $this->total_label ) . apply_filters( 'wc_stripe_payment_request_total_label_suffix', ' (via WooCommerce)' );
81
82
		// Checks if Stripe Gateway is enabled.
83
		if ( empty( $this->stripe_settings ) || ( isset( $this->stripe_settings['enabled'] ) && 'yes' !== $this->stripe_settings['enabled'] ) ) {
84
			return;
85
		}
86
87
		// Checks if Payment Request is enabled.
88
		if ( ! isset( $this->stripe_settings['payment_request'] ) || 'yes' !== $this->stripe_settings['payment_request'] ) {
89
			return;
90
		}
91
92
		// Don't load for change payment method page.
93
		if ( isset( $_GET['change_payment_method'] ) ) {
94
			return;
95
		}
96
97
		add_action( 'template_redirect', array( $this, 'set_session' ) );
98
		$this->init();
99
	}
100
101
	/**
102
	 * Checks if keys are set and valid.
103
	 *
104
	 * @since  4.0.6
105
	 * @return boolean True if the keys are set *and* valid, false otherwise (for example, if keys are empty or the secret key was pasted as publishable key).
106
	 */
107 View Code Duplication
	public function are_keys_set() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
108
		// NOTE: updates to this function should be added to are_keys_set()
109
		// in includes/abstracts/abstract-wc-stripe-payment-gateway.php
110
		if ( $this->testmode ) {
111
			return preg_match( '/^pk_test_/', $this->publishable_key )
112
			       && preg_match( '/^[rs]k_test_/', $this->secret_key );
113
		} else {
114
			return preg_match( '/^pk_live_/', $this->publishable_key )
115
			       && preg_match( '/^[rs]k_live_/', $this->secret_key );
116
		}
117
	}
118
119
	/**
120
	 * Get this instance.
121
	 *
122
	 * @since  4.0.6
123
	 * @return class
124
	 */
125
	public static function instance() {
126
		return self::$_this;
127
	}
128
129
	/**
130
	 * Sets the WC customer session if one is not set.
131
	 * This is needed so nonces can be verified by AJAX Request.
132
	 *
133
	 * @since  4.0.0
134
	 * @return void
135
	 */
136
	public function set_session() {
137
		if ( ! is_product() || ( isset( WC()->session ) && WC()->session->has_session() ) ) {
138
			return;
139
		}
140
141
		WC()->session->set_customer_session_cookie( true );
142
	}
143
144
	/**
145
	 * Initialize hooks.
146
	 *
147
	 * @since   4.0.0
148
	 * @version 4.0.0
149
	 * @return  void
150
	 */
151
	public function init() {
152
		add_action( 'wp_enqueue_scripts', array( $this, 'scripts' ) );
153
154
		add_action( 'woocommerce_after_add_to_cart_quantity', array( $this, 'display_payment_request_button_html' ), 1 );
155
		add_action( 'woocommerce_after_add_to_cart_quantity', array( $this, 'display_payment_request_button_separator_html' ), 2 );
156
157
		add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_payment_request_button_html' ), 1 );
158
		add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_payment_request_button_separator_html' ), 2 );
159
160
		add_action( 'woocommerce_checkout_before_customer_details', array( $this, 'display_payment_request_button_html' ), 1 );
161
		add_action( 'woocommerce_checkout_before_customer_details', array( $this, 'display_payment_request_button_separator_html' ), 2 );
162
163
		add_action( 'wc_ajax_wc_stripe_get_cart_details', array( $this, 'ajax_get_cart_details' ) );
164
		add_action( 'wc_ajax_wc_stripe_get_shipping_options', array( $this, 'ajax_get_shipping_options' ) );
165
		add_action( 'wc_ajax_wc_stripe_update_shipping_method', array( $this, 'ajax_update_shipping_method' ) );
166
		add_action( 'wc_ajax_wc_stripe_create_order', array( $this, 'ajax_create_order' ) );
167
		add_action( 'wc_ajax_wc_stripe_add_to_cart', array( $this, 'ajax_add_to_cart' ) );
168
		add_action( 'wc_ajax_wc_stripe_get_selected_product_data', array( $this, 'ajax_get_selected_product_data' ) );
169
		add_action( 'wc_ajax_wc_stripe_clear_cart', array( $this, 'ajax_clear_cart' ) );
170
		add_action( 'wc_ajax_wc_stripe_log_errors', array( $this, 'ajax_log_errors' ) );
171
172
		add_filter( 'woocommerce_gateway_title', array( $this, 'filter_gateway_title' ), 10, 2 );
173
		add_filter( 'woocommerce_validate_postcode', array( $this, 'postal_code_validation' ), 10, 3 );
174
175
		add_action( 'woocommerce_checkout_order_processed', array( $this, 'add_order_meta' ), 10, 2 );
176
	}
177
178
	/**
179
	 * Gets the button type.
180
	 *
181
	 * @since   4.0.0
182
	 * @version 4.0.0
183
	 * @return  string
184
	 */
185
	public function get_button_type() {
186
		return isset( $this->stripe_settings['payment_request_button_type'] ) ? $this->stripe_settings['payment_request_button_type'] : 'default';
187
	}
188
189
	/**
190
	 * Gets the button theme.
191
	 *
192
	 * @since   4.0.0
193
	 * @version 4.0.0
194
	 * @return  string
195
	 */
196
	public function get_button_theme() {
197
		return isset( $this->stripe_settings['payment_request_button_theme'] ) ? $this->stripe_settings['payment_request_button_theme'] : 'dark';
198
	}
199
200
	/**
201
	 * Gets the button height.
202
	 *
203
	 * @since   4.0.0
204
	 * @version 4.0.0
205
	 * @return  string
206
	 */
207
	public function get_button_height() {
208
		return isset( $this->stripe_settings['payment_request_button_height'] ) ? str_replace( 'px', '', $this->stripe_settings['payment_request_button_height'] ) : '64';
209
	}
210
211
	/**
212
	 * Checks if the button is branded.
213
	 *
214
	 * @since   4.4.0
215
	 * @version 4.4.0
216
	 * @return  boolean
217
	 */
218
	public function is_branded_button() {
219
		return 'branded' === $this->get_button_type();
220
	}
221
222
	/**
223
	 * Gets the branded button type.
224
	 *
225
	 * @since   4.4.0
226
	 * @version 4.4.0
227
	 * @return  string
228
	 */
229
	public function get_button_branded_type() {
230
		return isset( $this->stripe_settings['payment_request_button_branded_type'] ) ? $this->stripe_settings['payment_request_button_branded_type'] : 'default';
231
	}
232
233
	/**
234
	 * Checks if the button is custom.
235
	 *
236
	 * @since   4.4.0
237
	 * @version 4.4.0
238
	 * @return  boolean
239
	 */
240
	public function is_custom_button() {
241
		return 'custom' === $this->get_button_type();
242
	}
243
244
	/**
245
	 * Returns custom button css selector.
246
	 *
247
	 * @since   4.4.0
248
	 * @version 4.4.0
249
	 * @return  string
250
	 */
251
	public function custom_button_selector() {
252
		return $this->is_custom_button() ? '#wc-stripe-custom-button' : '';
253
	}
254
255
	/**
256
	 * Gets the custom button label.
257
	 *
258
	 * @since   4.4.0
259
	 * @version 4.4.0
260
	 * @return  string
261
	 */
262
	public function get_button_label() {
263
		return isset( $this->stripe_settings['payment_request_button_label'] ) ? $this->stripe_settings['payment_request_button_label'] : 'Buy now';
264
	}
265
266
	/**
267
	 * Gets the product data for the currently viewed page
268
	 *
269
	 * @since   4.0.0
270
	 * @version 4.0.0
271
	 * @return  mixed Returns false if not on a product page, the product information otherwise.
272
	 */
273
	public function get_product_data() {
274
		if ( ! is_product() ) {
275
			return false;
276
		}
277
278
		global $post;
279
280
		$product = wc_get_product( $post->ID );
281
282
		if ( 'variable' === $product->get_type() ) {
283
			$attributes = wc_clean( wp_unslash( $_GET ) );
284
285
			$data_store   = WC_Data_Store::load( 'product' );
286
			$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
287
288
			if ( ! empty( $variation_id ) ) {
289
				$product = wc_get_product( $variation_id );
290
			}
291
		}
292
293
		$data  = array();
294
		$items = array();
295
296
		$items[] = array(
297
			'label'  => $product->get_name(),
298
			'amount' => WC_Stripe_Helper::get_stripe_amount( $product->get_price() ),
299
		);
300
301 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...
302
			$items[] = array(
303
				'label'   => __( 'Tax', 'woocommerce-gateway-stripe' ),
304
				'amount'  => 0,
305
				'pending' => true,
306
			);
307
		}
308
309 View Code Duplication
		if ( wc_shipping_enabled() && $product->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...
310
			$items[] = array(
311
				'label'   => __( 'Shipping', 'woocommerce-gateway-stripe' ),
312
				'amount'  => 0,
313
				'pending' => true,
314
			);
315
316
			$data['shippingOptions'] = array(
317
				'id'     => 'pending',
318
				'label'  => __( 'Pending', 'woocommerce-gateway-stripe' ),
319
				'detail' => '',
320
				'amount' => 0,
321
			);
322
		}
323
324
		$data['displayItems'] = $items;
325
		$data['total']        = array(
326
			'label'   => apply_filters( 'wc_stripe_payment_request_total_label', $this->total_label ),
327
			'amount'  => WC_Stripe_Helper::get_stripe_amount( $product->get_price() ),
328
			'pending' => true,
329
		);
330
331
		$data['requestShipping'] = ( wc_shipping_enabled() && $product->needs_shipping() );
332
		$data['currency']        = strtolower( get_woocommerce_currency() );
333
		$data['country_code']    = substr( get_option( 'woocommerce_default_country' ), 0, 2 );
334
335
		return apply_filters( 'wc_stripe_payment_request_product_data', $data, $product );
336
	}
337
338
	/**
339
	 * Filters the gateway title to reflect Payment Request type
340
	 */
341
	public function filter_gateway_title( $title, $id ) {
342
		global $post;
343
344
		if ( ! is_object( $post ) ) {
345
			return $title;
346
		}
347
348
		$order        = wc_get_order( $post->ID );
349
		$method_title = is_object( $order ) ? $order->get_payment_method_title() : '';
350
351
		if ( 'stripe' === $id && ! empty( $method_title ) && 'Apple Pay (Stripe)' === $method_title ) {
352
			return $method_title;
353
		}
354
355
		if ( 'stripe' === $id && ! empty( $method_title ) && 'Chrome Payment Request (Stripe)' === $method_title ) {
356
			return $method_title;
357
		}
358
359
		return $title;
360
	}
361
362
	/**
363
	 * Removes postal code validation from WC.
364
	 *
365
	 * @since   3.1.4
366
	 * @version 4.0.0
367
	 */
368
	public function postal_code_validation( $valid, $postcode, $country ) {
369
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
370
371
		if ( ! isset( $gateways['stripe'] ) ) {
372
			return $valid;
373
		}
374
375
		$payment_request_type = isset( $_POST['payment_request_type'] ) ? wc_clean( $_POST['payment_request_type'] ) : '';
376
377
		if ( 'apple_pay' !== $payment_request_type ) {
378
			return $valid;
379
		}
380
381
		/**
382
		 * Currently Apple Pay truncates postal codes from UK and Canada to first 3 characters
383
		 * when passing it back from the shippingcontactselected object. This causes WC to invalidate
384
		 * the order and not let it go through. The remedy for now is just to remove this validation.
385
		 * Note that this only works with shipping providers that don't validate full postal codes.
386
		 */
387
		if ( 'GB' === $country || 'CA' === $country ) {
388
			return true;
389
		}
390
391
		return $valid;
392
	}
393
394
	/**
395
	 * Add needed order meta
396
	 *
397
	 * @param integer $order_id    The order ID.
398
	 * @param array   $posted_data The posted data from checkout form.
399
	 *
400
	 * @since   4.0.0
401
	 * @version 4.0.0
402
	 * @return  void
403
	 */
404
	public function add_order_meta( $order_id, $posted_data ) {
0 ignored issues
show
Unused Code introduced by
The parameter $posted_data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
405
		if ( empty( $_POST['payment_request_type'] ) ) {
406
			return;
407
		}
408
409
		$order = wc_get_order( $order_id );
410
411
		$payment_request_type = wc_clean( $_POST['payment_request_type'] );
412
413
		if ( 'apple_pay' === $payment_request_type ) {
414
			$order->set_payment_method_title( 'Apple Pay (Stripe)' );
415
			$order->save();
416
		}
417
418
		if ( 'payment_request_api' === $payment_request_type ) {
419
			$order->set_payment_method_title( 'Chrome Payment Request (Stripe)' );
420
			$order->save();
421
		}
422
	}
423
424
	/**
425
	 * Checks to make sure product type is supported.
426
	 *
427
	 * @since   3.1.0
428
	 * @version 4.0.0
429
	 * @return  array
430
	 */
431
	public function supported_product_types() {
432
		return apply_filters(
433
			'wc_stripe_payment_request_supported_types',
434
			array(
435
				'simple',
436
				'variable',
437
				'variation',
438
				'subscription',
439
				'variable-subscription',
440
				'subscription_variation',
441
				'booking',
442
				'bundle',
443
				'composite',
444
				'mix-and-match',
445
			)
446
		);
447
	}
448
449
	/**
450
	 * Checks the cart to see if all items are allowed to used.
451
	 *
452
	 * @since   3.1.4
453
	 * @version 4.0.0
454
	 * @return  boolean
455
	 */
456
	public function allowed_items_in_cart() {
457
		foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
458
			$_product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
459
460
			if ( ! in_array( $_product->get_type(), $this->supported_product_types() ) ) {
461
				return false;
462
			}
463
464
			// Trial subscriptions with shipping are not supported
465
			if ( class_exists( 'WC_Subscriptions_Order' ) && WC_Subscriptions_Cart::cart_contains_subscription() && $_product->needs_shipping() && WC_Subscriptions_Product::get_trial_length( $_product ) > 0 ) {
466
				return false;
467
			}
468
469
			// Pre Orders compatbility where we don't support charge upon release.
470
			if ( class_exists( 'WC_Pre_Orders_Order' ) && WC_Pre_Orders_Cart::cart_contains_pre_order() && WC_Pre_Orders_Product::product_is_charged_upon_release( WC_Pre_Orders_Cart::get_pre_order_product() ) ) {
471
				return false;
472
			}
473
		}
474
475
		return true;
476
	}
477
478
	/**
479
	 * Load public scripts and styles.
480
	 *
481
	 * @since   3.1.0
482
	 * @version 4.0.0
483
	 */
484
	public function scripts() {
485
		// If keys are not set bail.
486
		if ( ! $this->are_keys_set() ) {
487
			WC_Stripe_Logger::log( 'Keys are not set correctly.' );
488
			return;
489
		}
490
491
		// If no SSL bail.
492
		if ( ! $this->testmode && ! is_ssl() ) {
493
			WC_Stripe_Logger::log( 'Stripe Payment Request live mode requires SSL.' );
494
			return;
495
		}
496
497
		if ( ! is_product() && ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) ) {
498
			return;
499
		}
500
501
		if ( is_product() && ! $this->should_show_payment_button_on_product_page() ) {
502
			return;
503
		}
504
505
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
506
507
		$stripe_params = array(
508
			'ajax_url'        => WC_AJAX::get_endpoint( '%%endpoint%%' ),
509
			'stripe'          => array(
510
				'key'                => $this->publishable_key,
511
				'allow_prepaid_card' => apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no',
512
			),
513
			'nonce'           => array(
514
				'payment'                   => wp_create_nonce( 'wc-stripe-payment-request' ),
515
				'shipping'                  => wp_create_nonce( 'wc-stripe-payment-request-shipping' ),
516
				'update_shipping'           => wp_create_nonce( 'wc-stripe-update-shipping-method' ),
517
				'checkout'                  => wp_create_nonce( 'woocommerce-process_checkout' ),
518
				'add_to_cart'               => wp_create_nonce( 'wc-stripe-add-to-cart' ),
519
				'get_selected_product_data' => wp_create_nonce( 'wc-stripe-get-selected-product-data' ),
520
				'log_errors'                => wp_create_nonce( 'wc-stripe-log-errors' ),
521
				'clear_cart'                => wp_create_nonce( 'wc-stripe-clear-cart' ),
522
			),
523
			'i18n'            => array(
524
				'no_prepaid_card'  => __( 'Sorry, we\'re not accepting prepaid cards at this time.', 'woocommerce-gateway-stripe' ),
525
				/* translators: Do not translate the [option] placeholder */
526
				'unknown_shipping' => __( 'Unknown shipping option "[option]".', 'woocommerce-gateway-stripe' ),
527
			),
528
			'checkout'        => array(
529
				'url'               => wc_get_checkout_url(),
530
				'currency_code'     => strtolower( get_woocommerce_currency() ),
531
				'country_code'      => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
532
				'needs_shipping'    => WC()->cart->needs_shipping() ? 'yes' : 'no',
533
				// Defaults to 'required' to match how core initializes this option.
534
				'needs_payer_phone' => 'required' === get_option( 'woocommerce_checkout_phone_field', 'required' ),
535
			),
536
			'button'          => array(
537
				'type'         => $this->get_button_type(),
538
				'theme'        => $this->get_button_theme(),
539
				'height'       => $this->get_button_height(),
540
				'locale'       => apply_filters( 'wc_stripe_payment_request_button_locale', substr( get_locale(), 0, 2 ) ), // Default format is en_US.
541
				'is_custom'    => $this->is_custom_button(),
542
				'is_branded'   => $this->is_branded_button(),
543
				'css_selector' => $this->custom_button_selector(),
544
				'branded_type' => $this->get_button_branded_type(),
545
			),
546
			'is_product_page' => is_product(),
547
			'product'         => $this->get_product_data(),
548
		);
549
550
		wp_register_script( 'stripe', 'https://js.stripe.com/v3/', '', '3.0', true );
551
		wp_register_script( 'wc_stripe_payment_request', plugins_url( 'assets/js/stripe-payment-request' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array( 'jquery', 'stripe' ), WC_STRIPE_VERSION, true );
552
553
		wp_localize_script( 'wc_stripe_payment_request', 'wc_stripe_payment_request_params', apply_filters( 'wc_stripe_payment_request_params', $stripe_params ) );
554
555
		wp_enqueue_script( 'wc_stripe_payment_request' );
556
557
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
558
		if ( isset( $gateways['stripe'] ) ) {
559
			$gateways['stripe']->payment_scripts();
560
		}
561
	}
562
563
	/**
564
	 * Display the payment request button.
565
	 *
566
	 * @since   4.0.0
567
	 * @version 4.0.0
568
	 */
569
	public function display_payment_request_button_html() {
570
		global $post;
571
572
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
573
574
		if ( ! isset( $gateways['stripe'] ) ) {
575
			return;
576
		}
577
578
		if ( ! is_cart() && ! is_checkout() && ! is_product() && ! isset( $_GET['pay_for_order'] ) ) {
579
			return;
580
		}
581
582
		if ( is_checkout() && ! apply_filters( 'wc_stripe_show_payment_request_on_checkout', false, $post ) ) {
583
			return;
584
		}
585
586
		if ( is_product() && ! $this->should_show_payment_button_on_product_page() ) {
587
			return;
588
		} else if ( ! $this->should_show_payment_button_on_cart() ) {
589
			return;
590
		}
591
		?>
592
		<div id="wc-stripe-payment-request-wrapper" style="clear:both;padding-top:1.5em;display:none;">
593
			<div id="wc-stripe-payment-request-button">
594
				<?php
595
					if ( $this->is_custom_button() ) {
596
						$label = esc_html( $this->get_button_label() );
597
						$class_name = esc_attr( 'button ' .  $this->get_button_theme() );
598
						$style = esc_attr( 'height:' . $this->get_button_height() . 'px;' );
599
						echo "<button id=\"wc-stripe-custom-button\" class=\"$class_name\" style=\"$style\"> $label </button>";
600
					}
601
				?>
602
				<!-- A Stripe Element will be inserted here. -->
603
			</div>
604
		</div>
605
		<?php
606
	}
607
608
	/**
609
	 * Display payment request button separator.
610
	 *
611
	 * @since   4.0.0
612
	 * @version 4.0.0
613
	 */
614
	public function display_payment_request_button_separator_html() {
615
		global $post;
616
617
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
618
619
		if ( ! isset( $gateways['stripe'] ) ) {
620
			return;
621
		}
622
623
		if ( ! is_cart() && ! is_checkout() && ! is_product() && ! isset( $_GET['pay_for_order'] ) ) {
624
			return;
625
		}
626
627
		if ( is_checkout() && ! apply_filters( 'wc_stripe_show_payment_request_on_checkout', false, $post ) ) {
628
			return;
629
		}
630
631
		if ( is_product() && ! $this->should_show_payment_button_on_product_page() ) {
632
			return;
633
		} else if ( ! $this->should_show_payment_button_on_cart() ) {
634
			return;
635
		}
636
		?>
637
		<p id="wc-stripe-payment-request-button-separator" style="margin-top:1.5em;text-align:center;display:none;">&mdash; <?php esc_html_e( 'OR', 'woocommerce-gateway-stripe' ); ?> &mdash;</p>
638
		<?php
639
	}
640
641
	/**
642
	 * Whether payment button html should be rendered on the Cart
643
	 *
644
	 * @since  4.4.1
645
	 * @return boolean
646
	 */
647
	private function should_show_payment_button_on_cart() {
648
		if ( ! apply_filters( 'wc_stripe_show_payment_request_on_cart', true ) ) {
649
			return false;
650
		}
651
		if ( ! $this->allowed_items_in_cart() ) {
652
			WC_Stripe_Logger::log( 'Items in the cart has unsupported product type ( Payment Request button disabled )' );
653
			return false;
654
		}
655
		return true;
656
	}
657
658
	/**
659
	 * Whether payment button html should be rendered
660
	 *
661
	 * @since  4.3.2
662
	 * @return boolean
663
	 */
664
	private function should_show_payment_button_on_product_page() {
665
		global $post;
666
667
		$product = wc_get_product( $post->ID );
668
669
		if ( apply_filters( 'wc_stripe_hide_payment_request_on_product_page', false, $post ) ) {
670
			return false;
671
		}
672
673
		if ( ! is_object( $product ) || ! in_array( $product->get_type(), $this->supported_product_types() ) ) {
674
			return false;
675
		}
676
677
		// Trial subscriptions with shipping are not supported
678
		if ( class_exists( 'WC_Subscriptions_Order' ) && $product->needs_shipping() && WC_Subscriptions_Product::get_trial_length( $product ) > 0 ) {
679
			return false;
680
		}
681
682
		// Pre Orders charge upon release not supported.
683
		if ( class_exists( 'WC_Pre_Orders_Order' ) && WC_Pre_Orders_Product::product_is_charged_upon_release( $product ) ) {
684
			WC_Stripe_Logger::log( 'Pre Order charge upon release is not supported. ( Payment Request button disabled )' );
685
			return false;
686
		}
687
688
		// File upload addon not supported
689
		if ( class_exists( 'WC_Product_Addons_Helper' ) ) {
690
			$product_addons = WC_Product_Addons_Helper::get_product_addons( $product->get_id() );
691
			foreach ( $product_addons as $addon ) {
692
				if ( 'file_upload' === $addon['type'] ) {
693
					return false;
694
				}
695
			}
696
		}
697
698
		return true;
699
	}
700
701
	/**
702
	 * Log errors coming from Payment Request
703
	 *
704
	 * @since   3.1.4
705
	 * @version 4.0.0
706
	 */
707
	public function ajax_log_errors() {
708
		check_ajax_referer( 'wc-stripe-log-errors', 'security' );
709
710
		$errors = wc_clean( stripslashes( $_POST['errors'] ) );
711
712
		WC_Stripe_Logger::log( $errors );
713
714
		exit;
715
	}
716
717
	/**
718
	 * Clears cart.
719
	 *
720
	 * @since   3.1.4
721
	 * @version 4.0.0
722
	 */
723
	public function ajax_clear_cart() {
724
		check_ajax_referer( 'wc-stripe-clear-cart', 'security' );
725
726
		WC()->cart->empty_cart();
727
		exit;
728
	}
729
730
	/**
731
	 * Get cart details.
732
	 */
733
	public function ajax_get_cart_details() {
734
		check_ajax_referer( 'wc-stripe-payment-request', 'security' );
735
736
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
737
			define( 'WOOCOMMERCE_CART', true );
738
		}
739
740
		WC()->cart->calculate_totals();
741
742
		$currency = get_woocommerce_currency();
743
744
		// Set mandatory payment details.
745
		$data = array(
746
			'shipping_required' => WC()->cart->needs_shipping(),
747
			'order_data'        => array(
748
				'currency'     => strtolower( $currency ),
749
				'country_code' => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
750
			),
751
		);
752
753
		$data['order_data'] += $this->build_display_items();
754
755
		wp_send_json( $data );
756
	}
757
758
	/**
759
	 * Get shipping options.
760
	 *
761
	 * @see WC_Cart::get_shipping_packages().
762
	 * @see WC_Shipping::calculate_shipping().
763
	 * @see WC_Shipping::get_packages().
764
	 */
765
	public function ajax_get_shipping_options() {
766
		check_ajax_referer( 'wc-stripe-payment-request-shipping', 'security' );
767
768
		$shipping_address = filter_input_array(
769
			INPUT_POST,
770
			array(
771
				'country'   => FILTER_SANITIZE_STRING,
772
				'state'     => FILTER_SANITIZE_STRING,
773
				'postcode'  => FILTER_SANITIZE_STRING,
774
				'city'      => FILTER_SANITIZE_STRING,
775
				'address'   => FILTER_SANITIZE_STRING,
776
				'address_2' => FILTER_SANITIZE_STRING,
777
			)
778
		);
779
		$product_view_options      = filter_input_array( INPUT_POST, [ 'is_product_page' => FILTER_SANITIZE_STRING ] );
780
		$should_show_itemized_view = ! isset( $product_view_options['is_product_page'] ) ?: filter_var( $product_view_options['is_product_page'], FILTER_VALIDATE_BOOLEAN );
781
782
		$data = $this->get_shipping_options( $shipping_address, $should_show_itemized_view );
783
		wp_send_json( $data );
784
	}
785
786
	/**
787
	 * Gets shipping options available for specified shipping address
788
	 *
789
	 * @param array   $shipping_address       Shipping address.
790
	 * @param boolean $itemized_display_items Indicates whether to show subtotals or itemized views.
791
	 *
792
	 * @return array Shipping options data.
793
	 * phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag
794
	 */
795
	public function get_shipping_options( $shipping_address, $itemized_display_items = false ) {
796
		try {
797
			// Set the shipping options.
798
			$data = array();
799
800
			// Remember current shipping method before resetting.
801
			$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
802
			$this->calculate_shipping( apply_filters( 'wc_stripe_payment_request_shipping_posted_values', $shipping_address ) );
803
804
			$packages = WC()->shipping->get_packages();
805
806
			if ( ! empty( $packages ) && WC()->customer->has_calculated_shipping() ) {
807
				foreach ( $packages as $package_key => $package ) {
808
					if ( empty( $package['rates'] ) ) {
809
						throw new Exception( __( 'Unable to find shipping method for address.', 'woocommerce-gateway-stripe' ) );
810
					}
811
812
					foreach ( $package['rates'] as $key => $rate ) {
813
						$data['shipping_options'][] = array(
814
							'id'     => $rate->id,
815
							'label'  => $rate->label,
816
							'detail' => '',
817
							'amount' => WC_Stripe_Helper::get_stripe_amount( $rate->cost ),
818
						);
819
					}
820
				}
821
			} else {
822
				throw new Exception( __( 'Unable to find shipping method for address.', 'woocommerce-gateway-stripe' ) );
823
			}
824
825
			// The first shipping option is automatically applied on the client.
826
			// Keep chosen shipping method by sorting shipping options if the method still available for new address.
827
			// Fallback to the first available shipping method.
828
			if ( isset( $data['shipping_options'][0] ) ) {
829
				if ( isset( $chosen_shipping_methods[0] ) ) {
830
					$chosen_method_id         = $chosen_shipping_methods[0];
831
					$compare_shipping_options = function ( $a, $b ) use ( $chosen_method_id ) {
832
						if ( $a['id'] === $chosen_method_id ) {
833
							return -1;
834
						}
835
836
						if ( $b['id'] === $chosen_method_id ) {
837
							return 1;
838
						}
839
840
						return 0;
841
					};
842
					usort( $data['shipping_options'], $compare_shipping_options );
843
				}
844
845
				$first_shipping_method_id = $data['shipping_options'][0]['id'];
846
				$this->update_shipping_method( [ $first_shipping_method_id ] );
847
			}
848
849
			WC()->cart->calculate_totals();
850
851
			$data          += $this->build_display_items( $itemized_display_items );
852
			$data['result'] = 'success';
853
		} catch ( Exception $e ) {
854
			$data          += $this->build_display_items( $itemized_display_items );
855
			$data['result'] = 'invalid_shipping_address';
856
		}
857
858
		return $data;
859
	}
860
861
	/**
862
	 * Update shipping method.
863
	 */
864
	public function ajax_update_shipping_method() {
865
		check_ajax_referer( 'wc-stripe-update-shipping-method', 'security' );
866
867
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
868
			define( 'WOOCOMMERCE_CART', true );
869
		}
870
871
		$shipping_methods = filter_input( INPUT_POST, 'shipping_method', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
872
		$this->update_shipping_method( $shipping_methods );
873
874
		WC()->cart->calculate_totals();
875
876
		$product_view_options      = filter_input_array( INPUT_POST, [ 'is_product_page' => FILTER_SANITIZE_STRING ] );
877
		$should_show_itemized_view = ! isset( $product_view_options['is_product_page'] ) ?: filter_var( $product_view_options['is_product_page'], FILTER_VALIDATE_BOOLEAN );
878
879
		$data           = array();
880
		$data          += $this->build_display_items( $should_show_itemized_view );
881
		$data['result'] = 'success';
882
883
		wp_send_json( $data );
884
	}
885
886
	/**
887
	 * Updates shipping method in WC session
888
	 *
889
	 * @param array $shipping_methods Array of selected shipping methods ids.
890
	 */
891
	public function update_shipping_method( $shipping_methods ) {
892
		$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
893
894
		if ( is_array( $shipping_methods ) ) {
895
			foreach ( $shipping_methods as $i => $value ) {
896
				$chosen_shipping_methods[ $i ] = wc_clean( $value );
897
			}
898
		}
899
900
		WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
901
	}
902
903
	/**
904
	 * Gets the selected product data.
905
	 *
906
	 * @since   4.0.0
907
	 * @version 4.0.0
908
	 * @return  array $data
909
	 */
910
	public function ajax_get_selected_product_data() {
911
		check_ajax_referer( 'wc-stripe-get-selected-product-data', 'security' );
912
913
		try {
914
			$product_id   = absint( $_POST['product_id'] );
915
			$qty          = ! isset( $_POST['qty'] ) ? 1 : apply_filters( 'woocommerce_add_to_cart_quantity', absint( $_POST['qty'] ), $product_id );
916
			$addon_value  = isset( $_POST['addon_value'] ) ? max( floatval( $_POST['addon_value'] ), 0 ) : 0;
917
			$product      = wc_get_product( $product_id );
918
			$variation_id = null;
919
920
			if ( ! is_a( $product, 'WC_Product' ) ) {
921
				throw new Exception( sprintf( __( 'Product with the ID (%d) cannot be found.', 'woocommerce-gateway-stripe' ), $product_id ) );
922
			}
923
924
			if ( 'variable' === $product->get_type() && isset( $_POST['attributes'] ) ) {
925
				$attributes = wc_clean( wp_unslash( $_POST['attributes'] ) );
926
927
				$data_store   = WC_Data_Store::load( 'product' );
928
				$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
929
930
				if ( ! empty( $variation_id ) ) {
931
					$product = wc_get_product( $variation_id );
932
				}
933
			}
934
935
			// Force quantity to 1 if sold individually and check for existing item in cart.
936
			if ( $product->is_sold_individually() ) {
937
				$qty = apply_filters( 'wc_stripe_payment_request_add_to_cart_sold_individually_quantity', 1, $qty, $product_id, $variation_id );
938
			}
939
940
			if ( ! $product->has_enough_stock( $qty ) ) {
941
				/* translators: 1: product name 2: quantity in stock */
942
				throw new Exception( sprintf( __( 'You cannot add that amount of "%1$s"; to the cart because there is not enough stock (%2$s remaining).', 'woocommerce-gateway-stripe' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity(), $product ) ) );
943
			}
944
945
			$total = $qty * $product->get_price() + $addon_value;
946
947
			$quantity_label = 1 < $qty ? ' (x' . $qty . ')' : '';
948
949
			$data  = array();
950
			$items = array();
951
952
			$items[] = array(
953
				'label'  => $product->get_name() . $quantity_label,
954
				'amount' => WC_Stripe_Helper::get_stripe_amount( $total ),
955
			);
956
957 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...
958
				$items[] = array(
959
					'label'   => __( 'Tax', 'woocommerce-gateway-stripe' ),
960
					'amount'  => 0,
961
					'pending' => true,
962
				);
963
			}
964
965 View Code Duplication
			if ( wc_shipping_enabled() && $product->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...
966
				$items[] = array(
967
					'label'   => __( 'Shipping', 'woocommerce-gateway-stripe' ),
968
					'amount'  => 0,
969
					'pending' => true,
970
				);
971
972
				$data['shippingOptions'] = array(
973
					'id'     => 'pending',
974
					'label'  => __( 'Pending', 'woocommerce-gateway-stripe' ),
975
					'detail' => '',
976
					'amount' => 0,
977
				);
978
			}
979
980
			$data['displayItems'] = $items;
981
			$data['total']        = array(
982
				'label'   => $this->total_label,
983
				'amount'  => WC_Stripe_Helper::get_stripe_amount( $total ),
984
				'pending' => true,
985
			);
986
987
			$data['requestShipping'] = ( wc_shipping_enabled() && $product->needs_shipping() );
988
			$data['currency']        = strtolower( get_woocommerce_currency() );
989
			$data['country_code']    = substr( get_option( 'woocommerce_default_country' ), 0, 2 );
990
991
			wp_send_json( $data );
992
		} catch ( Exception $e ) {
993
			wp_send_json( array( 'error' => wp_strip_all_tags( $e->getMessage() ) ) );
994
		}
995
	}
996
997
	/**
998
	 * Adds the current product to the cart. Used on product detail page.
999
	 *
1000
	 * @since   4.0.0
1001
	 * @version 4.0.0
1002
	 * @return  array $data
1003
	 */
1004
	public function ajax_add_to_cart() {
1005
		check_ajax_referer( 'wc-stripe-add-to-cart', 'security' );
1006
1007
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
1008
			define( 'WOOCOMMERCE_CART', true );
1009
		}
1010
1011
		WC()->shipping->reset_shipping();
1012
1013
		$product_id   = absint( $_POST['product_id'] );
1014
		$qty          = ! isset( $_POST['qty'] ) ? 1 : absint( $_POST['qty'] );
1015
		$product      = wc_get_product( $product_id );
1016
		$product_type = $product->get_type();
1017
1018
		// First empty the cart to prevent wrong calculation.
1019
		WC()->cart->empty_cart();
1020
1021
		if ( ( 'variable' === $product_type || 'variable-subscription' === $product_type ) && isset( $_POST['attributes'] ) ) {
1022
			$attributes = wc_clean( wp_unslash( $_POST['attributes'] ) );
1023
1024
			$data_store   = WC_Data_Store::load( 'product' );
1025
			$variation_id = $data_store->find_matching_product_variation( $product, $attributes );
1026
1027
			WC()->cart->add_to_cart( $product->get_id(), $qty, $variation_id, $attributes );
1028
		}
1029
1030
		if ( 'simple' === $product_type || 'subscription' === $product_type ) {
1031
			WC()->cart->add_to_cart( $product->get_id(), $qty );
1032
		}
1033
1034
		WC()->cart->calculate_totals();
1035
1036
		$data           = array();
1037
		$data          += $this->build_display_items();
1038
		$data['result'] = 'success';
1039
1040
		wp_send_json( $data );
1041
	}
1042
1043
	/**
1044
	 * Normalizes the state/county field because in some
1045
	 * cases, the state/county field is formatted differently from
1046
	 * what WC is expecting and throws an error. An example
1047
	 * for Ireland the county dropdown in Chrome shows "Co. Clare" format
1048
	 *
1049
	 * @since   4.0.0
1050
	 * @version 4.0.0
1051
	 */
1052
	public function normalize_state() {
1053
		$billing_country  = ! empty( $_POST['billing_country'] ) ? wc_clean( $_POST['billing_country'] ) : '';
1054
		$shipping_country = ! empty( $_POST['shipping_country'] ) ? wc_clean( $_POST['shipping_country'] ) : '';
1055
		$billing_state    = ! empty( $_POST['billing_state'] ) ? wc_clean( $_POST['billing_state'] ) : '';
1056
		$shipping_state   = ! empty( $_POST['shipping_state'] ) ? wc_clean( $_POST['shipping_state'] ) : '';
1057
1058 View Code Duplication
		if ( $billing_state && $billing_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...
1059
			$valid_states = WC()->countries->get_states( $billing_country );
1060
1061
			// Valid states found for country.
1062
			if ( ! empty( $valid_states ) && is_array( $valid_states ) && sizeof( $valid_states ) > 0 ) {
1063
				foreach ( $valid_states as $state_abbr => $state ) {
1064
					if ( preg_match( '/' . preg_quote( $state ) . '/i', $billing_state ) ) {
1065
						$_POST['billing_state'] = $state_abbr;
1066
					}
1067
				}
1068
			}
1069
		}
1070
1071 View Code Duplication
		if ( $shipping_state && $shipping_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...
1072
			$valid_states = WC()->countries->get_states( $shipping_country );
1073
1074
			// Valid states found for country.
1075
			if ( ! empty( $valid_states ) && is_array( $valid_states ) && sizeof( $valid_states ) > 0 ) {
1076
				foreach ( $valid_states as $state_abbr => $state ) {
1077
					if ( preg_match( '/' . preg_quote( $state ) . '/i', $shipping_state ) ) {
1078
						$_POST['shipping_state'] = $state_abbr;
1079
					}
1080
				}
1081
			}
1082
		}
1083
	}
1084
1085
	/**
1086
	 * Create order. Security is handled by WC.
1087
	 *
1088
	 * @since   3.1.0
1089
	 * @version 4.0.0
1090
	 */
1091
	public function ajax_create_order() {
1092
		if ( WC()->cart->is_empty() ) {
1093
			wp_send_json_error( __( 'Empty cart', 'woocommerce-gateway-stripe' ) );
1094
		}
1095
1096
		if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
1097
			define( 'WOOCOMMERCE_CHECKOUT', true );
1098
		}
1099
1100
		$this->normalize_state();
1101
1102
		WC()->checkout()->process_checkout();
1103
1104
		die( 0 );
1105
	}
1106
1107
	/**
1108
	 * Calculate and set shipping method.
1109
	 *
1110
	 * @param array $address Shipping address.
1111
	 *
1112
	 * @since   3.1.0
1113
	 * @version 4.0.0
1114
	 */
1115
	protected function calculate_shipping( $address = array() ) {
1116
		$country   = $address['country'];
1117
		$state     = $address['state'];
1118
		$postcode  = $address['postcode'];
1119
		$city      = $address['city'];
1120
		$address_1 = $address['address'];
1121
		$address_2 = $address['address_2'];
1122
		$wc_states = WC()->countries->get_states( $country );
1123
1124
		/**
1125
		 * In some versions of Chrome, state can be a full name. So we need
1126
		 * to convert that to abbreviation as WC is expecting that.
1127
		 */
1128
		if ( 2 < strlen( $state ) && ! empty( $wc_states ) && ! isset( $wc_states[ $state ] ) ) {
1129
			$state = array_search( ucwords( strtolower( $state ) ), $wc_states, true );
1130
		}
1131
1132
		WC()->shipping->reset_shipping();
1133
1134
		if ( $postcode && WC_Validation::is_postcode( $postcode, $country ) ) {
1135
			$postcode = wc_format_postcode( $postcode, $country );
1136
		}
1137
1138
		if ( $country ) {
1139
			WC()->customer->set_location( $country, $state, $postcode, $city );
1140
			WC()->customer->set_shipping_location( $country, $state, $postcode, $city );
1141
		} else {
1142
			WC()->customer->set_billing_address_to_base();
1143
			WC()->customer->set_shipping_address_to_base();
1144
		}
1145
1146
		WC()->customer->set_calculated_shipping( true );
1147
		WC()->customer->save();
1148
1149
		$packages = array();
1150
1151
		$packages[0]['contents']                 = WC()->cart->get_cart();
1152
		$packages[0]['contents_cost']            = 0;
1153
		$packages[0]['applied_coupons']          = WC()->cart->applied_coupons;
1154
		$packages[0]['user']['ID']               = get_current_user_id();
1155
		$packages[0]['destination']['country']   = $country;
1156
		$packages[0]['destination']['state']     = $state;
1157
		$packages[0]['destination']['postcode']  = $postcode;
1158
		$packages[0]['destination']['city']      = $city;
1159
		$packages[0]['destination']['address']   = $address_1;
1160
		$packages[0]['destination']['address_2'] = $address_2;
1161
1162
		foreach ( WC()->cart->get_cart() as $item ) {
1163
			if ( $item['data']->needs_shipping() ) {
1164
				if ( isset( $item['line_total'] ) ) {
1165
					$packages[0]['contents_cost'] += $item['line_total'];
1166
				}
1167
			}
1168
		}
1169
1170
		$packages = apply_filters( 'woocommerce_cart_shipping_packages', $packages );
1171
1172
		WC()->shipping->calculate_shipping( $packages );
1173
	}
1174
1175
	/**
1176
	 * Builds the shippings methods to pass to Payment Request
1177
	 *
1178
	 * @since   3.1.0
1179
	 * @version 4.0.0
1180
	 */
1181
	protected function build_shipping_methods( $shipping_methods ) {
1182
		if ( empty( $shipping_methods ) ) {
1183
			return array();
1184
		}
1185
1186
		$shipping = array();
1187
1188
		foreach ( $shipping_methods as $method ) {
1189
			$shipping[] = array(
1190
				'id'     => $method['id'],
1191
				'label'  => $method['label'],
1192
				'detail' => '',
1193
				'amount' => WC_Stripe_Helper::get_stripe_amount( $method['amount']['value'] ),
1194
			);
1195
		}
1196
1197
		return $shipping;
1198
	}
1199
1200
	/**
1201
	 * Builds the line items to pass to Payment Request
1202
	 *
1203
	 * @since   3.1.0
1204
	 * @version 4.0.0
1205
	 */
1206
	protected function build_display_items( $itemized_display_items = false ) {
1207
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
1208
			define( 'WOOCOMMERCE_CART', true );
1209
		}
1210
1211
		$items     = array();
1212
		$subtotal  = 0;
1213
		$discounts = 0;
1214
1215
		// Default show only subtotal instead of itemization.
1216
		if ( ! apply_filters( 'wc_stripe_payment_request_hide_itemization', true ) || $itemized_display_items ) {
1217
			foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
1218
				$amount         = $cart_item['line_subtotal'];
1219
				$subtotal      += $cart_item['line_subtotal'];
1220
				$quantity_label = 1 < $cart_item['quantity'] ? ' (x' . $cart_item['quantity'] . ')' : '';
1221
1222
				$product_name = $cart_item['data']->get_name();
1223
1224
				$item = array(
1225
					'label'  => $product_name . $quantity_label,
1226
					'amount' => WC_Stripe_Helper::get_stripe_amount( $amount ),
1227
				);
1228
1229
				$items[] = $item;
1230
			}
1231
		}
1232
1233
		if ( version_compare( WC_VERSION, '3.2', '<' ) ) {
1234
			$discounts = wc_format_decimal( WC()->cart->get_cart_discount_total(), WC()->cart->dp );
1235
		} else {
1236
			$applied_coupons = array_values( WC()->cart->get_coupon_discount_totals() );
1237
1238
			foreach ( $applied_coupons as $amount ) {
1239
				$discounts += (float) $amount;
1240
			}
1241
		}
1242
1243
		$discounts   = wc_format_decimal( $discounts, WC()->cart->dp );
1244
		$tax         = wc_format_decimal( WC()->cart->tax_total + WC()->cart->shipping_tax_total, WC()->cart->dp );
1245
		$shipping    = wc_format_decimal( WC()->cart->shipping_total, WC()->cart->dp );
1246
		$items_total = wc_format_decimal( WC()->cart->cart_contents_total, WC()->cart->dp ) + $discounts;
1247
		$order_total = version_compare( WC_VERSION, '3.2', '<' ) ? wc_format_decimal( $items_total + $tax + $shipping - $discounts, WC()->cart->dp ) : WC()->cart->get_total( false );
1248
1249
		if ( wc_tax_enabled() ) {
1250
			$items[] = array(
1251
				'label'  => esc_html( __( 'Tax', 'woocommerce-gateway-stripe' ) ),
1252
				'amount' => WC_Stripe_Helper::get_stripe_amount( $tax ),
1253
			);
1254
		}
1255
1256 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...
1257
			$items[] = array(
1258
				'label'  => esc_html( __( 'Shipping', 'woocommerce-gateway-stripe' ) ),
1259
				'amount' => WC_Stripe_Helper::get_stripe_amount( $shipping ),
1260
			);
1261
		}
1262
1263 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...
1264
			$items[] = array(
1265
				'label'  => esc_html( __( 'Discount', 'woocommerce-gateway-stripe' ) ),
1266
				'amount' => WC_Stripe_Helper::get_stripe_amount( $discounts ),
1267
			);
1268
		}
1269
1270
		if ( version_compare( WC_VERSION, '3.2', '<' ) ) {
1271
			$cart_fees = WC()->cart->fees;
1272
		} else {
1273
			$cart_fees = WC()->cart->get_fees();
1274
		}
1275
1276
		// Include fees and taxes as display items.
1277
		foreach ( $cart_fees as $key => $fee ) {
1278
			$items[] = array(
1279
				'label'  => $fee->name,
1280
				'amount' => WC_Stripe_Helper::get_stripe_amount( $fee->amount ),
1281
			);
1282
		}
1283
1284
		return array(
1285
			'displayItems' => $items,
1286
			'total'        => array(
1287
				'label'   => $this->total_label,
1288
				'amount'  => max( 0, apply_filters( 'woocommerce_stripe_calculated_total', WC_Stripe_Helper::get_stripe_amount( $order_total ), $order_total, WC()->cart ) ),
1289
				'pending' => false,
1290
			),
1291
		);
1292
	}
1293
}
1294
1295
new WC_Stripe_Payment_Request();
1296