Completed
Push — develop ( f29211...ed262e )
by Remco
10:29 queued 57s
created

get_expiring_subscription_posts()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 20
nc 1
nop 2
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Subscriptions Module
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2018 Pronamic
7
 * @license   GPL-3.0-or-later
8
 * @package   Pronamic\WordPress\Pay\Subscriptions
9
 */
10
11
namespace Pronamic\WordPress\Pay\Subscriptions;
12
13
use DateInterval;
14
use DatePeriod;
15
use Pronamic\WordPress\Pay\Plugin;
16
use Pronamic\WordPress\Pay\Core\Gateway;
17
use Pronamic\WordPress\Pay\Core\Statuses;
18
use Pronamic\WordPress\Pay\Payments\Payment;
19
use WP_CLI;
0 ignored issues
show
Bug introduced by
The type WP_CLI was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use WP_Query;
21
22
/**
23
 * Title: Subscriptions module
24
 * Description:
25
 * Copyright: Copyright (c) 2005 - 2018
26
 * Company: Pronamic
27
 *
28
 * @see https://woocommerce.com/2017/04/woocommerce-3-0-release/
29
 * @see https://woocommerce.wordpress.com/2016/10/27/the-new-crud-classes-in-woocommerce-2-7/
30
 * @author Remco Tolsma
31
 * @version 3.7.0
32
 * @since 3.7.0
33
 */
34
class SubscriptionsModule {
35
	/**
36
	 * Plugin.
37
	 *
38
	 * @var Plugin $plugin
39
	 */
40
	public $plugin;
41
42
	/**
43
	 * Construct and initialize a subscriptions module object.
44
	 *
45
	 * @param Plugin $plugin The plugin.
46
	 */
47
	public function __construct( Plugin $plugin ) {
48
		$this->plugin = $plugin;
49
50
		// Actions.
51
		add_action( 'wp_loaded', array( $this, 'handle_subscription' ) );
52
53
		add_action( 'plugins_loaded', array( $this, 'maybe_schedule_subscription_payments' ), 5 );
54
55
		// Exclude payment and subscription notes.
56
		add_filter( 'comments_clauses', array( $this, 'exclude_subscription_comment_notes' ), 10, 2 );
57
58
		add_action( 'pronamic_pay_new_payment', array( $this, 'maybe_create_subscription' ) );
59
60
		// The 'pronamic_pay_update_subscription_payments' hook adds subscription payments and sends renewal notices.
61
		add_action( 'pronamic_pay_update_subscription_payments', array( $this, 'update_subscription_payments' ) );
62
63
		// Listen to payment status changes so we can update related subscriptions.
64
		add_action( 'pronamic_payment_status_update', array( $this, 'payment_status_update' ) );
65
66
		// WordPress CLI.
67
		// @see https://github.com/woocommerce/woocommerce/blob/3.3.1/includes/class-woocommerce.php#L365-L369.
68
		// @see https://github.com/woocommerce/woocommerce/blob/3.3.1/includes/class-wc-cli.php.
69
		// @see https://make.wordpress.org/cli/handbook/commands-cookbook/.
70
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
0 ignored issues
show
Bug introduced by
The constant Pronamic\WordPress\Pay\Subscriptions\WP_CLI was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
71
			WP_CLI::add_command( 'pay subscriptions test', array( $this, 'cli_subscriptions_test' ) );
72
		}
73
	}
74
75
	/**
76
	 * Handle subscription actions.
77
	 *
78
	 * Extensions like Gravity Forms can send action links in for example
79
	 * email notifications so users can cancel or renew their subscription.
80
	 */
81
	public function handle_subscription() {
82
		if ( ! filter_has_var( INPUT_GET, 'subscription' ) ) {
83
			return;
84
		}
85
86
		if ( ! filter_has_var( INPUT_GET, 'action' ) ) {
87
			return;
88
		}
89
90
		if ( ! filter_has_var( INPUT_GET, 'key' ) ) {
91
			return;
92
		}
93
94
		$subscription_id = filter_input( INPUT_GET, 'subscription', FILTER_SANITIZE_STRING );
95
		$subscription    = get_pronamic_subscription( $subscription_id );
96
97
		$action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING );
98
99
		$key = filter_input( INPUT_GET, 'key', FILTER_SANITIZE_STRING );
100
101
		// Check if subscription is valid.
102
		if ( ! $subscription ) {
103
			return;
104
		}
105
106
		// Check if subscription key is valid.
107
		if ( $key !== $subscription->get_key() ) {
108
			wp_redirect( home_url() );
109
110
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
111
		}
112
113
		// Check if we should redirect.
114
		$should_redirect = true;
115
116
		switch ( $action ) {
117
			case 'cancel':
118
				if ( Statuses::CANCELLED !== $subscription->get_status() ) {
119
					$subscription->set_status( Statuses::CANCELLED );
120
121
					$this->update_subscription( $subscription, $should_redirect );
122
				}
123
124
				break;
125
			case 'renew':
126
				$gateway = Plugin::get_gateway( $subscription->config_id );
127
128
				if ( Statuses::SUCCESS !== $subscription->get_status() ) {
129
					$payment = $this->start_recurring( $subscription, $gateway, true );
130
131
					if ( ! $gateway->has_error() ) {
132
						// Redirect.
133
						$gateway->redirect( $payment );
134
					}
135
				}
136
137
				wp_redirect( home_url() );
138
139
				exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
140
		}
141
	}
142
143
	/**
144
	 * Start a recurring payment at the specified gateway for the specified subscription.
145
	 *
146
	 * @param Subscription $subscription The subscription to start a recurring payment for.
147
	 * @param Gateway      $gateway      The gateway to start the recurring payment at.
148
	 * @param boolean      $renewal      Flag for renewal payment.
149
	 */
150
	public function start_recurring( Subscription $subscription, Gateway $gateway, $renewal = false ) {
151
		if ( empty( $subscription->next_payment ) ) {
152
			$subscription->status = Statuses::COMPLETED;
153
154
			$result = $this->plugin->subscriptions_data_store->update( $subscription );
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
Bug introduced by
Are you sure the assignment to $result is correct as $this->plugin->subscript...->update($subscription) targeting Pronamic\WordPress\Pay\S...sDataStoreCPT::update() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
155
156
			// @todo
157
			return;
158
		}
159
160
		if ( ! empty( $subscription->end_date ) && $subscription->end_date <= $subscription->next_payment ) {
161
			$subscription->next_payment = null;
162
			$subscription->status       = Statuses::COMPLETED;
163
164
			$result = $this->plugin->subscriptions_data_store->update( $subscription );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->plugin->subscript...->update($subscription) targeting Pronamic\WordPress\Pay\S...sDataStoreCPT::update() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
165
166
			// @todo
167
			return;
168
		}
169
170
		$start_date = $subscription->next_payment;
171
172
		$end_date = clone $start_date;
173
		$end_date->add( $subscription->get_date_interval() );
174
175
		$subscription->next_payment = $end_date;
176
177
		// Create follow up payment.
178
		$payment = new Payment();
179
180
		$payment->config_id        = $subscription->config_id;
0 ignored issues
show
Documentation Bug introduced by
The property $config_id was declared of type integer, but $subscription->config_id is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
181
		$payment->user_id          = $subscription->user_id;
0 ignored issues
show
Bug introduced by
The property user_id does not seem to exist on Pronamic\WordPress\Pay\Payments\Payment.
Loading history...
Bug introduced by
The property user_id does not seem to exist on Pronamic\WordPress\Pay\Subscriptions\Subscription.
Loading history...
182
		$payment->source           = $subscription->source;
183
		$payment->source_id        = $subscription->source_id;
184
		$payment->description      = $subscription->description;
185
		$payment->order_id         = $subscription->order_id;
186
		$payment->currency         = $subscription->currency;
187
		$payment->email            = $subscription->email;
188
		$payment->customer_name    = $subscription->customer_name;
189
		$payment->address          = $subscription->address;
190
		$payment->address          = $subscription->address;
191
		$payment->city             = $subscription->city;
192
		$payment->zip              = $subscription->zip;
193
		$payment->country          = $subscription->country;
194
		$payment->telephone_number = $subscription->telephone_number;
195
		$payment->method           = $subscription->payment_method;
196
		$payment->subscription     = $subscription;
197
		$payment->subscription_id  = $subscription->get_id();
198
		$payment->amount           = $subscription->amount;
199
		$payment->start_date       = $start_date;
200
		$payment->end_date         = $end_date;
201
		$payment->recurring_type   = 'recurring';
202
		$payment->recurring        = ! $renewal;
203
204
		// Start payment.
205
		$payment = Plugin::start_payment( $payment, $gateway );
206
207
		// Update subscription.
208
		$result = $this->plugin->subscriptions_data_store->update( $subscription );
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->plugin->subscript...->update($subscription) targeting Pronamic\WordPress\Pay\S...sDataStoreCPT::update() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
209
210
		return $payment;
211
	}
212
213
	/**
214
	 * Update the specified subscription and redirect if allowed.
215
	 *
216
	 * @param Subscription $subscription The updated subscription.
217
	 * @param boolean      $can_redirect Flag to redirect or not.
218
	 */
219
	public function update_subscription( $subscription = null, $can_redirect = true ) {
220
		if ( empty( $subscription ) ) {
221
			return;
222
		}
223
224
		$this->plugin->subscriptions_data_store->update( $subscription );
225
226
		if ( defined( 'DOING_CRON' ) && empty( $subscription->status ) ) {
227
			$can_redirect = false;
228
		}
229
230
		if ( $can_redirect ) {
231
			wp_redirect( home_url() );
232
233
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
234
		}
235
	}
236
237
	/**
238
	 * Comments clauses.
239
	 *
240
	 * @param array            $clauses The database query clauses.
241
	 * @param WP_Comment_Query $query   The WordPress comment query object.
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Pay\S...ptions\WP_Comment_Query was not found. Did you mean WP_Comment_Query? If so, make sure to prefix the type with \.
Loading history...
242
	 * @return array
243
	 */
244
	public function exclude_subscription_comment_notes( $clauses, $query ) {
245
		$type = $query->query_vars['type'];
246
247
		// Ignore subscription notes comments if it's not specifically requested.
248
		if ( 'subscription_note' !== $type ) {
249
			$clauses['where'] .= " AND comment_type != 'subscription_note'";
250
		}
251
252
		return $clauses;
253
	}
254
255
	/**
256
	 * Maybe schedule subscription payments.
257
	 */
258
	public function maybe_schedule_subscription_payments() {
259
		if ( wp_next_scheduled( 'pronamic_pay_update_subscription_payments' ) ) {
260
			return;
261
		}
262
263
		wp_schedule_event( time(), 'hourly', 'pronamic_pay_update_subscription_payments' );
264
	}
265
266
	/**
267
	 * Maybe create subscription for the specified payment.
268
	 *
269
	 * @param Payment $payment The new payment.
270
	 */
271
	public function maybe_create_subscription( $payment ) {
272
		// Check if there is already subscription attached to the payment.
273
		$subscription_id = $payment->get_subscription_id();
274
275
		if ( ! empty( $subscription_id ) ) {
276
			// Subscription already created.
277
			return;
278
		}
279
280
		// Check if there is a subscription object attached to the payment.
281
		$subscription_data = $payment->subscription;
282
283
		if ( empty( $subscription_data ) ) {
284
			return;
285
		}
286
287
		// New subscription.
288
		$subscription = new Subscription();
289
290
		$subscription->config_id       = $payment->config_id;
291
		$subscription->user_id         = $payment->user_id;
0 ignored issues
show
Bug introduced by
The property user_id does not seem to exist on Pronamic\WordPress\Pay\Payments\Payment.
Loading history...
Bug introduced by
The property user_id does not seem to exist on Pronamic\WordPress\Pay\Subscriptions\Subscription.
Loading history...
292
		$subscription->title           = sprintf( __( 'Subscription for %s', 'pronamic_ideal' ), $payment->title );
0 ignored issues
show
Bug introduced by
The property title does not seem to exist on Pronamic\WordPress\Pay\Subscriptions\Subscription.
Loading history...
293
		$subscription->frequency       = $subscription_data->get_frequency();
294
		$subscription->interval        = $subscription_data->get_interval();
295
		$subscription->interval_period = $subscription_data->get_interval_period();
0 ignored issues
show
Documentation Bug introduced by
The property $interval_period was declared of type integer, but $subscription_data->get_interval_period() is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
296
		$subscription->currency        = $subscription_data->get_currency();
297
		$subscription->amount          = $subscription_data->get_amount();
298
		$subscription->key             = uniqid( 'subscr_' );
299
		$subscription->source          = $payment->source;
300
		$subscription->source_id       = $payment->source_id;
301
		$subscription->description     = $payment->description;
302
		$subscription->email           = $payment->email;
303
		$subscription->customer_name   = $payment->customer_name;
304
		$subscription->payment_method  = $payment->method;
305
		$subscription->first_payment   = $payment->date;
0 ignored issues
show
Bug introduced by
The property first_payment does not seem to exist on Pronamic\WordPress\Pay\Subscriptions\Subscription.
Loading history...
306
307
		// @todo
308
		// Calculate dates
309
		// @see https://github.com/pronamic/wp-pronamic-ideal/blob/4.7.0/classes/Pronamic/WP/Pay/Plugin.php#L883-L964
310
		$interval = $subscription->get_date_interval();
311
312
		$start_date  = clone $payment->date;
313
		$expiry_date = clone $start_date;
314
315
		$next_date = clone $start_date;
316
		$next_date->add( $interval );
317
318
		$end_date = null;
319
320
		if ( $subscription_data->frequency ) {
321
			// @see https://stackoverflow.com/a/10818981/6411283
322
			$period = new DatePeriod( $start_date, $interval, $subscription_data->frequency );
0 ignored issues
show
Bug introduced by
$subscription_data->frequency of type string is incompatible with the type DateTimeInterface expected by parameter $end of DatePeriod::__construct(). ( Ignorable by Annotation )

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

322
			$period = new DatePeriod( $start_date, $interval, /** @scrutinizer ignore-type */ $subscription_data->frequency );
Loading history...
323
324
			$dates = iterator_to_array( $period );
325
326
			$end_date = end( $dates );
327
		}
328
329
		$subscription->start_date   = $start_date;
0 ignored issues
show
Documentation Bug introduced by
$start_date is of type DateTime, but the property $start_date was declared to be of type Pronamic\WordPress\Pay\DateTime. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
330
		$subscription->end_date     = $end_date;
331
		$subscription->expiry_date  = $expiry_date;
0 ignored issues
show
Documentation Bug introduced by
$expiry_date is of type DateTime, but the property $expiry_date was declared to be of type Pronamic\WordPress\Pay\DateTime. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
332
		$subscription->next_payment = $next_date;
0 ignored issues
show
Documentation Bug introduced by
$next_date is of type DateTime, but the property $next_payment was declared to be of type Pronamic\WordPress\Pay\DateTime. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
333
334
		// Create.
335
		$result = $this->plugin->subscriptions_data_store->create( $subscription );
336
337
		if ( $result ) {
338
			$payment->subscription_id = $subscription->get_id();
339
340
			$payment->recurring_type = 'first';
341
			$payment->start_date     = $start_date;
342
			$payment->end_date       = $next_date;
343
344
			$this->plugin->payments_data_store->update( $payment );
345
		}
346
	}
347
348
	/**
349
	 * Get expiring subscriptions.
350
	 *
351
	 * @see https://github.com/wp-premium/edd-software-licensing/blob/3.5.23/includes/license-renewals.php#L715-L746
352
	 * @see https://github.com/wp-premium/edd-software-licensing/blob/3.5.23/includes/license-renewals.php#L652-L712
353
	 *
354
	 * @param DateTime $start_date The start date of the period to check for expiring subscriptions.
355
	 * @param DateTime $end_date   The end date of the period to check for expiring subscriptions.
356
	 * @return array
357
	 */
358
	public function get_expiring_subscription_posts( DateTime $start_date, DateTime $end_date ) {
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Pay\Subscriptions\DateTime was not found. Did you mean DateTime? If so, make sure to prefix the type with \.
Loading history...
359
		$args = array(
360
			'post_type'   => 'pronamic_pay_subscr',
361
			'nopaging'    => true,
362
			'orderby'     => 'post_date',
363
			'order'       => 'ASC',
364
			'post_status' => array(
365
				'subscr_pending',
366
				'subscr_expired',
367
				'subscr_failed',
368
				'subscr_active',
369
			),
370
			'meta_query'  => array(
371
				array(
372
					'key'     => '_pronamic_subscription_expiry_date',
373
					'value'   => array(
374
						$start_date->format( DateTime::MYSQL ),
375
						$end_date->format( DateTime::MYSQL ),
376
					),
377
					'compare' => 'BETWEEN',
378
					'type'    => 'DATETIME',
379
				),
380
			),
381
		);
382
383
		$query = new WP_Query( $args );
384
385
		return $query->posts;
386
	}
387
388
	/**
389
	 * Update subscription payments.
390
	 */
391
	public function update_subscription_payments() {
392
		$this->send_subscription_renewal_notices();
393
394
		// Don't create payments for sources which schedule payments.
395
		$sources = array(
396
			'woocommerce',
397
		);
398
399
		$args = array(
400
			'post_type'   => 'pronamic_pay_subscr',
401
			'nopaging'    => true,
402
			'orderby'     => 'post_date',
403
			'order'       => 'ASC',
404
			'post_status' => array(
405
				'subscr_pending',
406
				'subscr_expired',
407
				'subscr_failed',
408
				'subscr_active',
409
			),
410
			'meta_query'  => array(
411
				array(
412
					'key'     => '_pronamic_subscription_source',
413
					'compare' => 'NOT IN',
414
					'value'   => $sources,
415
				),
416
				array(
417
					'key'     => '_pronamic_subscription_next_payment',
418
					'compare' => '<=',
419
					'value'   => current_time( 'mysql', true ),
420
					'type'    => 'DATETIME',
421
				),
422
			),
423
		);
424
425
		$query = new WP_Query( $args );
426
427
		foreach ( $query->posts as $post ) {
428
			$subscription = new Subscription( $post->ID );
429
430
			$gateway = Plugin::get_gateway( $subscription->config_id );
431
432
			$payment = $this->start_recurring( $subscription, $gateway );
433
434
			if ( $payment ) {
435
				Plugin::update_payment( $payment, false );
436
			}
437
		}
438
	}
439
440
	/**
441
	 * Send renewal notices.
442
	 *
443
	 * @see https://github.com/wp-premium/edd-software-licensing/blob/3.5.23/includes/license-renewals.php#L652-L712
444
	 * @see https://github.com/wp-premium/edd-software-licensing/blob/3.5.23/includes/license-renewals.php#L715-L746
445
	 * @see https://github.com/wp-premium/edd-software-licensing/blob/3.5.23/includes/classes/class-sl-emails.php#L41-L126
446
	 */
447
	public function send_subscription_renewal_notices() {
448
		$interval = new DateInterval( 'P1W' ); // 1 week
449
450
		$start_date = new DateTime( 'midnight', new DateTimeZone( 'UTC' ) );
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Pay\Subscriptions\DateTimeZone was not found. Did you mean DateTimeZone? If so, make sure to prefix the type with \.
Loading history...
451
452
		$end_date = clone $start_date;
453
		$end_date->add( $interval );
454
455
		$expiring_subscription_posts = $this->get_expiring_subscription_posts( $start_date, $end_date );
456
457
		foreach ( $expiring_subscription_posts as $post ) {
458
			$subscription = new Subscription( $post->ID );
459
460
			$expiry_date = $subscription->get_expiry_date();
461
462
			$sent_date_string = get_post_meta( $post->ID, '_pronamic_subscription_renewal_sent_1week', true );
463
464
			if ( $sent_date_string ) {
465
				$first_date = clone $expiry_date;
466
				$first_date->sub( $interval );
467
468
				$sent_date = new DateTime( $sent_date_string, new DateTimeZone( 'UTC' ) );
469
470
				if ( $sent_date > $first_date ) {
471
					// Prevent renewal notices from being sent more than once.
472
					continue;
473
				}
474
475
				delete_post_meta( $post->ID, '_pronamic_subscription_renewal_sent_1week' );
476
			}
477
478
			do_action( 'pronamic_subscription_renewal_notice_' . $subscription->get_source(), $subscription );
479
480
			update_post_meta( $post->ID, '_pronamic_subscription_renewal_sent_1week', $start_date->format( DateTime::MYSQL ) );
481
		}
482
	}
483
484
	/**
485
	 * Payment status update.
486
	 *
487
	 * @param Payment $payment The status updated payment.
488
	 */
489
	public function payment_status_update( $payment ) {
490
		// Check if the payment is connected to a subscription.
491
		$subscription = $payment->get_subscription();
492
493
		if ( empty( $subscription ) ) {
494
			// Payment not connected to a subscription, nothing to do.
495
			return;
496
		}
497
498
		if ( empty( $payment->start_date ) ) {
499
			return;
500
		}
501
502
		if ( empty( $payment->end_date ) ) {
503
			return;
504
		}
505
506
		if ( Statuses::CANCELLED === $subscription->get_status() ) {
507
			// If subscritpion is cancelled never change the subscription status.
508
			return;
509
		}
510
511
		// Status.
512
		$status_before = $subscription->get_status();
513
514
		switch ( $payment->get_status() ) {
515
			case Statuses::OPEN:
516
				// @todo
517
				break;
518
			case Statuses::SUCCESS:
519
				$subscription->set_status( Statuses::ACTIVE );
520
521
				if ( $subscription->expiry_date < $payment->end_date ) {
522
					$subscription->expiry_date = $payment->end_date;
0 ignored issues
show
Documentation Bug introduced by
$payment->end_date is of type DateTime, but the property $expiry_date was declared to be of type Pronamic\WordPress\Pay\DateTime. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
523
				}
524
525
				break;
526
			case Statuses::FAILURE:
527
				$subscription->set_status( Statuses::CANCELLED );
528
529
				break;
530
			case Statuses::CANCELLED:
531
				$subscription->set_status( Statuses::CANCELLED );
532
533
				break;
534
			case Statuses::EXPIRED:
535
				$subscription->set_status( Statuses::CANCELLED );
536
537
				break;
538
			case Statuses::COMPLETED:
539
				$subscription->set_status( Statuses::COMPLETED );
540
541
				break;
542
		}
543
544
		$status_after = $subscription->get_status();
545
546
		if ( $status_before !== $status_after ) {
547
			$subscription->add_note( sprintf(
548
				__( 'Subscription status changed from "%1$s" to "%2$s".', 'pronamic_ideal' ),
549
				esc_html( $this->plugin->subscriptions_data_store->get_meta_status_label( $status_before ) ),
550
				esc_html( $this->plugin->subscriptions_data_store->get_meta_status_label( $status_after ) )
551
			) );
552
		}
553
554
		// Update.
555
		$result = $this->plugin->subscriptions_data_store->update( $subscription );
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
Bug introduced by
Are you sure the assignment to $result is correct as $this->plugin->subscript...->update($subscription) targeting Pronamic\WordPress\Pay\S...sDataStoreCPT::update() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
556
	}
557
558
	/**
559
	 * CLI subscriptions test.
560
	 */
561
	public function cli_subscriptions_test() {
562
		$args = array(
563
			'post_type'   => 'pronamic_pay_subscr',
564
			'nopaging'    => true,
565
			'orderby'     => 'post_date',
566
			'order'       => 'ASC',
567
			'post_status' => array(
568
				'subscr_pending',
569
				'subscr_expired',
570
				'subscr_failed',
571
				'subscr_active',
572
			),
573
			'meta_query'  => array(
574
				array(
575
					'key'     => '_pronamic_subscription_source',
576
					'compare' => 'NOT IN',
577
					'value'   => array(
578
						// Don't create payments for sources which schedule payments.
579
						'woocommerce',
580
					),
581
				),
582
			),
583
		);
584
585
		$query = new WP_Query( $args );
586
587
		foreach ( $query->posts as $post ) {
588
			WP_CLI::log( sprintf( 'Processing post `%d` - "%s"…', $post->ID, get_the_title( $post ) ) );
589
590
			$subscription = new Subscription( $post->ID );
591
592
			$gateway = Plugin::get_gateway( $subscription->config_id );
593
594
			$payment = $this->start_recurring( $subscription, $gateway );
595
596
			if ( $payment ) {
597
				Plugin::update_payment( $payment, false );
598
			}
599
		}
600
601
		WP_CLI::success( 'Pronamic Pay subscriptions test.' );
602
	}
603
}
604