Completed
Push — master ( 85d0ea...ee5959 )
by Roy
08:36 queued 02:39
created

WC_Gateway_Stripe   D

Complexity

Total Complexity 124

Size/Duplication

Total Lines 748
Duplicated Lines 2.81 %

Coupling/Cohesion

Components 3
Dependencies 6

Importance

Changes 0
Metric Value
dl 21
loc 748
rs 4.4444
c 0
b 0
f 0
wmc 124
lcom 3
cbo 6

13 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 63 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 46 2
A admin_scripts() 0 9 4
F payment_scripts() 0 68 28
F process_payment() 10 158 27

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