Completed
Push — master ( e0634c...a8a1a1 )
by Roy
02:22
created

WC_Stripe::init_gateways()   D

Complexity

Conditions 9
Paths 68

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 38
rs 4.909
c 0
b 0
f 0
cc 9
eloc 23
nc 68
nop 0
1
<?php
2
/*
3
 * Plugin Name: WooCommerce Stripe Gateway
4
 * Plugin URI: https://wordpress.org/plugins/woocommerce-gateway-stripe/
5
 * Description: Take credit card payments on your store using Stripe.
6
 * Author: Automattic
7
 * Author URI: https://woocommerce.com/
8
 * Version: 3.0.6
9
 * Text Domain: woocommerce-gateway-stripe
10
 * Domain Path: /languages
11
 *
12
 * Copyright (c) 2016 Automattic
13
 *
14
 * This program is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation, either version 3 of the License, or
17
 * (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU General Public License
25
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26
*/
27
28
if ( ! defined( 'ABSPATH' ) ) {
29
	exit;
30
}
31
32
/**
33
 * Required minimums and constants
34
 */
35
define( 'WC_STRIPE_VERSION', '3.0.6' );
36
define( 'WC_STRIPE_MIN_PHP_VER', '5.3.0' );
37
define( 'WC_STRIPE_MIN_WC_VER', '2.5.0' );
38
define( 'WC_STRIPE_MAIN_FILE', __FILE__ );
39
define( 'WC_STRIPE_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
40
define( 'WC_STRIPE_PLUGIN_PATH', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
41
42
if ( ! class_exists( 'WC_Stripe' ) ) :
43
44
	class WC_Stripe {
45
46
		/**
47
		 * @var Singleton The reference the *Singleton* instance of this class
48
		 */
49
		private static $instance;
50
51
		/**
52
		 * @var Reference to logging class.
53
		 */
54
		private static $log;
55
56
		/**
57
		 * Returns the *Singleton* instance of this class.
58
		 *
59
		 * @return Singleton The *Singleton* instance.
60
		 */
61
		public static function get_instance() {
62
			if ( null === self::$instance ) {
63
				self::$instance = new self();
0 ignored issues
show
Documentation Bug introduced by
It seems like new self() of type object<WC_Stripe> is incompatible with the declared type object<Singleton> of property $instance.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
64
			}
65
			return self::$instance;
0 ignored issues
show
Bug Compatibility introduced by
The expression self::$instance; of type WC_Stripe|Singleton adds the type WC_Stripe to the return on line 65 which is incompatible with the return type documented by WC_Stripe::get_instance of type Singleton.
Loading history...
66
		}
67
68
		/**
69
		 * Private clone method to prevent cloning of the instance of the
70
		 * *Singleton* instance.
71
		 *
72
		 * @return void
73
		 */
74
		private function __clone() {}
75
76
		/**
77
		 * Private unserialize method to prevent unserializing of the *Singleton*
78
		 * instance.
79
		 *
80
		 * @return void
81
		 */
82
		private function __wakeup() {}
83
84
		/**
85
		 * Flag to indicate whether or not we need to load code for / support subscriptions.
86
		 *
87
		 * @var bool
88
		 */
89
		private $subscription_support_enabled = false;
90
91
		/**
92
		 * Flag to indicate whether or not we need to load support for pre-orders.
93
		 *
94
		 * @since 3.0.3
95
		 *
96
		 * @var bool
97
		 */
98
		private $pre_order_enabled = false;
99
100
		/**
101
		 * Notices (array)
102
		 * @var array
103
		 */
104
		public $notices = array();
105
106
		/**
107
		 * Protected constructor to prevent creating a new instance of the
108
		 * *Singleton* via the `new` operator from outside of this class.
109
		 */
110
		protected function __construct() {
111
			add_action( 'admin_init', array( $this, 'check_environment' ) );
112
			add_action( 'admin_notices', array( $this, 'admin_notices' ), 15 );
113
			add_action( 'plugins_loaded', array( $this, 'init' ) );
114
		}
115
116
		/**
117
		 * Init the plugin after plugins_loaded so environment variables are set.
118
		 */
119
		public function init() {
120
			// Don't hook anything else in the plugin if we're in an incompatible environment
121
			if ( self::get_environment_warning() ) {
122
				return;
123
			}
124
125
			include_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-api.php' );
126
			include_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-customer.php' );
127
128
			// Init the gateway itself
129
			$this->init_gateways();
130
131
			add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_action_links' ) );
132
			add_action( 'woocommerce_order_status_on-hold_to_processing', array( $this, 'capture_payment' ) );
133
			add_action( 'woocommerce_order_status_on-hold_to_completed', array( $this, 'capture_payment' ) );
134
			add_action( 'woocommerce_order_status_on-hold_to_cancelled', array( $this, 'cancel_payment' ) );
135
			add_action( 'woocommerce_order_status_on-hold_to_refunded', array( $this, 'cancel_payment' ) );
136
			add_filter( 'woocommerce_get_customer_payment_tokens', array( $this, 'woocommerce_get_customer_payment_tokens' ), 10, 3 );
137
			add_action( 'woocommerce_payment_token_deleted', array( $this, 'woocommerce_payment_token_deleted' ), 10, 2 );
138
			add_action( 'woocommerce_payment_token_set_default', array( $this, 'woocommerce_payment_token_set_default' ) );
139
140
			include_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-payment-request.php' );
141
		}
142
143
		/**
144
		 * Allow this class and other classes to add slug keyed notices (to avoid duplication)
145
		 */
146
		public function add_admin_notice( $slug, $class, $message ) {
147
			$this->notices[ $slug ] = array(
148
				'class'   => $class,
149
				'message' => $message,
150
			);
151
		}
152
153
		/**
154
		 * The backup sanity check, in case the plugin is activated in a weird way,
155
		 * or the environment changes after activation.
156
		 */
157
		public function check_environment() {
158
			$environment_warning = self::get_environment_warning();
159
160
			if ( $environment_warning && is_plugin_active( plugin_basename( __FILE__ ) ) ) {
161
				$this->add_admin_notice( 'bad_environment', 'error', $environment_warning );
162
			}
163
164
			// Check if secret key present. Otherwise prompt, via notice, to go to
165
			// setting.
166
			if ( ! class_exists( 'WC_Stripe_API' ) ) {
167
				include_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-api.php' );
168
			}
169
170
			$secret = WC_Stripe_API::get_secret_key();
171
172
			if ( empty( $secret ) && ! ( isset( $_GET['page'], $_GET['section'] ) && 'wc-settings' === $_GET['page'] && 'stripe' === $_GET['section'] ) ) {
173
				$setting_link = $this->get_setting_link();
174
				$this->add_admin_notice( 'prompt_connect', 'notice notice-warning', sprintf( __( 'Stripe is almost ready. To get started, <a href="%s">set your Stripe account keys</a>.', 'woocommerce-gateway-stripe' ), $setting_link ) );
175
			}
176
		}
177
178
		/**
179
		 * Checks the environment for compatibility problems.  Returns a string with the first incompatibility
180
		 * found or false if the environment has no problems.
181
		 */
182
		static function get_environment_warning() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
183 View Code Duplication
			if ( version_compare( phpversion(), WC_STRIPE_MIN_PHP_VER, '<' ) ) {
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...
184
				$message = __( 'WooCommerce Stripe - The minimum PHP version required for this plugin is %1$s. You are running %2$s.', 'woocommerce-gateway-stripe' );
185
186
				return sprintf( $message, WC_STRIPE_MIN_PHP_VER, phpversion() );
187
			}
188
189
			if ( ! defined( 'WC_VERSION' ) ) {
190
				return __( 'WooCommerce Stripe requires WooCommerce to be activated to work.', 'woocommerce-gateway-stripe' );
191
			}
192
193 View Code Duplication
			if ( version_compare( WC_VERSION, WC_STRIPE_MIN_WC_VER, '<' ) ) {
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...
194
				$message = __( 'WooCommerce Stripe - The minimum WooCommerce version required for this plugin is %1$s. You are running %2$s.', 'woocommerce-gateway-stripe' );
195
196
				return sprintf( $message, WC_STRIPE_MIN_WC_VER, WC_VERSION );
197
			}
198
199
			if ( ! function_exists( 'curl_init' ) ) {
200
				return __( 'WooCommerce Stripe - cURL is not installed.', 'woocommerce-gateway-stripe' );
201
			}
202
203
			return false;
204
		}
205
206
		/**
207
		 * Adds plugin action links
208
		 *
209
		 * @since 1.0.0
210
		 */
211
		public function plugin_action_links( $links ) {
212
			$setting_link = $this->get_setting_link();
213
214
			$plugin_links = array(
215
				'<a href="' . $setting_link . '">' . __( 'Settings', 'woocommerce-gateway-stripe' ) . '</a>',
216
				'<a href="https://docs.woothemes.com/document/stripe/">' . __( 'Docs', 'woocommerce-gateway-stripe' ) . '</a>',
217
				'<a href="http://support.woothemes.com/">' . __( 'Support', 'woocommerce-gateway-stripe' ) . '</a>',
218
			);
219
			return array_merge( $plugin_links, $links );
220
		}
221
222
		/**
223
		 * Get setting link.
224
		 *
225
		 * @since 1.0.0
226
		 *
227
		 * @return string Setting link
228
		 */
229
		public function get_setting_link() {
230
			$use_id_as_section = function_exists( 'WC' ) ? version_compare( WC()->version, '2.6', '>=' ) : false;
231
232
			$section_slug = $use_id_as_section ? 'stripe' : strtolower( 'WC_Gateway_Stripe' );
233
234
			return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $section_slug );
235
		}
236
237
		/**
238
		 * Display any notices we've collected thus far (e.g. for connection, disconnection)
239
		 */
240
		public function admin_notices() {
241
			foreach ( (array) $this->notices as $notice_key => $notice ) {
242
				echo "<div class='" . esc_attr( $notice['class'] ) . "'><p>";
243
				echo wp_kses( $notice['message'], array( 'a' => array( 'href' => array() ) ) );
244
				echo '</p></div>';
245
			}
246
		}
247
248
		/**
249
		 * Initialize the gateway. Called very early - in the context of the plugins_loaded action
250
		 *
251
		 * @since 1.0.0
252
		 */
253
		public function init_gateways() {
254
			if ( class_exists( 'WC_Subscriptions_Order' ) && function_exists( 'wcs_create_renewal_order' ) ) {
255
				$this->subscription_support_enabled = true;
256
			}
257
258
			if ( class_exists( 'WC_Pre_Orders_Order' ) ) {
259
				$this->pre_order_enabled = true;
260
			}
261
262
			if ( ! class_exists( 'WC_Payment_Gateway' ) ) {
263
				return;
264
			}
265
266
			if ( class_exists( 'WC_Payment_Gateway_CC' ) ) {
267
				include_once( dirname( __FILE__ ) . '/includes/class-wc-gateway-stripe.php' );
268
				include_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-apple-pay.php' );
269
			} else {
270
				include_once( dirname( __FILE__ ) . '/includes/legacy/class-wc-gateway-stripe.php' );
271
				include_once( dirname( __FILE__ ) . '/includes/legacy/class-wc-gateway-stripe-saved-cards.php' );
272
			}
273
274
			load_plugin_textdomain( 'woocommerce-gateway-stripe', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' );
275
			add_filter( 'woocommerce_payment_gateways', array( $this, 'add_gateways' ) );
276
277
			if ( is_admin() ) {
278
				add_action( 'wp_ajax_wc_stripe_apple_pay_domain', 'WC_Stripe_Apple_Pay::process_apple_pay_domain' );
279
			}
280
			
281
			$load_addons = (
282
				$this->subscription_support_enabled
283
				||
284
				$this->pre_order_enabled
285
			);
286
287
			if ( $load_addons ) {
288
				require_once( dirname( __FILE__ ) . '/includes/class-wc-gateway-stripe-addons.php' );
289
			}
290
		}
291
292
		/**
293
		 * Add the gateways to WooCommerce
294
		 *
295
		 * @since 1.0.0
296
		 */
297
		public function add_gateways( $methods ) {
298
			if ( $this->subscription_support_enabled || $this->pre_order_enabled ) {
299
				$methods[] = 'WC_Gateway_Stripe_Addons';
300
			} else {
301
				$methods[] = 'WC_Gateway_Stripe';
302
			}
303
			return $methods;
304
		}
305
306
		/**
307
		 * Capture payment when the order is changed from on-hold to complete or processing
308
		 *
309
		 * @param  int $order_id
310
		 */
311
		public function capture_payment( $order_id ) {
312
			$order = wc_get_order( $order_id );
313
314
			if ( 'stripe' === $order->payment_method ) {
315
				$charge   = get_post_meta( $order_id, '_stripe_charge_id', true );
316
				$captured = get_post_meta( $order_id, '_stripe_charge_captured', true );
317
318
				if ( $charge && 'no' === $captured ) {
319
					$result = WC_Stripe_API::request( array(
320
						'amount'   => $order->get_total() * 100,
321
						'expand[]' => 'balance_transaction',
322
					), 'charges/' . $charge . '/capture' );
323
324
					if ( is_wp_error( $result ) ) {
325
						$order->add_order_note( __( 'Unable to capture charge!', 'woocommerce-gateway-stripe' ) . ' ' . $result->get_error_message() );
326
					} else {
327
						$order->add_order_note( sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $result->id ) );
328
						update_post_meta( $order->id, '_stripe_charge_captured', 'yes' );
329
330
						// Store other data such as fees
331
						update_post_meta( $order->id, 'Stripe Payment ID', $result->id );
332
333 View Code Duplication
						if ( isset( $result->balance_transaction ) && isset( $result->balance_transaction->fee ) ) {
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...
334
							// Fees and Net needs to both come from Stripe to be accurate as the returned
335
							// values are in the local currency of the Stripe account, not from WC.
336
							$fee = ! empty( $result->balance_transaction->fee ) ? number_format( $result->balance_transaction->fee / 100, 2, '.', '' ) : 0;
337
							$net = ! empty( $result->balance_transaction->net ) ? number_format( $result->balance_transaction->net / 100, 2, '.', '' ) : 0;
338
							update_post_meta( $order->id, 'Stripe Fee', $fee );
339
							update_post_meta( $order->id, 'Net Revenue From Stripe', $net );
340
						}
341
					}
342
				}
343
			}
344
		}
345
346
		/**
347
		 * Cancel pre-auth on refund/cancellation
348
		 *
349
		 * @param  int $order_id
350
		 */
351
		public function cancel_payment( $order_id ) {
352
			$order = wc_get_order( $order_id );
353
354
			if ( 'stripe' === $order->payment_method ) {
355
				$charge   = get_post_meta( $order_id, '_stripe_charge_id', true );
356
357
				if ( $charge ) {
358
					$result = WC_Stripe_API::request( array(
359
						'amount' => $order->get_total() * 100,
360
					), 'charges/' . $charge . '/refund' );
361
362
					if ( is_wp_error( $result ) ) {
363
						$order->add_order_note( __( 'Unable to refund charge!', 'woocommerce-gateway-stripe' ) . ' ' . $result->get_error_message() );
364
					} else {
365
						$order->add_order_note( sprintf( __( 'Stripe charge refunded (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $result->id ) );
366
						delete_post_meta( $order->id, '_stripe_charge_captured' );
367
						delete_post_meta( $order->id, '_stripe_charge_id' );
368
					}
369
				}
370
			}
371
		}
372
373
		/**
374
		 * Gets saved tokens from API if they don't already exist in WooCommerce.
375
		 * @param array $tokens
376
		 * @return array
377
		 */
378
		public function woocommerce_get_customer_payment_tokens( $tokens, $customer_id, $gateway_id ) {
379
			if ( is_user_logged_in() && 'stripe' === $gateway_id && class_exists( 'WC_Payment_Token_CC' ) ) {
380
				$stripe_customer = new WC_Stripe_Customer( $customer_id );
381
				$stripe_cards    = $stripe_customer->get_cards();
382
				$stored_tokens   = array();
383
384
				foreach ( $tokens as $token ) {
385
					$stored_tokens[] = $token->get_token();
386
				}
387
388
				foreach ( $stripe_cards as $card ) {
389
					if ( ! in_array( $card->id, $stored_tokens ) ) {
390
						$token = new WC_Payment_Token_CC();
391
						$token->set_token( $card->id );
392
						$token->set_gateway_id( 'stripe' );
393
						$token->set_card_type( strtolower( $card->brand ) );
394
						$token->set_last4( $card->last4 );
395
						$token->set_expiry_month( $card->exp_month );
396
						$token->set_expiry_year( $card->exp_year );
397
						$token->set_user_id( $customer_id );
398
						$token->save();
399
						$tokens[ $token->get_id() ] = $token;
400
					}
401
				}
402
			}
403
			return $tokens;
404
		}
405
406
		/**
407
		 * Delete token from Stripe
408
		 */
409
		public function woocommerce_payment_token_deleted( $token_id, $token ) {
410
			if ( 'stripe' === $token->get_gateway_id() ) {
411
				$stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
412
				$stripe_customer->delete_card( $token->get_token() );
413
			}
414
		}
415
416
		/**
417
		 * Set as default in Stripe
418
		 */
419
		public function woocommerce_payment_token_set_default( $token_id ) {
420
			$token = WC_Payment_Tokens::get( $token_id );
421
			if ( 'stripe' === $token->get_gateway_id() ) {
422
				$stripe_customer = new WC_Stripe_Customer( get_current_user_id() );
423
				$stripe_customer->set_default_card( $token->get_token() );
424
			}
425
		}
426
427
		/**
428
		 * Checks Stripe minimum order value authorized per currency
429
		 */
430
		public static function get_minimum_amount() {
431
			// Check order amount
432
			switch ( get_woocommerce_currency() ) {
433
				case 'USD':
434
				case 'CAD':
435
				case 'EUR':
436
				case 'CHF':
437
				case 'AUD':
438
				case 'SGD':
439
					$minimum_amount = 50;
440
					break;
441
				case 'GBP':
442
					$minimum_amount = 30;
443
					break;
444
				case 'DKK':
445
					$minimum_amount = 250;
446
					break;
447
				case 'NOK':
448
				case 'SEK':
449
					$minimum_amount = 300;
450
					break;
451
				case 'JPY':
452
					$minimum_amount = 5000;
453
					break;
454
				case 'MXN':
455
					$minimum_amount = 1000;
456
					break;
457
				case 'HKD':
458
					$minimum_amount = 400;
459
					break;
460
				default:
461
					$minimum_amount = 50;
462
					break;
463
			}
464
465
			return $minimum_amount;
466
		}
467
468
		/**
469
		 * What rolls down stairs
470
		 * alone or in pairs,
471
		 * and over your neighbor's dog?
472
		 * What's great for a snack,
473
		 * And fits on your back?
474
		 * It's log, log, log
475
		 */
476
		public static function log( $message ) {
477
			if ( empty( self::$log ) ) {
478
				self::$log = new WC_Logger();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \WC_Logger() of type object<WC_Logger> is incompatible with the declared type object<Reference> of property $log.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
479
			}
480
481
			self::$log->add( 'woocommerce-gateway-stripe', $message );
482
		}
483
	}
484
485
	$GLOBALS['wc_stripe'] = WC_Stripe::get_instance();
486
487
endif;
488