Completed
Pull Request — master (#739)
by Roy
01:24
created

WC_Stripe_Helper::is_wc_lt()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Provides static methods as helpers.
8
 *
9
 * @since 4.0.0
10
 */
11
class WC_Stripe_Helper {
12
	const LEGACY_META_NAME_FEE      = 'Stripe Fee';
13
	const LEGACY_META_NAME_NET      = 'Net Revenue From Stripe';
14
	const META_NAME_FEE             = '_stripe_fee';
15
	const META_NAME_NET             = '_stripe_net';
16
	const META_NAME_STRIPE_CURRENCY = '_stripe_currency';
17
18
	/**
19
	 * Gets the Stripe currency for order.
20
	 *
21
	 * @since 4.1.0
22
	 * @param object $order
23
	 * @return string $currency
24
	 */
25
	public static function get_stripe_currency( $order = null ) {
26
		if ( is_null( $order ) ) {
27
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by WC_Stripe_Helper::get_stripe_currency of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
28
		}
29
30
		$order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
31
32
		return WC_Stripe_Helper::is_wc_lt( '3.0' ) ? get_post_meta( $order_id, self::META_NAME_STRIPE_CURRENCY, true ) : $order->get_meta( self::META_NAME_STRIPE_CURRENCY, true );
33
	}
34
35
	/**
36
	 * Updates the Stripe currency for order.
37
	 *
38
	 * @since 4.1.0
39
	 * @param object $order
40
	 * @param string $currency
41
	 */
42 View Code Duplication
	public static function update_stripe_currency( $order = null, $currency ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
43
		if ( is_null( $order ) ) {
44
			return false;
45
		}
46
47
		$order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
48
49
		WC_Stripe_Helper::is_wc_lt( '3.0' ) ? update_post_meta( $order_id, self::META_NAME_STRIPE_CURRENCY, $currency ) : $order->update_meta_data( self::META_NAME_STRIPE_CURRENCY, $currency );
50
	}
51
52
	/**
53
	 * Gets the Stripe fee for order. With legacy check.
54
	 *
55
	 * @since 4.1.0
56
	 * @param object $order
57
	 * @return string $amount
58
	 */
59 View Code Duplication
	public static function get_stripe_fee( $order = null ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
60
		if ( is_null( $order ) ) {
61
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by WC_Stripe_Helper::get_stripe_fee of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
62
		}
63
64
		$order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
65
66
		$amount = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? get_post_meta( $order_id, self::META_NAME_FEE, true ) : $order->get_meta( self::META_NAME_FEE, true );
67
68
		// If not found let's check for legacy name.
69
		if ( empty( $amount ) ) {
70
			$amount = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? get_post_meta( $order_id, self::LEGACY_META_NAME_FEE, true ) : $order->get_meta( self::LEGACY_META_NAME_FEE, true );
71
72
			// If found update to new name.
73
			if ( $amount ) {
74
				self::update_stripe_fee( $order, $amount );
75
			}
76
		}
77
78
		return $amount;
79
	}
80
81
	/**
82
	 * Updates the Stripe fee for order.
83
	 *
84
	 * @since 4.1.0
85
	 * @param object $order
86
	 * @param float $amount
87
	 */
88 View Code Duplication
	public static function update_stripe_fee( $order = null, $amount = 0.0 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
89
		if ( is_null( $order ) ) {
90
			return false;
91
		}
92
93
		$order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
94
95
		WC_Stripe_Helper::is_wc_lt( '3.0' ) ? update_post_meta( $order_id, self::META_NAME_FEE, $amount ) : $order->update_meta_data( self::META_NAME_FEE, $amount );
96
	}
97
98
	/**
99
	 * Deletes the Stripe fee for order.
100
	 *
101
	 * @since 4.1.0
102
	 * @param object $order
103
	 */
104
	public static function delete_stripe_fee( $order = null ) {
105
		if ( is_null( $order ) ) {
106
			return false;
107
		}
108
109
		$order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
110
111
		delete_post_meta( $order_id, self::META_NAME_FEE );
112
		delete_post_meta( $order_id, self::LEGACY_META_NAME_FEE );
113
	}
114
115
	/**
116
	 * Gets the Stripe net for order. With legacy check.
117
	 *
118
	 * @since 4.1.0
119
	 * @param object $order
120
	 * @return string $amount
121
	 */
122 View Code Duplication
	public static function get_stripe_net( $order = null ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
123
		if ( is_null( $order ) ) {
124
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by WC_Stripe_Helper::get_stripe_net of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
125
		}
126
127
		$order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
128
129
		$amount = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? get_post_meta( $order_id, self::META_NAME_NET, true ) : $order->get_meta( self::META_NAME_NET, true );
130
131
		// If not found let's check for legacy name.
132
		if ( empty( $amount ) ) {
133
			$amount = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? get_post_meta( $order_id, self::LEGACY_META_NAME_NET, true ) : $order->get_meta( self::LEGACY_META_NAME_NET, true );
134
135
			// If found update to new name.
136
			if ( $amount ) {
137
				self::update_stripe_net( $order, $amount );
138
			}
139
		}
140
141
		return $amount;
142
	}
143
144
	/**
145
	 * Updates the Stripe net for order.
146
	 *
147
	 * @since 4.1.0
148
	 * @param object $order
149
	 * @param float $amount
150
	 */
151 View Code Duplication
	public static function update_stripe_net( $order = null, $amount = 0.0 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
152
		if ( is_null( $order ) ) {
153
			return false;
154
		}
155
156
		$order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
157
158
		WC_Stripe_Helper::is_wc_lt( '3.0' ) ? update_post_meta( $order_id, self::META_NAME_NET, $amount ) : $order->update_meta_data( self::META_NAME_NET, $amount );
159
	}
160
161
	/**
162
	 * Deletes the Stripe net for order.
163
	 *
164
	 * @since 4.1.0
165
	 * @param object $order
166
	 */
167
	public static function delete_stripe_net( $order = null ) {
168
		if ( is_null( $order ) ) {
169
			return false;
170
		}
171
172
		$order_id = WC_Stripe_Helper::is_wc_lt( '3.0' ) ? $order->id : $order->get_id();
173
174
		delete_post_meta( $order_id, self::META_NAME_NET );
175
		delete_post_meta( $order_id, self::LEGACY_META_NAME_NET );
176
	}
177
178
	/**
179
	 * Get Stripe amount to pay
180
	 *
181
	 * @param float  $total Amount due.
182
	 * @param string $currency Accepted currency.
183
	 *
184
	 * @return float|int
185
	 */
186
	public static function get_stripe_amount( $total, $currency = '' ) {
187
		if ( ! $currency ) {
188
			$currency = get_woocommerce_currency();
189
		}
190
191
		if ( in_array( strtolower( $currency ), self::no_decimal_currencies() ) ) {
192
			return absint( $total );
193
		} else {
194
			return absint( wc_format_decimal( ( (float) $total * 100 ), wc_get_price_decimals() ) ); // In cents.
195
		}
196
	}
197
198
	/**
199
	 * Localize Stripe messages based on code
200
	 *
201
	 * @since 3.0.6
202
	 * @version 3.0.6
203
	 * @return array
204
	 */
205
	public static function get_localized_messages() {
206
		return apply_filters( 'wc_stripe_localized_messages', array(
207
			'invalid_number'           => __( 'The card number is not a valid credit card number.', 'woocommerce-gateway-stripe' ),
208
			'invalid_expiry_month'     => __( 'The card\'s expiration month is invalid.', 'woocommerce-gateway-stripe' ),
209
			'invalid_expiry_year'      => __( 'The card\'s expiration year is invalid.', 'woocommerce-gateway-stripe' ),
210
			'invalid_cvc'              => __( 'The card\'s security code is invalid.', 'woocommerce-gateway-stripe' ),
211
			'incorrect_number'         => __( 'The card number is incorrect.', 'woocommerce-gateway-stripe' ),
212
			'incomplete_number'        => __( 'The card number is incomplete.', 'woocommerce-gateway-stripe' ),
213
			'incomplete_cvc'           => __( 'The card\'s security code is incomplete.', 'woocommerce-gateway-stripe' ),
214
			'incomplete_expiry'        => __( 'The card\'s expiration date is incomplete.', 'woocommerce-gateway-stripe' ),
215
			'expired_card'             => __( 'The card has expired.', 'woocommerce-gateway-stripe' ),
216
			'incorrect_cvc'            => __( 'The card\'s security code is incorrect.', 'woocommerce-gateway-stripe' ),
217
			'incorrect_zip'            => __( 'The card\'s zip code failed validation.', 'woocommerce-gateway-stripe' ),
218
			'invalid_expiry_year_past' => __( 'The card\'s expiration year is in the past', 'woocommerce-gateway-stripe' ),
219
			'card_declined'            => __( 'The card was declined.', 'woocommerce-gateway-stripe' ),
220
			'missing'                  => __( 'There is no card on a customer that is being charged.', 'woocommerce-gateway-stripe' ),
221
			'processing_error'         => __( 'An error occurred while processing the card.', 'woocommerce-gateway-stripe' ),
222
			'invalid_request_error'    => __( 'Unable to process this payment, please try again or use alternative method.', 'woocommerce-gateway-stripe' ),
223
			'invalid_sofort_country'   => __( 'The billing country is not accepted by SOFORT. Please try another country.', 'woocommerce-gateway-stripe' ),
224
		) );
225
	}
226
227
	/**
228
	 * List of currencies supported by Stripe that has no decimals.
229
	 *
230
	 * @return array $currencies
231
	 */
232
	public static function no_decimal_currencies() {
233
		return array(
234
			'bif', // Burundian Franc
235
			'djf', // Djiboutian Franc
236
			'jpy', // Japanese Yen
237
			'krw', // South Korean Won
238
			'pyg', // Paraguayan Guaraní
239
			'vnd', // Vietnamese Đồng
240
			'xaf', // Central African Cfa Franc
241
			'xpf', // Cfp Franc
242
			'clp', // Chilean Peso
243
			'gnf', // Guinean Franc
244
			'kmf', // Comorian Franc
245
			'mga', // Malagasy Ariary
246
			'rwf', // Rwandan Franc
247
			'vuv', // Vanuatu Vatu
248
			'xof', // West African Cfa Franc
249
		);
250
	}
251
252
	/**
253
	 * Stripe uses smallest denomination in currencies such as cents.
254
	 * We need to format the returned currency from Stripe into human readable form.
255
	 * The amount is not used in any calculations so returning string is sufficient.
256
	 *
257
	 * @param object $balance_transaction
258
	 * @param string $type Type of number to format
259
	 * @return string
260
	 */
261
	public static function format_balance_fee( $balance_transaction, $type = 'fee' ) {
262
		if ( ! is_object( $balance_transaction ) ) {
263
			return;
264
		}
265
266
		if ( in_array( strtolower( $balance_transaction->currency ), self::no_decimal_currencies() ) ) {
267
			if ( 'fee' === $type ) {
268
				return $balance_transaction->fee;
269
			}
270
271
			return $balance_transaction->net;
272
		}
273
274
		if ( 'fee' === $type ) {
275
			return number_format( $balance_transaction->fee / 100, 2, '.', '' );
276
		}
277
278
		return number_format( $balance_transaction->net / 100, 2, '.', '' );
279
	}
280
281
	/**
282
	 * Checks Stripe minimum order value authorized per currency
283
	 */
284
	public static function get_minimum_amount() {
285
		// Check order amount
286
		switch ( get_woocommerce_currency() ) {
287
			case 'USD':
288
			case 'CAD':
289
			case 'EUR':
290
			case 'CHF':
291
			case 'AUD':
292
			case 'SGD':
293
				$minimum_amount = 50;
294
				break;
295
			case 'GBP':
296
				$minimum_amount = 30;
297
				break;
298
			case 'DKK':
299
				$minimum_amount = 250;
300
				break;
301
			case 'NOK':
302
			case 'SEK':
303
				$minimum_amount = 300;
304
				break;
305
			case 'JPY':
306
				$minimum_amount = 5000;
307
				break;
308
			case 'MXN':
309
				$minimum_amount = 1000;
310
				break;
311
			case 'HKD':
312
				$minimum_amount = 400;
313
				break;
314
			default:
315
				$minimum_amount = 50;
316
				break;
317
		}
318
319
		return $minimum_amount;
320
	}
321
322
	/**
323
	 * Gets all the saved setting options from a specific method.
324
	 * If specific setting is passed, only return that.
325
	 *
326
	 * @since 4.0.0
327
	 * @version 4.0.0
328
	 * @param string $method The payment method to get the settings from.
329
	 * @param string $setting The name of the setting to get.
330
	 */
331
	public static function get_settings( $method = null, $setting = null ) {
332
		$all_settings = null === $method ? get_option( 'woocommerce_stripe_settings', array() ) : get_option( 'woocommerce_stripe_' . $method . '_settings', array() );
333
334
		if ( null === $setting ) {
335
			return $all_settings;
336
		}
337
338
		return isset( $all_settings[ $setting ] ) ? $all_settings[ $setting ] : '';
339
	}
340
341
	/**
342
	 * Checks if Pre Orders is available.
343
	 *
344
	 * @since 4.1.0
345
	 * @return bool
346
	 */
347
	public static function is_pre_orders_exists() {
348
		return class_exists( 'WC_Pre_Orders_Order' );
349
	}
350
351
	/**
352
	 * Check if WC version is pre 3.0.
353
	 *
354
	 * @todo Remove in the future.
355
	 * @since 4.0.0
356
	 * @version 4.0.0
357
	 * @return bool
358
	 */
359
	public static function is_pre_30() {
360
		error_log( 'This function has been deprecated since 4.1.11. Please use is_wc_lt( $version ) instead.' );
361
362
		return self::is_wc_lt( '3.0' );
363
	}
364
365
	/**
366
	 * Checks if WC version is less than passed in version.
367
	 *
368
	 * @since 4.1.11
369
	 * @param string $version Version to check against.
370
	 * @return bool
371
	 */
372
	public static function is_wc_lt( $version ) {
373
		return version_compare( WC_VERSION, $version, '<' );
374
	}
375
376
	/**
377
	 * Gets the webhook URL for Stripe triggers. Used mainly for
378
	 * asyncronous redirect payment methods in which statuses are
379
	 * not immediately chargeable.
380
	 *
381
	 * @since 4.0.0
382
	 * @version 4.0.0
383
	 * @return string
384
	 */
385
	public static function get_webhook_url() {
386
		return add_query_arg( 'wc-api', 'wc_stripe', trailingslashit( get_home_url() ) );
387
	}
388
389
	/**
390
	 * Gets the order by Stripe source ID.
391
	 *
392
	 * @since 4.0.0
393
	 * @version 4.0.0
394
	 * @param string $source_id
395
	 */
396 View Code Duplication
	public static function get_order_by_source_id( $source_id ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
397
		global $wpdb;
398
399
		$order_id = $wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT ID FROM $wpdb->posts as posts LEFT JOIN $wpdb->postmeta as meta ON posts.ID = meta.post_id WHERE meta.meta_value = %s AND meta.meta_key = %s", $source_id, '_stripe_source_id' ) );
400
401
		if ( ! empty( $order_id ) ) {
402
			return wc_get_order( $order_id );
403
		}
404
405
		return false;
406
	}
407
408
	/**
409
	 * Gets the order by Stripe charge ID.
410
	 *
411
	 * @since 4.0.0
412
	 * @version 4.0.0
413
	 * @param string $charge_id
414
	 */
415 View Code Duplication
	public static function get_order_by_charge_id( $charge_id ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
416
		global $wpdb;
417
418
		$order_id = $wpdb->get_var( $wpdb->prepare( "SELECT DISTINCT ID FROM $wpdb->posts as posts LEFT JOIN $wpdb->postmeta as meta ON posts.ID = meta.post_id WHERE meta.meta_value = %s AND meta.meta_key = %s", $charge_id, '_transaction_id' ) );
419
420
		if ( ! empty( $order_id ) ) {
421
			return wc_get_order( $order_id );
422
		}
423
424
		return false;
425
	}
426
427
	/**
428
	 * Sanitize statement descriptor text.
429
	 *
430
	 * Stripe requires max of 22 characters and no
431
	 * special characters with ><"'.
432
	 *
433
	 * @since 4.0.0
434
	 * @param string $statement_descriptor
435
	 * @return string $statement_descriptor Sanitized statement descriptor
436
	 */
437
	public static function clean_statement_descriptor( $statement_descriptor = '' ) {
438
		$disallowed_characters = array( '<', '>', '"', "'" );
439
440
		// Remove special characters.
441
		$statement_descriptor = str_replace( $disallowed_characters, '', $statement_descriptor );
442
443
		$statement_descriptor = substr( trim( $statement_descriptor ), 0, 22 );
444
445
		return $statement_descriptor;
446
	}
447
}
448