Completed
Push — master ( 6d8164...bf7249 )
by Roy
03:26
created

WC_Gateway_Stripe::admin_scripts()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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