Completed
Push — master ( e3a5fe...3adadd )
by Roy
05:10
created

WC_Gateway_Stripe::display_order_payout()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 29
Code Lines 16

Duplication

Lines 29
Ratio 100 %

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 3
nop 1
dl 29
loc 29
rs 8.5806
c 0
b 0
f 0
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * WC_Gateway_Stripe class.
8
 *
9
 * @extends WC_Payment_Gateway
10
 */
11
class WC_Gateway_Stripe extends WC_Stripe_Payment_Gateway {
12
	/**
13
	 * The delay between retries.
14
	 *
15
	 * @var int
16
	 */
17
	public $retry_interval;
18
19
	/**
20
	 * Should we capture Credit cards
21
	 *
22
	 * @var bool
23
	 */
24
	public $capture;
25
26
	/**
27
	 * Alternate credit card statement name
28
	 *
29
	 * @var bool
30
	 */
31
	public $statement_descriptor;
32
33
	/**
34
	 * Checkout enabled
35
	 *
36
	 * @var bool
37
	 */
38
	public $stripe_checkout;
39
40
	/**
41
	 * Stripe Checkout description.
42
	 *
43
	 * @var string
44
	 */
45
	public $stripe_checkout_description;
46
47
	/**
48
	 * Require 3D Secure enabled
49
	 *
50
	 * @var bool
51
	 */
52
	public $three_d_secure;
53
54
	/**
55
	 * Credit card image
56
	 *
57
	 * @var string
58
	 */
59
	public $stripe_checkout_image;
60
61
	/**
62
	 * Should we store the users credit cards?
63
	 *
64
	 * @var bool
65
	 */
66
	public $saved_cards;
67
68
	/**
69
	 * API access secret key
70
	 *
71
	 * @var string
72
	 */
73
	public $secret_key;
74
75
	/**
76
	 * Api access publishable key
77
	 *
78
	 * @var string
79
	 */
80
	public $publishable_key;
81
82
	/**
83
	 * Do we accept bitcoin?
84
	 *
85
	 * @var bool
86
	 */
87
	public $bitcoin;
88
89
	/**
90
	 * Do we accept Payment Request?
91
	 *
92
	 * @var bool
93
	 */
94
	public $payment_request;
95
96
	/**
97
	 * Is test mode active?
98
	 *
99
	 * @var bool
100
	 */
101
	public $testmode;
102
103
	/**
104
	 * Inline CC form styling
105
	 *
106
	 * @var string
107
	 */
108
	public $inline_cc_form;
109
110
	/**
111
	 * Pre Orders Object
112
	 *
113
	 * @var object
114
	 */
115
	public $pre_orders;
116
117
	/**
118
	 * Constructor
119
	 */
120
	public function __construct() {
121
		$this->retry_interval       = 1;
122
		$this->id                   = 'stripe';
123
		$this->method_title         = __( 'Stripe', 'woocommerce-gateway-stripe' );
124
		/* translators: 1) link to Stripe register page 2) link to Stripe api keys page */
125
		$this->method_description   = sprintf( __( 'Stripe works by adding payment fields on the checkout and then sending the details to Stripe for verification. <a href="%1$s" target="_blank">Sign up</a> for a Stripe account, and <a href="%2$s" target="_blank">get your Stripe account keys</a>.', 'woocommerce-gateway-stripe' ), 'https://dashboard.stripe.com/register', 'https://dashboard.stripe.com/account/apikeys' );
126
		$this->has_fields           = true;
127
		$this->supports             = array(
128
			'products',
129
			'refunds',
130
			'tokenization',
131
			'add_payment_method',
132
			'subscriptions',
133
			'subscription_cancellation',
134
			'subscription_suspension',
135
			'subscription_reactivation',
136
			'subscription_amount_changes',
137
			'subscription_date_changes',
138
			'subscription_payment_method_change',
139
			'subscription_payment_method_change_customer',
140
			'subscription_payment_method_change_admin',
141
			'multiple_subscriptions',
142
			'pre-orders',
143
		);
144
145
		// Load the form fields.
146
		$this->init_form_fields();
147
148
		// Load the settings.
149
		$this->init_settings();
150
151
		// Get setting values.
152
		$this->title                       = $this->get_option( 'title' );
153
		$this->description                 = $this->get_option( 'description' );
154
		$this->enabled                     = $this->get_option( 'enabled' );
155
		$this->testmode                    = 'yes' === $this->get_option( 'testmode' );
156
		$this->inline_cc_form              = 'yes' === $this->get_option( 'inline_cc_form' );
0 ignored issues
show
Documentation Bug introduced by
The property $inline_cc_form was declared of type string, but 'yes' === $this->get_option('inline_cc_form') is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
157
		$this->capture                     = 'yes' === $this->get_option( 'capture', 'yes' );
158
		$this->statement_descriptor        = WC_Stripe_Helper::clean_statement_descriptor( $this->get_option( 'statement_descriptor' ) );
0 ignored issues
show
Documentation Bug introduced by
The property $statement_descriptor was declared of type boolean, but \WC_Stripe_Helper::clean...statement_descriptor')) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
159
		$this->three_d_secure              = 'yes' === $this->get_option( 'three_d_secure' );
160
		$this->stripe_checkout             = 'yes' === $this->get_option( 'stripe_checkout' );
161
		$this->stripe_checkout_image       = $this->get_option( 'stripe_checkout_image', '' );
162
		$this->stripe_checkout_description = $this->get_option( 'stripe_checkout_description' );
163
		$this->saved_cards                 = 'yes' === $this->get_option( 'saved_cards' );
164
		$this->secret_key                  = $this->testmode ? $this->get_option( 'test_secret_key' ) : $this->get_option( 'secret_key' );
165
		$this->publishable_key             = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' );
166
		$this->bitcoin                     = 'USD' === strtoupper( get_woocommerce_currency() ) && 'yes' === $this->get_option( 'stripe_bitcoin' );
167
		$this->payment_request             = 'yes' === $this->get_option( 'payment_request', 'yes' );
168
169
		if ( $this->stripe_checkout ) {
170
			$this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-stripe' );
171
		}
172
173
		WC_Stripe_API::set_secret_key( $this->secret_key );
174
175
		// Hooks.
176
		add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
177
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
178
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
179
		add_action( 'woocommerce_admin_order_totals_after_total', array( $this, 'display_order_fee' ), 10, 1 );
180
		add_action( 'woocommerce_admin_order_totals_after_total', array( $this, 'display_order_payout' ), 20, 1 );
181
		add_action( 'woocommerce_customer_save_address', array( $this, 'show_update_card_notice' ), 10, 2 );
182
		add_action( 'woocommerce_receipt_stripe', array( $this, 'stripe_checkout_receipt_page' ) );
183
		add_action( 'woocommerce_api_' . strtolower( get_class( $this ) ), array( $this, 'stripe_checkout_return_handler' ) );
184
185 View Code Duplication
		if ( WC_Stripe_Helper::is_pre_orders_exists() ) {
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...
186
			$this->pre_orders = new WC_Stripe_Pre_Orders_Compat();
187
188
			add_action( 'wc_pre_orders_process_pre_order_completion_payment_' . $this->id, array( $this->pre_orders, 'process_pre_order_release_payment' ) );
189
		}
190
	}
191
192
	/**
193
	 * Checks if keys are set.
194
	 *
195
	 * @since 4.0.6
196
	 * @return bool
197
	 */
198
	public function are_keys_set() {
199
		if ( empty( $this->secret_key ) || empty( $this->publishable_key ) ) {
200
			return false;
201
		}
202
203
		return true;
204
	}
205
206
	/**
207
	 * Checks if gateway should be available to use.
208
	 *
209
	 * @since 4.0.2
210
	 */
211
	public function is_available() {
212
		if ( is_add_payment_method_page() && ! $this->saved_cards ) {
213
			return false;
214
		}
215
216
		return parent::is_available();
217
	}
218
219
	/**
220
	 * Adds a notice for customer when they update their billing address.
221
	 *
222
	 * @since 4.1.0
223
	 * @param int $user_id
224
	 * @param array $load_address
225
	 */
226
	public function show_update_card_notice( $user_id, $load_address ) {
227
		if ( ! $this->saved_cards || ! WC_Stripe_Payment_Tokens::customer_has_saved_methods( $user_id ) || 'billing' !== $load_address ) {
228
			return;
229
		}
230
231
		/* translators: 1) Opening anchor tag 2) closing anchor tag */
232
		wc_add_notice( sprintf( __( 'If your billing address has been changed for saved payment methods, be sure to remove any %1$ssaved payment methods%2$s on file and re-add them.', 'woocommerce-gateway-stripe' ), '<a href="' . esc_url( wc_get_endpoint_url( 'payment-methods' ) ) . '" class="wc-stripe-update-card-notice" style="text-decoration:underline;">', '</a>' ), 'notice' );
233
	}
234
235
	/**
236
	 * Get_icon function.
237
	 *
238
	 * @since 1.0.0
239
	 * @version 4.0.0
240
	 * @return string
241
	 */
242
	public function get_icon() {
243
		$icons = $this->payment_icons();
244
245
		$icons_str = '';
246
247
		$icons_str .= $icons['visa'];
248
		$icons_str .= $icons['amex'];
249
		$icons_str .= $icons['mastercard'];
250
251
		if ( 'USD' === get_woocommerce_currency() ) {
252
			$icons_str .= $icons['discover'];
253
			$icons_str .= $icons['jcb'];
254
			$icons_str .= $icons['diners'];
255
		}
256
257
		if ( $this->bitcoin && $this->stripe_checkout ) {
258
			$icons_str .= $icons['bitcoin'];
259
		}
260
261
		return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
262
	}
263
264
	/**
265
	 * Initialise Gateway Settings Form Fields
266
	 */
267
	public function init_form_fields() {
268
		$this->form_fields = require( dirname( __FILE__ ) . '/admin/stripe-settings.php' );
269
	}
270
271
	/**
272
	 * Payment form on checkout page
273
	 */
274
	public function payment_fields() {
275
		$user                 = wp_get_current_user();
276
		$display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && $this->saved_cards;
277
		$total                = WC()->cart->total;
278
		$user_email           = '';
279
280
		// If paying from order, we need to get total from order not cart.
281 View Code Duplication
		if ( isset( $_GET['pay_for_order'] ) && ! empty( $_GET['key'] ) ) {
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...
282
			$order      = wc_get_order( wc_get_order_id_by_order_key( wc_clean( $_GET['key'] ) ) );
283
			$total      = $order->get_total();
284
			$user_email = WC_Stripe_Helper::is_pre_30() ? $order->billing_email : $order->get_billing_email();
285
		} else {
286
			if ( $user->ID ) {
287
				$user_email = get_user_meta( $user->ID, 'billing_email', true );
288
				$user_email = $user_email ? $user_email : $user->user_email;
289
			}
290
		}
291
292
		if ( is_add_payment_method_page() ) {
293
			$pay_button_text = __( 'Add Card', 'woocommerce-gateway-stripe' );
294
			$total        = '';
295
		} elseif ( function_exists( 'wcs_order_contains_subscription' ) && isset( $_GET['change_payment_method'] ) ) {
296
			$pay_button_text = __( 'Change Payment Method', 'woocommerce-gateway-stripe' );
297
			$total        = '';
298
		} else {
299
			$pay_button_text = '';
300
		}
301
302
		ob_start();
303
304
		echo '<div
305
			id="stripe-payment-data"
306
			data-panel-label="' . esc_attr( $pay_button_text ) . '"
307
			data-description="' . esc_attr( strip_tags( $this->stripe_checkout_description ) ) . '"
308
			data-email="' . esc_attr( $user_email ) . '"
309
			data-verify-zip="' . esc_attr( apply_filters( 'wc_stripe_checkout_verify_zip', false ) ? 'true' : 'false' ) . '"
310
			data-billing-address="' . esc_attr( apply_filters( 'wc_stripe_checkout_require_billing_address', false ) ? 'true' : 'false' ) . '"
311
			data-shipping-address="' . esc_attr( apply_filters( 'wc_stripe_checkout_require_shipping_address', false ) ? 'true' : 'false' ) . '" 
312
			data-amount="' . esc_attr( WC_Stripe_Helper::get_stripe_amount( $total ) ) . '"
313
			data-name="' . esc_attr( $this->statement_descriptor ) . '"
314
			data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
315
			data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
316
			data-bitcoin="' . esc_attr( ( $this->bitcoin && $this->capture ) ? 'true' : 'false' ) . '"
317
			data-locale="' . esc_attr( apply_filters( 'wc_stripe_checkout_locale', $this->get_locale() ) ) . '"
318
			data-three-d-secure="' . esc_attr( $this->three_d_secure ? 'true' : 'false' ) . '"
319
			data-allow-remember-me="' . esc_attr( apply_filters( 'wc_stripe_allow_remember_me', true ) ? 'true' : 'false' ) . '">';
320
321 View Code Duplication
		if ( $this->description ) {
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...
322
			if ( $this->testmode ) {
323
				/* translators: link to Stripe testing page */
324
				$this->description .= ' ' . sprintf( __( 'TEST MODE ENABLED. In test mode, you can use the card number 4242424242424242 with any CVC and a valid expiration date or check the <a href="%s" target="_blank">Testing Stripe documentation</a> for more card numbers.', 'woocommerce-gateway-stripe' ), 'https://stripe.com/docs/testing' );
325
				$this->description  = trim( $this->description );
326
			}
327
328
			echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $this->description ) ) );
329
		}
330
331
		if ( $display_tokenization ) {
332
			$this->tokenization_script();
333
			$this->saved_payment_methods();
334
		}
335
336
		if ( ! $this->stripe_checkout ) {
337
			$this->elements_form();
338
		}
339
340
		if ( apply_filters( 'wc_stripe_display_save_payment_method_checkbox', $display_tokenization ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) {
341
342
			if ( ! $this->stripe_checkout ) {
343
				$this->save_payment_method_checkbox();
344
			} elseif ( $this->stripe_checkout && isset( $_GET['pay_for_order'] ) && ! empty( $_GET['key'] ) ) {
345
				$this->save_payment_method_checkbox();
346
			}
347
		}
348
349
		echo '</div>';
350
351
		ob_end_flush();
352
	}
353
354
	/**
355
	 * Renders the Stripe elements form.
356
	 *
357
	 * @since 4.0.0
358
	 * @version 4.0.0
359
	 */
360
	public function elements_form() {
361
		?>
362
		<fieldset id="wc-<?php echo esc_attr( $this->id ); ?>-cc-form" class="wc-credit-card-form wc-payment-form" style="background:transparent;">
363
			<?php do_action( 'woocommerce_credit_card_form_start', $this->id ); ?>
364
365
			<?php if ( $this->inline_cc_form ) { ?>
366
				<label for="card-element">
367
					<?php esc_html_e( 'Credit or debit card', 'woocommerce-gateway-stripe' ); ?>
368
				</label>
369
370
				<div id="stripe-card-element" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
371
				<!-- a Stripe Element will be inserted here. -->
372
				</div>
373
			<?php } else { ?>
374
				<div class="form-row form-row-wide">
375
					<label><?php _e( 'Card Number', 'woocommerce-gateway-stripe' ); ?> <span class="required">*</span></label>
376
377
					<div id="stripe-card-element" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
378
					<!-- a Stripe Element will be inserted here. -->
379
					</div>
380
				</div>
381
382
				<div class="form-row form-row-first">
383
					<label><?php _e( 'Expiry Date', 'woocommerce-gateway-stripe' ); ?> <span class="required">*</span></label>
384
385
					<div id="stripe-exp-element" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
386
					<!-- a Stripe Element will be inserted here. -->
387
					</div>
388
				</div>
389
390
				<div class="form-row form-row-last">
391
					<label><?php _e( 'Card Code (CVC)', 'woocommerce-gateway-stripe' ); ?> <span class="required">*</span></label>
392
				<div id="stripe-cvc-element" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
393
				<!-- a Stripe Element will be inserted here. -->
394
				</div>
395
				</div>
396
				<div class="clear"></div>
397
			<?php } ?>
398
399
			<!-- Used to display form errors -->
400
			<div class="stripe-source-errors" role="alert"></div>
401
			<?php do_action( 'woocommerce_credit_card_form_end', $this->id ); ?>
402
			<div class="clear"></div>
403
		</fieldset>
404
		<?php
405
	}
406
407
	/**
408
	 * Load admin scripts.
409
	 *
410
	 * @since 3.1.0
411
	 * @version 3.1.0
412
	 */
413
	public function admin_scripts() {
414
		if ( 'woocommerce_page_wc-settings' !== get_current_screen()->id ) {
415
			return;
416
		}
417
418
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
419
420
		wp_enqueue_script( 'woocommerce_stripe_admin', plugins_url( 'assets/js/stripe-admin' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array(), WC_STRIPE_VERSION, true );
421
	}
422
423
	/**
424
	 * Payment_scripts function.
425
	 *
426
	 * Outputs scripts used for stripe payment
427
	 *
428
	 * @since 3.1.0
429
	 * @version 4.0.0
430
	 */
431
	public function payment_scripts() {
432
		if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) {
433
			return;
434
		}
435
436
		// If Stripe is not enabled bail.
437
		if ( 'no' === $this->enabled ) {
438
			return;
439
		}
440
441
		// If keys are not set bail.
442
		if ( ! $this->are_keys_set() ) {
443
			WC_Stripe_Logger::log( 'Keys are not set correctly.' );
444
			return;
445
		}
446
447
		// If no SSL bail.
448
		if ( ! $this->testmode && ! is_ssl() ) {
449
			WC_Stripe_Logger::log( 'Stripe live mode requires SSL.' );
450
		}
451
452
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
453
454
		wp_register_style( 'stripe_styles', plugins_url( 'assets/css/stripe-styles.css', WC_STRIPE_MAIN_FILE ), array(), WC_STRIPE_VERSION );
455
		wp_enqueue_style( 'stripe_styles' );
456
		wp_register_script( 'stripe_checkout', 'https://checkout.stripe.com/checkout.js', '', WC_STRIPE_VERSION, true );
457
		wp_register_script( 'stripe', 'https://js.stripe.com/v3/', '', '3.0', true );
458
		wp_register_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array( 'jquery-payment', 'stripe' ), WC_STRIPE_VERSION, true );
459
460
		$stripe_params = array(
461
			'key'                  => $this->publishable_key,
462
			'i18n_terms'           => __( 'Please accept the terms and conditions first', 'woocommerce-gateway-stripe' ),
463
			'i18n_required_fields' => __( 'Please fill in required checkout fields first', 'woocommerce-gateway-stripe' ),
464
		);
465
466
		// If we're on the pay page we need to pass stripe.js the address of the order.
467
		if ( isset( $_GET['pay_for_order'] ) && 'true' === $_GET['pay_for_order'] ) {
468
			$order_id = wc_get_order_id_by_order_key( urldecode( $_GET['key'] ) );
469
			$order    = wc_get_order( $order_id );
470
471
			$stripe_params['billing_first_name'] = WC_Stripe_Helper::is_pre_30() ? $order->billing_first_name : $order->get_billing_first_name();
472
			$stripe_params['billing_last_name']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_last_name : $order->get_billing_last_name();
473
			$stripe_params['billing_address_1']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_address_1 : $order->get_billing_address_1();
474
			$stripe_params['billing_address_2']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_address_2 : $order->get_billing_address_2();
475
			$stripe_params['billing_state']      = WC_Stripe_Helper::is_pre_30() ? $order->billing_state : $order->get_billing_state();
476
			$stripe_params['billing_city']       = WC_Stripe_Helper::is_pre_30() ? $order->billing_city : $order->get_billing_city();
477
			$stripe_params['billing_postcode']   = WC_Stripe_Helper::is_pre_30() ? $order->billing_postcode : $order->get_billing_postcode();
478
			$stripe_params['billing_country']    = WC_Stripe_Helper::is_pre_30() ? $order->billing_country : $order->get_billing_country();
479
		}
480
481
		$stripe_params['no_prepaid_card_msg']                     = __( 'Sorry, we\'re not accepting prepaid cards at this time. Your credit card has not been charge. Please try with alternative payment method.', 'woocommerce-gateway-stripe' );
482
		$stripe_params['no_sepa_owner_msg']                       = __( 'Please enter your IBAN account name.', 'woocommerce-gateway-stripe' );
483
		$stripe_params['no_sepa_iban_msg']                        = __( 'Please enter your IBAN account number.', 'woocommerce-gateway-stripe' );
484
		$stripe_params['sepa_mandate_notification']               = apply_filters( 'wc_stripe_sepa_mandate_notification', 'email' );
485
		$stripe_params['allow_prepaid_card']                      = apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no';
486
		$stripe_params['inline_cc_form']                          = $this->inline_cc_form ? 'yes' : 'no';
487
		$stripe_params['stripe_checkout_require_billing_address'] = apply_filters( 'wc_stripe_checkout_require_billing_address', false ) ? 'yes' : 'no';
488
		$stripe_params['is_checkout']                             = ( is_checkout() && empty( $_GET['pay_for_order'] ) ) ? 'yes' : 'no';
489
		$stripe_params['return_url']                              = $this->get_stripe_return_url();
490
		$stripe_params['ajaxurl']                                 = WC_AJAX::get_endpoint( '%%endpoint%%' );
491
		$stripe_params['stripe_nonce']                            = wp_create_nonce( '_wc_stripe_nonce' );
492
		$stripe_params['statement_descriptor']                    = $this->statement_descriptor;
493
		$stripe_params['elements_options']                        = apply_filters( 'wc_stripe_elements_options', array() );
494
		$stripe_params['is_stripe_checkout']                      = $this->stripe_checkout ? 'yes' : 'no';
495
		$stripe_params['is_change_payment_page']                  = isset( $_GET['change_payment_method'] ) ? 'yes' : 'no';
496
		$stripe_params['is_add_payment_page']                     = is_wc_endpoint_url( 'add-payment-method' ) ? 'yes' : 'no';
497
		$stripe_params['is_pay_for_order_page']                   = is_wc_endpoint_url( 'order-pay' ) ? 'yes' : 'no';
498
		$stripe_params['validate_modal_checkout']                 = apply_filters( 'wc_stripe_validate_modal_checkout', false ) ? 'yes' : 'no';
499
		$stripe_params['elements_styling']                        = apply_filters( 'wc_stripe_elements_styling', false );
500
		$stripe_params['elements_classes']                        = apply_filters( 'wc_stripe_elements_classes', false );
501
502
		// merge localized messages to be use in JS
503
		$stripe_params = array_merge( $stripe_params, WC_Stripe_Helper::get_localized_messages() );
504
505
		wp_localize_script( 'woocommerce_stripe', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
506
		wp_localize_script( 'woocommerce_stripe_checkout', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
507
508
		if ( $this->stripe_checkout ) {
509
			wp_enqueue_script( 'stripe_checkout' );
510
		}
511
512
		$this->tokenization_script();
513
		wp_enqueue_script( 'woocommerce_stripe' );
514
	}
515
516
	/**
517
	 * Add Stripe Checkout items to receipt page.
518
	 *
519
	 * @since 4.1.0
520
	 */
521
	public function stripe_checkout_receipt_page( $order_id ) {
522
		if ( ! $this->stripe_checkout ) {
523
			return;
524
		}
525
526
		$user                 = wp_get_current_user();
527
		$total                = WC()->cart->total;
528
		$user_email           = '';
529
		$display_tokenization = $this->supports( 'tokenization' ) && $this->saved_cards;
530
531
		// If paying from order, we need to get total from order not cart.
532 View Code Duplication
		if ( ! empty( $_GET['key'] ) ) {
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...
533
			$order      = wc_get_order( wc_get_order_id_by_order_key( wc_clean( $_GET['key'] ) ) );
534
			$total      = $order->get_total();
535
			$user_email = WC_Stripe_Helper::is_pre_30() ? $order->billing_email : $order->get_billing_email();
536
		} else {
537
			if ( $user->ID ) {
538
				$user_email = get_user_meta( $user->ID, 'billing_email', true );
539
				$user_email = $user_email ? $user_email : $user->user_email;
540
			}
541
		}
542
543
		ob_start();
544
545
		do_action( 'wc_stripe_checkout_receipt_page_before_form' );
546
547
		echo '<form method="post" class="woocommerce-checkout" action="' . WC()->api_request_url( get_class( $this ) ) . '">';
548
		echo '<div
549
			id="stripe-payment-data"
550
			data-panel-label="' . esc_attr( apply_filters( 'wc_stripe_checkout_label', '' ) ) . '"
551
			data-description="' . esc_attr( strip_tags( $this->stripe_checkout_description ) ) . '"
552
			data-email="' . esc_attr( $user_email ) . '"
553
			data-verify-zip="' . esc_attr( apply_filters( 'wc_stripe_checkout_verify_zip', false ) ? 'true' : 'false' ) . '"
554
			data-billing-address="' . esc_attr( apply_filters( 'wc_stripe_checkout_require_billing_address', false ) ? 'true' : 'false' ) . '"
555
			data-shipping-address="' . esc_attr( apply_filters( 'wc_stripe_checkout_require_shipping_address', false ) ? 'true' : 'false' ) . '" 
556
			data-amount="' . esc_attr( WC_Stripe_Helper::get_stripe_amount( $total ) ) . '"
557
			data-name="' . esc_attr( $this->statement_descriptor ) . '"
558
			data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
559
			data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
560
			data-bitcoin="' . esc_attr( ( $this->bitcoin && $this->capture ) ? 'true' : 'false' ) . '"
561
			data-locale="' . esc_attr( apply_filters( 'wc_stripe_checkout_locale', $this->get_locale() ) ) . '"
562
			data-three-d-secure="' . esc_attr( $this->three_d_secure ? 'true' : 'false' ) . '"
563
			data-allow-remember-me="' . esc_attr( apply_filters( 'wc_stripe_allow_remember_me', true ) ? 'true' : 'false' ) . '">';
564
		echo '<input type="hidden" name="order_id" value="' . esc_attr( $order_id ) . '" />';
565
		echo '<input type="hidden" name="stripe_checkout_order" value="yes" />';
566
567
		if (
568
			apply_filters( 'wc_stripe_display_save_payment_method_checkbox', $display_tokenization ) &&
569
			( ! function_exists( 'wcs_order_contains_subscription' ) || ( function_exists( 'wcs_order_contains_subscription' ) && ! WC_Subscriptions_Cart::cart_contains_subscription() ) ) &&
570
			( ! WC_Stripe_Helper::is_pre_orders_exists() || ( WC_Stripe_Helper::is_pre_orders_exists() && ! $this->pre_orders->is_pre_order( $order_id ) ) )
571
		) {
572
			$this->save_payment_method_checkbox();
573
		}
574
575
		wp_nonce_field( 'stripe-checkout-process', 'stripe_checkout_process_nonce' );
576
577
		do_action( 'wc_stripe_checkout_receipt_page_before_form_submit' );
578
579
		echo '<button type="submit" class="wc-stripe-checkout-button">' . __( 'Place Order', 'woocommerce-gateway-stripe' ) . '</button>';
580
581
		do_action( 'wc_stripe_checkout_receipt_page_after_form_submit' );
582
583
		echo '</form>';
584
585
		do_action( 'wc_stripe_checkout_receipt_page_after_form' );
586
587
		echo '</div>';
588
589
		ob_end_flush();
590
	}
591
592
	/**
593
	 * Handles the return from processing the payment.
594
	 *
595
	 * @since 4.1.0
596
	 */
597
	public function stripe_checkout_return_handler() {
598
		if ( ! $this->stripe_checkout ) {
599
			return;
600
		}
601
602
		if ( ! wp_verify_nonce( $_POST['stripe_checkout_process_nonce'], 'stripe-checkout-process' ) ) {
603
			return;
604
		}
605
606
		$order_id = wc_clean( $_POST['order_id'] );
607
		$order    = wc_get_order( $order_id );
608
609
		do_action( 'wc_stripe_checkout_return_handler', $order );
610
611
		if ( WC_Stripe_Helper::is_pre_orders_exists() && $this->pre_orders->is_pre_order( $order_id ) && WC_Pre_Orders_Order::order_requires_payment_tokenization( $order_id ) ) {
612
			$result = $this->pre_orders->process_pre_order( $order_id );
613
		} else {
614
			$result = $this->process_payment( $order_id );
615
		}
616
617
		if ( 'success' === $result['result'] ) {
618
			wp_redirect( $result['redirect'] );
619
			exit;
620
		}
621
622
		// Redirects back to pay order page.
623
		wp_safe_redirect( $order->get_checkout_payment_url( true ) );
624
		exit;
625
	}
626
627
	/**
628
	 * Checks if we need to redirect for Stripe Checkout.
629
	 *
630
	 * @since 4.1.0
631
	 * @return bool
632
	 */
633
	public function maybe_redirect_stripe_checkout() {
634
		return (
635
			$this->stripe_checkout &&
636
			! isset( $_POST['stripe_checkout_order'] ) &&
637
			! $this->is_using_saved_payment_method() &&
638
			! is_wc_endpoint_url( 'order-pay' )
639
		);
640
	}
641
642
	/**
643
	 * Process the payment
644
	 *
645
	 * @since 1.0.0
646
	 * @since 4.1.0 Add 4th parameter to track previous error.
647
	 * @param int  $order_id Reference.
648
	 * @param bool $retry Should we retry on fail.
649
	 * @param bool $force_save_source Force save the payment source.
650
	 * @param mix $previous_error Any error message from previous request.
651
	 *
652
	 * @throws Exception If payment will not be accepted.
653
	 *
654
	 * @return array|void
655
	 */
656
	public function process_payment( $order_id, $retry = true, $force_save_source = false, $previous_error = false ) {
657
		try {
658
			$order = wc_get_order( $order_id );
659
660
			if ( $this->maybe_redirect_stripe_checkout() ) {
661
				WC_Stripe_Logger::log( sprintf( 'Redirecting to Stripe Checkout page for order %s', $order_id ) );
662
663
				return array(
664
					'result'   => 'success',
665
					'redirect' => $order->get_checkout_payment_url( true ),
666
				);
667
			}
668
669
			if ( $this->maybe_process_pre_orders( $order_id ) ) {
670
				return $this->pre_orders->process_pre_order( $order_id );
671
			}
672
673
			// This comes from the create account checkbox in the checkout page.
674
			$create_account = ! empty( $_POST['createaccount'] ) ? true : false;
675
676
			if ( $create_account ) {
677
				$new_customer_id     = WC_Stripe_Helper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
678
				$new_stripe_customer = new WC_Stripe_Customer( $new_customer_id );
679
				$new_stripe_customer->create_customer();
680
			}
681
682
			$prepared_source = $this->prepare_source( get_current_user_id(), $force_save_source );
683
			$source_object   = $prepared_source->source_object;
684
685
			// Check if we don't allow prepaid credit cards.
686 View Code Duplication
			if ( ! apply_filters( 'wc_stripe_allow_prepaid_card', true ) && $this->is_prepaid_card( $source_object ) ) {
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...
687
				$localized_message = __( 'Sorry, we\'re not accepting prepaid cards at this time. Your credit card has not been charge. Please try with alternative payment method.', 'woocommerce-gateway-stripe' );
688
				throw new WC_Stripe_Exception( print_r( $source_object, true ), $localized_message );
689
			}
690
691 View Code Duplication
			if ( empty( $prepared_source->source ) ) {
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...
692
				$localized_message = __( 'Payment processing failed. Please retry.', 'woocommerce-gateway-stripe' );
693
				throw new WC_Stripe_Exception( print_r( $prepared_source, true ), $localized_message );
694
			}
695
696
			$this->save_source_to_order( $order, $prepared_source );
697
698
			// Result from Stripe API request.
699
			$response = null;
700
701
			if ( $order->get_total() > 0 ) {
702
				// This will throw exception if not valid.
703
				$this->validate_minimum_order_amount( $order );
704
705
				/*
706
				 * Check if card 3DS is required or optional with 3DS setting.
707
				 * Will need to first create 3DS source and require redirection
708
				 * for customer to login to their credit card company.
709
				 * Note that if we need to save source, the original source must be first
710
				 * attached to a customer in Stripe before it can be charged.
711
				 */
712
				if ( $this->is_3ds_required( $source_object ) ) {
713
					$response = $this->create_3ds_source( $order, $source_object );
714
715
					if ( ! empty( $response->error ) ) {
716
						$localized_message = $response->error->message;
717
718
						$order->add_order_note( $localized_message );
719
720
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
721
					}
722
723
					// Update order meta with 3DS source.
724
					if ( WC_Stripe_Helper::is_pre_30() ) {
725
						update_post_meta( $order_id, '_stripe_source_id', $response->id );
726
					} else {
727
						$order->update_meta_data( '_stripe_source_id', $response->id );
728
						$order->save();
729
					}
730
731
					WC_Stripe_Logger::log( 'Info: Redirecting to 3DS...' );
732
733
					return array(
734
						'result'   => 'success',
735
						'redirect' => esc_url_raw( $response->redirect->url ),
736
					);
737
				}
738
739
				WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
740
741
				/* If we're doing a retry and source is chargeable, we need to pass
742
				 * a different idempotency key and retry for success.
743
				 */
744
				if ( $this->need_update_idempotency_key( $source_object, $previous_error ) ) {
745
					add_filter( 'wc_stripe_idempotency_key', array( $this, 'change_idempotency_key' ), 10, 2 );
746
				}
747
748
				// Make the request.
749
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $prepared_source ) );
750
751 View Code Duplication
				if ( ! empty( $response->error ) ) {
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...
752
					// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
753
					if ( $this->is_no_such_customer_error( $response->error ) ) {
754
						if ( WC_Stripe_Helper::is_pre_30() ) {
755
							delete_user_meta( $order->customer_user, '_stripe_customer_id' );
756
							delete_post_meta( $order_id, '_stripe_customer_id' );
757
						} else {
758
							delete_user_meta( $order->get_customer_id(), '_stripe_customer_id' );
759
							$order->delete_meta_data( '_stripe_customer_id' );
760
							$order->save();
761
						}
762
					}
763
764
					if ( $this->is_no_such_token_error( $response->error ) && $prepared_source->token_id ) {
765
						// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
766
						$wc_token = WC_Payment_Tokens::get( $prepared_source->token_id );
767
						$wc_token->delete();
768
						$localized_message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
769
						$order->add_order_note( $localized_message );
770
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
771
					}
772
773
					// We want to retry.
774
					if ( $this->is_retryable_error( $response->error ) ) {
775
						if ( $retry ) {
776
							// Don't do anymore retries after this.
777
							if ( 5 <= $this->retry_interval ) {
778
								return $this->process_payment( $order_id, false, $force_save_source, $response->error );
779
							}
780
781
							sleep( $this->retry_interval );
782
783
							$this->retry_interval++;
784
785
							return $this->process_payment( $order_id, true, $force_save_source, $response->error );
786
						} else {
787
							$localized_message = __( 'Sorry, we are unable to process your payment at this time. Please retry later.', 'woocommerce-gateway-stripe' );
788
							$order->add_order_note( $localized_message );
789
							throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
790
						}
791
					}
792
793
					$localized_messages = WC_Stripe_Helper::get_localized_messages();
794
795
					if ( 'card_error' === $response->error->type ) {
796
						$localized_message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
797
					} else {
798
						$localized_message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
799
					}
800
801
					$order->add_order_note( $localized_message );
802
803
					throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
804
				}
805
806
				do_action( 'wc_gateway_stripe_process_payment', $response, $order );
807
808
				// Process valid response.
809
				$this->process_response( $response, $order );
810
			} else {
811
				$order->payment_complete();
812
			}
813
814
			// Remove cart.
815
			WC()->cart->empty_cart();
816
817
			// Return thank you page redirect.
818
			return array(
819
				'result'   => 'success',
820
				'redirect' => $this->get_return_url( $order ),
821
			);
822
823
		} catch ( WC_Stripe_Exception $e ) {
824
			wc_add_notice( $e->getLocalizedMessage(), 'error' );
825
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
826
827
			do_action( 'wc_gateway_stripe_process_payment_error', $e, $order );
828
829
			/* translators: error message */
830
			$order->update_status( 'failed' );
831
832
			if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
833
				$this->send_failed_order_email( $order_id );
834
			}
835
836
			return array(
837
				'result'   => 'fail',
838
				'redirect' => '',
839
			);
840
		}
841
	}
842
843
	/**
844
	 * Displays the Stripe fee
845
	 *
846
	 * @since 4.1.0
847
	 *
848
	 * @param int $order_id
849
	 */
850 View Code Duplication
	public function display_order_fee( $order_id ) {
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...
851
		if ( apply_filters( 'wc_stripe_hide_display_order_fee', false, $order_id ) ) {
852
			return;
853
		}
854
855
		$order = wc_get_order( $order_id );
856
857
		$fee      = WC_Stripe_Helper::get_stripe_fee( $order );
858
		$currency = WC_Stripe_Helper::get_stripe_currency( $order );
859
860
		if ( ! $fee || ! $currency ) {
861
			return;
862
		}
863
864
		?>
865
866
		<tr>
867
			<td class="label stripe-fee">
868
				<?php echo wc_help_tip( __( 'This represents the fee Stripe collects for the transaction.', 'woocommerce-gateway-stripe' ) ); ?>
869
				<?php esc_html_e( 'Stripe Fee:', 'woocommerce-gateway-stripe' ); ?>
870
			</td>
871
			<td width="1%"></td>
872
			<td class="total">
873
				-&nbsp;<?php echo wc_price( $fee, array( 'currency' => $currency ) ); ?>
874
			</td>
875
		</tr>
876
877
		<?php
878
	}
879
880
	/**
881
	 * Displays the net total of the transaction without the charges of Stripe.
882
	 *
883
	 * @since 4.1.0
884
	 *
885
	 * @param int $order_id
886
	 */
887 View Code Duplication
	public function display_order_payout( $order_id ) {
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...
888
		if ( apply_filters( 'wc_stripe_hide_display_order_payout', false, $order_id ) ) {
889
			return;
890
		}
891
892
		$order = wc_get_order( $order_id );
893
894
		$net      = WC_Stripe_Helper::get_stripe_net( $order );
895
		$currency = WC_Stripe_Helper::get_stripe_currency( $order );
896
897
		if ( ! $net || ! $currency ) {
898
			return;
899
		}
900
901
		?>
902
903
		<tr>
904
			<td class="label stripe-payout">
905
				<?php echo wc_help_tip( __( 'This represents the net total that will be credited to your Stripe bank account. This may be in the currency that is set in your Stripe account.', 'woocommerce-gateway-stripe' ) ); ?>
906
				<?php esc_html_e( 'Stripe Payout:', 'woocommerce-gateway-stripe' ); ?>
907
			</td>
908
			<td width="1%"></td>
909
			<td class="total">
910
				<?php echo wc_price( $net, array( 'currency' => $currency ) ); ?>
911
			</td>
912
		</tr>
913
914
		<?php
915
	}
916
}
917