Passed
Push — master ( 7db120...6e1f17 )
by Brian
05:24 queued 11s
created

WPInv_Subscription::get_customer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * Contains the subscription class.
4
 *
5
 * @since 1.0.19
6
 * @package Invoicing
7
 */
8
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * The Subscription Class
13
 *
14
 * @since  1.0.0
15
 */
16
class WPInv_Subscription extends GetPaid_Data {
17
18
	/**
19
	 * Which data store to load.
20
	 *
21
	 * @var string
22
	 */
23
	protected $data_store_name = 'subscription';
24
25
	/**
26
	 * This is the name of this object type.
27
	 *
28
	 * @var string
29
	 */
30
	protected $object_type = 'subscription';
31
32
	/**
33
	 * Item 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
		'customer_id'       => 0,
40
		'frequency'         => 1,
41
		'period'            => 'D',
42
		'initial_amount'    => null,
43
		'recurring_amount'  => null,
44
		'bill_times'        => 0,
45
		'transaction_id'    => '',
46
		'parent_payment_id' => null,
47
		'product_id'        => 0,
48
		'created'           => '0000-00-00 00:00:00',
49
		'expiration'        => '0000-00-00 00:00:00',
50
		'trial_period'      => null,
51
		'status'            => 'pending',
52
		'profile_id'        => '',
53
		'gateway'           => '',
54
		'customer'          => '',
55
	);
56
57
	/**
58
	 * Stores the status transition information.
59
	 *
60
	 * @since 1.0.19
61
	 * @var bool
62
	 */
63
	protected $status_transition = false;
64
65
	private $subs_db;
66
67
	/**
68
	 * Get the subscription if ID is passed, otherwise the subscription is new and empty.
69
	 *
70
	 * @param  int|string|object|WPInv_Subscription $subscription Subscription id, profile_id, or object to read.
71
	 * @param  bool $deprecated
72
	 */
73
	function __construct( $subscription = 0, $deprecated = false ) {
74
75
		parent::__construct( $subscription );
0 ignored issues
show
Bug introduced by
It seems like $subscription can also be of type string; however, parameter $read of GetPaid_Data::__construct() does only seem to accept array|integer|object, 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

75
		parent::__construct( /** @scrutinizer ignore-type */ $subscription );
Loading history...
76
77
		if ( ! $deprecated && ! empty( $subscription ) && is_numeric( $subscription ) ) {
78
			$this->set_id( $subscription );
0 ignored issues
show
Bug introduced by
It seems like $subscription 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

78
			$this->set_id( /** @scrutinizer ignore-type */ $subscription );
Loading history...
79
		} elseif ( $subscription instanceof self ) {
80
			$this->set_id( $subscription->get_id() );
81
		} elseif ( ! empty( $subscription->id ) ) {
82
			$this->set_id( $subscription->id );
83
		} elseif ( $deprecated && $subscription_id = self::get_subscription_id_by_field( $subscription, 'profile_id' ) ) {
84
			$this->set_id( $subscription_id );
85
		} else {
86
			$this->set_object_read( true );
87
		}
88
89
		// Load the datastore.
90
		$this->data_store = GetPaid_Data_Store::load( $this->data_store_name );
91
92
		if ( $this->get_id() > 0 ) {
93
			$this->data_store->read( $this );
94
		}
95
96
	}
97
98
	/**
99
	 * Given an invoice id, profile id, transaction id, it returns the subscription's id.
100
	 *
101
	 *
102
	 * @static
103
	 * @param string $value
104
	 * @param string $field Either invoice_id, transaction_id or profile_id.
105
	 * @since 1.0.19
106
	 * @return int
107
	 */
108
	public static function get_subscription_id_by_field( $value, $field = 'profile_id' ) {
109
        global $wpdb;
110
111
		// Trim the value.
112
		$value = trim( $value );
113
114
		if ( empty( $value ) ) {
115
			return 0;
116
		}
117
118
		if ( 'invoice_id' == $field ) {
119
			$field = 'parent_payment_id';
120
		}
121
122
        // Valid fields.
123
        $fields = array(
124
			'parent_payment_id',
125
			'transaction_id',
126
			'profile_id'
127
		);
128
129
		// Ensure a field has been passed.
130
		if ( empty( $field ) || ! in_array( $field, $fields ) ) {
131
			return 0;
132
		}
133
134
		// Maybe retrieve from the cache.
135
		$subscription_id   = wp_cache_get( $value, "getpaid_subscription_{$field}s_to_subscription_ids" );
136
		if ( ! empty( $subscription_id ) ) {
137
			return $subscription_id;
138
		}
139
140
        // Fetch from the db.
141
        $table            = $wpdb->prefix . 'wpinv_subscriptions';
142
        $subscription_id  = $wpdb->get_var(
143
            $wpdb->prepare( "SELECT `id` FROM $table WHERE `$field`=%s LIMIT 1", $value )
144
        );
145
146
		if ( empty( $subscription_id ) ) {
147
			return 0;
148
		}
149
150
		// Update the cache with our data.
151
		wp_cache_set( $value, $subscription_id, "getpaid_invoice_{$field}s_to_subscription_ids" );
152
153
		return $subscription_id;
154
	}
155
156
	/**
157
     * Checks if a subscription key is set.
158
     */
159
    public function _isset( $key ) {
160
        return isset( $this->data[$key] ) || method_exists( $this, "get_$key" );
161
	}
162
163
	/*
164
	|--------------------------------------------------------------------------
165
	| CRUD methods
166
	|--------------------------------------------------------------------------
167
	|
168
	| Methods which create, read, update and delete subscriptions from the database.
169
	|
170
    */
171
172
	/*
173
	|--------------------------------------------------------------------------
174
	| Getters
175
	|--------------------------------------------------------------------------
176
	*/
177
178
	/**
179
	 * Get customer id.
180
	 *
181
	 * @since 1.0.19
182
	 * @param  string $context View or edit context.
183
	 * @return int
184
	 */
185
	public function get_customer_id( $context = 'view' ) {
186
		return (int) $this->get_prop( 'customer_id', $context );
187
	}
188
189
	/**
190
	 * Get customer information.
191
	 *
192
	 * @since 1.0.19
193
	 * @param  string $context View or edit context.
194
	 * @return WP_User|false WP_User object on success, false on failure.
195
	 */
196
	public function get_customer( $context = 'view' ) {
197
		return get_userdata( $this->get_customer_id( $context ) );
198
	}
199
200
	/**
201
	 * Get parent invoice id.
202
	 *
203
	 * @since 1.0.19
204
	 * @param  string $context View or edit context.
205
	 * @return int
206
	 */
207
	public function get_parent_invoice_id( $context = 'view' ) {
208
		return (int) $this->get_prop( 'parent_payment_id', $context );
209
	}
210
211
	/**
212
	 * Alias for self::get_parent_invoice_id().
213
	 *
214
	 * @since 1.0.19
215
	 * @param  string $context View or edit context.
216
	 * @return int
217
	 */
218
    public function get_parent_payment_id( $context = 'view' ) {
219
        return $this->get_parent_invoice_id( $context );
220
	}
221
222
	/**
223
     * Alias for self::get_parent_invoice_id().
224
     *
225
     * @since  1.0.0
226
     * @return int
227
     */
228
    public function get_original_payment_id( $context = 'view' ) {
229
        return $this->get_parent_invoice_id( $context );
230
    }
231
232
	/**
233
	 * Get parent invoice.
234
	 *
235
	 * @since 1.0.19
236
	 * @param  string $context View or edit context.
237
	 * @return WPInv_Invoice
238
	 */
239
	public function get_parent_invoice( $context = 'view' ) {
240
		return new WPInv_Invoice( $this->get_parent_invoice_id( $context ) );
241
	}
242
243
	/**
244
	 * Alias for self::get_parent_invoice().
245
	 *
246
	 * @since 1.0.19
247
	 * @param  string $context View or edit context.
248
	 * @return WPInv_Invoice
249
	 */
250
    public function get_parent_payment( $context = 'view' ) {
251
        return $this->get_parent_invoice( $context );
252
	}
253
254
	/**
255
	 * Get subscription's product id.
256
	 *
257
	 * @since 1.0.19
258
	 * @param  string $context View or edit context.
259
	 * @return int
260
	 */
261
	public function get_product_id( $context = 'view' ) {
262
		return (int) $this->get_prop( 'product_id', $context );
263
	}
264
265
	/**
266
	 * Get the subscription product.
267
	 *
268
	 * @since 1.0.19
269
	 * @param  string $context View or edit context.
270
	 * @return WPInv_Item
271
	 */
272
	public function get_product( $context = 'view' ) {
273
		return new WPInv_Item( $this->get_product_id( $context ) );
274
	}
275
276
	/**
277
	 * Get parent invoice's gateway.
278
	 *
279
	 * Here for backwards compatibility.
280
	 *
281
	 * @since 1.0.19
282
	 * @param  string $context View or edit context.
283
	 * @return string
284
	 */
285
	public function get_gateway( $context = 'view' ) {
286
		return $this->get_parent_invoice( $context )->get_gateway();
287
	}
288
289
	/**
290
	 * Get the period of a renewal.
291
	 *
292
	 * @since 1.0.19
293
	 * @param  string $context View or edit context.
294
	 * @return string
295
	 */
296
	public function get_period( $context = 'view' ) {
297
		return $this->get_prop( 'period', $context );
298
	}
299
300
	/**
301
	 * Get number of periods each renewal is valid for.
302
	 *
303
	 * @since 1.0.19
304
	 * @param  string $context View or edit context.
305
	 * @return int
306
	 */
307
	public function get_frequency( $context = 'view' ) {
308
		return (int) $this->get_prop( 'frequency', $context );
309
	}
310
311
	/**
312
	 * Get the initial amount for the subscription.
313
	 *
314
	 * @since 1.0.19
315
	 * @param  string $context View or edit context.
316
	 * @return float
317
	 */
318
	public function get_initial_amount( $context = 'view' ) {
319
		return (float) wpinv_sanitize_amount( $this->get_prop( 'initial_amount', $context ) );
320
	}
321
322
	/**
323
	 * Get the recurring amount for the subscription.
324
	 *
325
	 * @since 1.0.19
326
	 * @param  string $context View or edit context.
327
	 * @return float
328
	 */
329
	public function get_recurring_amount( $context = 'view' ) {
330
		return (float) wpinv_sanitize_amount( $this->get_prop( 'recurring_amount', $context ) );
331
	}
332
333
	/**
334
	 * Get number of times that this subscription has been renewed.
335
	 *
336
	 * @since 1.0.19
337
	 * @param  string $context View or edit context.
338
	 * @return int
339
	 */
340
	public function get_bill_times( $context = 'view' ) {
341
		return (int) $this->get_prop( 'bill_times', $context );
342
	}
343
344
	/**
345
	 * Get transaction id of this subscription's parent invoice.
346
	 *
347
	 * @since 1.0.19
348
	 * @param  string $context View or edit context.
349
	 * @return string
350
	 */
351
	public function get_transaction_id( $context = 'view' ) {
352
		return $this->get_prop( 'transaction_id', $context );
353
	}
354
355
	/**
356
	 * Get the date that the subscription was created.
357
	 *
358
	 * @since 1.0.19
359
	 * @param  string $context View or edit context.
360
	 * @return string
361
	 */
362
	public function get_created( $context = 'view' ) {
363
		return $this->get_prop( 'created', $context );
364
	}
365
366
	/**
367
	 * Alias for self::get_created().
368
	 *
369
	 * @since 1.0.19
370
	 * @param  string $context View or edit context.
371
	 * @return string
372
	 */
373
	public function get_date_created( $context = 'view' ) {
374
		return $this->get_created( $context );
375
    }
376
377
	/**
378
	 * Get GMT date when the subscription was created.
379
	 *
380
	 * @since 1.0.19
381
	 * @param  string $context View or edit context.
382
	 * @return string
383
	 */
384
	public function get_date_created_gmt( $context = 'view' ) {
385
        $date = $this->get_date_created( $context );
386
387
        if ( $date ) {
388
            $date = get_gmt_from_date( $date );
389
        }
390
		return $date;
391
	}
392
393
	/**
394
	 * Get the date that the subscription will renew.
395
	 *
396
	 * @since 1.0.19
397
	 * @param  string $context View or edit context.
398
	 * @return string
399
	 */
400
	public function get_next_renewal_date( $context = 'view' ) {
401
		return $this->get_prop( 'expiration', $context );
402
	}
403
404
	/**
405
	 * Alias for self::get_next_renewal_date().
406
	 *
407
	 * @since 1.0.19
408
	 * @param  string $context View or edit context.
409
	 * @return string
410
	 */
411
	public function get_expiration( $context = 'view' ) {
412
		return $this->get_next_renewal_date( $context );
413
    }
414
415
	/**
416
	 * Get GMT date when the subscription will renew.
417
	 *
418
	 * @since 1.0.19
419
	 * @param  string $context View or edit context.
420
	 * @return string
421
	 */
422
	public function get_next_renewal_date_gmt( $context = 'view' ) {
423
        $date = $this->get_next_renewal_date( $context );
424
425
        if ( $date ) {
426
            $date = get_gmt_from_date( $date );
427
        }
428
		return $date;
429
	}
430
431
	/**
432
	 * Get the subscription's trial period.
433
	 *
434
	 * @since 1.0.19
435
	 * @param  string $context View or edit context.
436
	 * @return string
437
	 */
438
	public function get_trial_period( $context = 'view' ) {
439
		return $this->get_prop( 'trial_period', $context );
440
	}
441
442
	/**
443
	 * Get the subscription's status.
444
	 *
445
	 * @since 1.0.19
446
	 * @param  string $context View or edit context.
447
	 * @return string
448
	 */
449
	public function get_status( $context = 'view' ) {
450
		return $this->get_prop( 'status', $context );
451
	}
452
453
	/**
454
	 * Get the subscription's profile id.
455
	 *
456
	 * @since 1.0.19
457
	 * @param  string $context View or edit context.
458
	 * @return string
459
	 */
460
	public function get_profile_id( $context = 'view' ) {
461
		return $this->get_prop( 'profile_id', $context );
462
	}
463
464
	/*
465
	|--------------------------------------------------------------------------
466
	| Setters
467
	|--------------------------------------------------------------------------
468
	*/
469
470
	/**
471
	 * Set customer id.
472
	 *
473
	 * @since 1.0.19
474
	 * @param  int $value The customer's id.
475
	 */
476
	public function set_customer_id( $value ) {
477
		$this->set_prop( 'customer_id', (int) $value );
478
	}
479
480
	/**
481
	 * Set parent invoice id.
482
	 *
483
	 * @since 1.0.19
484
	 * @param  int $value The parent invoice id.
485
	 */
486
	public function set_parent_invoice_id( $value ) {
487
		$this->set_prop( 'parent_payment_id', (int) $value );
488
	}
489
490
	/**
491
	 * Alias for self::set_parent_invoice_id().
492
	 *
493
	 * @since 1.0.19
494
	 * @param  int $value The parent invoice id.
495
	 */
496
    public function set_parent_payment_id( $value ) {
497
        $this->set_parent_invoice_id( $value );
498
	}
499
500
	/**
501
     * Alias for self::set_parent_invoice_id().
502
     *
503
     * @since 1.0.19
504
	 * @param  int $value The parent invoice id.
505
     */
506
    public function set_original_payment_id( $value ) {
507
        $this->set_parent_invoice_id( $value );
508
	}
509
510
	/**
511
	 * Set subscription's product id.
512
	 *
513
	 * @since 1.0.19
514
	 * @param  int $value The subscription product id.
515
	 */
516
	public function set_product_id( $value ) {
517
		$this->set_prop( 'product_id', (int) $value );
518
	}
519
520
	/**
521
	 * Set the period of a renewal.
522
	 *
523
	 * @since 1.0.19
524
	 * @param  string $value The renewal period.
525
	 */
526
	public function set_period( $value ) {
527
		$this->set_prop( 'period', $value );
528
	}
529
530
	/**
531
	 * Set number of periods each renewal is valid for.
532
	 *
533
	 * @since 1.0.19
534
	 * @param  int $value The subscription frequency.
535
	 */
536
	public function set_frequency( $value ) {
537
		$this->set_prop( 'frequency', (int) $value );
538
	}
539
540
	/**
541
	 * Set the initial amount for the subscription.
542
	 *
543
	 * @since 1.0.19
544
	 * @param  float $value The initial subcription amount.
545
	 */
546
	public function set_initial_amount( $value ) {
547
		$this->set_prop( 'initial_amount', wpinv_sanitize_amount( $value ) );
548
	}
549
550
	/**
551
	 * Set the recurring amount for the subscription.
552
	 *
553
	 * @since 1.0.19
554
	 * @param  float $value The recurring subcription amount.
555
	 */
556
	public function set_recurring_amount( $value ) {
557
		$this->set_prop( 'recurring_amount', wpinv_sanitize_amount( $value ) );
558
	}
559
560
	/**
561
	 * Set number of times that this subscription has been renewed.
562
	 *
563
	 * @since 1.0.19
564
	 * @param  int $value Bill times.
565
	 */
566
	public function set_bill_times( $value ) {
567
		$this->set_prop( 'bill_times', (int) $value );
568
	}
569
570
	/**
571
	 * Get transaction id of this subscription's parent invoice.
572
	 *
573
	 * @since 1.0.19
574
	 * @param string $value Bill times.
575
	 */
576
	public function set_transaction_id( $value ) {
577
		$this->set_prop( 'transaction_id', $value );
578
	}
579
580
	/**
581
	 * Set date when this subscription started.
582
	 *
583
	 * @since 1.0.19
584
	 * @param string $value strtotime compliant date.
585
	 */
586
	public function set_created( $value ) {
587
        $date = strtotime( $value );
588
589
        if ( $date && $value !== '0000-00-00 00:00:00' ) {
590
            $this->set_prop( 'created', date( 'Y-m-d H:i:s', $date ) );
591
            return;
592
        }
593
594
		$this->set_prop( 'created', '' );
595
596
	}
597
598
	/**
599
	 * Alias for self::set_created().
600
	 *
601
	 * @since 1.0.19
602
	 * @param string $value strtotime compliant date.
603
	 */
604
	public function set_date_created( $value ) {
605
		$this->set_created( $value );
606
    }
607
608
	/**
609
	 * Set the date that the subscription will renew.
610
	 *
611
	 * @since 1.0.19
612
	 * @param string $value strtotime compliant date.
613
	 */
614
	public function set_next_renewal_date( $value ) {
615
		$date = strtotime( $value );
616
617
        if ( $date && $value !== '0000-00-00 00:00:00' ) {
618
            $this->set_prop( 'expiration', date( 'Y-m-d H:i:s', $date ) );
619
            return;
620
		}
621
622
		$this->set_prop( 'expiration', '' );
623
624
	}
625
626
	/**
627
	 * Alias for self::set_next_renewal_date().
628
	 *
629
	 * @since 1.0.19
630
	 * @param string $value strtotime compliant date.
631
	 */
632
	public function set_expiration( $value ) {
633
		$this->set_next_renewal_date( $value );
634
    }
635
636
	/**
637
	 * Set the subscription's trial period.
638
	 *
639
	 * @since 1.0.19
640
	 * @param string $value trial period e.g 1 year.
641
	 */
642
	public function set_trial_period( $value ) {
643
		$this->set_prop( 'trial_period', $value );
644
	}
645
646
	/**
647
	 * Set the subscription's status.
648
	 *
649
	 * @since 1.0.19
650
	 * @param string $status The subscription status.
651
	 */
652
	public function set_status( $status ) {
653
		if ( array_key_exists( $status, getpaid_get_subscription_statuses() ) ) {
654
			$this->set_prop( 'status', $status );
655
		}
656
	}
657
658
	/**
659
	 * Set the subscription's (remote) profile id.
660
	 *
661
	 * @since 1.0.19
662
	 * @param  string $value the remote profile id.
663
	 */
664
	public function set_profile_id( $value ) {
665
		$this->set_prop( 'profile_id', $value );
666
	}
667
668
	/*
669
	|--------------------------------------------------------------------------
670
	| Boolean methods
671
	|--------------------------------------------------------------------------
672
	|
673
	| Return true or false.
674
	|
675
	*/
676
677
	/**
678
     * Checks if the subscription has a given status.
679
	 * 
680
	 * @param string|array String or array of strings to check for.
681
	 * @return bool
682
     */
683
    public function has_status( $status ) {
684
        return in_array( $this->get_status(), wpinv_parse_list( $status ) );
685
	}
686
687
	/*
688
	|--------------------------------------------------------------------------
689
	| Additional methods
690
	|--------------------------------------------------------------------------
691
	|
692
	| Calculating subscription details.
693
	|
694
	*/
695
696
	/**
697
	 * Backwards compatibilty.
698
	 */
699
	public function create( $data = array() ) {
700
701
		// Set the properties.
702
		if ( is_array( $data ) ) {
703
			$this->set_props( $data );
704
		}
705
706
		// Save the item.
707
		return $this->save();
708
709
	}
710
711
	/**
712
	 * Backwards compatibilty.
713
	 */
714
	public function update( $args = array() ) {
715
		return $this->create( $args );
716
	}
717
718
    /**
719
     * Retrieve renewal payments for a subscription
720
     *
721
     * @since  1.0.0
722
     * @return WP_Post[]
723
     */
724
    public function get_child_payments() {
725
        return get_posts(
0 ignored issues
show
Bug Best Practice introduced by
The expression return get_posts(array('...ype' => 'wpi_invoice')) returns an array which contains values of type integer which are incompatible with the documented value type WP_Post.
Loading history...
726
			array(
727
            	'post_parent'    => $this->get_parent_payment_id(),
728
            	'posts_per_page' => '999',
729
            	'post_status'    => array( 'publish', 'wpi-processing', 'wpi-renewal' ),
730
            	'orderby'        => 'ID',
731
            	'order'          => 'DESC',
732
            	'post_type'      => 'wpi_invoice'
733
			)
734
		);
735
    }
736
737
    /**
738
     * Counts the number of payments made to the subscription
739
     *
740
     * @since  1.0.0
741
     * @return int
742
     */
743
    public function get_total_payments() {
744
        $child_payments = $this->get_child_payments();
745
        $total_payments = !empty( $child_payments ) ? count( $child_payments ) : 0;
746
747
        if ( 'pending' != $this->status ) {
748
                $total_payments++;
749
        }
750
751
        return $total_payments;
752
    }
753
754
    /**
755
     * Returns the number of times the subscription has been billed
756
     *
757
     * @since  1.0.2
758
     * @return int
759
     */
760
    public function get_times_billed() {
761
        $times_billed = (int)$this->get_total_payments();
762
763
        if ( ! empty( $this->trial_period ) && $times_billed > 0 ) {
0 ignored issues
show
Bug Best Practice introduced by
The property trial_period does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
764
            $times_billed--;
765
        }
766
767
        return $times_billed;
768
    }
769
770
    /**
771
     * Records a new payment on the subscription
772
     *
773
     * @since  2.4
774
     * @param  array $args Array of values for the payment, including amount and transaction ID
775
	 * @param  WPInv_Invoice $invoice
776
     * @return bool
777
     */
778
    public function add_payment( $args = array(), $invoice = false ) {
779
780
		// Process each payment once.
781
        if ( ! empty( $args['transaction_id'] ) && $this->payment_exists( $args['transaction_id'] ) ) {
782
            return false;
783
        }
784
785
		// Are we creating a new invoice?
786
		if ( empty( $invoice ) ) {
787
			$invoice = $this->create_payment();
788
789
			if ( empty( $invoice ) ) {
790
				return false;
791
			}
792
793
			$invoice->set_status( 'wpi-renewal' );
794
795
		}
796
797
		// Maybe set a transaction id.
798
		if ( ! empty( $args['transaction_id'] ) ) {
799
			$invoice->set_transaction_id( $args['transaction_id'] );
800
		}
801
802
		// Set the completed date.
803
		$invoice->set_completed_date( current_time( 'mysql' ) );
804
805
		// And the gateway.
806
		if ( ! empty( $args['gateway'] ) ) {
807
			$invoice->set_gateway( $args['gateway'] );
808
		}
809
810
		$invoice->save();
811
812
		if ( ! $invoice->get_id() ) {
813
			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...
814
		}
815
816
		do_action( 'getpaid_after_create_subscription_renewal_invoice', $invoice, $this );
817
		do_action( 'wpinv_recurring_add_subscription_payment', $invoice, $this );
818
        do_action( 'wpinv_recurring_record_payment', $invoice->get_id(), $this->parent_payment_id, $invoice->get_recurring_total(), $invoice->get_transaction_id() );
0 ignored issues
show
Bug Best Practice introduced by
The property parent_payment_id does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
819
820
        update_post_meta( $invoice->get_id(), '_wpinv_subscription_id', $this->id );
821
822
        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...
823
	}
824
825
	/**
826
     * Creates a new invoice and returns it.
827
     *
828
     * @since  1.0.19
829
     * @param  array $args Array of values for the payment, including amount and transaction ID
830
     * @return WPInv_Invoice|bool
831
     */
832
    public function create_payment() {
833
834
		// Do we have a parent invoice?
835
        if ( ! $this->parent_payment_id ) {
0 ignored issues
show
Bug Best Practice introduced by
The property parent_payment_id does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
836
            return false;
837
        }
838
839
		// Ensure that the parent invoice is available.
840
        $parent_invoice = wpinv_get_invoice( $this->parent_payment_id );
841
        if ( empty( $parent_invoice ) ) {
842
            return false;
843
        }
844
845
		// Duplicate the parent invoice.
846
		$invoice = new WPInv_Invoice();
847
		$invoice->set_props( $parent_invoice->get_data() );
848
		$invoice->set_id( 0 );
849
		$invoice->set_parent_id( $parent_invoice->get_parent() );
850
		$invoice->set_transaction_id( '' );
851
		$invoice->set_key( $invoice->generate_key( 'renewal_' ) );
852
		$invoice->set_number( '' );
853
		$invoice->set_completed_date( '' );
854
		$invoice->set_status( 'wpi-pending' );
855
		$invoice->recalculate_total();
856
		$invoice->save();
857
858
		return $invoice->get_id() ? $invoice : false;
859
    }
860
861
	/**
862
	 * Renews a subscription
863
	 *
864
	 * @since  1.0.0
865
	 * @return bool
866
	 */
867
	public function renew() {
868
869
		// Calculate new expiration
870
		$expires        = $this->get_expiration_time();
871
		$base_date      = $expires > current_time( 'timestamp' ) ? $expires : current_time( 'timestamp' );
872
		$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...
873
		$new_expiration = strtotime( "+ {$frequency} {$this->period}", strtotime( $base_date ) );
0 ignored issues
show
Bug Best Practice introduced by
The property period does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
874
		$new_expiration = apply_filters( 'wpinv_subscription_renewal_expiration', date( 'Y-m-d H:i:s', $new_expiration ), $this->id, $this );
875
876
		do_action( 'wpinv_subscription_pre_renew', $this->id, $new_expiration, $this );
877
878
		$this->status = 'active';
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
879
		$times_billed = $this->get_times_billed();
880
881
		// Complete subscription if applicable
882
		if ( $this->bill_times > 0 && $times_billed >= $this->bill_times ) {
0 ignored issues
show
Bug Best Practice introduced by
The property bill_times does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
883
			$this->complete();
884
			$this->status = 'completed';
885
			return;
886
		}
887
888
		$args = array(
889
			'expiration' => $new_expiration,
890
			'status'     => $this->status,
891
		);
892
893
        $this->subs_db->update( $this->id, $args );
894
895
		$this->expiration = $new_expiration;
0 ignored issues
show
Bug Best Practice introduced by
The property expiration does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
896
897
		do_action( 'wpinv_subscription_post_renew', $this->id, $new_expiration, $this );
898
		do_action( 'wpinv_recurring_set_subscription_status', $this->id, $this->status, $this );
899
900
	}
901
902
	/**
903
	 * Marks a subscription as completed
904
	 *
905
	 * Subscription is completed when the number of payments matches the billing_times field
906
	 *
907
	 * @since  1.0.0
908
	 * @return void
909
	 */
910
	public function complete() {
911
912
		// Only mark a subscription as complete if it's not already cancelled.
913
		if ( 'cancelled' === $this->status ) {
914
			return;
915
		}
916
917
		$args = array(
918
			'status' => 'completed'
919
		);
920
921
		if( $this->subs_db->update( $this->id, $args ) ) {
922
923
			$this->status = 'completed';
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
924
925
			do_action( 'wpinv_subscription_completed', $this->id, $this );
926
927
		}
928
929
	}
930
931
	/**
932
	 * Marks a subscription as expired
933
	 *
934
	 * Subscription is completed when the billing times is reached
935
	 *
936
	 * @since  1.0.0
937
	 * @param  $check_expiration bool True if expiration date should be checked with merchant processor before expiring
938
	 * @return void
939
	 */
940
	public function expire( $check_expiration = false ) {
941
942
		$expiration = $this->expiration;
943
944
		if( $check_expiration ) {
945
946
			// check_expiration() updates $this->expiration so compare to $expiration above
947
948
			if( $expiration < $this->get_expiration() && current_time( 'timestamp' ) < $this->get_expiration_time() ) {
949
950
				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...
951
			}
952
953
		}
954
955
		$args = array(
956
			'status' => 'expired'
957
		);
958
959
		if( $this->subs_db->update( $this->id, $args ) ) {
960
961
			$this->status = 'expired';
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
962
963
			do_action( 'wpinv_subscription_expired', $this->id, $this );
964
965
		}
966
967
	}
968
969
	/**
970
	 * Marks a subscription as failing
971
	 *
972
	 * @since  2.4.2
973
	 * @return void
974
	 */
975
	public function failing() {
976
977
		$args = array(
978
			'status' => 'failing'
979
		);
980
981
		if( $this->subs_db->update( $this->id, $args ) ) {
982
983
			$this->status = 'failing';
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
984
985
			do_action( 'wpinv_subscription_failing', $this->id, $this );
986
			do_action( 'wpinv_recurring_payment_failed', $this );
987
988
		}
989
990
	}
991
992
    /**
993
     * Marks a subscription as cancelled
994
     *
995
     * @since  1.0.0
996
     * @return void
997
     */
998
    public function cancel() {
999
        if ( 'cancelled' === $this->status ) {
1000
            return; // Already cancelled
1001
        }
1002
1003
        $args = array(
1004
            'status' => 'cancelled'
1005
        );
1006
1007
        if ( $this->subs_db->update( $this->id, $args ) ) {
1008
            if ( is_user_logged_in() ) {
1009
                $userdata = get_userdata( get_current_user_id() );
1010
                $user     = $userdata->display_name;
1011
            } else {
1012
                $user = __( 'gateway', 'invoicing' );
1013
            }
1014
1015
            $note = sprintf( __( 'Subscription has been cancelled by %s', 'invoicing' ), $user );
1016
            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

1016
            /** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $this->parent_payment_id, $note, '', '', true );
Loading history...
Bug Best Practice introduced by
The property parent_payment_id does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
1017
1018
            $this->status = 'cancelled';
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1019
1020
            do_action( 'wpinv_subscription_cancelled', $this->id, $this );
1021
        }
1022
    }
1023
1024
	/**
1025
	 * Determines if subscription can be cancelled
1026
	 *
1027
	 * This method is filtered by payment gateways in order to return true on subscriptions
1028
	 * that can be cancelled with a profile ID through the merchant processor
1029
	 *
1030
	 * @since  1.0.0
1031
	 * @return bool
1032
	 */
1033
	public function can_cancel() {
1034
        $ret = false;
1035
	    if( $this->gateway === 'manual' || in_array( $this->status, $this->get_cancellable_statuses() ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property gateway does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
1036
            $ret = true;
1037
        }
1038
		return apply_filters( 'wpinv_subscription_can_cancel', $ret, $this );
1039
	}
1040
1041
    /**
1042
     * Returns an array of subscription statuses that can be cancelled
1043
     *
1044
     * @access      public
1045
     * @since       1.0.0
1046
     * @return      array
1047
     */
1048
    public function get_cancellable_statuses() {
1049
        return apply_filters( 'wpinv_recurring_cancellable_statuses', array( 'active', 'trialling', 'failing' ) );
1050
    }
1051
1052
	/**
1053
	 * Retrieves the URL to cancel subscription
1054
	 *
1055
	 * @since  1.0.0
1056
	 * @return string
1057
	 */
1058
	public function get_cancel_url() {
1059
1060
		$url = wp_nonce_url( add_query_arg( array( 'wpinv_action' => 'cancel_subscription', 'sub_id' => $this->id ) ), 'wpinv-recurring-cancel' );
1061
1062
		return apply_filters( 'wpinv_subscription_cancel_url', $url, $this );
1063
	}
1064
1065
	/**
1066
	 * Determines if subscription can be manually renewed
1067
	 *
1068
	 * This method is filtered by payment gateways in order to return true on subscriptions
1069
	 * that can be renewed manually
1070
	 *
1071
	 * @since  2.5
1072
	 * @return bool
1073
	 */
1074
	public function can_renew() {
1075
1076
		return apply_filters( 'wpinv_subscription_can_renew', true, $this );
1077
	}
1078
1079
	/**
1080
	 * Retrieves the URL to renew a subscription
1081
	 *
1082
	 * @since  2.5
1083
	 * @return string
1084
	 */
1085
	public function get_renew_url() {
1086
1087
		$url = wp_nonce_url( add_query_arg( array( 'wpinv_action' => 'renew_subscription', 'sub_id' => $this->id ) ), 'wpinv-recurring-renew' );
1088
1089
		return apply_filters( 'wpinv_subscription_renew_url', $url, $this );
1090
	}
1091
1092
	/**
1093
	 * Determines if subscription can have their payment method updated
1094
	 *
1095
	 * @since  1.0.0
1096
	 * @return bool
1097
	 */
1098
	public function can_update() {
1099
		return apply_filters( 'wpinv_subscription_can_update', false, $this );
1100
	}
1101
1102
	/**
1103
	 * Retrieves the URL to update subscription
1104
	 *
1105
	 * @since  1.0.0
1106
	 * @return void
1107
	 */
1108
	public function get_update_url() {
1109
1110
		$url = add_query_arg( array( 'action' => 'update', 'subscription_id' => $this->id ) );
1111
1112
		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...
1113
	}
1114
1115
	/**
1116
	 * Determines if subscription is active
1117
	 *
1118
	 * @since  1.0.0
1119
	 * @return void
1120
	 */
1121
	public function is_active() {
1122
1123
		$ret = false;
1124
1125
		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...
1126
			$ret = true;
1127
		}
1128
1129
		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...
1130
1131
	}
1132
1133
	/**
1134
	 * Determines if subscription is expired
1135
	 *
1136
	 * @since  1.0.0
1137
	 * @return void
1138
	 */
1139
	public function is_expired() {
1140
1141
		$ret = false;
1142
1143
		if ( $this->status == 'expired' ) {
1144
1145
			$ret = true;
1146
1147
		} elseif( 'active' === $this->status || 'cancelled' === $this->status || $this->status == 'trialling'  ) {
1148
1149
			$ret        = false;
1150
			$expiration = $this->get_expiration_time();
1151
1152
			if( $expiration && strtotime( 'NOW', current_time( 'timestamp' ) ) > $expiration ) {
1153
				$ret = true;
1154
1155
				if ( 'active' === $this->status || $this->status == 'trialling'  ) {
1156
					$this->expire();
1157
				}
1158
			}
1159
1160
		}
1161
1162
		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...
1163
1164
	}
1165
1166
	/**
1167
	 * Retrieves the expiration date in a timestamp
1168
	 *
1169
	 * @since  1.0.0
1170
	 * @return int
1171
	 */
1172
	public function get_expiration_time() {
1173
		return strtotime( $this->expiration, current_time( 'timestamp' ) );
1174
	}
1175
1176
	/**
1177
	 * Retrieves the subscription status label
1178
	 *
1179
	 * @since  1.0.0
1180
	 * @return int
1181
	 */
1182
	public function get_status_label() {
1183
		$statuses = getpaid_get_subscription_statuses();
1184
		return isset( $statuses[ $this->get_status() ] ) ? $statuses[ $this->get_status() ] : ucfirst( $this->get_status() );
0 ignored issues
show
Bug Best Practice introduced by
The expression return IssetNode ? $stat...st($this->get_status()) also could return the type string which is incompatible with the documented return type integer.
Loading history...
1185
	}
1186
1187
    /**
1188
     * Retrieves the subscription status label
1189
     *
1190
     * @since  1.0.0
1191
     * @return int
1192
     */
1193
    public function get_status_label_html() {
1194
1195
        switch( $get_status = $this->get_status() ) {
1196
            case 'active' :
1197
                $status = __( 'Active', 'invoicing' );
1198
                $class = 'label-info';
1199
                break;
1200
1201
            case 'cancelled' :
1202
                $status = __( 'Cancelled', 'invoicing' );
1203
                $class = 'label-danger';
1204
                break;
1205
1206
            case 'expired' :
1207
                $status = __( 'Expired', 'invoicing' );
1208
                $class = 'label-default';
1209
                break;
1210
1211
            case 'pending' :
1212
                $status = __( 'Pending', 'invoicing' );
1213
                $class = 'label-primary';
1214
                break;
1215
1216
            case 'failing' :
1217
                $status = __( 'Failing', 'invoicing' );
1218
                $class = 'label-danger';
1219
                break;
1220
1221
            case 'trialling' :
1222
                $status = __( 'Trialling', 'invoicing' );
1223
                $class = 'label-info';
1224
                break;
1225
1226
            case 'completed' :
1227
                $status = __( 'Completed', 'invoicing' );
1228
                $class = 'label-success';
1229
                break;
1230
1231
            default:
1232
                $status = ucfirst( $this->get_status() );
1233
                $class = 'label-default';
1234
                break;
1235
        }
1236
1237
        $label = '<span class="sub-status label label-sub-' . $get_status . ' ' . $class . '">' . $status . '</span>';
1238
1239
        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...
1240
    }
1241
1242
    /**
1243
     * Determines if a payment exists with the specified transaction ID
1244
     *
1245
     * @since  2.4
1246
     * @param  string $txn_id The transaction ID from the merchant processor
1247
     * @return bool
1248
     */
1249
    public function payment_exists( $txn_id = '' ) {
1250
		$invoice_id = WPInv_Invoice::get_invoice_id_by_field( $txn_id, 'transaction_id' );
1251
        return ! empty( $invoice_id );
1252
    }
1253
1254
}
1255