Issues (850)

Security Analysis    4 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection (1)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection (2)
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting (1)
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/class-wpinv-discount.php (9 issues)

1
<?php
2
/**
3
 * Contains Discount calculation class
4
 *
5
 * @since   1.0.15
6
 */
7
8
defined( 'ABSPATH' ) || exit;
9
10
/**
11
 * Discount class.
12
 *
13
 * @since 1.0.15
14
 *
15
 */
16
class WPInv_Discount extends GetPaid_Data {
17
18
	/**
19
	 * Which data store to load.
20
	 *
21
	 * @var string
22
	 */
23
    protected $data_store_name = 'discount';
24
25
    /**
26
	 * This is the name of this object type.
27
	 *
28
	 * @var string
29
	 */
30
	protected $object_type = 'discount';
31
32
	/**
33
	 * Discount Data array. This is the core item data exposed in APIs.
34
	 *
35
	 * @since 1.0.19
36
	 * @var array
37
	 */
38
	protected $data = array(
39
		'status'         => 'draft',
40
		'version'        => '',
41
		'date_created'   => null,
42
        'date_modified'  => null,
43
        'name'           => 'no-name',
44
        'description'    => '',
45
        'author'         => 1,
46
        'code'           => null,
47
        'type'           => 'percent',
48
        'expiration'     => null,
49
        'start'          => null,
50
        'items'          => array(),
51
        'excluded_items' => array(),
52
		'required_items' => array(),
53
        'uses'           => 0,
54
        'max_uses'       => null,
55
        'is_recurring'   => null,
56
        'is_single_use'  => null,
57
        'min_total'      => null,
58
        'max_total'      => null,
59
        'amount'         => null,
60
    );
61
62
	/**
63
	 * Stores meta in cache for future reads.
64
	 *
65
	 * A group must be set to to enable caching.
66
	 *
67
	 * @var string
68
	 */
69
	protected $cache_group = 'getpaid_discounts';
70
71
    /**
72
     * Stores a reference to the original WP_Post object
73
     *
74
     * @var WP_Post
75
     */
76
	protected $post = null;
77
78
	/**
79
	 * Get the discount if ID is passed, otherwise the discount is new and empty.
80
	 *
81
	 * @param int|array|string|WPInv_Discount|WP_Post $discount discount data, object, ID or code.
82
	 */
83
	public function __construct( $discount = 0 ) {
84
		parent::__construct( $discount );
85
86
		if ( is_numeric( $discount ) && 'wpi_discount' === get_post_type( $discount ) ) {
0 ignored issues
show
It seems like $discount can also be of type string; however, parameter $post of get_post_type() does only seem to accept WP_Post|integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

86
		if ( is_numeric( $discount ) && 'wpi_discount' === get_post_type( /** @scrutinizer ignore-type */ $discount ) ) {
Loading history...
87
			$this->set_id( $discount );
0 ignored issues
show
It seems like $discount can also be of type string; however, parameter $id of GetPaid_Data::set_id() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

87
			$this->set_id( /** @scrutinizer ignore-type */ $discount );
Loading history...
88
		} elseif ( $discount instanceof self ) {
89
			$this->set_id( $discount->get_id() );
90
		} elseif ( ! empty( $discount->ID ) ) {
91
			$this->set_id( $discount->ID );
92
		} elseif ( is_array( $discount ) ) {
93
			$this->set_props( $discount );
94
95
			if ( isset( $discount['ID'] ) ) {
96
				$this->set_id( $discount['ID'] );
97
			}
98
} elseif ( is_scalar( $discount ) && $discount = self::get_discount_id_by_code( $discount ) ) {
99
			$this->set_id( $discount );
100
		} else {
101
			$this->set_object_read( true );
102
		}
103
104
        // Load the datastore.
105
		$this->data_store = GetPaid_Data_Store::load( $this->data_store_name );
106
107
		if ( $this->get_id() > 0 ) {
108
            $this->post = get_post( $this->get_id() );
0 ignored issues
show
Documentation Bug introduced by
It seems like get_post($this->get_id()) can also be of type array. However, the property $post is declared as type WP_Post. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
109
            $this->ID   = $this->get_id();
0 ignored issues
show
Bug Best Practice introduced by
The property ID does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
110
			$this->data_store->read( $this );
111
        }
112
113
	}
114
115
	/**
116
	 * Fetch a discount from the db/cache
117
	 *
118
	 *
119
	 * @static
120
	 * @param string $field The field to query against: 'ID', 'discount_code'
121
	 * @param string|int $value The field value
122
	 * @deprecated
123
	 * @since 1.0.15
124
	 * @return array|bool array of discount details on success. False otherwise.
125
	 */
126
	public static function get_data_by( $field, $value ) {
127
128
		if ( 'id' == strtolower( $field ) ) {
129
			// Make sure the value is numeric to avoid casting objects, for example,
130
			// to int 1.
131
			if ( ! is_numeric( $value ) ) {
132
				return false;
133
            }
134
			$value = intval( $value );
135
			if ( $value < 1 ) {
136
				return false;
137
            }
138
		}
139
140
		if ( ! $value || ! is_string( $field ) ) {
141
			return false;
142
		}
143
144
		$field = trim( $field );
145
146
		// prepare query args
147
		switch ( strtolower( $field ) ) {
148
			case 'id':
149
				$discount_id = $value;
150
				$args        = array( 'include' => array( $value ) );
151
				break;
152
			case 'discount_code':
153
			case 'code':
154
				$value       = trim( $value );
155
				$discount_id = wp_cache_get( $value, 'WPInv_Discount_Codes' );
156
				$args        = array(
157
					'meta_key'   => '_wpi_discount_code',
158
					'meta_value' => $value,
159
				);
160
				break;
161
			case 'name':
162
				$discount_id = 0;
163
				$args        = array( 'name' => trim( $value ) );
164
				break;
165
			default:
166
				$args        = apply_filters( "wpinv_discount_get_data_by_{$field}_args", null, $value );
167
				if ( ! is_array( $args ) ) {
168
					return apply_filters( "wpinv_discount_get_data_by_$field", false, $value );
169
				}
170
}
171
172
		// Check if there is a cached value.
173
		if ( ! empty( $discount_id ) && $discount = wp_cache_get( (int) $discount_id, 'WPInv_Discounts' ) ) {
174
			return $discount;
175
		}
176
177
		$args = array_merge(
178
			$args,
179
			array(
180
				'post_type'      => 'wpi_discount',
181
				'posts_per_page' => 1,
182
				'post_status'    => array( 'publish', 'pending', 'draft', 'expired' ),
183
			)
184
		);
185
186
		$discount = get_posts( $args );
187
188
		if ( empty( $discount ) ) {
189
			return false;
190
		}
191
192
		$discount = $discount[0];
193
194
		// Prepare the return data.
195
		$return = array(
196
            'ID'             => $discount->ID,
197
            'code'           => get_post_meta( $discount->ID, '_wpi_discount_code', true ),
198
            'amount'         => get_post_meta( $discount->ID, '_wpi_discount_amount', true ),
199
            'date_created'   => $discount->post_date,
200
			'date_modified'  => $discount->post_modified,
201
			'status'         => $discount->post_status,
202
			'start'          => get_post_meta( $discount->ID, '_wpi_discount_start', true ),
203
            'expiration'     => get_post_meta( $discount->ID, '_wpi_discount_expiration', true ),
204
            'type'           => get_post_meta( $discount->ID, '_wpi_discount_type', true ),
205
            'description'    => $discount->post_excerpt,
206
            'uses'           => get_post_meta( $discount->ID, '_wpi_discount_uses', true ),
207
            'is_single_use'  => get_post_meta( $discount->ID, '_wpi_discount_is_single_use', true ),
208
            'items'          => get_post_meta( $discount->ID, '_wpi_discount_items', true ),
209
            'excluded_items' => get_post_meta( $discount->ID, '_wpi_discount_excluded_items', true ),
210
			'required_items' => get_post_meta( $discount->ID, '_wpi_discount_required_items', true ),
211
            'max_uses'       => get_post_meta( $discount->ID, '_wpi_discount_max_uses', true ),
212
            'is_recurring'   => get_post_meta( $discount->ID, '_wpi_discount_is_recurring', true ),
213
            'min_total'      => get_post_meta( $discount->ID, '_wpi_discount_min_total', true ),
214
            'max_total'      => get_post_meta( $discount->ID, '_wpi_discount_max_total', true ),
215
        );
216
217
		$return = apply_filters( 'wpinv_discount_properties', $return );
218
219
		// Update the cache with our data
220
		wp_cache_add( $discount->ID, $return, 'WPInv_Discounts' );
221
		wp_cache_add( $return['code'], $discount->ID, 'WPInv_Discount_Codes' );
222
223
		return $return;
224
	}
225
226
	/**
227
	 * Given a discount code, it returns a discount id.
228
	 *
229
	 *
230
	 * @static
231
	 * @param string $discount_code
232
	 * @since 1.0.15
233
	 * @return int
234
	 */
235
	public static function get_discount_id_by_code( $discount_code ) {
236
237
		// Trim the code.
238
		$discount_code = trim( $discount_code );
239
240
		// Ensure a value has been passed.
241
		if ( empty( $discount_code ) ) {
242
			return 0;
243
		}
244
245
		// Maybe retrieve from the cache.
246
		$discount_id   = wp_cache_get( $discount_code, 'getpaid_discount_codes' );
247
		if ( ! empty( $discount_id ) ) {
248
			return $discount_id;
249
		}
250
251
		// Fetch the first discount codes.
252
		$discounts = get_posts(
253
			array(
254
				'meta_key'       => '_wpi_discount_code',
255
				'meta_value'     => $discount_code,
256
				'post_type'      => 'wpi_discount',
257
				'posts_per_page' => 1,
258
				'post_status'    => array( 'publish', 'pending', 'draft', 'expired' ),
259
				'fields'         => 'ids',
260
			)
261
		);
262
263
		if ( empty( $discounts ) ) {
264
			return 0;
265
		}
266
267
		$discount_id = $discounts[0];
268
269
		// Update the cache with our data
270
		wp_cache_add( get_post_meta( $discount_id, '_wpi_discount_code', true ), $discount_id, 'getpaid_discount_codes' );
0 ignored issues
show
It seems like get_post_meta($discount_...i_discount_code', true) can also be of type false; however, parameter $key of wp_cache_add() does only seem to accept integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

270
		wp_cache_add( /** @scrutinizer ignore-type */ get_post_meta( $discount_id, '_wpi_discount_code', true ), $discount_id, 'getpaid_discount_codes' );
Loading history...
271
272
		return $discount_id;
273
	}
274
275
	/**
276
	 * Magic method for checking the existence of a certain custom field.
277
	 *
278
	 * @since 1.0.15
279
	 * @access public
280
	 *
281
	 * @return bool Whether the given discount field is set.
282
	 */
283
	public function __isset( $key ) {
284
		return isset( $this->data[ $key ] ) || method_exists( $this, "get_$key" );
285
	}
286
287
	/*
288
	|--------------------------------------------------------------------------
289
	| CRUD methods
290
	|--------------------------------------------------------------------------
291
	|
292
	| Methods which create, read, update and delete discounts from the database.
293
	|
294
    */
295
296
    /*
297
	|--------------------------------------------------------------------------
298
	| Getters
299
	|--------------------------------------------------------------------------
300
	*/
301
302
	/**
303
	 * Get discount status.
304
	 *
305
	 * @since 1.0.19
306
	 * @param  string $context View or edit context.
307
	 * @return string
308
	 */
309
	public function get_status( $context = 'view' ) {
310
		return $this->get_prop( 'status', $context );
311
    }
312
313
    /**
314
	 * Get plugin version when the discount was created.
315
	 *
316
	 * @since 1.0.19
317
	 * @param  string $context View or edit context.
318
	 * @return string
319
	 */
320
	public function get_version( $context = 'view' ) {
321
		return $this->get_prop( 'version', $context );
322
    }
323
324
    /**
325
	 * Get date when the discount was created.
326
	 *
327
	 * @since 1.0.19
328
	 * @param  string $context View or edit context.
329
	 * @return string
330
	 */
331
	public function get_date_created( $context = 'view' ) {
332
		return $this->get_prop( 'date_created', $context );
333
    }
334
335
    /**
336
	 * Get GMT date when the discount was created.
337
	 *
338
	 * @since 1.0.19
339
	 * @param  string $context View or edit context.
340
	 * @return string
341
	 */
342
	public function get_date_created_gmt( $context = 'view' ) {
343
        $date = $this->get_date_created( $context );
344
345
        if ( $date ) {
346
            $date = get_gmt_from_date( $date );
347
        }
348
		return $date;
349
    }
350
351
    /**
352
	 * Get date when the discount was last modified.
353
	 *
354
	 * @since 1.0.19
355
	 * @param  string $context View or edit context.
356
	 * @return string
357
	 */
358
	public function get_date_modified( $context = 'view' ) {
359
		return $this->get_prop( 'date_modified', $context );
360
    }
361
362
    /**
363
	 * Get GMT date when the discount was last modified.
364
	 *
365
	 * @since 1.0.19
366
	 * @param  string $context View or edit context.
367
	 * @return string
368
	 */
369
	public function get_date_modified_gmt( $context = 'view' ) {
370
        $date = $this->get_date_modified( $context );
371
372
        if ( $date ) {
373
            $date = get_gmt_from_date( $date );
374
        }
375
		return $date;
376
    }
377
378
    /**
379
	 * Get the discount name.
380
	 *
381
	 * @since 1.0.19
382
	 * @param  string $context View or edit context.
383
	 * @return string
384
	 */
385
	public function get_name( $context = 'view' ) {
386
		return $this->get_prop( 'name', $context );
387
    }
388
389
    /**
390
	 * Alias of self::get_name().
391
	 *
392
	 * @since 1.0.19
393
	 * @param  string $context View or edit context.
394
	 * @return string
395
	 */
396
	public function get_title( $context = 'view' ) {
397
		return $this->get_name( $context );
398
    }
399
400
    /**
401
	 * Get the discount description.
402
	 *
403
	 * @since 1.0.19
404
	 * @param  string $context View or edit context.
405
	 * @return string
406
	 */
407
	public function get_description( $context = 'view' ) {
408
		return $this->get_prop( 'description', $context );
409
    }
410
411
    /**
412
	 * Alias of self::get_description().
413
	 *
414
	 * @since 1.0.19
415
	 * @param  string $context View or edit context.
416
	 * @return string
417
	 */
418
	public function get_excerpt( $context = 'view' ) {
419
		return $this->get_description( $context );
420
    }
421
422
    /**
423
	 * Alias of self::get_description().
424
	 *
425
	 * @since 1.0.19
426
	 * @param  string $context View or edit context.
427
	 * @return string
428
	 */
429
	public function get_summary( $context = 'view' ) {
430
		return $this->get_description( $context );
431
    }
432
433
    /**
434
	 * Get the owner of the discount.
435
	 *
436
	 * @since 1.0.19
437
	 * @param  string $context View or edit context.
438
	 * @return string
439
	 */
440
	public function get_author( $context = 'view' ) {
441
		return (int) $this->get_prop( 'author', $context );
442
	}
443
444
	/**
445
	 * Get the discount code.
446
	 *
447
	 * @since 1.0.19
448
	 * @param  string $context View or edit context.
449
	 * @return string
450
	 */
451
	public function get_code( $context = 'view' ) {
452
		return $this->get_prop( 'code', $context );
453
	}
454
455
	/**
456
	 * Alias for self::get_code().
457
	 *
458
	 * @since 1.0.19
459
	 * @param  string $context View or edit context.
460
	 * @return string
461
	 */
462
	public function get_coupon_code( $context = 'view' ) {
463
		return $this->get_code( $context );
464
	}
465
466
	/**
467
	 * Alias for self::get_code().
468
	 *
469
	 * @since 1.0.19
470
	 * @param  string $context View or edit context.
471
	 * @return string
472
	 */
473
	public function get_discount_code( $context = 'view' ) {
474
		return $this->get_code( $context );
475
	}
476
477
	/**
478
	 * Get the discount's amount.
479
	 *
480
	 * @since 1.0.19
481
	 * @param  string $context View or edit context.
482
	 * @return float
483
	 */
484
	public function get_amount( $context = 'view' ) {
485
		return $context == 'view' ? floatval( $this->get_prop( 'amount', $context ) ) : $this->get_prop( 'amount', $context );
486
	}
487
488
	/**
489
	 * Get the discount's formated amount/rate.
490
	 *
491
	 * @since 1.0.19
492
	 * @return string
493
	 */
494
	public function get_formatted_amount() {
495
496
		if ( $this->is_type( 'flat' ) ) {
497
			$rate = wpinv_price( $this->get_amount() );
498
		} else {
499
			$rate = $this->get_amount() . '%';
500
		}
501
502
		return apply_filters( 'wpinv_format_discount_rate', $rate, $this->get_type(), $this->get_amount() );
503
	}
504
505
	/**
506
	 * Get the discount's start date.
507
	 *
508
	 * @since 1.0.19
509
	 * @param  string $context View or edit context.
510
	 * @return string
511
	 */
512
	public function get_start( $context = 'view' ) {
513
		return $this->get_prop( 'start', $context );
514
	}
515
516
	/**
517
	 * Alias for self::get_start().
518
	 *
519
	 * @since 1.0.19
520
	 * @param  string $context View or edit context.
521
	 * @return string
522
	 */
523
	public function get_start_date( $context = 'view' ) {
524
		return $this->get_start( $context );
525
	}
526
527
	/**
528
	 * Get the discount's expiration date.
529
	 *
530
	 * @since 1.0.19
531
	 * @param  string $context View or edit context.
532
	 * @return string
533
	 */
534
	public function get_expiration( $context = 'view' ) {
535
		return $this->get_prop( 'expiration', $context );
536
	}
537
538
	/**
539
	 * Alias for self::get_expiration().
540
	 *
541
	 * @since 1.0.19
542
	 * @param  string $context View or edit context.
543
	 * @return string
544
	 */
545
	public function get_expiration_date( $context = 'view' ) {
546
		return $this->get_expiration( $context );
547
	}
548
549
	/**
550
	 * Alias for self::get_expiration().
551
	 *
552
	 * @since 1.0.19
553
	 * @param  string $context View or edit context.
554
	 * @return string
555
	 */
556
	public function get_end_date( $context = 'view' ) {
557
		return $this->get_expiration( $context );
558
	}
559
560
	/**
561
	 * Get the discount's type.
562
	 *
563
	 * @since 1.0.19
564
	 * @param  string $context View or edit context.
565
	 * @return string
566
	 */
567
	public function get_type( $context = 'view' ) {
568
		return $this->get_prop( 'type', $context );
569
	}
570
571
	/**
572
	 * Get the number of times a discount has been used.
573
	 *
574
	 * @since 1.0.19
575
	 * @param  string $context View or edit context.
576
	 * @return int
577
	 */
578
	public function get_uses( $context = 'view' ) {
579
		return (int) $this->get_prop( 'uses', $context );
580
	}
581
582
	/**
583
	 * Get the discount's usage, i.e uses / max uses.
584
	 *
585
	 * @since 1.0.19
586
	 * @return string
587
	 */
588
	public function get_usage() {
589
590
		if ( ! $this->has_limit() ) {
591
			return $this->get_uses() . ' / ' . ' &infin;';
592
		}
593
594
		return $this->get_uses() . ' / ' . (int) $this->get_max_uses();
595
596
	}
597
598
	/**
599
	 * Get the maximum number of time a discount can be used.
600
	 *
601
	 * @since 1.0.19
602
	 * @param  string $context View or edit context.
603
	 * @return int
604
	 */
605
	public function get_max_uses( $context = 'view' ) {
606
		$max_uses = $this->get_prop( 'max_uses', $context );
607
		return empty( $max_uses ) ? null : $max_uses;
608
	}
609
610
	/**
611
	 * Checks if this is a single use discount or not.
612
	 *
613
	 * @since 1.0.19
614
	 * @param  string $context View or edit context.
615
	 * @return bool
616
	 */
617
	public function get_is_single_use( $context = 'view' ) {
618
		return $this->get_prop( 'is_single_use', $context );
619
	}
620
621
	/**
622
	 * Get the items that can be used with this discount.
623
	 *
624
	 * @since 1.0.19
625
	 * @param  string $context View or edit context.
626
	 * @return array
627
	 */
628
	public function get_items( $context = 'view' ) {
629
		return array_filter( wp_parse_id_list( $this->get_prop( 'items', $context ) ) );
630
	}
631
632
	/**
633
	 * Alias for self::get_items().
634
	 *
635
	 * @since 1.0.19
636
	 * @param  string $context View or edit context.
637
	 * @return array
638
	 */
639
	public function get_allowed_items( $context = 'view' ) {
640
		return $this->get_items( $context );
641
	}
642
643
	/**
644
	 * Get the items that are not allowed to use this discount.
645
	 *
646
	 * @since 1.0.19
647
	 * @param  string $context View or edit context.
648
	 * @return array
649
	 */
650
	public function get_excluded_items( $context = 'view' ) {
651
		return array_filter( wp_parse_id_list( $this->get_prop( 'excluded_items', $context ) ) );
652
	}
653
654
	/**
655
	 * Get the items that are required to be in the cart before using this discount.
656
	 *
657
	 * @since 1.0.19
658
	 * @param  string $context View or edit context.
659
	 * @return array
660
	 */
661
	public function get_required_items( $context = 'view' ) {
662
		return array_filter( wp_parse_id_list( $this->get_prop( 'required_items', $context ) ) );
663
	}
664
665
	/**
666
	 * Checks if this is a recurring discount or not.
667
	 *
668
	 * @since 1.0.19
669
	 * @param  string $context View or edit context.
670
	 * @return int|string|bool
671
	 */
672
	public function get_is_recurring( $context = 'view' ) {
673
		return $this->get_prop( 'is_recurring', $context );
674
	}
675
676
	/**
677
	 * Get's the minimum total amount allowed for this discount.
678
	 *
679
	 * @since 1.0.19
680
	 * @param  string $context View or edit context.
681
	 * @return float
682
	 */
683
	public function get_min_total( $context = 'view' ) {
684
		$minimum = $this->get_prop( 'min_total', $context );
685
		return empty( $minimum ) ? null : $minimum;
686
	}
687
688
	/**
689
	 * Alias for self::get_min_total().
690
	 *
691
	 * @since 1.0.19
692
	 * @param  string $context View or edit context.
693
	 * @return float
694
	 */
695
	public function get_minimum_total( $context = 'view' ) {
696
		return $this->get_min_total( $context );
697
	}
698
699
	/**
700
	 * Get's the maximum total amount allowed for this discount.
701
	 *
702
	 * @since 1.0.19
703
	 * @param  string $context View or edit context.
704
	 * @return float
705
	 */
706
	public function get_max_total( $context = 'view' ) {
707
		$maximum = $this->get_prop( 'max_total', $context );
708
		return empty( $maximum ) ? null : $maximum;
709
	}
710
711
	/**
712
	 * Alias for self::get_max_total().
713
	 *
714
	 * @since 1.0.19
715
	 * @param  string $context View or edit context.
716
	 * @return float
717
	 */
718
	public function get_maximum_total( $context = 'view' ) {
719
		return $this->get_max_total( $context );
720
	}
721
722
	/**
723
	 * Magic method for accessing discount properties.
724
	 *
725
	 * @since 1.0.15
726
	 * @access public
727
	 *
728
	 * @param string $key Discount data to retrieve
729
	 * @param  string $context View or edit context.
730
	 * @return mixed Value of the given discount property (if set).
731
	 */
732
	public function get( $key, $context = 'view' ) {
733
        return $this->get_prop( $key, $context );
734
	}
735
736
	/*
737
	|--------------------------------------------------------------------------
738
	| Setters
739
	|--------------------------------------------------------------------------
740
	|
741
	| Functions for setting discount data. These should not update anything in the
742
	| database itself and should only change what is stored in the class
743
	| object.
744
	*/
745
746
	/**
747
	 * Sets discount status.
748
	 *
749
	 * @since 1.0.19
750
	 * @param  string $status New status.
751
	 * @return array details of change.
752
	 */
753
	public function set_status( $status ) {
754
        $old_status = $this->get_status();
755
756
        $this->set_prop( 'status', $status );
757
758
		return array(
759
			'from' => $old_status,
760
			'to'   => $status,
761
		);
762
    }
763
764
    /**
765
	 * Set plugin version when the discount was created.
766
	 *
767
	 * @since 1.0.19
768
	 */
769
	public function set_version( $value ) {
770
		$this->set_prop( 'version', $value );
771
    }
772
773
    /**
774
	 * Set date when the discount was created.
775
	 *
776
	 * @since 1.0.19
777
	 * @param string $value Value to set.
778
     * @return bool Whether or not the date was set.
779
	 */
780
	public function set_date_created( $value ) {
781
        $date = strtotime( $value );
782
783
        if ( $date ) {
784
            $this->set_prop( 'date_created', date( 'Y-m-d H:i:s', $date ) );
785
            return true;
786
        }
787
788
        return false;
789
790
    }
791
792
    /**
793
	 * Set date when the discount was last modified.
794
	 *
795
	 * @since 1.0.19
796
	 * @param string $value Value to set.
797
     * @return bool Whether or not the date was set.
798
	 */
799
	public function set_date_modified( $value ) {
800
        $date = strtotime( $value );
801
802
        if ( $date ) {
803
            $this->set_prop( 'date_modified', date( 'Y-m-d H:i:s', $date ) );
804
            return true;
805
        }
806
807
        return false;
808
809
    }
810
811
    /**
812
	 * Set the discount name.
813
	 *
814
	 * @since 1.0.19
815
	 * @param  string $value New name.
816
	 */
817
	public function set_name( $value ) {
818
        $name = sanitize_text_field( $value );
819
		$this->set_prop( 'name', $name );
820
    }
821
822
    /**
823
	 * Alias of self::set_name().
824
	 *
825
	 * @since 1.0.19
826
	 * @param  string $value New name.
827
	 */
828
	public function set_title( $value ) {
829
		$this->set_name( $value );
830
    }
831
832
    /**
833
	 * Set the discount description.
834
	 *
835
	 * @since 1.0.19
836
	 * @param  string $value New description.
837
	 */
838
	public function set_description( $value ) {
839
        $description = wp_kses_post( $value );
840
		return $this->set_prop( 'description', $description );
0 ignored issues
show
Are you sure the usage of $this->set_prop('description', $description) targeting GetPaid_Data::set_prop() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
841
    }
842
843
    /**
844
	 * Alias of self::set_description().
845
	 *
846
	 * @since 1.0.19
847
	 * @param  string $value New description.
848
	 */
849
	public function set_excerpt( $value ) {
850
		$this->set_description( $value );
851
    }
852
853
    /**
854
	 * Alias of self::set_description().
855
	 *
856
	 * @since 1.0.19
857
	 * @param  string $value New description.
858
	 */
859
	public function set_summary( $value ) {
860
		$this->set_description( $value );
861
    }
862
863
    /**
864
	 * Set the owner of the discount.
865
	 *
866
	 * @since 1.0.19
867
	 * @param  int $value New author.
868
	 */
869
	public function set_author( $value ) {
870
		$this->set_prop( 'author', (int) $value );
871
	}
872
873
	/**
874
	 * Sets the discount code.
875
	 *
876
	 * @since 1.0.19
877
	 * @param string $value New discount code.
878
	 */
879
	public function set_code( $value ) {
880
		$code = sanitize_text_field( $value );
881
		$this->set_prop( 'code', $code );
882
	}
883
884
	/**
885
	 * Alias of self::set_code().
886
	 *
887
	 * @since 1.0.19
888
	 * @param string $value New discount code.
889
	 */
890
	public function set_coupon_code( $value ) {
891
		$this->set_code( $value );
892
	}
893
894
	/**
895
	 * Alias of self::set_code().
896
	 *
897
	 * @since 1.0.19
898
	 * @param string $value New discount code.
899
	 */
900
	public function set_discount_code( $value ) {
901
		$this->set_code( $value );
902
	}
903
904
	/**
905
	 * Sets the discount amount.
906
	 *
907
	 * @since 1.0.19
908
	 * @param float $value New discount code.
909
	 */
910
	public function set_amount( $value ) {
911
		$amount = floatval( wpinv_sanitize_amount( $value ) );
912
		$this->set_prop( 'amount', $amount );
913
	}
914
915
	/**
916
	 * Sets the discount's start date.
917
	 *
918
	 * @since 1.0.19
919
	 * @param float $value New start date.
920
	 */
921
	public function set_start( $value ) {
922
		$date = strtotime( $value );
923
924
        if ( $date ) {
925
            $this->set_prop( 'start', date( 'Y-m-d H:i', $date ) );
926
            return true;
927
		}
928
929
		$this->set_prop( 'start', '' );
930
931
        return false;
932
	}
933
934
	/**
935
	 * Alias of self::set_start().
936
	 *
937
	 * @since 1.0.19
938
	 * @param string $value New start date.
939
	 */
940
	public function set_start_date( $value ) {
941
		$this->set_start( $value );
0 ignored issues
show
$value of type string is incompatible with the type double expected by parameter $value of WPInv_Discount::set_start(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

941
		$this->set_start( /** @scrutinizer ignore-type */ $value );
Loading history...
942
	}
943
944
	/**
945
	 * Sets the discount's expiration date.
946
	 *
947
	 * @since 1.0.19
948
	 * @param float $value New expiration date.
949
	 */
950
	public function set_expiration( $value ) {
951
		$date = strtotime( $value );
952
953
        if ( $date ) {
954
            $this->set_prop( 'expiration', date( 'Y-m-d H:i', $date ) );
955
            return true;
956
        }
957
958
		$this->set_prop( 'expiration', '' );
959
        return false;
960
	}
961
962
	/**
963
	 * Alias of self::set_expiration().
964
	 *
965
	 * @since 1.0.19
966
	 * @param string $value New expiration date.
967
	 */
968
	public function set_expiration_date( $value ) {
969
		$this->set_expiration( $value );
0 ignored issues
show
$value of type string is incompatible with the type double expected by parameter $value of WPInv_Discount::set_expiration(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

969
		$this->set_expiration( /** @scrutinizer ignore-type */ $value );
Loading history...
970
	}
971
972
	/**
973
	 * Alias of self::set_expiration().
974
	 *
975
	 * @since 1.0.19
976
	 * @param string $value New expiration date.
977
	 */
978
	public function set_end_date( $value ) {
979
		$this->set_expiration( $value );
0 ignored issues
show
$value of type string is incompatible with the type double expected by parameter $value of WPInv_Discount::set_expiration(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

979
		$this->set_expiration( /** @scrutinizer ignore-type */ $value );
Loading history...
980
	}
981
982
	/**
983
	 * Sets the discount type.
984
	 *
985
	 * @since 1.0.19
986
	 * @param string $value New discount type.
987
	 */
988
	public function set_type( $value ) {
989
		if ( $value && array_key_exists( sanitize_text_field( $value ), wpinv_get_discount_types() ) ) {
990
			$this->set_prop( 'type', sanitize_text_field( $value ) );
991
		}
992
	}
993
994
	/**
995
	 * Sets the number of times a discount has been used.
996
	 *
997
	 * @since 1.0.19
998
	 * @param int $value usage count.
999
	 */
1000
	public function set_uses( $value ) {
1001
1002
		$value = (int) $value;
1003
1004
		if ( $value < 0 ) {
1005
			$value = 0;
1006
		}
1007
1008
		$this->set_prop( 'uses', (int) $value );
1009
	}
1010
1011
	/**
1012
	 * Sets the maximum number of times a discount can be used.
1013
	 *
1014
	 * @since 1.0.19
1015
	 * @param int $value maximum usage count.
1016
	 */
1017
	public function set_max_uses( $value ) {
1018
		$this->set_prop( 'max_uses', absint( $value ) );
1019
	}
1020
1021
	/**
1022
	 * Sets if this is a single use discount or not.
1023
	 *
1024
	 * @since 1.0.19
1025
	 * @param int|bool $value is single use.
1026
	 */
1027
	public function set_is_single_use( $value ) {
1028
		$this->set_prop( 'is_single_use', (bool) $value );
1029
	}
1030
1031
	/**
1032
	 * Sets the items that can be used with this discount.
1033
	 *
1034
	 * @since 1.0.19
1035
	 * @param array $value items.
1036
	 */
1037
	public function set_items( $value ) {
1038
		$this->set_prop( 'items', array_filter( wp_parse_id_list( $value ) ) );
1039
	}
1040
1041
	/**
1042
	 * Alias for self::set_items().
1043
	 *
1044
	 * @since 1.0.19
1045
	 * @param array $value items.
1046
	 */
1047
	public function set_allowed_items( $value ) {
1048
		$this->set_items( $value );
1049
	}
1050
1051
	/**
1052
	 * Sets the items that can not be used with this discount.
1053
	 *
1054
	 * @since 1.0.19
1055
	 * @param array $value items.
1056
	 */
1057
	public function set_excluded_items( $value ) {
1058
		$this->set_prop( 'excluded_items', array_filter( wp_parse_id_list( $value ) ) );
1059
	}
1060
1061
	/**
1062
	 * Sets the items that are required to be in the cart before using this discount.
1063
	 *
1064
	 * @since 1.0.19
1065
	 * @param array $value items.
1066
	 */
1067
	public function set_required_items( $value ) {
1068
		$this->set_prop( 'required_items', array_filter( wp_parse_id_list( $value ) ) );
1069
	}
1070
1071
	/**
1072
	 * Sets if this is a recurring discounts or not.
1073
	 *
1074
	 * @since 1.0.19
1075
	 * @param int|bool $value is recurring.
1076
	 */
1077
	public function set_is_recurring( $value ) {
1078
		$this->set_prop( 'is_recurring', (bool) $value );
1079
	}
1080
1081
	/**
1082
	 * Sets the minimum total that can not be used with this discount.
1083
	 *
1084
	 * @since 1.0.19
1085
	 * @param float $value minimum total.
1086
	 */
1087
	public function set_min_total( $value ) {
1088
		$this->set_prop( 'min_total', (float) wpinv_sanitize_amount( $value ) );
1089
	}
1090
1091
	/**
1092
	 * Alias for self::set_min_total().
1093
	 *
1094
	 * @since 1.0.19
1095
	 * @param float $value minimum total.
1096
	 */
1097
	public function set_minimum_total( $value ) {
1098
		$this->set_min_total( $value );
1099
	}
1100
1101
	/**
1102
	 * Sets the maximum total that can not be used with this discount.
1103
	 *
1104
	 * @since 1.0.19
1105
	 * @param float $value maximum total.
1106
	 */
1107
	public function set_max_total( $value ) {
1108
		$this->set_prop( 'max_total', (float) wpinv_sanitize_amount( $value ) );
1109
	}
1110
1111
	/**
1112
	 * Alias for self::set_max_total().
1113
	 *
1114
	 * @since 1.0.19
1115
	 * @param float $value maximum total.
1116
	 */
1117
	public function set_maximum_total( $value ) {
1118
		$this->set_max_total( $value );
1119
	}
1120
1121
	/**
1122
	 * @deprecated
1123
	 */
1124
	public function refresh(){}
1125
1126
	/**
1127
	 * @deprecated
1128
	 *
1129
	 */
1130
	public function update_status( $status = 'publish' ) {
1131
1132
		if ( $this->exists() && $this->get_status() != $status ) {
1133
			$this->set_status( $status );
1134
			$this->save();
1135
		}
1136
1137
	}
1138
1139
	/*
1140
	|--------------------------------------------------------------------------
1141
	| Conditionals
1142
	|--------------------------------------------------------------------------
1143
	|
1144
	| Checks if a condition is true or false.
1145
	|
1146
	*/
1147
1148
	/**
1149
	 * Checks whether a discount exists in the database or not
1150
	 *
1151
	 * @since 1.0.15
1152
	 */
1153
	public function exists() {
1154
		$id = $this->get_id();
1155
		return ! empty( $id );
1156
	}
1157
1158
	/**
1159
	 * Checks the discount type.
1160
	 *
1161
	 *
1162
	 * @param  string $type the discount type to check against
1163
	 * @since 1.0.15
1164
	 * @return bool
1165
	 */
1166
	public function is_type( $type ) {
1167
		return $this->get_type() == $type;
1168
	}
1169
1170
	/**
1171
	 * Checks whether the discount is published or not
1172
	 *
1173
	 * @since 1.0.15
1174
	 * @return bool
1175
	 */
1176
	public function is_active() {
1177
		return $this->get_status() == 'publish';
1178
	}
1179
1180
	/**
1181
	 * Checks whether the discount has max uses
1182
	 *
1183
	 * @since 1.0.15
1184
	 * @return bool
1185
	 */
1186
	public function has_limit() {
1187
		$limit = $this->get_max_uses();
1188
		return ! empty( $limit );
1189
	}
1190
1191
	/**
1192
	 * Checks whether the discount has ever been used.
1193
	 *
1194
	 * @since 1.0.15
1195
	 * @return bool
1196
	 */
1197
	public function has_uses() {
1198
		return $this->get_uses() > 0;
1199
	}
1200
1201
	/**
1202
	 * Checks whether the discount is has exided the usage limit or not
1203
	 *
1204
	 * @since 1.0.15
1205
	 * @return bool
1206
	 */
1207
	public function has_exceeded_limit() {
1208
1209
		if ( ! $this->has_limit() || ! $this->has_uses() ) {
1210
			$exceeded = false;
1211
		} else {
1212
			$exceeded = (int) $this->get_max_uses() <= $this->get_uses();
1213
		}
1214
1215
		return apply_filters( 'wpinv_is_discount_maxed_out', $exceeded, $this->get_id(), $this, $this->get_code() );
1216
	}
1217
1218
	/**
1219
	 * Checks whether the discount has an expiration date.
1220
	 *
1221
	 * @since 1.0.15
1222
	 * @return bool
1223
	 */
1224
	public function has_expiration_date() {
1225
		$date = $this->get_expiration_date();
1226
		return ! empty( $date );
1227
	}
1228
1229
	/**
1230
	 * Checks if the discount is expired
1231
	 *
1232
	 * @since 1.0.15
1233
	 * @return bool
1234
	 */
1235
	public function is_expired() {
1236
		$expired = $this->has_expiration_date() ? current_time( 'timestamp' ) > strtotime( $this->get_expiration_date() ) : false;
1237
		return apply_filters( 'wpinv_is_discount_expired', $expired, $this->get_id(), $this, $this->get_code() );
1238
	}
1239
1240
	/**
1241
	 * Checks whether the discount has a start date.
1242
	 *
1243
	 * @since 1.0.15
1244
	 * @return bool
1245
	 */
1246
	public function has_start_date() {
1247
		$date = $this->get_start_date();
1248
		return ! empty( $date );
1249
	}
1250
1251
	/**
1252
	 * Checks the discount start date.
1253
	 *
1254
	 * @since 1.0.15
1255
	 * @return bool
1256
	 */
1257
	public function has_started() {
1258
		$started = $this->has_start_date() ? true : current_time( 'timestamp' ) > strtotime( $this->get_start_date() );
1259
		return apply_filters( 'wpinv_is_discount_started', $started, $this->get_id(), $this, $this->get_code() );
1260
	}
1261
1262
	/**
1263
	 * Checks the discount has allowed items or not.
1264
	 *
1265
	 * @since 1.0.15
1266
	 * @return bool
1267
	 */
1268
	public function has_allowed_items() {
1269
		$allowed_items = $this->get_allowed_items();
1270
		return ! empty( $allowed_items );
1271
	}
1272
1273
	/**
1274
	 * Checks the discount has excluded items or not.
1275
	 *
1276
	 * @since 1.0.15
1277
	 * @return bool
1278
	 */
1279
	public function has_excluded_items() {
1280
		$excluded_items = $this->get_excluded_items();
1281
		return ! empty( $excluded_items );
1282
	}
1283
1284
	/**
1285
	 * Check if a discount is valid for a given item id.
1286
	 *
1287
	 * @param  int|int[]  $item_ids
1288
	 * @since 1.0.15
1289
	 * @return boolean
1290
	 */
1291
	public function is_valid_for_items( $item_ids ) {
1292
1293
		$item_ids = array_filter( wp_parse_id_list( $item_ids ) );
1294
		$included = array_intersect( $item_ids, $this->get_allowed_items() );
1295
		$excluded = array_intersect( $item_ids, $this->get_excluded_items() );
1296
1297
		if ( $this->has_excluded_items() && ! empty( $excluded ) ) {
1298
			return false;
1299
		}
1300
1301
		if ( $this->has_allowed_items() && empty( $included ) ) {
1302
			return false;
1303
		}
1304
1305
		return true;
1306
	}
1307
1308
	/**
1309
	 * Checks the discount has required items or not.
1310
	 *
1311
	 * @since 1.0.15
1312
	 * @return bool
1313
	 */
1314
	public function has_required_items() {
1315
		$required_items = $this->get_required_items();
1316
		return ! empty( $required_items );
1317
	}
1318
1319
	/**
1320
	 * Checks if the required items are met
1321
	 *
1322
	 * @param  int|int[]  $item_ids
1323
	 * @since 1.0.15
1324
	 * @return boolean
1325
	 */
1326
	public function is_required_items_met( $item_ids ) {
1327
1328
		if ( ! $this->has_required_items() ) {
1329
			return true;
1330
		}
1331
1332
		return ! array_diff( $this->get_required_items(), array_filter( wp_parse_id_list( $item_ids ) ) );
1333
	}
1334
1335
	/**
1336
	 * Check if a discount is valid for the given amount
1337
	 *
1338
	 * @param  float  $amount The amount to check against
1339
	 * @since 1.0.15
1340
	 * @return boolean
1341
	 */
1342
	public function is_valid_for_amount( $amount ) {
1343
		return $this->is_minimum_amount_met( $amount ) && $this->is_maximum_amount_met( $amount );
1344
	}
1345
1346
	/**
1347
	 * Checks if the minimum amount is set
1348
	 *
1349
	 * @since 1.0.15
1350
	 * @return boolean
1351
	 */
1352
	public function has_minimum_amount() {
1353
		$minimum = $this->get_minimum_total();
1354
		return ! empty( $minimum );
1355
	}
1356
1357
	/**
1358
	 * Checks if the minimum amount is met
1359
	 *
1360
	 * @param  float  $amount The amount to check against
1361
	 * @since 1.0.15
1362
	 * @return boolean
1363
	 */
1364
	public function is_minimum_amount_met( $amount ) {
1365
		$amount = floatval( wpinv_sanitize_amount( $amount ) );
1366
		$min_met = ! ( $this->has_minimum_amount() && $amount < floatval( wpinv_sanitize_amount( $this->get_minimum_total() ) ) );
1367
		return apply_filters( 'wpinv_is_discount_min_met', $min_met, $this->get_id(), $this, $this->get_code(), $amount );
1368
	}
1369
1370
	/**
1371
	 * Checks if the maximum amount is set
1372
	 *
1373
	 * @since 1.0.15
1374
	 * @return boolean
1375
	 */
1376
	public function has_maximum_amount() {
1377
		$maximum = $this->get_maximum_total();
1378
		return ! empty( $maximum );
1379
	}
1380
1381
	/**
1382
	 * Checks if the maximum amount is met
1383
	 *
1384
	 * @param  float  $amount The amount to check against
1385
	 * @since 1.0.15
1386
	 * @return boolean
1387
	 */
1388
	public function is_maximum_amount_met( $amount ) {
1389
		$amount = floatval( wpinv_sanitize_amount( $amount ) );
1390
		$max_met = ! ( $this->has_maximum_amount() && $amount > floatval( wpinv_sanitize_amount( $this->get_maximum_total() ) ) );
1391
		return apply_filters( 'wpinv_is_discount_max_met', $max_met, $this->get_id(), $this, $this->get_code(), $amount );
1392
	}
1393
1394
	/**
1395
	 * Checks if the discount is recurring.
1396
	 *
1397
	 * @since 1.0.15
1398
	 * @return boolean
1399
	 */
1400
	public function is_recurring() {
1401
		$recurring = $this->get_is_recurring();
1402
		return ! empty( $recurring );
1403
	}
1404
1405
	/**
1406
	 * Checks if the discount is single use.
1407
	 *
1408
	 * @since 1.0.15
1409
	 * @return boolean
1410
	 */
1411
	public function is_single_use() {
1412
		$usage = $this->get_is_single_use();
1413
		return ! empty( $usage );
1414
	}
1415
1416
	/**
1417
	 * Check if a discount is valid for the given user
1418
	 *
1419
	 * @param  int|string  $user
1420
	 * @since 1.0.15
1421
	 * @return boolean
1422
	 */
1423
	public function is_valid_for_user( $user ) {
1424
1425
		// Ensure that the discount is single use.
1426
		if ( empty( $user ) || ! $this->is_single_use() ) {
1427
			return true;
1428
		}
1429
1430
		// Prepare the user id.
1431
		$user_id = 0;
1432
        if ( is_numeric( $user ) ) {
1433
            $user_id = absint( $user );
1434
        } elseif ( is_email( $user ) && $user_data = get_user_by( 'email', $user ) ) {
1435
            $user_id = $user_data->ID;
1436
        } elseif ( $user_data = get_user_by( 'login', $user ) ) {
1437
            $user_id = $user_data->ID;
1438
        }
1439
1440
		// Ensure that we have a user.
1441
		if ( empty( $user_id ) ) {
1442
			return true;
1443
		}
1444
1445
		// Get all payments with matching user id.
1446
        $payments = wpinv_get_invoices(
1447
            array(
1448
				'user'     => $user_id,
1449
				'limit'    => false,
1450
				'paginate' => false,
1451
            )
1452
        );
1453
		$code     = strtolower( $this->get_code() );
1454
1455
		// For each payment...
1456
		foreach ( $payments as $payment ) {
1457
1458
			// Only check for paid invoices.
1459
			if ( $payment->is_paid() && strtolower( $payment->get_discount_code() ) == $code ) {
1460
				return false;
1461
			}
1462
}
1463
1464
		return true;
1465
	}
1466
1467
	/**
1468
	 * Deletes the discount from the database
1469
	 *
1470
	 * @since 1.0.15
1471
	 * @return boolean
1472
	 */
1473
	public function remove() {
1474
		return $this->delete();
1475
	}
1476
1477
	/**
1478
	 * Increases a discount's usage.
1479
	 *
1480
	 * @since 1.0.15
1481
	 * @param int $by The number of usages to increas by.
1482
	 * @return int
1483
	 */
1484
	public function increase_usage( $by = 1 ) {
1485
1486
		// Abort if zero.
1487
		if ( empty( $by ) ) {
1488
			return;
1489
		}
1490
1491
		// Increase the usage.
1492
		$this->set_uses( $this->get_uses() + (int) $by );
1493
1494
		// Save the discount.
1495
		$this->save();
1496
1497
		// Fire relevant hooks.
1498
		if ( (int) $by > 0 ) {
1499
			do_action( 'wpinv_discount_increase_use_count', $this->get_uses(), $this->get_id(), $this->get_code(), absint( $by ) );
1500
		} else {
1501
			do_action( 'wpinv_discount_decrease_use_count', $this->get_uses(), $this->get_id(), $this->get_code(), absint( $by ) );
1502
		}
1503
1504
		// Return the number of times the discount has been used.
1505
		return $this->get_uses();
1506
	}
1507
1508
	/**
1509
	 * Alias of self::__toString()
1510
	 *
1511
	 * @since 1.0.15
1512
	 * @return string|false
1513
	 */
1514
	public function get_data_as_json() {
1515
		return $this->__toString();
1516
	}
1517
1518
	/**
1519
	 * Returns a discount's discounted amount.
1520
	 *
1521
	 * @since 1.0.15
1522
	 * @param float $amount
1523
	 * @return float
1524
	 */
1525
	public function get_discounted_amount( $amount ) {
1526
1527
		// Convert amount to float.
1528
		$amount = (float) $amount;
1529
1530
		// Get discount amount.
1531
		$discount_amount = $this->get_amount();
1532
1533
		if ( empty( $discount_amount ) ) {
1534
			return 0;
1535
		}
1536
1537
		// Format the amount.
1538
		$discount_amount = floatval( wpinv_sanitize_amount( $discount_amount ) );
1539
1540
		// If this is a percentage discount.
1541
		if ( $this->is_type( 'percent' ) ) {
1542
            $discount_amount = $amount * ( $discount_amount / 100 );
1543
		}
1544
1545
		// Discount can not be less than zero...
1546
		if ( $discount_amount < 0 ) {
1547
			$discount_amount = 0;
1548
		}
1549
1550
		// ... or more than the amount.
1551
		if ( $discount_amount > $amount ) {
1552
			$discount_amount = $amount;
1553
		}
1554
1555
		return apply_filters( 'wpinv_discount_total_discount_amount', $discount_amount, $amount, $this );
1556
	}
1557
1558
}
1559