Completed
Push — master ( de9661...9046cf )
by Roy
03:01
created

WC_Gateway_Stripe   D

Complexity

Total Complexity 133

Size/Duplication

Total Lines 764
Duplicated Lines 1.96 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
dl 15
loc 764
rs 4.4444
c 0
b 0
f 0
wmc 133
lcom 2
cbo 6

14 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 64 5
A is_available() 0 7 3
A get_icon() 0 21 4
B init_apple_pay() 0 11 9
B register_apple_pay_domain() 0 35 4
D process_apple_pay_verification() 0 45 9
C admin_notices() 0 26 7
A init_form_fields() 0 3 1
F payment_fields() 11 68 21
B elements_form() 0 32 2
A admin_scripts() 0 9 4
F payment_scripts() 0 67 28
A create_3ds_source() 0 17 3
F process_payment() 4 154 33

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WC_Gateway_Stripe often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WC_Gateway_Stripe, and based on these observations, apply Extract Interface, too.

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
	 * Checks if gateway should be available to use.
187
	 *
188
	 * @since 4.0.2
189
	 */
190
	public function is_available() {
191
		if ( is_add_payment_method_page() && ! $this->saved_cards ) {
192
			return false;
193
		}
194
195
		return parent::is_available();
196
	}
197
198
	/**
199
	 * Get_icon function.
200
	 *
201
	 * @since 1.0.0
202
	 * @version 4.0.0
203
	 * @return string
204
	 */
205
	public function get_icon() {
206
		$icons = $this->payment_icons();
207
208
		$icons_str = '';
209
210
		$icons_str .= $icons['visa'];
211
		$icons_str .= $icons['amex'];
212
		$icons_str .= $icons['mastercard'];
213
214
		if ( 'USD' === get_woocommerce_currency() ) {
215
			$icons_str .= $icons['discover'];
216
			$icons_str .= $icons['jcb'];
217
			$icons_str .= $icons['diners'];
218
		}
219
220
		if ( $this->bitcoin && $this->stripe_checkout ) {
221
			$icons_str .= $icons['bitcoin'];
222
		}
223
224
		return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
225
	}
226
227
	/**
228
	 * Initializes Apple Pay process on settings page.
229
	 *
230
	 * @since 3.1.0
231
	 * @version 3.1.0
232
	 */
233
	public function init_apple_pay() {
234
		if (
235
			is_admin() &&
236
			isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] &&
237
			isset( $_GET['tab'] ) && 'checkout' === $_GET['tab'] &&
238
			isset( $_GET['section'] ) && 'stripe' === $_GET['section'] &&
239
			$this->payment_request
240
		) {
241
			$this->process_apple_pay_verification();
242
		}
243
	}
244
245
	/**
246
	 * Registers the domain with Stripe/Apple Pay
247
	 *
248
	 * @since 3.1.0
249
	 * @version 3.1.0
250
	 * @param string $secret_key
251
	 */
252
	private function register_apple_pay_domain( $secret_key = '' ) {
253
		if ( empty( $secret_key ) ) {
254
			throw new Exception( __( 'Unable to verify domain - missing secret key.', 'woocommerce-gateway-stripe' ) );
255
		}
256
257
		$endpoint = 'https://api.stripe.com/v1/apple_pay/domains';
258
259
		$data = array(
260
			'domain_name' => $_SERVER['HTTP_HOST'],
261
		);
262
263
		$headers = array(
264
			'User-Agent'    => 'WooCommerce Stripe Apple Pay',
265
			'Authorization' => 'Bearer ' . $secret_key,
266
		);
267
268
		$response = wp_remote_post( $endpoint, array(
269
			'headers' => $headers,
270
			'body'    => http_build_query( $data ),
271
		) );
272
273
		if ( is_wp_error( $response ) ) {
274
			/* translators: error message */
275
			throw new Exception( sprintf( __( 'Unable to verify domain - %s', 'woocommerce-gateway-stripe' ), $response->get_error_message() ) );
276
		}
277
278
		if ( 200 !== $response['response']['code'] ) {
279
			$parsed_response = json_decode( $response['body'] );
280
281
			$this->apple_pay_verify_notice = $parsed_response->error->message;
282
283
			/* translators: error message */
284
			throw new Exception( sprintf( __( 'Unable to verify domain - %s', 'woocommerce-gateway-stripe' ), $parsed_response->error->message ) );
285
		}
286
	}
287
288
	/**
289
	 * Processes the Apple Pay domain verification.
290
	 *
291
	 * @since 3.1.0
292
	 * @version 3.1.0
293
	 */
294
	public function process_apple_pay_verification() {
295
		$gateway_settings = get_option( 'woocommerce_stripe_settings', array() );
296
297
		try {
298
			$path     = untrailingslashit( $_SERVER['DOCUMENT_ROOT'] );
299
			$dir      = '.well-known';
300
			$file     = 'apple-developer-merchantid-domain-association';
301
			$fullpath = $path . '/' . $dir . '/' . $file;
302
303
			if ( ! empty( $gateway_settings['apple_pay_domain_set'] ) && 'yes' === $gateway_settings['apple_pay_domain_set'] && file_exists( $fullpath ) ) {
304
				return;
305
			}
306
307
			if ( ! file_exists( $path . '/' . $dir ) ) {
308
				if ( ! @mkdir( $path . '/' . $dir, 0755 ) ) {
309
					throw new Exception( __( 'Unable to create domain association folder to domain root.', 'woocommerce-gateway-stripe' ) );
310
				}
311
			}
312
313
			if ( ! file_exists( $fullpath ) ) {
314
				if ( ! @copy( WC_STRIPE_PLUGIN_PATH . '/' . $file, $fullpath ) ) {
315
					throw new Exception( __( 'Unable to copy domain association file to domain root.', 'woocommerce-gateway-stripe' ) );
316
				}
317
			}
318
319
			// At this point then the domain association folder and file should be available.
320
			// Proceed to verify/and or verify again.
321
			$this->register_apple_pay_domain( $this->secret_key );
322
323
			// No errors to this point, verification success!
324
			$gateway_settings['apple_pay_domain_set'] = 'yes';
325
			$this->apple_pay_domain_set = true;
326
327
			update_option( 'woocommerce_stripe_settings', $gateway_settings );
328
329
			WC_Stripe_Logger::log( 'Your domain has been verified with Apple Pay!' );
330
331
		} catch ( Exception $e ) {
332
			$gateway_settings['apple_pay_domain_set'] = 'no';
333
334
			update_option( 'woocommerce_stripe_settings', $gateway_settings );
335
336
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
337
		}
338
	}
339
340
	/**
341
	 * Check if SSL is enabled and notify the user
342
	 */
343
	public function admin_notices() {
344
		if ( 'no' === $this->enabled ) {
345
			return;
346
		}
347
348
		if ( $this->payment_request && ! empty( $this->apple_pay_verify_notice ) ) {
349
			$allowed_html = array(
350
				'a' => array(
351
					'href' => array(),
352
					'title' => array(),
353
				),
354
			);
355
356
			echo '<div class="error stripe-apple-pay-message"><p>' . wp_kses( make_clickable( $this->apple_pay_verify_notice ), $allowed_html ) . '</p></div>';
357
		}
358
359
		/**
360
		 * Apple pay is enabled by default and domain verification initializes
361
		 * when setting screen is displayed. So if domain verification is not set,
362
		 * something went wrong so lets notify user.
363
		 */
364
		if ( ! empty( $this->secret_key ) && $this->payment_request && ! $this->apple_pay_domain_set ) {
365
			/* translators: 1) HTML anchor open tag 2) HTML anchor closing tag */
366
			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>';
367
		}
368
	}
369
370
	/**
371
	 * Initialise Gateway Settings Form Fields
372
	 */
373
	public function init_form_fields() {
374
		$this->form_fields = require( dirname( __FILE__ ) . '/admin/stripe-settings.php' );
375
	}
376
377
	/**
378
	 * Payment form on checkout page
379
	 */
380
	public function payment_fields() {
381
		$user                 = wp_get_current_user();
382
		$display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && $this->saved_cards;
383
		$total                = WC()->cart->total;
384
		$user_email           = '';
385
386
		// If paying from order, we need to get total from order not cart.
387
		if ( isset( $_GET['pay_for_order'] ) && ! empty( $_GET['key'] ) ) {
388
			$order      = wc_get_order( wc_get_order_id_by_order_key( wc_clean( $_GET['key'] ) ) );
389
			$total      = $order->get_total();
390
			$user_email = WC_Stripe_Helper::is_pre_30() ? $order->billing_email : $order->get_billing_email();
391
		} else {
392
			if ( $user->ID ) {
393
				$user_email = get_user_meta( $user->ID, 'billing_email', true );
394
				$user_email = $user_email ? $user_email : $user->user_email;
395
			}
396
		}
397
398
		if ( is_add_payment_method_page() ) {
399
			$pay_button_text = __( 'Add Card', 'woocommerce-gateway-stripe' );
400
			$total        = '';
401
		} else {
402
			$pay_button_text = '';
403
		}
404
405
		echo '<div
406
			id="stripe-payment-data"
407
			data-panel-label="' . esc_attr( $pay_button_text ) . '"
408
			data-description=""
409
			data-email="' . esc_attr( $user_email ) . '"
410
			data-amount="' . esc_attr( WC_Stripe_Helper::get_stripe_amount( $total ) ) . '"
411
			data-name="' . esc_attr( $this->statement_descriptor ) . '"
412
			data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '"
413
			data-image="' . esc_attr( $this->stripe_checkout_image ) . '"
414
			data-bitcoin="' . esc_attr( $this->bitcoin ? 'true' : 'false' ) . '"
415
			data-locale="' . esc_attr( $this->stripe_checkout_locale ? $this->stripe_checkout_locale : 'en' ) . '"
416
			data-three-d-secure="' . esc_attr( $this->three_d_secure ? 'true' : 'false' ) . '"
417
			data-allow-remember-me="' . esc_attr( $this->saved_cards ? 'true' : 'false' ) . '">';
418
419 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...
420
			if ( $this->testmode ) {
421
				/* translators: link to Stripe testing page */
422
				$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' );
423
				$this->description  = trim( $this->description );
424
			}
425
			echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $this->description ) ) );
426
		}
427
428
		if ( $display_tokenization ) {
429
			$this->tokenization_script();
430
			$this->saved_payment_methods();
431
		}
432
433
		if ( ! $this->stripe_checkout ) {
434
			if ( apply_filters( 'wc_stripe_use_elements_checkout_form', true ) ) {
435
				$this->elements_form();
436
			} else {
437
				$this->form();
438
				echo '<div class="stripe-source-errors" role="alert"></div>';
439
			}
440
		}
441
442 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...
443
			$this->save_payment_method_checkbox();
444
		}
445
446
		echo '</div>';
447
	}
448
449
	/**
450
	 * Renders the Stripe elements form.
451
	 *
452
	 * @since 4.0.0
453
	 * @version 4.0.0
454
	 */
455
	public function elements_form() {
456
		?>
457
		<fieldset id="wc-<?php echo esc_attr( $this->id ); ?>-cc-form" class="wc-credit-card-form wc-payment-form" style="background:transparent;">
458
			<?php do_action( 'woocommerce_credit_card_form_start', $this->id ); ?>
459
			<label for="card-element">
460
				<?php esc_html_e( 'Credit or debit card', 'woocommerce-gateway-stripe' ); ?>
461
			</label>
462
463
			<?php if ( $this->inline_cc_form ) { ?>
464
				<div id="stripe-card-element" style="background:#fff;padding:0 1em;border:1px solid #ddd;margin:5px 0;padding:10px 5px;">
465
				<!-- a Stripe Element will be inserted here. -->
466
				</div>
467
			<?php } else { ?>
468
				<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;">
469
				<!-- a Stripe Element will be inserted here. -->
470
				</div>
471
				<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;">
472
				<!-- a Stripe Element will be inserted here. -->
473
				</div>
474
				<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;">
475
				<!-- a Stripe Element will be inserted here. -->
476
				</div>
477
				<div class="clear"></div>
478
			<?php } ?>
479
480
			<!-- Used to display form errors -->
481
			<div class="stripe-source-errors" role="alert"></div>
482
			<?php do_action( 'woocommerce_credit_card_form_end', $this->id ); ?>
483
			<div class="clear"></div>
484
		</fieldset>
485
		<?php
486
	}
487
488
	/**
489
	 * Load admin scripts.
490
	 *
491
	 * @since 3.1.0
492
	 * @version 3.1.0
493
	 */
494
	public function admin_scripts() {
495
		if ( 'woocommerce_page_wc-settings' !== get_current_screen()->id ) {
496
			return;
497
		}
498
499
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
500
501
		wp_enqueue_script( 'woocommerce_stripe_admin', plugins_url( 'assets/js/stripe-admin' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array(), WC_STRIPE_VERSION, true );
502
	}
503
504
	/**
505
	 * Payment_scripts function.
506
	 *
507
	 * Outputs scripts used for stripe payment
508
	 *
509
	 * @since 3.1.0
510
	 * @version 4.0.0
511
	 */
512
	public function payment_scripts() {
513
		if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) {
514
			return;
515
		}
516
517
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
518
519
		wp_register_style( 'stripe_paymentfonts', plugins_url( 'assets/css/stripe-paymentfonts.css', WC_STRIPE_MAIN_FILE ), array(), '1.2.5' );
520
		wp_enqueue_style( 'stripe_paymentfonts' );
521
		wp_register_script( 'stripe_checkout', 'https://checkout.stripe.com/checkout.js', '', WC_STRIPE_VERSION, true );
522
		wp_register_script( 'stripev2', 'https://js.stripe.com/v2/', '', '2.0', true );
523
		wp_register_script( 'stripe', 'https://js.stripe.com/v3/', '', '3.0', true );
524
		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 );
525
526
		$stripe_params = array(
527
			'key'                  => $this->publishable_key,
528
			'i18n_terms'           => __( 'Please accept the terms and conditions first', 'woocommerce-gateway-stripe' ),
529
			'i18n_required_fields' => __( 'Please fill in required checkout fields first', 'woocommerce-gateway-stripe' ),
530
		);
531
532
		// If we're on the pay page we need to pass stripe.js the address of the order.
533
		if ( isset( $_GET['pay_for_order'] ) && 'true' === $_GET['pay_for_order'] ) {
534
			$order_id = wc_get_order_id_by_order_key( urldecode( $_GET['key'] ) );
535
			$order    = wc_get_order( $order_id );
536
537
			$stripe_params['billing_first_name'] = WC_Stripe_Helper::is_pre_30() ? $order->billing_first_name : $order->get_billing_first_name();
538
			$stripe_params['billing_last_name']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_last_name : $order->get_billing_last_name();
539
			$stripe_params['billing_address_1']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_address_1 : $order->get_billing_address_1();
540
			$stripe_params['billing_address_2']  = WC_Stripe_Helper::is_pre_30() ? $order->billing_address_2 : $order->get_billing_address_2();
541
			$stripe_params['billing_state']      = WC_Stripe_Helper::is_pre_30() ? $order->billing_state : $order->get_billing_state();
542
			$stripe_params['billing_city']       = WC_Stripe_Helper::is_pre_30() ? $order->billing_city : $order->get_billing_city();
543
			$stripe_params['billing_postcode']   = WC_Stripe_Helper::is_pre_30() ? $order->billing_postcode : $order->get_billing_postcode();
544
			$stripe_params['billing_country']    = WC_Stripe_Helper::is_pre_30() ? $order->billing_country : $order->get_billing_country();
545
		}
546
547
		$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' );
548
		$stripe_params['no_bank_country_msg']                     = __( 'Please select a country for your bank.', 'woocommerce-gateway-stripe' );
549
		$stripe_params['no_sepa_owner_msg']                       = __( 'Please enter your IBAN account name.', 'woocommerce-gateway-stripe' );
550
		$stripe_params['no_sepa_iban_msg']                        = __( 'Please enter your IBAN account number.', 'woocommerce-gateway-stripe' );
551
		$stripe_params['allow_prepaid_card']                      = apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no';
552
		$stripe_params['inline_cc_form']                          = $this->inline_cc_form ? 'yes' : 'no';
553
		$stripe_params['stripe_checkout_require_billing_address'] = apply_filters( 'wc_stripe_checkout_require_billing_address', false ) ? 'yes' : 'no';
554
		$stripe_params['is_checkout']                             = ( is_checkout() && empty( $_GET['pay_for_order'] ) );
555
		$stripe_params['return_url']                              = $this->get_stripe_return_url();
556
		$stripe_params['ajaxurl']                                 = WC_AJAX::get_endpoint( '%%endpoint%%' );
557
		$stripe_params['stripe_nonce']                            = wp_create_nonce( '_wc_stripe_nonce' );
558
		$stripe_params['statement_descriptor']                    = $this->statement_descriptor;
559
		$stripe_params['use_elements']                            = apply_filters( 'wc_stripe_use_elements_checkout_form', true ) ? 'yes' : 'no';
560
		$stripe_params['is_stripe_checkout']                      = $this->stripe_checkout ? 'yes' : 'no';
561
		$stripe_params['is_change_payment_page']                  = ( isset( $_GET['pay_for_order'] ) || isset( $_GET['change_payment_method'] ) ) ? 'yes' : 'no';
562
		$stripe_params['is_add_payment_method_page']              = is_add_payment_method_page() ? 'yes' : 'no';
563
		$stripe_params['elements_styling']                        = apply_filters( 'wc_stripe_elements_styling', false );
564
		$stripe_params['elements_classes']                        = apply_filters( 'wc_stripe_elements_classes', false );
565
566
		// merge localized messages to be use in JS
567
		$stripe_params = array_merge( $stripe_params, WC_Stripe_Helper::get_localized_messages() );
568
569
		wp_localize_script( 'woocommerce_stripe', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
570
		wp_localize_script( 'woocommerce_stripe_checkout', 'wc_stripe_params', apply_filters( 'wc_stripe_params', $stripe_params ) );
571
572
		if ( $this->stripe_checkout ) {
573
			wp_enqueue_script( 'stripe_checkout' );
574
		}
575
576
		$this->tokenization_script();
577
		wp_enqueue_script( 'woocommerce_stripe' );
578
	}
579
580
	/**
581
	 * Creates the 3DS source for charge.
582
	 *
583
	 * @since 4.0.0
584
	 * @version 4.0.0
585
	 * @param object $order
586
	 * @param object $source_object
587
	 * @return mixed
588
	 */
589
	public function create_3ds_source( $order, $source_object ) {
590
		$currency                    = WC_Stripe_Helper::is_pre_30() ? $order->get_order_currency() : $order->get_currency();
591
		$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...
592
		$return_url                  = $this->get_stripe_return_url( $order );
593
594
		$post_data                   = array();
595
		$post_data['amount']         = WC_Stripe_Helper::get_stripe_amount( $order->get_total(), $currency );
596
		$post_data['currency']       = strtolower( $currency );
597
		$post_data['type']           = 'three_d_secure';
598
		$post_data['owner']          = $this->get_owner_details( $order );
599
		$post_data['three_d_secure'] = array( 'card' => $source_object->id );
600
		$post_data['redirect']       = array( 'return_url' => $return_url );
601
602
		WC_Stripe_Logger::log( 'Info: Begin creating 3DS source' );
603
604
		return WC_Stripe_API::request( $post_data, 'sources' );
605
	}
606
607
	/**
608
	 * Process the payment
609
	 *
610
	 * @since 1.0.0
611
	 * @version 4.0.0
612
	 * @param int  $order_id Reference.
613
	 * @param bool $retry Should we retry on fail.
614
	 * @param bool $force_save_source Force save the payment source.
615
	 *
616
	 * @throws Exception If payment will not be accepted.
617
	 *
618
	 * @return array|void
619
	 */
620
	public function process_payment( $order_id, $retry = true, $force_save_source = false ) {
621
		try {
622
			$order          = wc_get_order( $order_id );
623
			$source_object  = ! empty( $_POST['stripe_source'] ) ? json_decode( wc_clean( stripslashes( $_POST['stripe_source'] ) ) ) : false;
624
625
			// This comes from the create account checkbox in the checkout page.
626
			$create_account = ! empty( $_POST['createaccount'] ) ? true : false;
627
628
			if ( $create_account ) {
629
				$new_customer_id     = WC_Stripe_Helper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
630
				$new_stripe_customer = new WC_Stripe_Customer( $new_customer_id );
631
				$new_stripe_customer->create_customer();
632
			}
633
634
			$prepared_source = $this->prepare_source( get_current_user_id(), $force_save_source );
635
636
			// Check if we don't allow prepaid credit cards.
637
			if ( ! apply_filters( 'wc_stripe_allow_prepaid_card', true ) ) {
638
				$stripe_checkout_object = ! empty( $_POST['stripe_checkout_object'] ) ? json_decode( wc_clean( stripslashes( $_POST['stripe_checkout_object'] ) ) ) : false;
639
640
				if ( $stripe_checkout_object && 'token' === $stripe_checkout_object->object && 'prepaid' === $stripe_checkout_object->card->funding ) {
641
					$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' );
642
					throw new WC_Stripe_Exception( print_r( $stripe_checkout_object, true ), $localized_message );
643
				}
644
			}
645
646 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...
647
				$localized_message = __( 'Payment processing failed. Please retry.', 'woocommerce-gateway-stripe' );
648
				throw new WC_Stripe_Exception( print_r( $prepared_source, true ), $localized_message );
649
			}
650
651
			// Store source to order meta.
652
			$this->save_source( $order, $prepared_source );
653
654
			// Result from Stripe API request.
655
			$response = null;
656
657
			if ( $order->get_total() > 0 ) {
658
				// This will throw exception if not valid.
659
				$this->validate_minimum_order_amount( $order );
660
661
				/**
662
				 * Check if card 3DS is required or optional with 3DS setting.
663
				 * Will need to first create 3DS source and require redirection
664
				 * for customer to login to their credit card company.
665
				 * Note that if we need to save source, the original source must be first
666
				 * attached to a customer in Stripe before it can be charged.
667
				 */
668
				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 ) ) ) {
669
670
					$response = $this->create_3ds_source( $order, $source_object );
671
672
					if ( ! empty( $response->error ) ) {
673
						$localized_message = $response->error->message;
674
675
						$order->add_order_note( $localized_message );
676
677
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
678
					}
679
680
					// Update order meta with 3DS source.
681
					if ( WC_Stripe_Helper::is_pre_30() ) {
682
						update_post_meta( $order_id, '_stripe_source_id', $response->id );
683
					} else {
684
						$order->update_meta_data( '_stripe_source_id', $response->id );
685
						$order->save();
686
					}
687
688
					WC_Stripe_Logger::log( 'Info: Redirecting to 3DS...' );
689
690
					return array(
691
						'result'   => 'success',
692
						'redirect' => esc_url_raw( $response->redirect->url ),
693
					);
694
				}
695
696
				WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
697
698
				// Make the request.
699
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $prepared_source ) );
700
701
				if ( ! empty( $response->error ) ) {
702
					// If it is an API error such connection or server, let's retry.
703
					if ( 'api_connection_error' === $response->error->type || 'api_error' === $response->error->type ) {
704
						if ( $retry ) {
705
							sleep( 5 );
706
							return $this->process_payment( $order_id, false, $force_save_source );
707
						} else {
708
							$localized_message = 'API connection error and retries exhausted.';
709
							$order->add_order_note( $localized_message );
710
							throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
711
						}
712
					}
713
714
					// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
715
					if ( preg_match( '/No such customer/i', $response->error->message ) && $retry ) {
716
						delete_user_meta( WC_Stripe_Helper::is_pre_30() ? $order->customer_user : $order->get_customer_id(), '_stripe_customer_id' );
717
718
						return $this->process_payment( $order_id, false, $force_save_source );
719
					} elseif ( preg_match( '/No such token/i', $response->error->message ) && $prepared_source->token_id ) {
720
						// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
721
						$wc_token = WC_Payment_Tokens::get( $prepared_source->token_id );
722
						$wc_token->delete();
723
						$localized_message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
724
						$order->add_order_note( $localized_message );
725
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
726
					}
727
728
					$localized_messages = WC_Stripe_Helper::get_localized_messages();
729
730
					if ( 'card_error' === $response->error->type ) {
731
						$localized_message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
732
					} else {
733
						$localized_message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
734
					}
735
736
					$order->add_order_note( $localized_message );
737
738
					throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
739
				}
740
741
				do_action( 'wc_gateway_stripe_process_payment', $response, $order );
742
743
				// Process valid response.
744
				$this->process_response( $response, $order );
745
			} else {
746
				$order->payment_complete();
747
			}
748
749
			// Remove cart.
750
			WC()->cart->empty_cart();
751
752
			// Return thank you page redirect.
753
			return array(
754
				'result'   => 'success',
755
				'redirect' => $this->get_return_url( $order ),
756
			);
757
758
		} catch ( WC_Stripe_Exception $e ) {
759
			wc_add_notice( $e->getLocalizedMessage(), 'error' );
760
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
761
762
			do_action( 'wc_gateway_stripe_process_payment_error', $e, $order );
763
764
			if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
765
				$this->send_failed_order_email( $order_id );
766
			}
767
768
			return array(
769
				'result'   => 'fail',
770
				'redirect' => '',
771
			);
772
		}
773
	}
774
}
775