Passed
Pull Request — master (#390)
by Brian
04:21
created

WPInv_Subscription::get_child_payments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 11
rs 10
1
<?php
2
3
// Exit if accessed directly
4
if ( ! defined( 'ABSPATH' ) ) {
5
	exit;
6
}
7
8
9
/**
10
 * The Subscription Class
11
 *
12
 * @since  1.0.0
13
 */
14
class WPInv_Subscription {
15
16
	private $subs_db;
17
18
	public $id                = 0;
19
	public $customer_id       = 0;
20
	public $period            = '';
21
	public $initial_amount    = '';
22
	public $recurring_amount  = '';
23
	public $bill_times        = 0;
24
	public $transaction_id    = '';
25
	public $parent_payment_id = 0;
26
	public $product_id        = 0;
27
	public $created           = '0000-00-00 00:00:00';
28
	public $expiration        = '0000-00-00 00:00:00';
29
	public $trial_period      = '';
30
	public $status            = 'pending';
31
	public $profile_id        = '';
32
	public $gateway           = '';
33
	public $customer;
34
35
	/**
36
	 * Get us started
37
	 *
38
	 * @since  1.0.0
39
	 * @return void
40
	 */
41
	function __construct( $_id_or_object = 0, $_by_profile_id = false ) {
42
43
		$this->subs_db = new WPInv_Subscriptions_DB;
44
45
		if( $_by_profile_id ) {
46
47
			$_sub = $this->subs_db->get_by( 'profile_id', $_id_or_object );
48
49
			if( empty( $_sub ) ) {
50
				return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
51
			}
52
53
			$_id_or_object = $_sub;
54
55
		}
56
57
		return $this->setup_subscription( $_id_or_object );
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setup_subscription($_id_or_object) targeting WPInv_Subscription::setup_subscription() 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...
58
	}
59
60
	/**
61
	 * Setup the subscription object
62
	 *
63
	 * @since  1.0.0
64
	 * @return void
65
	 */
66
	private function setup_subscription( $id_or_object = 0 ) {
67
68
		if( empty( $id_or_object ) ) {
69
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
70
		}
71
72
		if( is_numeric( $id_or_object ) ) {
73
74
			$sub = $this->subs_db->get( $id_or_object );
75
76
		} elseif( is_object( $id_or_object ) ) {
77
78
			$sub = $id_or_object;
79
80
		}
81
82
		if( empty( $sub ) ) {
83
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
84
		}
85
86
		foreach( $sub as $key => $value ) {
87
			$this->$key = $value;
88
		}
89
90
		$this->customer = get_userdata( $this->customer_id );
91
		$this->gateway  = wpinv_get_payment_gateway( $this->parent_payment_id );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_payment_gateway() has been deprecated. ( Ignorable by Annotation )

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

91
		$this->gateway  = /** @scrutinizer ignore-deprecated */ wpinv_get_payment_gateway( $this->parent_payment_id );
Loading history...
92
93
		do_action( 'wpinv_recurring_setup_subscription', $this );
94
95
		return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type WPInv_Subscription which is incompatible with the documented return type void.
Loading history...
96
	}
97
98
	/**
99
	 * Magic __get function to dispatch a call to retrieve a private property
100
	 *
101
	 * @since 1.0.0
102
	 */
103
	public function __get( $key ) {
104
105
		if( method_exists( $this, 'get_' . $key ) ) {
106
107
			return call_user_func( array( $this, 'get_' . $key ) );
108
109
		} else {
110
111
			return new WP_Error( 'wpinv-subscription-invalid-property', sprintf( __( 'Can\'t get property %s', 'invoicing' ), $key ) );
112
113
		}
114
115
	}
116
117
	/**
118
	 * Creates a subscription
119
	 *
120
	 * @since  1.0.0
121
	 * @param  array  $data Array of attributes for a subscription
122
	 * @return mixed  false if data isn't passed and class not instantiated for creation
123
	 */
124
	public function create( $data = array() ) {
125
126
		if ( $this->id != 0 ) {
127
			return false;
128
		}
129
130
		$defaults = array(
131
			'customer_id'       => 0,
132
			'frequency'         => '',
133
			'period'            => '',
134
			'initial_amount'    => '',
135
			'recurring_amount'  => '',
136
			'bill_times'        => 0,
137
			'parent_payment_id' => 0,
138
			'product_id'        => 0,
139
			'created'           => '',
140
			'expiration'        => '',
141
			'status'            => '',
142
			'profile_id'        => '',
143
		);
144
145
		$args = wp_parse_args( $data, $defaults );
0 ignored issues
show
Security Variable Injection introduced by
$data can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and Data is passed through wpinv_clean()
    in includes/gateways/class-getpaid-authorize-net-gateway.php on line 924
  2. array('transaction_id' => wpinv_clean($_POST['x_trans_id']), 'gateway' => $this->id) is assigned to $args
    in includes/gateways/class-getpaid-authorize-net-gateway.php on line 923
  3. WPInv_Subscription::add_payment() is called
    in includes/gateways/class-getpaid-authorize-net-gateway.php on line 928
  4. Enters via parameter $args
    in includes/wpinv-subscription.php on line 268
  5. WPInv_Invoice::set_gateway() is called
    in includes/wpinv-subscription.php on line 297
  6. Enters via parameter $value
    in includes/class-wpinv-invoice.php on line 2807
  7. GetPaid_Data::set_prop() is called
    in includes/class-wpinv-invoice.php on line 2808
  8. Enters via parameter $value
    in includes/data-stores/class-getpaid-data.php on line 793
  9. Data is passed through maybe_unserialize(), and maybe_unserialize($value) is assigned to property WPInv_Invoice::$changes
    in includes/data-stores/class-getpaid-data.php on line 797
  10. Read from property WPInv_Invoice::$changes, and array_key_exists($prop, $this->changes) ? $this->changes[$prop] : $this->data[$prop] is assigned to $value
    in includes/data-stores/class-getpaid-data.php on line 850
  11. $value is returned
    in includes/data-stores/class-getpaid-data.php on line 857
  12. $this->get_prop('transaction_id', $context) is returned
    in includes/class-wpinv-invoice.php on line 1584
  13. $invoice->get_transaction_id() is assigned to $transaction_id
    in includes/admin/wpinv-upgrade-functions.php on line 111
  14. array('product_id' => $item->ID, 'customer_id' => $invoice->user_id, 'parent_payment_id' => $invoice->ID, 'status' => $status, 'frequency' => $interval, 'period' => $period, 'initial_amount' => $invoice->get_total(), 'recurring_amount' => $invoice->get_recurring_details('total'), 'bill_times' => $bill_times, 'created' => $invoice_date, 'expiration' => $expiration, 'trial_period' => $trial_period, 'profile_id' => $profile_id, 'transaction_id' => $transaction_id) is assigned to $args
    in includes/admin/wpinv-upgrade-functions.php on line 160
  15. WPInv_Subscription::create() is called
    in includes/admin/wpinv-upgrade-functions.php on line 183
  16. Enters via parameter $data
    in includes/wpinv-subscription.php on line 124

Used in variable context

  1. wp_parse_args() is called
    in includes/wpinv-subscription.php on line 145
  2. Enters via parameter $args
    in wordpress/wp-includes/functions.php on line 4413
  3. wp_parse_str() is called
    in wordpress/wp-includes/functions.php on line 4419
  4. Enters via parameter $string
    in wordpress/wp-includes/formatting.php on line 4938
  5. parse_str() is called
    in wordpress/wp-includes/formatting.php on line 4939

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
146
147
		if( $args['expiration'] && strtotime( 'NOW', current_time( 'timestamp' ) ) > strtotime( $args['expiration'], current_time( 'timestamp' ) ) ) {
148
149
			if( 'active' == $args['status'] || 'trialling' == $args['status'] ) {
150
151
				// Force an active subscription to expired if expiration date is in the past
152
				$args['status'] = 'expired';
153
154
			}
155
		}
156
157
		do_action( 'wpinv_subscription_pre_create', $args );
158
159
		$id = $this->subs_db->insert( $args, 'subscription' );
160
161
		do_action( 'wpinv_subscription_post_create', $id, $args );
162
163
		return $this->setup_subscription( $id );
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setup_subscription($id) targeting WPInv_Subscription::setup_subscription() 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...
164
165
	}
166
167
	/**
168
	 * Updates a subscription
169
	 *
170
	 * @since  1.0.0
171
	 * @param  array $args Array of fields to update
172
	 * @return bool
173
	 */
174
	public function update( $args = array() ) {
175
176
		$ret = $this->subs_db->update( $this->id, $args );
177
178
		do_action( 'wpinv_recurring_update_subscription', $this->id, $args, $this );
179
180
		if ( $ret && isset( $args['profile_id'] ) ) {
181
			update_post_meta( $this->parent_payment_id, 'subscription_id', $args['profile_id'] );
182
		}
183
184
		return $ret;
185
186
	}
187
188
	/**
189
	 * Delete the subscription
190
	 *
191
	 * @since  1.0.0
192
	 * @return bool
193
	 */
194
	public function delete() {
195
		return $this->subs_db->delete( $this->id );
196
	}
197
198
    /**
199
     * Retrieves the parent payment ID
200
     *
201
     * @since  1.0.0
202
     * @return int
203
     */
204
    public function get_original_payment_id() {
205
        return $this->parent_payment_id;
206
    }
207
208
    /**
209
     * Retrieve renewal payments for a subscription
210
     *
211
     * @since  1.0.0
212
     * @return array
213
     */
214
    public function get_child_payments() {
215
        $payments = get_posts( array(
216
            'post_parent'    => (int) $this->parent_payment_id,
217
            'posts_per_page' => '999',
218
            'post_status'    => array( 'publish', 'wpi-processing', 'wpi-renewal' ),
219
            'orderby'        => 'ID',
220
            'order'          => 'DESC',
221
            'post_type'      => 'wpi_invoice'
222
        ) );
223
224
        return $payments;
225
    }
226
227
    /**
228
     * Counts the number of payments made to the subscription
229
     *
230
     * @since  2.4
231
     * @return int
232
     */
233
    public function get_total_payments() {
234
        $child_payments = $this->get_child_payments();
235
        $total_payments = !empty( $child_payments ) ? count( $child_payments ) : 0;
236
237
        if ( 'pending' != $this->status ) {
238
                $total_payments++;
239
        }
240
241
        return $total_payments;
242
    }
243
244
    /**
245
     * Returns the number of times the subscription has been billed
246
     *
247
     * @since  1.0.2
248
     * @return int
249
     */
250
    public function get_times_billed() {
251
        $times_billed = (int)$this->get_total_payments();
252
253
        if ( ! empty( $this->trial_period ) && $times_billed > 0 ) {
254
            $times_billed--;
255
        }
256
257
        return $times_billed;
258
    }
259
260
    /**
261
     * Records a new payment on the subscription
262
     *
263
     * @since  2.4
264
     * @param  array $args Array of values for the payment, including amount and transaction ID
265
	 * @param  WPInv_Invoice $invoice
266
     * @return bool
267
     */
268
    public function add_payment( $args = array(), $invoice = false ) {
269
270
		// Process each payment once.
271
        if ( ! empty( $args['transaction_id'] ) && $this->payment_exists( $args['transaction_id'] ) ) {
272
            return false;
273
        }
274
275
		// Are we creating a new invoice?
276
		if ( empty( $invoice ) ) {
277
			$invoice = $this->create_payment();
278
279
			if ( empty( $invoice ) ) {
280
				return false;
281
			}
282
283
			$invoice->set_status( 'wpi-renewal' );
284
285
		}
286
287
		// Maybe set a transaction id.
288
		if ( ! empty( $args['transaction_id'] ) ) {
289
			$invoice->set_transaction_id( $args['transaction_id'] );
290
		}
291
292
		// Set the completed date.
293
		$invoice->set_completed_date( current_time( 'mysql' ) );
294
295
		// And the gateway.
296
		if ( ! empty( $args['gateway'] ) ) {
297
			$invoice->set_gateway( $args['gateway'] );
298
		}
299
300
		$invoice->save();
301
302
		if ( ! $invoice->get_id() ) {
303
			return 0;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 0 returns the type integer which is incompatible with the documented return type boolean.
Loading history...
304
		}
305
306
		do_action( 'getpaid_after_create_subscription_renewal_invoice', $invoice, $this );
307
		do_action( 'wpinv_recurring_add_subscription_payment', $invoice, $this );
308
        do_action( 'wpinv_recurring_record_payment', $invoice->get_id(), $this->parent_payment_id, $invoice->get_recurring_total(), $invoice->get_transaction_id() );
309
310
        update_post_meta( $invoice->get_id(), '_wpinv_subscription_id', $this->id );
311
312
        return $invoice->get_id();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $invoice->get_id() returns the type integer which is incompatible with the documented return type boolean.
Loading history...
313
	}
314
315
	/**
316
     * Creates a new invoice and returns it.
317
     *
318
     * @since  1.0.19
319
     * @param  array $args Array of values for the payment, including amount and transaction ID
320
     * @return WPInv_Invoice|bool
321
     */
322
    public function create_payment() {
323
324
		// Do we have a parent invoice?
325
        if ( ! $this->parent_payment_id ) {
326
            return false;
327
        }
328
329
		// Ensure that the parent invoice is available.
330
        $parent_invoice = wpinv_get_invoice( $this->parent_payment_id );
331
        if ( empty( $parent_invoice ) ) {
332
            return false;
333
        }
334
335
		// Duplicate the parent invoice.
336
		$invoice = new WPInv_Invoice();
337
		$invoice->set_props( $parent_invoice->get_data() );
338
		$invoice->set_id( 0 );
339
		$invoice->set_parent_id( $parent_invoice->get_parent() );
340
		$invoice->set_transaction_id( '' );
341
		$invoice->set_key( $invoice->generate_key( 'renewal_' ) );
342
		$invoice->set_number( '' );
343
		$invoice->set_completed_date( '' );
344
		$invoice->set_status( 'wpi-pending' );
345
		$invoice->recalculate_total();
346
		$invoice->save();
347
348
		return $invoice->get_id() ? $invoice : false;
349
    }
350
351
	/**
352
	 * Retrieves the transaction ID from the subscription
353
	 *
354
	 * @since  1.0.0
355
	 * @return bool
356
	 */
357
	public function get_transaction_id() {
358
359
		if( empty( $this->transaction_id ) ) {
360
361
			$txn_id = wpinv_get_payment_transaction_id( $this->parent_payment_id );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_payment_transaction_id() has been deprecated. ( Ignorable by Annotation )

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

361
			$txn_id = /** @scrutinizer ignore-deprecated */ wpinv_get_payment_transaction_id( $this->parent_payment_id );
Loading history...
362
363
			if( ! empty( $txn_id ) && (int) $this->parent_payment_id !== (int) $txn_id ) {
364
				$this->set_transaction_id( $txn_id );
365
			}
366
367
		}
368
369
		return $this->transaction_id;
370
371
	}
372
373
	/**
374
	 * Stores the transaction ID for the subscription purchase
375
	 *
376
	 * @since  1.0.0.4
377
	 * @return bool
378
	 */
379
	public function set_transaction_id( $txn_id = '' ) {
380
		$this->update( array( 'transaction_id' => $txn_id ) );
381
		$this->transaction_id = $txn_id;
382
	}
383
384
	/**
385
	 * Renews a subscription
386
	 *
387
	 * @since  1.0.0
388
	 * @return bool
389
	 */
390
	public function renew() {
391
392
		// Calculate new expiration
393
		$expires        = $this->get_expiration_time();
394
		$base_date      = $expires > current_time( 'timestamp' ) ? $expires : current_time( 'timestamp' );
395
		$frequency      = isset( $this->frequency ) ? $this->frequency : 1;
0 ignored issues
show
Bug Best Practice introduced by
The property frequency does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
396
		$new_expiration = strtotime( "+ {$frequency} {$this->period}", strtotime( $base_date ) );
397
		$new_expiration = apply_filters( 'wpinv_subscription_renewal_expiration', date( 'Y-m-d H:i:s', $new_expiration ), $this->id, $this );
398
399
		do_action( 'wpinv_subscription_pre_renew', $this->id, $new_expiration, $this );
400
401
		$this->status = 'active';
402
		$times_billed = $this->get_times_billed();
403
404
		// Complete subscription if applicable
405
		if ( $this->bill_times > 0 && $times_billed >= $this->bill_times ) {
406
			$this->complete();
407
			$this->status = 'completed';
408
			return;
409
		}
410
411
		$args = array(
412
			'expiration' => $new_expiration,
413
			'status'     => $this->status,
414
		);
415
416
        $this->subs_db->update( $this->id, $args );
417
418
		$this->expiration = $new_expiration;
419
420
		do_action( 'wpinv_subscription_post_renew', $this->id, $new_expiration, $this );
421
		do_action( 'wpinv_recurring_set_subscription_status', $this->id, $this->status, $this );
422
423
	}
424
425
	/**
426
	 * Marks a subscription as completed
427
	 *
428
	 * Subscription is completed when the number of payments matches the billing_times field
429
	 *
430
	 * @since  1.0.0
431
	 * @return void
432
	 */
433
	public function complete() {
434
435
		// Only mark a subscription as complete if it's not already cancelled.
436
		if ( 'cancelled' === $this->status ) {
437
			return;
438
		}
439
440
		$args = array(
441
			'status' => 'completed'
442
		);
443
444
		if( $this->subs_db->update( $this->id, $args ) ) {
445
446
			$this->status = 'completed';
447
448
			do_action( 'wpinv_subscription_completed', $this->id, $this );
449
450
		}
451
452
	}
453
454
	/**
455
	 * Marks a subscription as expired
456
	 *
457
	 * Subscription is completed when the billing times is reached
458
	 *
459
	 * @since  1.0.0
460
	 * @param  $check_expiration bool True if expiration date should be checked with merchant processor before expiring
461
	 * @return void
462
	 */
463
	public function expire( $check_expiration = false ) {
464
465
		$expiration = $this->expiration;
466
467
		if( $check_expiration ) {
468
469
			// check_expiration() updates $this->expiration so compare to $expiration above
470
471
			if( $expiration < $this->get_expiration() && current_time( 'timestamp' ) < $this->get_expiration_time() ) {
472
473
				return false; // Do not mark as expired since real expiration date is in the future
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
474
			}
475
476
		}
477
478
		$args = array(
479
			'status' => 'expired'
480
		);
481
482
		if( $this->subs_db->update( $this->id, $args ) ) {
483
484
			$this->status = 'expired';
485
486
			do_action( 'wpinv_subscription_expired', $this->id, $this );
487
488
		}
489
490
	}
491
492
	/**
493
	 * Marks a subscription as failing
494
	 *
495
	 * @since  2.4.2
496
	 * @return void
497
	 */
498
	public function failing() {
499
500
		$args = array(
501
			'status' => 'failing'
502
		);
503
504
		if( $this->subs_db->update( $this->id, $args ) ) {
505
506
			$this->status = 'failing';
507
508
			do_action( 'wpinv_subscription_failing', $this->id, $this );
509
			do_action( 'wpinv_recurring_payment_failed', $this );
510
511
		}
512
513
	}
514
515
    /**
516
     * Marks a subscription as cancelled
517
     *
518
     * @since  1.0.0
519
     * @return void
520
     */
521
    public function cancel() {
522
        if ( 'cancelled' === $this->status ) {
523
            return; // Already cancelled
524
        }
525
526
        $args = array(
527
            'status' => 'cancelled'
528
        );
529
530
        if ( $this->subs_db->update( $this->id, $args ) ) {
531
            if ( is_user_logged_in() ) {
532
                $userdata = get_userdata( get_current_user_id() );
533
                $user     = $userdata->display_name;
534
            } else {
535
                $user = __( 'gateway', 'invoicing' );
536
            }
537
538
            $note = sprintf( __( 'Subscription has been cancelled by %s', 'invoicing' ), $user );
539
            wpinv_insert_payment_note( $this->parent_payment_id, $note, '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

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

539
            /** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $this->parent_payment_id, $note, '', '', true );
Loading history...
540
541
            $this->status = 'cancelled';
542
543
            do_action( 'wpinv_subscription_cancelled', $this->id, $this );
544
        }
545
    }
546
547
	/**
548
	 * Determines if subscription can be cancelled
549
	 *
550
	 * This method is filtered by payment gateways in order to return true on subscriptions
551
	 * that can be cancelled with a profile ID through the merchant processor
552
	 *
553
	 * @since  1.0.0
554
	 * @return bool
555
	 */
556
	public function can_cancel() {
557
        $ret = false;
558
	    if( $this->gateway === 'manual' || in_array( $this->status, $this->get_cancellable_statuses() ) ) {
559
            $ret = true;
560
        }
561
		return apply_filters( 'wpinv_subscription_can_cancel', $ret, $this );
562
	}
563
564
    /**
565
     * Returns an array of subscription statuses that can be cancelled
566
     *
567
     * @access      public
568
     * @since       1.0.0
569
     * @return      array
570
     */
571
    public function get_cancellable_statuses() {
572
        return apply_filters( 'wpinv_recurring_cancellable_statuses', array( 'active', 'trialling', 'failing' ) );
573
    }
574
575
	/**
576
	 * Retrieves the URL to cancel subscription
577
	 *
578
	 * @since  1.0.0
579
	 * @return string
580
	 */
581
	public function get_cancel_url() {
582
583
		$url = wp_nonce_url( add_query_arg( array( 'wpinv_action' => 'cancel_subscription', 'sub_id' => $this->id ) ), 'wpinv-recurring-cancel' );
584
585
		return apply_filters( 'wpinv_subscription_cancel_url', $url, $this );
586
	}
587
588
	/**
589
	 * Determines if subscription can be manually renewed
590
	 *
591
	 * This method is filtered by payment gateways in order to return true on subscriptions
592
	 * that can be renewed manually
593
	 *
594
	 * @since  2.5
595
	 * @return bool
596
	 */
597
	public function can_renew() {
598
599
		return apply_filters( 'wpinv_subscription_can_renew', true, $this );
600
	}
601
602
	/**
603
	 * Retrieves the URL to renew a subscription
604
	 *
605
	 * @since  2.5
606
	 * @return string
607
	 */
608
	public function get_renew_url() {
609
610
		$url = wp_nonce_url( add_query_arg( array( 'wpinv_action' => 'renew_subscription', 'sub_id' => $this->id ) ), 'wpinv-recurring-renew' );
611
612
		return apply_filters( 'wpinv_subscription_renew_url', $url, $this );
613
	}
614
615
	/**
616
	 * Determines if subscription can have their payment method updated
617
	 *
618
	 * @since  1.0.0
619
	 * @return bool
620
	 */
621
	public function can_update() {
622
		return apply_filters( 'wpinv_subscription_can_update', false, $this );
623
	}
624
625
	/**
626
	 * Retrieves the URL to update subscription
627
	 *
628
	 * @since  1.0.0
629
	 * @return void
630
	 */
631
	public function get_update_url() {
632
633
		$url = add_query_arg( array( 'action' => 'update', 'subscription_id' => $this->id ) );
634
635
		return apply_filters( 'wpinv_subscription_update_url', $url, $this );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('wp...date_url', $url, $this) also could return the type string which is incompatible with the documented return type void.
Loading history...
636
	}
637
638
	/**
639
	 * Determines if subscription is active
640
	 *
641
	 * @since  1.0.0
642
	 * @return void
643
	 */
644
	public function is_active() {
645
646
		$ret = false;
647
648
		if( ! $this->is_expired() && ( $this->status == 'active' || $this->status == 'cancelled' || $this->status == 'trialling' ) ) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->is_expired() targeting WPInv_Subscription::is_expired() 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...
649
			$ret = true;
650
		}
651
652
		return apply_filters( 'wpinv_subscription_is_active', $ret, $this->id, $this );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('wp...$ret, $this->id, $this) also could return the type boolean which is incompatible with the documented return type void.
Loading history...
653
654
	}
655
656
	/**
657
	 * Determines if subscription is expired
658
	 *
659
	 * @since  1.0.0
660
	 * @return void
661
	 */
662
	public function is_expired() {
663
664
		$ret = false;
665
666
		if ( $this->status == 'expired' ) {
667
668
			$ret = true;
669
670
		} elseif( 'active' === $this->status || 'cancelled' === $this->status || $this->status == 'trialling'  ) {
671
672
			$ret        = false;
673
			$expiration = $this->get_expiration_time();
674
675
			if( $expiration && strtotime( 'NOW', current_time( 'timestamp' ) ) > $expiration ) {
676
				$ret = true;
677
678
				if ( 'active' === $this->status || $this->status == 'trialling'  ) {
679
					$this->expire();
680
				}
681
			}
682
683
		}
684
685
		return apply_filters( 'wpinv_subscription_is_expired', $ret, $this->id, $this );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('wp...$ret, $this->id, $this) also could return the type boolean which is incompatible with the documented return type void.
Loading history...
686
687
	}
688
689
	/**
690
	 * Retrieves the expiration date
691
	 *
692
	 * @since  1.0.0
693
	 * @return string
694
	 */
695
	public function get_expiration() {
696
		return $this->expiration;
697
	}
698
699
	/**
700
	 * Retrieves the expiration date in a timestamp
701
	 *
702
	 * @since  1.0.0
703
	 * @return int
704
	 */
705
	public function get_expiration_time() {
706
		return strtotime( $this->expiration, current_time( 'timestamp' ) );
707
	}
708
709
	/**
710
	 * Retrieves the subscription status
711
	 *
712
	 * @since  1.0.0
713
	 * @return int
714
	 */
715
	public function get_status() {
716
717
		// Monitor for page load delays on pages with large subscription lists (IE: Subscriptions table in admin)
718
		$this->is_expired();
719
		return $this->status;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->status returns the type string which is incompatible with the documented return type integer.
Loading history...
720
	}
721
722
	/**
723
	 * Retrieves the subscription status label
724
	 *
725
	 * @since  1.0.0
726
	 * @return int
727
	 */
728
	public function get_status_label() {
729
730
		switch( $this->get_status() ) {
731
			case 'active' :
732
				$status = __( 'Active', 'invoicing' );
733
				break;
734
735
			case 'cancelled' :
736
				$status = __( 'Cancelled', 'invoicing' );
737
				break;
738
739
			case 'expired' :
740
				$status = __( 'Expired', 'invoicing' );
741
				break;
742
743
			case 'pending' :
744
				$status = __( 'Pending', 'invoicing' );
745
				break;
746
747
			case 'failing' :
748
				$status = __( 'Failing', 'invoicing' );
749
				break;
750
751
			case 'trialling' :
752
				$status = __( 'Trialling', 'invoicing' );
753
				break;
754
755
			case 'completed' :
756
				$status = __( 'Completed', 'invoicing' );
757
				break;
758
759
			default:
760
				$status = ucfirst( $this->get_status() );
761
				break;
762
		}
763
764
		return $status;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $status returns the type string which is incompatible with the documented return type integer.
Loading history...
765
	}
766
767
    /**
768
     * Retrieves the subscription status label
769
     *
770
     * @since  1.0.0
771
     * @return int
772
     */
773
    public function get_status_label_html() {
774
775
        switch( $get_status = $this->get_status() ) {
776
            case 'active' :
777
                $status = __( 'Active', 'invoicing' );
778
                $class = 'label-info';
779
                break;
780
781
            case 'cancelled' :
782
                $status = __( 'Cancelled', 'invoicing' );
783
                $class = 'label-danger';
784
                break;
785
786
            case 'expired' :
787
                $status = __( 'Expired', 'invoicing' );
788
                $class = 'label-default';
789
                break;
790
791
            case 'pending' :
792
                $status = __( 'Pending', 'invoicing' );
793
                $class = 'label-primary';
794
                break;
795
796
            case 'failing' :
797
                $status = __( 'Failing', 'invoicing' );
798
                $class = 'label-danger';
799
                break;
800
801
            case 'trialling' :
802
                $status = __( 'Trialling', 'invoicing' );
803
                $class = 'label-info';
804
                break;
805
806
            case 'completed' :
807
                $status = __( 'Completed', 'invoicing' );
808
                $class = 'label-success';
809
                break;
810
811
            default:
812
                $status = ucfirst( $this->get_status() );
813
                $class = 'label-default';
814
                break;
815
        }
816
817
        $label = '<span class="sub-status label label-sub-' . $get_status . ' ' . $class . '">' . $status . '</span>';
818
819
        return apply_filters( 'wpinv_subscription_status_label_html', $label, $get_status, $status );
0 ignored issues
show
Bug Best Practice introduced by
The expression return apply_filters('wp..., $get_status, $status) also could return the type string which is incompatible with the documented return type integer.
Loading history...
820
    }
821
822
    /**
823
     * Determines if a payment exists with the specified transaction ID
824
     *
825
     * @since  2.4
826
     * @param  string $txn_id The transaction ID from the merchant processor
827
     * @return bool
828
     */
829
    public function payment_exists( $txn_id = '' ) {
830
		$invoice_id = WPInv_Invoice::get_invoice_id_by_field( $txn_id, 'transaction_id' );
831
        return ! empty( $invoice_id );
832
    }
833
834
}
835