Completed
Pull Request — master (#467)
by Caleb
08:13
created

woocommerce-gateway-stripe.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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: WooCommerce
7
 * Author URI: https://woocommerce.com/
8
 * Version: 4.0.2
9
 * Requires at least: 4.4
10
 * Tested up to: 4.9
11
 * WC requires at least: 2.6
12
 * WC tested up to: 3.3
13
 * Text Domain: woocommerce-gateway-stripe
14
 *
15
 */
16
17
if ( ! defined( 'ABSPATH' ) ) {
18
	exit;
19
}
20
21
if ( ! class_exists( 'WC_Stripe' ) ) :
22
	/**
23
	 * Required minimums and constants
24
	 */
25
	define( 'WC_STRIPE_VERSION', '4.0.2' );
26
	define( 'WC_STRIPE_MIN_PHP_VER', '5.6.0' );
27
	define( 'WC_STRIPE_MIN_WC_VER', '2.6.0' );
28
	define( 'WC_STRIPE_MAIN_FILE', __FILE__ );
29
	define( 'WC_STRIPE_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
30
	define( 'WC_STRIPE_PLUGIN_PATH', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
31
32
	class WC_Stripe {
33
34
		/**
35
		 * @var Singleton The reference the *Singleton* instance of this class
36
		 */
37
		private static $instance;
38
39
		/**
40
		 * @var Reference to logging class.
41
		 */
42
		private static $log;
0 ignored issues
show
The property $log is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
43
44
		/**
45
		 * Returns the *Singleton* instance of this class.
46
		 *
47
		 * @return Singleton The *Singleton* instance.
48
		 */
49
		public static function get_instance() {
50
			if ( null === self::$instance ) {
51
				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...
52
			}
53
			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 53 which is incompatible with the return type documented by WC_Stripe::get_instance of type Singleton.
Loading history...
54
		}
55
56
		/**
57
		 * Private clone method to prevent cloning of the instance of the
58
		 * *Singleton* instance.
59
		 *
60
		 * @return void
61
		 */
62
		private function __clone() {}
63
64
		/**
65
		 * Private unserialize method to prevent unserializing of the *Singleton*
66
		 * instance.
67
		 *
68
		 * @return void
69
		 */
70
		private function __wakeup() {}
71
72
		/**
73
		 * Notices (array)
74
		 * @var array
75
		 */
76
		public $notices = array();
77
78
		/**
79
		 * Protected constructor to prevent creating a new instance of the
80
		 * *Singleton* via the `new` operator from outside of this class.
81
		 */
82
		private function __construct() {
83
			add_action( 'admin_init', array( $this, 'check_environment' ) );
84
			add_action( 'admin_notices', array( $this, 'admin_notices' ), 15 );
85
			add_action( 'plugins_loaded', array( $this, 'init' ) );
86
			add_action( 'wp_loaded', array( $this, 'hide_notices' ) );
87
		}
88
89
		/**
90
		 * Init the plugin after plugins_loaded so environment variables are set.
91
		 *
92
		 * @since 1.0.0
93
		 * @version 4.0.0
94
		 */
95
		public function init() {
96
			require_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-exception.php' );
97
			require_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-logger.php' );
98
			require_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-helper.php' );
99
			include_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-api.php' );
100
101
			// Don't hook anything else in the plugin if we're in an incompatible environment
102
			if ( self::get_environment_warning() ) {
103
				return;
104
			}
105
106
			load_plugin_textdomain( 'woocommerce-gateway-stripe', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' );
107
108
			require_once( dirname( __FILE__ ) . '/includes/abstracts/abstract-wc-stripe-payment-gateway.php' );
109
			require_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-webhook-handler.php' );
110
			require_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-sepa-payment-token.php' );
111
			require_once( dirname( __FILE__ ) . '/includes/class-wc-gateway-stripe.php' );
112
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-gateway-stripe-bancontact.php' );
113
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-gateway-stripe-sofort.php' );
114
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-gateway-stripe-giropay.php' );
115
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-gateway-stripe-ideal.php' );
116
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-gateway-stripe-p24.php' );
117
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-gateway-stripe-alipay.php' );
118
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-gateway-stripe-sepa.php' );
119
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-gateway-stripe-bitcoin.php' );
120
			require_once( dirname( __FILE__ ) . '/includes/payment-methods/class-wc-stripe-payment-request.php' );
121
			require_once( dirname( __FILE__ ) . '/includes/compat/class-wc-stripe-compat.php' );
122
			require_once( dirname( __FILE__ ) . '/includes/compat/class-wc-stripe-sepa-compat.php' );
123
			require_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-order-handler.php' );
124
			require_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-payment-tokens.php' );
125
			require_once( dirname( __FILE__ ) . '/includes/class-wc-stripe-customer.php' );
126
127
			// REMOVE IN THE FUTURE.
128
			require_once( dirname( __FILE__ ) . '/includes/deprecated/class-wc-stripe-apple-pay.php' );
129
130
			add_filter( 'woocommerce_payment_gateways', array( $this, 'add_gateways' ) );
131
			add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_action_links' ) );
132
			add_filter( 'woocommerce_get_sections_checkout', array( $this, 'filter_gateway_order_admin' ) );
133
		}
134
135
		/**
136
		 * Hides any admin notices.
137
		 *
138
		 * @since 4.0.0
139
		 * @version 4.0.0
140
		 */
141
		public function hide_notices() {
142
			if ( isset( $_GET['wc-stripe-hide-notice'] ) && isset( $_GET['_wc_stripe_notice_nonce'] ) ) {
143
				if ( ! wp_verify_nonce( $_GET['_wc_stripe_notice_nonce'], 'wc_stripe_hide_notices_nonce' ) ) {
144
					wp_die( __( 'Action failed. Please refresh the page and retry.', 'woocommerce-gateway-stripe' ) );
145
				}
146
147
				if ( ! current_user_can( 'manage_woocommerce' ) ) {
148
					wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
149
				}
150
151
				$notice = wc_clean( $_GET['wc-stripe-hide-notice'] );
152
153
				switch ( $notice ) {
154
					case 'ssl':
155
						update_option( 'wc_stripe_show_ssl_notice', 'no' );
156
						break;
157
					case 'keys':
158
						update_option( 'wc_stripe_show_keys_notice', 'no' );
159
						break;
160
				}
161
			}
162
		}
163
164
		/**
165
		 * Allow this class and other classes to add slug keyed notices (to avoid duplication).
166
		 *
167
		 * @since 1.0.0
168
		 * @version 4.0.0
169
		 */
170
		public function add_admin_notice( $slug, $class, $message, $dismissible = false ) {
171
			$this->notices[ $slug ] = array(
172
				'class'       => $class,
173
				'message'     => $message,
174
				'dismissible' => $dismissible,
175
			);
176
		}
177
178
		/**
179
		 * Display any notices we've collected thus far (e.g. for connection, disconnection).
180
		 *
181
		 * @since 1.0.0
182
		 * @version 4.0.0
183
		 */
184
		public function admin_notices() {
185
			if ( ! current_user_can( 'manage_woocommerce' ) ) {
186
				return;
187
			}
188
189
			foreach ( (array) $this->notices as $notice_key => $notice ) {
190
				echo '<div class="' . esc_attr( $notice['class'] ) . '" style="position:relative;">';
191
192
				if ( $notice['dismissible'] ) {
193
				?>
194
					<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc-stripe-hide-notice', $notice_key ), 'wc_stripe_hide_notices_nonce', '_wc_stripe_notice_nonce' ) ); ?>" class="woocommerce-message-close notice-dismiss" style="position:absolute;right:1px;padding:9px;text-decoration:none;"></a>
195
				<?php
196
				}
197
198
				echo '<p>';
199
				echo wp_kses( $notice['message'], array( 'a' => array( 'href' => array() ) ) );
200
				echo '</p></div>';
201
			}
202
		}
203
204
		/**
205
		 * Checks the environment for compatibility problems.  Returns a string with the first incompatibility
206
		 * found or false if the environment has no problems.
207
		 *
208
		 * @since 1.0.0
209
		 * @version 4.0.0
210
		 */
211
		public function get_environment_warning() {
212 View Code Duplication
			if ( version_compare( phpversion(), WC_STRIPE_MIN_PHP_VER, '<' ) ) {
0 ignored issues
show
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...
213
				/* translators: 1) int version 2) int version */
214
				$message = __( 'WooCommerce Stripe - The minimum PHP version required for this plugin is %1$s. You are running %2$s.', 'woocommerce-gateway-stripe' );
215
216
				return sprintf( $message, WC_STRIPE_MIN_PHP_VER, phpversion() );
217
			}
218
219
			if ( ! defined( 'WC_VERSION' ) ) {
220
				return __( 'WooCommerce Stripe requires WooCommerce to be activated to work.', 'woocommerce-gateway-stripe' );
221
			}
222
223 View Code Duplication
			if ( version_compare( WC_VERSION, WC_STRIPE_MIN_WC_VER, '<' ) ) {
0 ignored issues
show
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...
224
				/* translators: 1) int version 2) int version */
225
				$message = __( 'WooCommerce Stripe - The minimum WooCommerce version required for this plugin is %1$s. You are running %2$s.', 'woocommerce-gateway-stripe' );
226
227
				return sprintf( $message, WC_STRIPE_MIN_WC_VER, WC_VERSION );
228
			}
229
230
			if ( ! function_exists( 'curl_init' ) ) {
231
				return __( 'WooCommerce Stripe - cURL is not installed.', 'woocommerce-gateway-stripe' );
232
			}
233
234
			return false;
235
		}
236
237
		/**
238
		 * Get setting link.
239
		 *
240
		 * @since 1.0.0
241
		 *
242
		 * @return string Setting link
243
		 */
244
		public function get_setting_link() {
245
			$use_id_as_section = function_exists( 'WC' ) ? version_compare( WC()->version, '2.6', '>=' ) : false;
246
247
			$section_slug = $use_id_as_section ? 'stripe' : strtolower( 'WC_Gateway_Stripe' );
248
249
			return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $section_slug );
250
		}
251
252
		/**
253
		 * The backup sanity check, in case the plugin is activated in a weird way,
254
		 * or the environment changes after activation. Also handles upgrade routines.
255
		 *
256
		 * @since 1.0.0
257
		 * @version 4.0.0
258
		 */
259
		public function check_environment() {
260
			if ( ! defined( 'IFRAME_REQUEST' ) && ( WC_STRIPE_VERSION !== get_option( 'wc_stripe_version' ) ) ) {
261
				$this->install();
262
263
				do_action( 'woocommerce_stripe_updated' );
264
			}
265
266
			$environment_warning = $this->get_environment_warning();
267
268
			if ( $environment_warning && is_plugin_active( plugin_basename( __FILE__ ) ) ) {
269
				$this->add_admin_notice( 'bad_environment', 'error', $environment_warning );
270
			}
271
272
			$show_ssl_notice  = get_option( 'wc_stripe_show_ssl_notice' );
273
			$show_keys_notice = get_option( 'wc_stripe_show_keys_notice' );
274
			$options          = get_option( 'woocommerce_stripe_settings' );
275
276
			if ( isset( $options['enabled'] ) && 'yes' === $options['enabled'] && empty( $show_keys_notice ) ) {
277
				$secret  = WC_Stripe_API::get_secret_key();
278
279
				if ( empty( $secret ) && ! ( isset( $_GET['page'], $_GET['section'] ) && 'wc-settings' === $_GET['page'] && 'stripe' === $_GET['section'] ) ) {
280
					$setting_link = $this->get_setting_link();
281
					/* translators: 1) link */
282
					$this->add_admin_notice( 'keys', '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 ), true );
283
				}
284
			}
285
286
			if ( empty( $show_ssl_notice ) && isset( $options['enabled'] ) && 'yes' === $options['enabled'] ) {
287
				// Show message if enabled and FORCE SSL is disabled and WordpressHTTPS plugin is not detected.
288
				if ( ( function_exists( 'wc_site_is_https' ) && ! wc_site_is_https() ) && ( 'no' === get_option( 'woocommerce_force_ssl_checkout' ) && ! class_exists( 'WordPressHTTPS' ) ) ) {
289
					/* translators: 1) link 2) link */
290
					$this->add_admin_notice( 'ssl', 'notice notice-warning', sprintf( __( 'Stripe is enabled, but the <a href="%1$s">force SSL option</a> is disabled; your checkout may not be secure! Please enable SSL and ensure your server has a valid <a href="%2$s" target="_blank">SSL certificate</a> - Stripe will only work in test mode.', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout' ), 'https://en.wikipedia.org/wiki/Transport_Layer_Security' ), true );
291
				}
292
			}
293
		}
294
295
		/**
296
		 * Updates the plugin version in db
297
		 *
298
		 * @since 3.1.0
299
		 * @version 4.0.0
300
		 */
301
		public function update_plugin_version() {
302
			delete_option( 'wc_stripe_version' );
303
			update_option( 'wc_stripe_version', WC_STRIPE_VERSION );
304
		}
305
306
		/**
307
		 * Handles upgrade routines.
308
		 *
309
		 * @since 3.1.0
310
		 * @version 3.1.0
311
		 */
312
		public function install() {
313
			if ( ! defined( 'WC_STRIPE_INSTALLING' ) ) {
314
				define( 'WC_STRIPE_INSTALLING', true );
315
			}
316
317
			$this->update_plugin_version();
318
		}
319
320
		/**
321
		 * Adds plugin action links.
322
		 *
323
		 * @since 1.0.0
324
		 * @version 4.0.0
325
		 */
326
		public function plugin_action_links( $links ) {
327
			$plugin_links = array(
328
				'<a href="admin.php?page=wc-settings&tab=checkout&section=stripe">' . esc_html__( 'Settings', 'woocommerce-gateway-stripe' ) . '</a>',
329
				'<a href="https://docs.woocommerce.com/document/stripe/">' . esc_html__( 'Docs', 'woocommerce-gateway-stripe' ) . '</a>',
330
				'<a href="https://woocommerce.com/contact-us/">' . esc_html__( 'Support', 'woocommerce-gateway-stripe' ) . '</a>',
331
			);
332
			return array_merge( $plugin_links, $links );
333
		}
334
335
		/**
336
		 * Add the gateways to WooCommerce.
337
		 *
338
		 * @since 1.0.0
339
		 * @version 4.0.0
340
		 */
341
		public function add_gateways( $methods ) {
342
			if ( class_exists( 'WC_Subscriptions_Order' ) && function_exists( 'wcs_create_renewal_order' ) || class_exists( 'WC_Pre_Orders_Order' ) ) {
343
				$methods[] = 'WC_Stripe_Compat';
344
				$methods[] = 'WC_Stripe_Sepa_Compat';
345
			} else {
346
				$methods[] = 'WC_Gateway_Stripe';
347
				$methods[] = 'WC_Gateway_Stripe_Sepa';
348
			}
349
350
			$methods[] = 'WC_Gateway_Stripe_Bancontact';
351
			$methods[] = 'WC_Gateway_Stripe_Sofort';
352
			$methods[] = 'WC_Gateway_Stripe_Giropay';
353
			$methods[] = 'WC_Gateway_Stripe_Ideal';
354
			$methods[] = 'WC_Gateway_Stripe_P24';
355
			$methods[] = 'WC_Gateway_Stripe_Alipay';
356
			$methods[] = 'WC_Gateway_Stripe_Bitcoin';
357
358
			return $methods;
359
		}
360
361
		/**
362
		 * Modifies the order of the gateways displayed in admin.
363
		 *
364
		 * @since 4.0.0
365
		 * @version 4.0.0
366
		 */
367
		public function filter_gateway_order_admin( $sections ) {
368
			unset( $sections['stripe'] );
369
			unset( $sections['stripe_bancontact'] );
370
			unset( $sections['stripe_sofort'] );
371
			unset( $sections['stripe_giropay'] );
372
			unset( $sections['stripe_ideal'] );
373
			unset( $sections['stripe_p24'] );
374
			unset( $sections['stripe_alipay'] );
375
			unset( $sections['stripe_sepa'] );
376
			unset( $sections['stripe_bitcoin'] );
377
378
			$sections['stripe']            = 'Stripe';
379
			$sections['stripe_bancontact'] = __( 'Stripe Bancontact', 'woocommerce-gateway-stripe' );
380
			$sections['stripe_sofort']     = __( 'Stripe SOFORT', 'woocommerce-gateway-stripe' );
381
			$sections['stripe_giropay']    = __( 'Stripe Giropay', 'woocommerce-gateway-stripe' );
382
			$sections['stripe_ideal']      = __( 'Stripe iDeal', 'woocommerce-gateway-stripe' );
383
			$sections['stripe_p24']        = __( 'Stripe P24', 'woocommerce-gateway-stripe' );
384
			$sections['stripe_alipay']     = __( 'Stripe Alipay', 'woocommerce-gateway-stripe' );
385
			$sections['stripe_sepa']       = __( 'Stripe SEPA Direct Debit', 'woocommerce-gateway-stripe' );
386
			$sections['stripe_bitcoin']    = __( 'Stripe Bitcoin', 'woocommerce-gateway-stripe' );
387
388
			return $sections;
389
		}
390
	}
391
392
	WC_Stripe::get_instance();
393
394
endif;
395