PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / 4.0.3
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress v4.0.3
4.17.2 4.17.1 4.17.0 4.16.19 4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / src / Membership / Models / Subscription / SubscriptionEntity.php
wp-user-avatar / src / Membership / Models / Subscription Last commit date
SubscriptionBillingFrequency.php 4 years ago SubscriptionEntity.php 4 years ago SubscriptionFactory.php 4 years ago SubscriptionStatus.php 4 years ago SubscriptionTrialPeriod.php 4 years ago index.php 4 years ago
SubscriptionEntity.php
682 lines
1 <?php
2
3 namespace ProfilePress\Core\Membership\Models\Subscription;
4
5 use ProfilePress\Core\Classes\PROFILEPRESS_sql;
6 use ProfilePress\Core\Membership\Models\AbstractModel;
7 use ProfilePress\Core\Membership\Models\ModelInterface;
8 use ProfilePress\Core\Membership\Models\Order\OrderEntity as OrderEntity;
9 use ProfilePress\Core\Membership\Models\Order\OrderFactory;
10 use ProfilePress\Core\Membership\Models\Order\OrderStatus;
11 use ProfilePress\Core\Membership\PaymentMethods\PaymentMethods;
12 use ProfilePress\Core\Membership\Repositories\OrderRepository;
13 use ProfilePress\Core\Membership\Repositories\SubscriptionRepository;
14 use ProfilePress\Core\Membership\Services\Calculator;
15 use ProfilePress\Core\Membership\Services\OrderService;
16 use ProfilePressVendor\Carbon\CarbonImmutable;
17
18 /**
19 * @property int $id
20 * @property int $parent_order_id
21 * @property int $plan_id
22 * @property int $customer_id
23 * @property string $billing_frequency
24 * @property string $initial_amount
25 * @property string $recurring_amount
26 * @property string $initial_tax
27 * @property string $initial_tax_rate
28 * @property string $recurring_tax
29 * @property string $recurring_tax_rate
30 * @property int $total_payments
31 * @property string $trial_period
32 * @property string $profile_id
33 * @property string $status
34 * @property array $notes
35 * @property string $created_date
36 * @property string $expiration_date
37 */
38 class SubscriptionEntity extends AbstractModel implements ModelInterface
39 {
40 /**
41 * Subscription ID
42 *
43 * @var int
44 */
45 protected $id = 0;
46
47 /**
48 * Subscription Parent order ID
49 *
50 * @var int
51 */
52 protected $parent_order_id = 0;
53
54 /**
55 * The Plan ID that this subscription is for.
56 *
57 * @var int
58 */
59 protected $plan_id = 0;
60
61 /**
62 * Customer ID with this subscription.
63 *
64 * @var int
65 */
66 protected $customer_id = 0;
67
68 /**
69 * Billing frequency
70 *
71 * @var string
72 */
73 protected $billing_frequency = SubscriptionBillingFrequency::MONTHLY;
74
75 /**
76 * @var string
77 */
78 protected $initial_amount = '0';
79
80 /**
81 * @var string
82 */
83 protected $recurring_amount = '0';
84
85 /**
86 * @var string
87 */
88 protected $initial_tax = '0';
89
90 /**
91 * @var string
92 */
93 protected $initial_tax_rate = '0';
94
95 /**
96 * @var string
97 */
98 protected $recurring_tax = '0';
99
100 /**
101 * @var string
102 */
103 protected $recurring_tax_rate = '0';
104
105 /**
106 * @var int
107 */
108 protected $total_payments = 0;
109
110 /**
111 * @var string
112 */
113 protected $trial_period = SubscriptionTrialPeriod::DISABLED;
114
115 /**
116 * @var string
117 */
118 protected $profile_id = '';
119
120 /**
121 * @var string
122 */
123 protected $status = SubscriptionStatus::PENDING;
124
125 /**
126 * @var string
127 */
128 protected $notes = [];
129
130 /**
131 * @var string
132 */
133 protected $created_date = '';
134
135 /**
136 * @var string
137 */
138 protected $expiration_date = '';
139
140 public function __construct($data = [])
141 {
142 if (is_array($data) && ! empty($data)) {
143
144 foreach ($data as $key => $value) {
145
146 if ($key == 'notes' && ! is_array($value)) {
147 $value = empty($value) ? '{}' : $value;
148 $this->$key = json_decode($value, true);
149 } else {
150 $this->$key = $value;
151 }
152 }
153 }
154 }
155
156 /**
157 * @return bool
158 */
159 public function exists()
160 {
161 return ! empty($this->id);
162 }
163
164 public function get_id()
165 {
166 return absint($this->id);
167 }
168
169 public function is_active()
170 {
171 $ret = false;
172
173 $active_statuses = [
174 SubscriptionStatus::ACTIVE,
175 SubscriptionStatus::COMPLETED,
176 SubscriptionStatus::TRIALLING,
177 ];
178
179 if ( ! $this->is_expired() && in_array($this->status, $active_statuses, true)) {
180 $ret = true;
181 }
182
183 return apply_filters('ppress_subscription_is_active', $ret, $this->id, $this);
184 }
185
186 public function is_expired()
187 {
188 $ret = false;
189
190 if ($this->status == SubscriptionStatus::EXPIRED) {
191
192 $ret = true;
193
194 } elseif ( ! $this->is_lifetime() && in_array($this->status, [SubscriptionStatus::ACTIVE, SubscriptionStatus::CANCELLED, SubscriptionStatus::TRIALLING])) {
195
196 $ret = false;
197
198 if (CarbonImmutable::parse($this->expiration_date, wp_timezone())->diffInDays() >= 2) {
199
200 $ret = true;
201
202 if (in_array($this->status, [SubscriptionStatus::ACTIVE, SubscriptionStatus::TRIALLING])) {
203 $this->expire();
204 }
205 }
206 }
207
208 return apply_filters('ppress_subscription_is_expired', $ret, $this->id, $this);
209 }
210
211 public function is_cancelled()
212 {
213 return $this->status == SubscriptionStatus::CANCELLED;
214 }
215
216 public function is_completed()
217 {
218 return $this->status == SubscriptionStatus::COMPLETED;
219 }
220
221 public function is_recurring()
222 {
223 return $this->billing_frequency != SubscriptionBillingFrequency::ONE_TIME;
224 }
225
226 public function is_lifetime()
227 {
228 return ! $this->is_recurring() || Calculator::init($this->recurring_amount)->isNegativeOrZero();
229 }
230
231 public function has_trial()
232 {
233 return $this->is_recurring() && $this->trial_period != SubscriptionTrialPeriod::DISABLED;
234 }
235
236 public function get_parent_order_id()
237 {
238 return absint($this->parent_order_id);
239 }
240
241 public function get_plan_id()
242 {
243 return absint($this->plan_id);
244 }
245
246 public function get_customer_id()
247 {
248 return absint($this->customer_id);
249 }
250
251 /**
252 * Total sub initial amount including tax.
253 *
254 * @return string
255 */
256 public function get_initial_amount()
257 {
258 return (string)$this->initial_amount;
259 }
260
261 /**
262 * Total sub recurring amount including tax.
263 *
264 * @return string
265 */
266 public function get_recurring_amount()
267 {
268 return (string)$this->recurring_amount;
269 }
270
271 public function get_initial_tax()
272 {
273 return empty($this->initial_tax) ? '0' : (string)$this->initial_tax;
274 }
275
276 public function get_initial_tax_rate()
277 {
278 return empty($this->initial_tax_rate) ? '0' : (string)$this->initial_tax_rate;
279 }
280
281 public function get_recurring_tax()
282 {
283 return empty($this->recurring_tax) ? '0' : (string)$this->recurring_tax;
284 }
285
286 public function get_recurring_tax_rate()
287 {
288 return empty($this->recurring_tax_rate) ? '0' : (string)$this->recurring_tax_rate;
289 }
290
291 public function get_total_payments()
292 {
293 return absint($this->total_payments);
294 }
295
296 public function get_completed_order_count()
297 {
298 return OrderRepository::init()->retrieveBy([
299 'subscription_id' => $this->id,
300 'status' => OrderStatus::COMPLETED
301 ], true);
302 }
303
304 public function set_trial_period($period)
305 {
306 $valid_periods = (new \ReflectionClass(SubscriptionTrialPeriod::class))->getConstants();
307
308 if (in_array($period, $valid_periods)) {
309 $this->trial_period = $period;
310 }
311 }
312
313 public function get_profile_id()
314 {
315 return apply_filters('ppress_subscription_profile_id', $this->profile_id, $this->get_id(), $this);
316 }
317
318 protected function set_status($status)
319 {
320 $valid_statuses = (new \ReflectionClass(SubscriptionStatus::class))->getConstants();
321
322 if (in_array($status, $valid_statuses)) {
323 $this->status = $status;
324 }
325 }
326
327 public function get_status()
328 {
329 return apply_filters('ppress_subscription_status', $this->status, $this->get_id(), $this);
330 }
331
332 public function get_payment_method()
333 {
334 return OrderFactory::fromId($this->parent_order_id)->payment_method;
335 }
336
337 public function get_formatted_expiration_date()
338 {
339 $date = esc_html__('Lifetime', 'wp-user-avatar');
340
341 if ( ! $this->is_lifetime()) {
342 $date = ppress_format_date($this->expiration_date);
343 }
344
345 return $date;
346 }
347
348 public function get_notes()
349 {
350 return apply_filters('ppress_subscription_note', $this->notes, $this->get_id(), $this);
351 }
352
353 public function add_note($note)
354 {
355 $notes = $this->notes;
356
357 $notes[] = ppress_format_date_time(time()) . '|' . $note;
358
359 return SubscriptionRepository::init()->updateColumn($this->id, 'notes', json_encode($notes));
360 }
361
362 public function get_subscription_terms()
363 {
364 $price = $this->get_recurring_amount();
365
366 if ($this->billing_frequency == SubscriptionBillingFrequency::ONE_TIME) {
367 $price = $this->get_initial_amount();
368 }
369
370 $cycle = ppress_display_amount(
371 $price,
372 OrderFactory::fromId($this->parent_order_id)->currency
373 );
374
375 $cycle .= ' / ';
376
377 $cycle .= SubscriptionBillingFrequency::get_label($this->billing_frequency);
378
379 return $cycle;
380 }
381
382 /**
383 * Retrieve all subscription related payments.
384 *
385 * @return OrderEntity[]|string
386 */
387 public function get_all_orders()
388 {
389 return OrderRepository::init()->retrieveBy([
390 'subscription_id' => $this->id,
391 'number' => 0
392 ]);
393 }
394
395 /**
396 * @param $profile_id
397 *
398 * @return false|int
399 */
400 public function activate_subscription($profile_id = '')
401 {
402 if ($this->is_active()) return false;
403
404 $this->status = SubscriptionStatus::ACTIVE;
405
406 if ( ! empty($profile_id)) {
407 $this->profile_id = $profile_id;
408 }
409
410 $sub_id = $this->save();
411
412 do_action('ppress_subscription_activated', $this);
413
414 $this->maybe_complete_subscription();
415
416 return $sub_id;
417 }
418
419 /**
420 * @param $profile_id
421 *
422 * @return false|int
423 */
424 public function enable_subscription_trial($profile_id = '')
425 {
426 if ($this->is_active()) return false;
427
428 $this->status = SubscriptionStatus::TRIALLING;
429
430 if ( ! empty($profile_id)) {
431 $this->profile_id = $profile_id;
432 }
433
434 $sub_id = $this->save();
435
436 do_action('ppress_subscription_enabled_trial', $this);
437
438 $this->maybe_complete_subscription();
439
440 return $sub_id;
441 }
442
443 public function update_profile_id($val)
444 {
445 return SubscriptionRepository::init()->updateColumn($this->id, 'profile_id', $val);
446 }
447
448 /**
449 * @param $subscription_status
450 *
451 * @return false|int
452 */
453 public function update_status($subscription_status)
454 {
455 $old_status = $this->status;
456
457 $this->status = $subscription_status;
458
459 $response = $this->save();
460
461 $user = is_user_logged_in() ? wp_get_current_user()->user_login : esc_html__('payment method', 'wp-user-avatar');
462
463 $this->add_note(
464 sprintf(__('Subscription changed from %s to %s by %s', 'wp-user-avatar'), $old_status, $this->status, $user)
465 );
466
467 do_action('ppress_subscription_status_updated', $subscription_status, $old_status, $this);
468
469 return $response;
470 }
471
472
473 /**
474 * Returns the number of times the subscription has been billed
475 *
476 * @return int
477 */
478 public function get_times_billed()
479 {
480 $orders = OrderRepository::init()->retrieveBy([
481 'subscription_id' => $this->id,
482 'status' => [OrderStatus::COMPLETED],
483 'number' => 0
484 ]);
485
486 return count($orders);
487 }
488
489 /**
490 * Determines if subscription can be cancelled
491 *
492 * This method is filtered by payment methods in order to return true on subscriptions
493 * that can be cancelled with a profile ID through the merchant processor
494 *
495 * @return bool
496 */
497 public function can_cancel()
498 {
499 if ($this->is_lifetime()) return false;
500
501 return apply_filters('ppress_subscription_can_cancel', false, $this);
502 }
503
504 public function has_cancellation_requested()
505 {
506 $val = PROFILEPRESS_sql::get_meta_data_by_key('subscription_cancellation_requested_' . $this->id);
507
508 return is_array($val) && isset($val[0]['meta_value']) && 'true' == $val[0]['meta_value'];
509 }
510
511 public function add_cancellation_requested()
512 {
513 $this->delete_cancellation_requested(); // cleanup first
514 PROFILEPRESS_sql::add_meta_data('subscription_cancellation_requested_' . $this->id, 'true');
515 }
516
517 public function delete_cancellation_requested()
518 {
519 PROFILEPRESS_sql::delete_meta_data_by_meta_key('subscription_cancellation_requested_' . $this->id);
520 }
521
522 /**
523 * @param bool $gateway_cancel set to true to cancel sub in gateway too.
524 *
525 * @return false|void
526 */
527 public function cancel($gateway_cancel = false)
528 {
529 if ($this->is_cancelled()) return false;
530
531 $sub = clone $this; // gateway cancel() check sub status first before cancelling. hence we need a copy of the sub before it's cancelled below.
532
533 $old_status = $this->status;
534
535 $this->update_status(SubscriptionStatus::CANCELLED);
536
537 if ($gateway_cancel === true && $sub->can_cancel()) {
538 PaymentMethods::get_instance()->get_by_id($this->get_payment_method())->cancel($sub);
539 }
540
541 $this->add_cancellation_requested();
542
543 do_action('ppress_subscription_cancelled', $this, $old_status);
544 }
545
546 /**
547 * @return false|void
548 */
549 public function complete()
550 {
551 // Prevent setting a subscription as complete if it was previously set as cancelled.
552 if ($this->is_cancelled()) return false;
553
554 if ($this->update_status(SubscriptionStatus::COMPLETED)) {
555
556 do_action('ppress_subscription_completed', $this);
557 }
558 }
559
560 /**
561 * @param bool $check_expiration
562 *
563 * @return false|void
564 */
565 public function expire($check_expiration = false)
566 {
567 if ($this->is_lifetime()) return false;
568
569 if ($check_expiration && time() < ppress_strtotime_utc($this->expiration_date)) {
570 return false; // Do not mark as expired since real expiration date is in the future
571 }
572
573 $this->update_status(SubscriptionStatus::EXPIRED);
574
575 do_action('ppress_subscription_expired', $this);
576 }
577
578 public function renew($change_expiry_date = true, $expiration_date = '')
579 {
580 if ( ! empty($expiration_date)) {
581 $expiration = CarbonImmutable::createFromTimestampUTC($expiration_date);
582 } else {
583
584 $old_expiry_date = ppress_date_to_utc_timestamp($this->expiration_date);
585
586 // Determine what date to use as the start for the new expiration calculation
587 if ($old_expiry_date > time() && $this->is_active()) {
588 $base_date = $old_expiry_date;
589 } else {
590 $base_date = time();
591 }
592
593 $cabonInstance = CarbonImmutable::createFromTimestampUTC($base_date);
594
595 switch ($this->billing_frequency) {
596 case SubscriptionBillingFrequency::DAILY :
597 $expiration = $cabonInstance->addDay();
598 break;
599 case SubscriptionBillingFrequency::WEEKLY :
600 $expiration = $cabonInstance->addWeek();
601 break;
602 case SubscriptionBillingFrequency::QUARTERLY :
603 $expiration = $cabonInstance->addMonths(3);
604 break;
605 case SubscriptionBillingFrequency::EVERY_6_MONTHS :
606 $expiration = $cabonInstance->addMonths(6);
607 break;
608 case SubscriptionBillingFrequency::YEARLY :
609 $expiration = $cabonInstance->addYear();
610 break;
611 default:
612 $expiration = $cabonInstance->addMonth();
613 break;
614 }
615 }
616
617 $expiration = apply_filters('ppress_subscription_renewal_expiration', $expiration->toDateTimeString(), $this->id, $this);
618
619 do_action('ppress_subscription_pre_renew', $this->id, $expiration, $this);
620
621 if ($change_expiry_date === true) {
622 $this->expiration_date = $expiration;
623 }
624
625 $this->status = SubscriptionStatus::ACTIVE;
626 $this->save();
627
628 $this->maybe_complete_subscription();
629
630 do_action('ppress_subscription_post_renew', $this->id, $expiration, $this);
631 }
632
633 public function maybe_complete_subscription()
634 {
635 if ($this->is_completed()) return;
636
637 $times_billed = $this->get_times_billed();
638
639 // Complete subscription if applicable
640 if ($this->total_payments > 0 && $times_billed >= $this->total_payments) {
641 $this->complete();
642 }
643 }
644
645 /**
646 * @param $args
647 *
648 * @return false|int
649 */
650 public function add_renewal_order($args)
651 {
652 $result = OrderService::init()->record_subscription_renewal_order(
653 $args,
654 $this
655 );
656
657 do_action('ppress_subscription_renewal_order_added', $args, $this);
658
659 return $result;
660 }
661
662 /**
663 * @return false|int
664 */
665 public function save()
666 {
667 if ($this->exists()) {
668
669 $result = SubscriptionRepository::init()->update($this);
670
671 do_action('ppress_membership_update_subscription', $result, $this);
672
673 return $result;
674 }
675
676 $result = SubscriptionRepository::init()->add($this);
677
678 do_action('ppress_membership_add_subscription', $result, $this);
679
680 return $result;
681 }
682 }