Completed
Push — master ( 2515df...302e86 )
by Roy
02:08
created

WC_Gateway_Stripe::get_icon()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.0534
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 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
	 * Should we capture Credit cards
14
	 *
15
	 * @var bool
16
	 */
17
	public $capture;
18
19
	/**
20
	 * Alternate credit card statement name
21
	 *
22
	 * @var bool
23
	 */
24
	public $statement_descriptor;
25
26
	/**
27
	 * Checkout enabled
28
	 *
29
	 * @var bool
30
	 */
31
	public $stripe_checkout;
32
33
	/**
34
	 * Require 3D Secure enabled
35
	 *
36
	 * @var bool
37
	 */
38
	public $three_d_secure;
39
40
	/**
41
	 * Checkout Locale
42
	 *
43
	 * @var string
44
	 */
45
	public $stripe_checkout_locale;
46
47
	/**
48
	 * Credit card image
49
	 *
50
	 * @var string
51
	 */
52
	public $stripe_checkout_image;
53
54
	/**
55
	 * Should we store the users credit cards?
56
	 *
57
	 * @var bool
58
	 */
59
	public $saved_cards;
60
61
	/**
62
	 * API access secret key
63
	 *
64
	 * @var string
65
	 */
66
	public $secret_key;
67
68
	/**
69
	 * Api access publishable key
70
	 *
71
	 * @var string
72
	 */
73
	public $publishable_key;
74
75
	/**
76
	 * Do we accept bitcoin?
77
	 *
78
	 * @var bool
79
	 */
80
	public $bitcoin;
81
82
	/**
83
	 * Do we accept Payment Request?
84
	 *
85
	 * @var bool
86
	 */
87
	public $payment_request;
88
89
	/**
90
	 * Apple Pay Domain Set.
91
	 *
92
	 * @var bool
93
	 */
94
	public $apple_pay_domain_set;
95
96
	/**
97
	 * Is test mode active?
98
	 *
99
	 * @var bool
100
	 */
101
	public $testmode;
102
103
	/**
104
	 * Stores Apple Pay domain verification issues.
105
	 *
106
	 * @var string
107
	 */
108
	public $apple_pay_verify_notice;
109
110
	/**
111
	 * Inline CC form styling
112
	 *
113
	 * @var string
114
	 */
115
	public $inline_cc_form;
116
117
	/**
118
	 * Constructor
119
	 */
120
	public function __construct() {
121
		$this->id                   = 'stripe';
122
		$this->method_title         = __( 'Stripe', 'woocommerce-gateway-stripe' );
123
		/* translators: 1) link to Stripe register page 2) link to Stripe api keys page */
124
		$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' );
125
		$this->has_fields           = true;
126
		$this->supports             = array(
127
			'products',
128
			'refunds',
129
			'tokenization',
130
			'add_payment_method',
131
			'subscriptions',
132
			'subscription_cancellation',
133
			'subscription_suspension',
134
			'subscription_reactivation',
135
			'subscription_amount_changes',
136
			'subscription_date_changes',
137
			'subscription_payment_method_change',
138
			'subscription_payment_method_change_customer',
139
			'subscription_payment_method_change_admin',
140
			'multiple_subscriptions',
141
			'pre-orders',
142
		);
143
144
		// Load the form fields.
145
		$this->init_form_fields();
146
147
		// Load the settings.
148
		$this->init_settings();
149
150
		// Get setting values.
151
		$this->title                   = $this->get_option( 'title' );
152
		$this->description             = $this->get_option( 'description' );
153
		$this->enabled                 = $this->get_option( 'enabled' );
154
		$this->testmode                = 'yes' === $this->get_option( 'testmode' );
155
		$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...
156
		$this->capture                 = 'yes' === $this->get_option( 'capture', 'yes' );
157
		$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...
158
		$this->three_d_secure          = 'yes' === $this->get_option( 'three_d_secure' );
159
		$this->stripe_checkout         = 'yes' === $this->get_option( 'stripe_checkout' );
160
		$this->stripe_checkout_locale  = $this->get_option( 'stripe_checkout_locale' );
161
		$this->stripe_checkout_image   = $this->get_option( 'stripe_checkout_image', '' );
162
		$this->saved_cards             = 'yes' === $this->get_option( 'saved_cards' );
163
		$this->secret_key              = $this->testmode ? $this->get_option( 'test_secret_key' ) : $this->get_option( 'secret_key' );
164
		$this->publishable_key         = $this->testmode ? $this->get_option( 'test_publishable_key' ) : $this->get_option( 'publishable_key' );
165
		$this->bitcoin                 = 'USD' === strtoupper( get_woocommerce_currency() ) && 'yes' === $this->get_option( 'stripe_bitcoin' );
166
		$this->payment_request         = 'yes' === $this->get_option( 'payment_request', 'yes' );
167
		$this->apple_pay_domain_set    = 'yes' === $this->get_option( 'apple_pay_domain_set', 'no' );
168
		$this->apple_pay_verify_notice = '';
169
170
		if ( $this->stripe_checkout ) {
171
			$this->order_button_text = __( 'Continue to payment', 'woocommerce-gateway-stripe' );
172
		}
173
174
		WC_Stripe_API::set_secret_key( $this->secret_key );
175
176
		$this->init_apple_pay();
177
178
		// Hooks.
179
		add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
180
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
181
		add_action( 'admin_notices', array( $this, 'admin_notices' ) );
182
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
183
	}
184
185
	/**
186
	 * Get_icon function.
187
	 *
188
	 * @since 1.0.0
189
	 * @version 4.0.0
190
	 * @return string
191
	 */
192
	public function get_icon() {
193
		$icons = $this->payment_icons();
194
195
		$icons_str = '';
196
197
		$icons_str .= $icons['visa'];
198
		$icons_str .= $icons['amex'];
199
		$icons_str .= $icons['mastercard'];
200
201
		if ( 'USD' === get_woocommerce_currency() ) {
202
			$icons_str .= $icons['discover'];
203
			$icons_str .= $icons['jcb'];
204
			$icons_str .= $icons['diners'];
205
		}
206
207
		if ( $this->bitcoin && $this->stripe_checkout ) {
208
			$icons_str .= $icons['bitcoin'];
209
		}
210
211
		return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
212
	}
213
214
	/**
215
	 * Initializes Apple Pay process on settings page.
216
	 *
217
	 * @since 3.1.0
218
	 * @version 3.1.0
219
	 */
220
	public function init_apple_pay() {
221
		if (
222
			is_admin() &&
223
			isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] &&
224
			isset( $_GET['tab'] ) && 'checkout' === $_GET['tab'] &&
225
			isset( $_GET['section'] ) && 'stripe' === $_GET['section'] &&
226
			$this->payment_request
227
		) {
228
			$this->process_apple_pay_verification();
229
		}
230
	}
231
232
	/**
233
	 * Registers the domain with Stripe/Apple Pay
234
	 *
235
	 * @since 3.1.0
236
	 * @version 3.1.0
237
	 * @param string $secret_key
238
	 */
239
	private function register_apple_pay_domain( $secret_key = '' ) {
240
		if ( empty( $secret_key ) ) {
241
			throw new Exception( __( 'Unable to verify domain - missing secret key.', 'woocommerce-gateway-stripe' ) );
242
		}
243
244
		$endpoint = 'https://api.stripe.com/v1/apple_pay/domains';
245
246
		$data = array(
247
			'domain_name' => $_SERVER['HTTP_HOST'],
248
		);
249
250
		$headers = array(
251
			'User-Agent'    => 'WooCommerce Stripe Apple Pay',
252
			'Authorization' => 'Bearer ' . $secret_key,
253
		);
254
255
		$response = wp_remote_post( $endpoint, array(
256
			'headers' => $headers,
257
			'body'    => http_build_query( $data ),
258
		) );
259
260
		if ( is_wp_error( $response ) ) {
261
			/* translators: error message */
262
			throw new Exception( sprintf( __( 'Unable to verify domain - %s', 'woocommerce-gateway-stripe' ), $response->get_error_message() ) );
263
		}
264
265
		if ( 200 !== $response['response']['code'] ) {
266
			$parsed_response = json_decode( $response['body'] );
267
268
			$this->apple_pay_verify_notice = $parsed_response->error->message;
269
270
			/* translators: error message */
271
			throw new Exception( sprintf( __( 'Unable to verify domain - %s', 'woocommerce-gateway-stripe' ), $parsed_response->error->message ) );
272
		}
273
	}
274
275
	/**
276
	 * Processes the Apple Pay domain verification.
277
	 *
278
	 * @since 3.1.0
279
	 * @version 3.1.0
280
	 */
281
	public function process_apple_pay_verification() {
282
		$gateway_settings = get_option( 'woocommerce_stripe_settings', array() );
283
284
		try {
285
			$path     = untrailingslashit( $_SERVER['DOCUMENT_ROOT'] );
286
			$dir      = '.well-known';
287
			$file     = 'apple-developer-merchantid-domain-association';
288
			$fullpath = $path . '/' . $dir . '/' . $file;
289
290
			if ( ! empty( $gateway_settings['apple_pay_domain_set'] ) && 'yes' === $gateway_settings['apple_pay_domain_set'] && file_exists( $fullpath ) ) {
291
				return;
292
			}
293
294
			if ( ! file_exists( $path . '/' . $dir ) ) {
295
				if ( ! @mkdir( $path . '/' . $dir, 0755 ) ) {
296
					throw new Exception( __( 'Unable to create domain association folder to domain root.', 'woocommerce-gateway-stripe' ) );
297
				}
298
			}
299
300
			if ( ! file_exists( $fullpath ) ) {
301
				if ( ! @copy( WC_STRIPE_PLUGIN_PATH . '/' . $file, $fullpath ) ) {
302
					throw new Exception( __( 'Unable to copy domain association file to domain root.', 'woocommerce-gateway-stripe' ) );
303
				}
304
			}
305
306
			// At this point then the domain association folder and file should be available.
307
			// Proceed to verify/and or verify again.
308
			$this->register_apple_pay_domain( $this->secret_key );
309
310
			// No errors to this point, verification success!
311
			$gateway_settings['apple_pay_domain_set'] = 'yes';
312
			$this->apple_pay_domain_set = true;
313
314
			update_option( 'woocommerce_stripe_settings', $gateway_settings );
315
316
			WC_Stripe_Logger::log( 'Your domain has been verified with Apple Pay!' );
317
318
		} catch ( Exception $e ) {
319
			$gateway_settings['apple_pay_domain_set'] = 'no';
320
321
			update_option( 'woocommerce_stripe_settings', $gateway_settings );
322
323
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
324
		}
325
	}
326
327
	/**
328
	 * Check if SSL is enabled and notify the user
329
	 */
330
	public function admin_notices() {
331
		if ( 'no' === $this->enabled ) {
332
			return;
333
		}
334
335
		if ( $this->payment_request && ! empty( $this->apple_pay_verify_notice ) ) {
336
			$allowed_html = array(
337
				'a' => array(
338
					'href' => array(),
339
					'title' => array(),
340
				),
341
			);
342
343
			echo '<div class="error stripe-apple-pay-message"><p>' . wp_kses( make_clickable( $this->apple_pay_verify_notice ), $allowed_html ) . '</p></div>';
344
		}
345
346
		/**
347
		 * Apple pay is enabled by default and domain verification initializes
348
		 * when setting screen is displayed. So if domain verification is not set,
349
		 * something went wrong so lets notify user.
350
		 */
351
		if ( ! empty( $this->secret_key ) && $this->payment_request && ! $this->apple_pay_domain_set ) {
352
			/* translators: 1) HTML anchor open tag 2) HTML anchor closing tag */
353
			echo '<div class="error stripe-apple-pay-message"><p>' . sprintf( __( 'Apple Pay domain verification failed. Please check the %1$slog%2$s to see the issue. (Logging must be enabled to see recorded logs)', 'woocommerce-gateway-stripe' ), '<a href="' . admin_url( 'admin.php?page=wc-status&tab=logs' ) . '">', '</a>' ) . '</p></div>';
354
		}
355
	}
356
357
	/**
358
	 * Initialise Gateway Settings Form Fields
359
	 */
360
	public function init_form_fields() {
361
		$this->form_fields = require( dirname( __FILE__ ) . '/admin/stripe-settings.php' );
362
	}
363
364
	/**
365
	 * Payment form on checkout page
366
	 */
367
	public function payment_fields() {
368
		$user                 = wp_get_current_user();
369
		$display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && $this->saved_cards;
370
		$total                = WC()->cart->total;
371
		$user_email           = '';
372
373
		// If paying from order, we need to get total from order not cart.
374
		if ( isset( $_GET['pay_for_order'] ) && ! empty( $_GET['key'] ) ) {
375
			$order      = wc_get_order( wc_get_order_id_by_order_key( wc_clean( $_GET['key'] ) ) );
376
			$total      = $order->get_total();
377
			$user_email = WC_Stripe_Helper::is_pre_30() ? $order->billing_email : $order->get_billing_email();
378
		} else {
379
			if ( $user->ID ) {
380
				$user_email = get_user_meta( $user->ID, 'billing_email', true );
381
				$user_email = $user_email ? $user_email : $user->user_email;
382
			}
383
		}
384
385
		if ( is_add_payment_method_page() ) {
386
			$pay_button_text = __( 'Add Card', 'woocommerce-gateway-stripe' );
387
			$total        = '';
388
		} else {
389
			$pay_button_text = '';
390
		}
391
392
		echo '<div
393
			id="stripe-payment-data"
394
			data-panel-label="' . esc_attr( $pay_button_text ) . '"
395
			data-description=""
396
			data-email="' . esc_attr( $user_email ) . '"
397
			data-amount="' . esc_attr( WC_Stripe_Helper::get_stripe_amount( $total ) ) . '"
398
			data-name="' . esc_attr( $this->statement_descriptor ) . '"
399
			data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
400
			data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
401
			data-bitcoin="' . esc_attr( $this->bitcoin ? 'true' : 'false' ) . '"
402
			data-locale="' . esc_attr( $this->stripe_checkout_locale ? $this->stripe_checkout_locale : 'en' ) . '"
403
			data-three-d-secure="' . esc_attr( $this->three_d_secure ? 'true' : 'false' ) . '"
404
			data-allow-remember-me="' . esc_attr( $this->saved_cards ? 'true' : 'false' ) . '">';
405
406 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...
407
			if ( $this->testmode ) {
408
				/* translators: link to Stripe testing page */
409
				$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 documentation "<a href="%s" target="_blank">Testing Stripe</a>" for more card numbers.', 'woocommerce-gateway-stripe' ), 'https://stripe.com/docs/testing' );
410
				$this->description  = trim( $this->description );
411
			}
412
			echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $this->description ) ) );
413
		}
414
415
		if ( $display_tokenization ) {
416
			$this->tokenization_script();
417
			$this->saved_payment_methods();
418
		}
419
420
		if ( ! $this->stripe_checkout ) {
421
			if ( apply_filters( 'wc_stripe_use_elements_checkout_form', true ) ) {
422
				$this->elements_form();
423
			} else {
424
				$this->form();
425
				echo '<div class="stripe-source-errors" role="alert"></div>';
426
			}
427
		}
428
429 View Code Duplication
		if ( apply_filters( 'wc_stripe_display_save_payment_method_checkbox', $display_tokenization ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) {
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...
430
			$this->save_payment_method_checkbox();
431
		}
432
433
		echo '</div>';
434
	}
435
436
	/**
437
	 * Renders the Stripe elements form.
438
	 *
439
	 * @since 4.0.0
440
	 * @version 4.0.0
441
	 */
442
	public function elements_form() {
443
		?>
444
		<fieldset id="wc-<?php echo esc_attr( $this->id ); ?>-cc-form" class="wc-credit-card-form wc-payment-form" style="background:transparent;">
445
			<?php do_action( 'woocommerce_credit_card_form_start', $this->id ); ?>
446
			<label for="card-element">
447
				<?php esc_html_e( 'Credit or debit card', 'woocommerce-gateway-stripe' ); ?>
448
			</label>
449
450
			<?php if ( $this->inline_cc_form ) { ?>
451
				<div id="stripe-card-element" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
452
				<!-- a Stripe Element will be inserted here. -->
453
				</div>
454
			<?php } else { ?>
455
				<div id="stripe-card-element" class="form-row form-row-wide" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
456
				<!-- a Stripe Element will be inserted here. -->
457
				</div>
458
				<div id="stripe-exp-element" class="form-row form-row-first" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
459
				<!-- a Stripe Element will be inserted here. -->
460
				</div>
461
				<div id="stripe-cvc-element" class="form-row form-row-last" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
462
				<!-- a Stripe Element will be inserted here. -->
463
				</div>
464
				<div class="clear"></div>
465
			<?php } ?>
466
467
			<!-- Used to display form errors -->
468
			<div class="stripe-source-errors" role="alert"></div>
469
			<?php do_action( 'woocommerce_credit_card_form_end', $this->id ); ?>
470
			<div class="clear"></div>
471
		</fieldset>
472
		<?php
473
	}
474
475
	/**
476
	 * Load admin scripts.
477
	 *
478
	 * @since 3.1.0
479
	 * @version 3.1.0
480
	 */
481
	public function admin_scripts() {
482
		if ( 'woocommerce_page_wc-settings' !== get_current_screen()->id ) {
483
			return;
484
		}
485
486
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
487
488
		wp_enqueue_script( 'woocommerce_stripe_admin', plugins_url( 'assets/js/stripe-admin' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array(), WC_STRIPE_VERSION, true );
489
	}
490
491
	/**
492
	 * Payment_scripts function.
493
	 *
494
	 * Outputs scripts used for stripe payment
495
	 *
496
	 * @since 3.1.0
497
	 * @version 4.0.0
498
	 */
499
	public function payment_scripts() {
500
		if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) {
501
			return;
502
		}
503
504
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
505
506
		wp_register_style( 'stripe_paymentfonts', plugins_url( 'assets/css/stripe-paymentfonts.css', WC_STRIPE_MAIN_FILE ), array(), '1.2.5' );
507
		wp_enqueue_style( 'stripe_paymentfonts' );
508
		wp_register_script( 'stripe_checkout', 'https://checkout.stripe.com/checkout.js', '', WC_STRIPE_VERSION, true );
509
		wp_register_script( 'stripev2', 'https://js.stripe.com/v2/', '', '2.0', true );
510
		wp_register_script( 'stripe', 'https://js.stripe.com/v3/', '', '3.0', true );
511
		wp_register_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array( 'jquery-payment', 'stripev2', 'stripe' ), WC_STRIPE_VERSION, true );
512
513
		$stripe_params = array(
514
			'key'                  => $this->publishable_key,
515
			'i18n_terms'           => __( 'Please accept the terms and conditions first', 'woocommerce-gateway-stripe' ),
516
			'i18n_required_fields' => __( 'Please fill in required checkout fields first', 'woocommerce-gateway-stripe' ),
517
		);
518
519
		// If we're on the pay page we need to pass stripe.js the address of the order.
520
		if ( isset( $_GET['pay_for_order'] ) && 'true' === $_GET['pay_for_order'] ) {
521
			$order_id = wc_get_order_id_by_order_key( urldecode( $_GET['key'] ) );
522
			$order    = wc_get_order( $order_id );
523
524
			$stripe_params['billing_first_name'] = WC_Stripe_Helper::is_pre_30() ? $order->billing_first_name : $order->get_billing_first_name();
525
			$stripe_params['billing_last_name']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_last_name : $order->get_billing_last_name();
526
			$stripe_params['billing_address_1']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_address_1 : $order->get_billing_address_1();
527
			$stripe_params['billing_address_2']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_address_2 : $order->get_billing_address_2();
528
			$stripe_params['billing_state']      = WC_Stripe_Helper::is_pre_30() ? $order->billing_state : $order->get_billing_state();
529
			$stripe_params['billing_city']       = WC_Stripe_Helper::is_pre_30() ? $order->billing_city : $order->get_billing_city();
530
			$stripe_params['billing_postcode']   = WC_Stripe_Helper::is_pre_30() ? $order->billing_postcode : $order->get_billing_postcode();
531
			$stripe_params['billing_country']    = WC_Stripe_Helper::is_pre_30() ? $order->billing_country : $order->get_billing_country();
532
		}
533
534
		$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' );
535
		$stripe_params['no_bank_country_msg']                     = __( 'Please select a country for your bank.', 'woocommerce-gateway-stripe' );
536
		$stripe_params['no_sepa_owner_msg']                       = __( 'Please enter your IBAN account name.', 'woocommerce-gateway-stripe' );
537
		$stripe_params['no_sepa_iban_msg']                        = __( 'Please enter your IBAN account number.', 'woocommerce-gateway-stripe' );
538
		$stripe_params['allow_prepaid_card']                      = apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no';
539
		$stripe_params['inline_cc_form']                          = $this->inline_cc_form ? 'yes' : 'no';
540
		$stripe_params['stripe_checkout_require_billing_address'] = apply_filters( 'wc_stripe_checkout_require_billing_address', false ) ? 'yes' : 'no';
541
		$stripe_params['is_checkout']                             = ( is_checkout() && empty( $_GET['pay_for_order'] ) );
542
		$stripe_params['return_url']                              = $this->get_stripe_return_url();
543
		$stripe_params['ajaxurl']                                 = WC_AJAX::get_endpoint( '%%endpoint%%' );
544
		$stripe_params['stripe_nonce']                            = wp_create_nonce( '_wc_stripe_nonce' );
545
		$stripe_params['statement_descriptor']                    = $this->statement_descriptor;
546
		$stripe_params['use_elements']                            = apply_filters( 'wc_stripe_use_elements_checkout_form', true ) ? 'yes' : 'no';
547
		$stripe_params['is_stripe_checkout']                      = $this->stripe_checkout ? 'yes' : 'no';
548
		$stripe_params['is_change_payment_page']                  = ( isset( $_GET['pay_for_order'] ) || isset( $_GET['change_payment_method'] ) ) ? 'yes' : 'no';
549
		$stripe_params['is_add_payment_method_page']              = is_add_payment_method_page() ? 'yes' : 'no';
550
		$stripe_params['elements_styling']                        = apply_filters( 'wc_stripe_elements_styling', false );
551
		$stripe_params['elements_classes']                        = apply_filters( 'wc_stripe_elements_classes', false );
552
553
		// merge localized messages to be use in JS
554
		$stripe_params = array_merge( $stripe_params, WC_Stripe_Helper::get_localized_messages() );
555
556
		wp_localize_script( 'woocommerce_stripe', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
557
		wp_localize_script( 'woocommerce_stripe_checkout', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
558
559
		if ( $this->stripe_checkout ) {
560
			wp_enqueue_script( 'stripe_checkout' );
561
		}
562
563
		$this->tokenization_script();
564
		wp_enqueue_script( 'woocommerce_stripe' );
565
	}
566
567
	/**
568
	 * Creates the 3DS source for charge.
569
	 *
570
	 * @since 4.0.0
571
	 * @version 4.0.0
572
	 * @param object $order
573
	 * @param object $source_object
574
	 * @return mixed
575
	 */
576
	public function create_3ds_source( $order, $source_object ) {
577
		$currency                    = WC_Stripe_Helper::is_pre_30() ? $order->get_order_currency() : $order->get_currency();
578
		$order_id                    = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
0 ignored issues
show
Unused Code introduced by
$order_id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
579
		$return_url                  = $this->get_stripe_return_url( $order );
580
581
		$post_data                   = array();
582
		$post_data['amount']         = WC_Stripe_Helper::get_stripe_amount( $order->get_total(), $currency );
583
		$post_data['currency']       = strtolower( $currency );
584
		$post_data['type']           = 'three_d_secure';
585
		$post_data['owner']          = $this->get_owner_details( $order );
586
		$post_data['three_d_secure'] = array( 'card' => $source_object->id );
587
		$post_data['redirect']       = array( 'return_url' => $return_url );
588
589
		WC_Stripe_Logger::log( 'Info: Begin creating 3DS source' );
590
591
		return WC_Stripe_API::request( $post_data, 'sources' );
592
	}
593
594
	/**
595
	 * Process the payment
596
	 *
597
	 * @since 1.0.0
598
	 * @version 4.0.0
599
	 * @param int  $order_id Reference.
600
	 * @param bool $retry Should we retry on fail.
601
	 * @param bool $force_save_source Force save the payment source.
602
	 *
603
	 * @throws Exception If payment will not be accepted.
604
	 *
605
	 * @return array|void
606
	 */
607
	public function process_payment( $order_id, $retry = true, $force_save_source = false ) {
608
		try {
609
			$order          = wc_get_order( $order_id );
610
			$source_object  = ! empty( $_POST['stripe_source'] ) ? json_decode( wc_clean( stripslashes( $_POST['stripe_source'] ) ) ) : false;
611
612
			// This comes from the create account checkbox in the checkout page.
613
			$create_account = ! empty( $_POST['createaccount'] ) ? true : false;
614
615
			if ( $create_account ) {
616
				$new_customer_id     = WC_Stripe_Helper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
617
				$new_stripe_customer = new WC_Stripe_Customer( $new_customer_id );
618
				$new_stripe_customer->create_customer();
619
			}
620
621
			$prepared_source = $this->prepare_source( get_current_user_id(), $force_save_source );
622
623
			// Check if we don't allow prepaid credit cards.
624
			if ( ! apply_filters( 'wc_stripe_allow_prepaid_card', true ) ) {
625
				$stripe_checkout_object = ! empty( $_POST['stripe_checkout_object'] ) ? json_decode( wc_clean( stripslashes( $_POST['stripe_checkout_object'] ) ) ) : false;
626
627
				if ( $stripe_checkout_object && 'token' === $stripe_checkout_object->object && 'prepaid' === $stripe_checkout_object->card->funding ) {
628
					$error_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' );
629
					throw new Exception( $error_msg );
630
				}
631
			}
632
633
			if ( empty( $prepared_source->source ) ) {
634
				$error_msg = __( 'Payment processing failed. Please retry.', 'woocommerce-gateway-stripe' );
635
				throw new Exception( $error_msg );
636
			}
637
638
			// Store source to order meta.
639
			$this->save_source( $order, $prepared_source );
640
641
			// Result from Stripe API request.
642
			$response = null;
643
644
			if ( $order->get_total() > 0 ) {
645
				// This will throw exception if not valid.
646
				$this->validate_minimum_order_amount( $order );
647
648
				/**
649
				 * Check if card 3DS is required or optional with 3DS setting.
650
				 * Will need to first create 3DS source and require redirection
651
				 * for customer to login to their credit card company.
652
				 * Note that if we need to save source, the original source must be first
653
				 * attached to a customer in Stripe before it can be charged.
654
				 */
655
				if ( $source_object && ( 'card' === $source_object->type && 'required' === $source_object->card->three_d_secure || ( $this->three_d_secure && 'optional' === $source_object->card->three_d_secure ) ) ) {
656
657
					$response = $this->create_3ds_source( $order, $source_object );
658
659
					if ( ! empty( $response->error ) ) {
660
						$message = $response->error->message;
661
662
						$order->add_order_note( $message );
663
664
						throw new Exception( $message );
665
					}
666
667
					// Update order meta with 3DS source.
668
					if ( WC_Stripe_Helper::is_pre_30() ) {
669
						update_post_meta( $order_id, '_stripe_source_id', $response->id );
670
					} else {
671
						$order->update_meta_data( '_stripe_source_id', $response->id );
672
						$order->save();
673
					}
674
675
					WC_Stripe_Logger::log( 'Info: Redirecting to 3DS...' );
676
677
					return array(
678
						'result'   => 'success',
679
						'redirect' => esc_url_raw( $response->redirect->url ),
680
					);
681
				}
682
683
				WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
684
685
				// Make the request.
686
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $prepared_source ) );
687
688 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...
689
					// If it is an API error such connection or server, let's retry.
690
					if ( 'api_connection_error' === $response->error->type || 'api_error' === $response->error->type ) {
691
						if ( $retry ) {
692
							sleep( 5 );
693
							return $this->process_payment( $order_id, false, $force_save_source );
694
						} else {
695
							$message = 'API connection error and retries exhausted.';
696
							$order->add_order_note( $message );
697
							throw new Exception( $message );
698
						}
699
					}
700
701
					// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
702
					if ( preg_match( '/No such customer/i', $response->error->message ) && $retry ) {
703
						delete_user_meta( WC_Stripe_Helper::is_pre_30() ? $order->customer_user : $order->get_customer_id(), '_stripe_customer_id' );
704
705
						return $this->process_payment( $order_id, false, $force_save_source );
706
					} elseif ( preg_match( '/No such token/i', $response->error->message ) && $prepared_source->token_id ) {
707
						// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
708
						$wc_token = WC_Payment_Tokens::get( $prepared_source->token_id );
709
						$wc_token->delete();
710
						$message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
711
						$order->add_order_note( $message );
712
						throw new Exception( $message );
713
					}
714
715
					$localized_messages = WC_Stripe_Helper::get_localized_messages();
716
717
					if ( 'card_error' === $response->error->type ) {
718
						$message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
719
					} else {
720
						$message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
721
					}
722
723
					$order->add_order_note( $message );
724
725
					throw new Exception( $message );
726
				}
727
728
				do_action( 'wc_gateway_stripe_process_payment', $response, $order );
729
730
				// Process valid response.
731
				$this->process_response( $response, $order );
732
			} else {
733
				$order->payment_complete();
734
			}
735
736
			// Remove cart.
737
			WC()->cart->empty_cart();
738
739
			// Return thank you page redirect.
740
			return array(
741
				'result'   => 'success',
742
				'redirect' => $this->get_return_url( $order ),
743
			);
744
745
		} catch ( Exception $e ) {
746
			wc_add_notice( $e->getMessage(), 'error' );
747
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
748
749
			do_action( 'wc_gateway_stripe_process_payment_error', $e, $order );
750
751
			if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
752
				$this->send_failed_order_email( $order_id );
753
			}
754
755
			return array(
756
				'result'   => 'fail',
757
				'redirect' => '',
758
			);
759
		}
760
	}
761
}
762