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

includes/class-wc-stripe-apple-pay.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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;
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
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
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 ) {
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 ) {
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 ) {
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 ) {
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() ) {
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 );
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()
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 );
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() ) {
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() ) {
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;
793
			$shipping_address['last_name']  = $lastname;
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