Completed
Pull Request — master (#743)
by Roy
01:53
created

WC_Gateway_Stripe::elements_form()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

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