Failed Conditions
Push — develop ( 9ec485...d3f65e )
by Remco
05:01
created

Extension::__construct()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 75
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 41
nc 1
nop 0
dl 0
loc 75
ccs 0
cts 47
cp 0
crap 20
rs 9.264
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Extension
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2019 Pronamic
7
 * @license   GPL-3.0-or-later
8
 * @package   Pronamic\WordPress\Pay\Extensions\MemberPress
9
 */
10
11
namespace Pronamic\WordPress\Pay\Extensions\MemberPress;
12
13
use MeprDb;
14
use MeprOptions;
15
use MeprProduct;
16
use MeprSubscription;
17
use MeprTransaction;
18
use MeprUtils;
19
use Pronamic\WordPress\Pay\Core\Statuses;
20
use Pronamic\WordPress\Pay\Payments\Payment;
21
use Pronamic\WordPress\Pay\Subscriptions\Subscription;
22
23
/**
24
 * WordPress pay MemberPress extension
25
 *
26
 * @author  Remco Tolsma
27
 * @version 2.0.4
28
 * @since   1.0.0
29
 */
30
class Extension {
31
	/**
32
	 * The slug of this addon
33
	 *
34
	 * @var string
35
	 */
36
	const SLUG = 'memberpress';
37
38
	/**
39
	 * Bootstrap
40
	 */
41
	public static function bootstrap() {
42
		new self();
43
	}
44
45
	/**
46
	 * Constructs and initializes the MemberPress extension.
47
	 */
48
	public function __construct() {
49
		// @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/lib/MeprGatewayFactory.php#L48-50
50
		add_filter( 'mepr-gateway-paths', array( $this, 'gateway_paths' ) );
51
52
		add_filter( 'pronamic_payment_redirect_url_' . self::SLUG, array( __CLASS__, 'redirect_url' ), 10, 2 );
53
		add_action( 'pronamic_payment_status_update_' . self::SLUG, array( __CLASS__, 'status_update' ), 10, 1 );
54
55
		add_filter( 'pronamic_payment_source_text_' . self::SLUG, array( __CLASS__, 'source_text' ), 10, 2 );
56
		add_filter( 'pronamic_payment_source_description_' . self::SLUG, array( __CLASS__, 'source_description' ), 10, 2 );
57
		add_filter( 'pronamic_payment_source_url_' . self::SLUG, array( __CLASS__, 'source_url' ), 10, 2 );
58
		add_filter( 'pronamic_subscription_source_text_' . self::SLUG, array( __CLASS__, 'subscription_source_text' ), 10, 2 );
59
		add_filter( 'pronamic_subscription_source_description_' . self::SLUG, array( __CLASS__, 'subscription_source_description' ), 10, 2 );
60
		add_filter( 'pronamic_subscription_source_url_' . self::SLUG, array( __CLASS__, 'subscription_source_url' ), 10, 2 );
61
62
		add_action( 'mepr_subscription_pre_delete', array( $this, 'subscription_pre_delete' ), 10, 1 );
63
64
		add_action( 'mepr_subscription_transition_status', array( $this, 'memberpress_subscription_transition_status' ), 10, 3 );
65
66
		add_filter( 'mepr_view_get_string', function( $view, $slug, $vars ) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
67
			if ( '/admin/transactions/trans_form' !== $slug ) {
68
				return $view;
69
			}
70
71
			$view .= '<tr valign="top">
72
  <th scope="row"><label for="trans_num">Test*:</label></th>
73
  <td>
74
    <input type="text" name="trans_num" id="trans_num" value="tr_xQfb8uavMT" class="regular-text" />
75
    <p class="description">A unique Invoice ID for this Transaction. Only edit this if you absolutely have to.</p>
76
  </td>
77
</tr>';
78
79
			return $view;
80
		}, 10, 3 );
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
81
82
		// @link https://github.com/wp-premium/memberpress-business/blob/1.3.36/app/controllers/MeprTransactionsCtrl.php#L17-L21
83
		$hook = 'memberpress_page_memberpress-trans';
84
85
		add_filter( 'manage_' .$hook . '_columns', function( $columns ) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
Coding Style introduced by
Concat operator must be surrounded by a single space
Loading history...
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
86
			$columns['pronamic_payment'] = __( 'Pronamic Payment', 'pronamic_ideal' );
87
88
			return $columns;
89
		}, 10 );
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
90
91
		add_filter( 'mepr_view_get_string', function( $view, $slug, $vars ) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
92
			if ( '/admin/transactions/row' !== $slug ) {
93
				return $view;
94
			}
95
96
			$new_column = '<td>Test</dt>';
97
98
			$view = str_replace( '</tr>', $new_column . '</tr>', $view );
99
100
			return $view;
101
		}, 10, 3 );
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
102
103
		// @link https://github.com/wp-premium/memberpress-business/blob/1.3.36/app/controllers/MeprSubscriptionsCtrl.php#L19-L26
104
		$hook = 'memberpress_page_memberpress-subscriptions';
105
106
		add_filter( 'manage_' .$hook . '_columns', function( $columns ) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
Coding Style introduced by
Concat operator must be surrounded by a single space
Loading history...
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
107
			$columns['pronamic_subscription'] = __( 'Pronamic Subscription', 'pronamic_ideal' );
108
109
			return $columns;
110
		}, 10 );
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
111
112
		add_filter( 'mepr_view_get_string', function( $view, $slug, $vars ) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
113
			if ( '/admin/subscriptions/row' !== $slug ) {
114
				return $view;
115
			}
116
117
			$new_column = '<td>Test</dt>';
118
119
			$view = str_replace( '</tr>', $new_column . '</tr>', $view );
120
121
			return $view;
122
		}, 10, 3 );
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, each argument should be on a separate line.

For a function calls that spawns multiple lines, the coding style suggests to split arguments to separate lines like this:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
);
Loading history...
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
123
	}
124
125
	/**
126
	 * Gateway paths.
127
	 *
128
	 * @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/lib/MeprGatewayFactory.php#L48-50
129
	 *
130
	 * @param array $paths Array with gateway paths.
131
	 * @return array
132
	 */
133
	public function gateway_paths( $paths ) {
134
		$paths[] = dirname( __FILE__ ) . '/../gateways/';
135
136
		return $paths;
137
	}
138
139
	/**
140
	 * Payment redirect URL filter.
141
	 *
142
	 * @since 1.0.1
143
	 *
144
	 * @param string  $url     Payment redirect URL.
145
	 * @param Payment $payment Payment to redirect for.
146
	 *
147
	 * @return string
148
	 */
149
	public static function redirect_url( $url, Payment $payment ) {
150
		global $transaction;
151
152
		$transaction_id = $payment->get_source_id();
153
154
		$transaction = new MeprTransaction( $transaction_id );
155
156
		switch ( $payment->get_status() ) {
157
			case Statuses::CANCELLED:
158
			case Statuses::EXPIRED:
159
			case Statuses::FAILURE:
160
				$product = $transaction->product();
161
162
				$url = add_query_arg(
163
					array(
164
						'action'   => 'payment_form',
165
						'txn'      => $transaction->trans_num,
166
						'errors'   => array(
167
							__( 'Payment failed. Please try again.', 'pronamic_ideal' ),
168
						),
169
						'_wpnonce' => wp_create_nonce( 'mepr_payment_form' ),
170
					),
171
					$product->url()
172
				);
173
174
				break;
175
			case Statuses::SUCCESS:
176
				// @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/models/MeprOptions.php#L768-782
177
				$mepr_options = MeprOptions::fetch();
178
179
				$product         = new MeprProduct( $transaction->product_id );
180
				$sanitized_title = sanitize_title( $product->post_title );
181
182
				$args = array(
183
					'membership_id' => $product->ID,
184
					'membership'    => $sanitized_title,
185
					'trans_num'     => $transaction->trans_num,
186
				);
187
188
				$url = $mepr_options->thankyou_page_url( http_build_query( $args ) );
189
190
				break;
191
			case Statuses::OPEN:
192
			default:
193
				break;
194
		}
195
196
		return $url;
197
	}
198
199
	/**
200
	 * Update lead status of the specified payment.
201
	 *
202
	 * @link https://github.com/Charitable/Charitable/blob/1.1.4/includes/gateways/class-charitable-gateway-paypal.php#L229-L357
203
	 *
204
	 * @param Payment $payment The payment whose status is updated.
205
	 */
206
	public static function status_update( Payment $payment ) {
207
		$transaction_id = $payment->get_source_id();
208
209
		$transaction = new MeprTransaction( $transaction_id );
210
211
		if ( $payment->get_recurring() ) {
212
			$subscription = $transaction->subscription();
213
214
			if ( empty( $subscription ) || empty( $subscription->id ) ) {
215
				$subscription_id = $payment->get_subscription()->get_source_id();
216
217
				$subscription = new MeprSubscription( $subscription_id );
218
			}
219
220
			// Same source ID and first transaction ID for recurring payment means we need to add a new transaction.
221
			if ( $payment->get_source_id() === $subscription->id ) {
222
				// First transaction.
223
				$first_txn = $subscription->first_txn();
224
225
				if ( false === $first_txn || ! ( $first_txn instanceof MeprTransaction ) ) {
226
					$first_txn             = new MeprTransaction();
227
					$first_txn->user_id    = $subscription->user_id;
228
					$first_txn->product_id = $subscription->product_id;
229
					$first_txn->coupon_id  = $subscription->coupon_id;
230
					$first_txn->gateway    = null;
231
				}
232
233
				// Transaction number.
234
				$trans_num = $payment->get_transaction_id();
235
236
				if ( empty( $trans_num ) ) {
237
					$trans_num = uniqid();
238
				}
239
240
				// New transaction.
241
				$transaction                  = new MeprTransaction();
242
				$transaction->created_at      = $payment->post->post_date_gmt;
243
				$transaction->user_id         = $first_txn->user_id;
244
				$transaction->product_id      = $first_txn->product_id;
245
				$transaction->coupon_id       = $first_txn->coupon_id;
246
				$transaction->gateway         = $first_txn->gateway;
247
				$transaction->trans_num       = $trans_num;
248
				$transaction->txn_type        = MeprTransaction::$payment_str;
249
				$transaction->status          = MeprTransaction::$pending_str;
250
				$transaction->subscription_id = $subscription->id;
251
252
				$transaction->set_gross( $payment->get_total_amount()->get_value() );
253
254
				$transaction->store();
255
256
				// Set source ID.
257
				$payment->set_meta( 'source_id', $transaction->id );
258
259
				$payment->source_id = $transaction->id;
260
261
				if ( MeprSubscription::$active_str === $subscription->status ) {
262
					/*
263
					 * We create a 'confirmed' 'subscription_confirmation'
264
					 * transaction for a grace period of 15 days.
265
					 *
266
					 * Transactions of type "subscription_confirmation" with a
267
					 * status of "confirmed" are hidden in the UI, and are used
268
					 * as a way to provide free trial periods and the 24 hour
269
					 * grace period on a recurring subscription signup.
270
					 *
271
					 * @link https://docs.memberpress.com/article/219-where-is-data-stored.
272
					 */
273
					$subscription_confirmation                  = new MeprTransaction();
274
					$subscription_confirmation->created_at      = $payment->post->post_date_gmt;
275
					$subscription_confirmation->user_id         = $first_txn->user_id;
276
					$subscription_confirmation->product_id      = $first_txn->product_id;
277
					$subscription_confirmation->coupon_id       = $first_txn->coupon_id;
278
					$subscription_confirmation->gateway         = $first_txn->gateway;
279
					$subscription_confirmation->trans_num       = $trans_num;
280
					$subscription_confirmation->txn_type        = MeprTransaction::$subscription_confirmation_str;
281
					$subscription_confirmation->status          = MeprTransaction::$confirmed_str;
282
					$subscription_confirmation->subscription_id = $subscription->id;
283
					$subscription_confirmation->expires_at      = MeprUtils::ts_to_mysql_date( strtotime( $payment->post->post_date_gmt ) + MeprUtils::days( 15 ), 'Y-m-d 23:59:59' );
0 ignored issues
show
Bug Best Practice introduced by
The property expires_at does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
284
285
					$subscription_confirmation->set_subtotal( 0.00 );
286
287
					$subscription_confirmation->store();
288
				}
289
			}
290
		}
291
292
		$should_update = ! MemberPress::transaction_has_status(
293
			$transaction,
294
			array(
295
				MeprTransaction::$failed_str,
296
				MeprTransaction::$complete_str,
297
			)
298
		);
299
300
		if ( $should_update ) {
301
			$gateway = new Gateway();
302
303
			$gateway->mp_txn = $transaction;
304
305
			switch ( $payment->get_status() ) {
306
				case Statuses::CANCELLED:
307
				case Statuses::EXPIRED:
308
				case Statuses::FAILURE:
309
					$gateway->record_payment_failure();
310
311
					break;
312
				case Statuses::SUCCESS:
313
					if ( $payment->get_recurring() ) {
314
						$gateway->record_subscription_payment();
315
					} else {
316
						$gateway->record_payment();
317
					}
318
319
					break;
320
				case Statuses::OPEN:
321
				default:
322
					break;
323
			}
324
		}
325
	}
326
327
	/**
328
	 * Subscription deleted.
329
	 *
330
	 * @param int $subscription_id MemberPress subscription id.
331
	 */
332
	public function subscription_pre_delete( $subscription_id ) {
333
		$subscription = get_pronamic_subscription_by_meta( '_pronamic_subscription_source_id', $subscription_id );
334
335
		if ( ! $subscription ) {
336
			return;
337
		}
338
339
		// Add note.
340
		$note = sprintf(
341
			/* translators: %s: MemberPress */
342
			__( '%s subscription deleted.', 'pronamic_ideal' ),
343
			__( 'MemberPress', 'pronamic_ideal' )
344
		);
345
346
		$subscription->add_note( $note );
347
348
		// The status of canceled or completed subscriptions will not be changed automatically.
349
		if ( ! in_array( $subscription->get_status(), array( Statuses::CANCELLED, Statuses::COMPLETED ), true ) ) {
350
			$subscription->set_status( Statuses::CANCELLED );
351
352
			$subscription->save();
353
		}
354
	}
355
356
	/**
357
	 * Source text.
358
	 *
359
	 * @param string  $text    Source text.
360
	 * @param Payment $payment Payment to create the source text for.
361
	 *
362
	 * @return string
363
	 */
364
	public static function source_text( $text, Payment $payment ) {
365
		$text = __( 'MemberPress', 'pronamic_ideal' ) . '<br />';
366
367
		$text .= sprintf(
368
			'<a href="%s">%s</a>',
369
			add_query_arg(
370
				array(
371
					'page'   => 'memberpress-trans',
372
					'action' => 'edit',
373
					'id'     => $payment->source_id,
374
				),
375
				admin_url( 'admin.php' )
376
			),
377
			/* translators: %s: payment source id */
378
			sprintf( __( 'Transaction %s', 'pronamic_ideal' ), $payment->source_id )
379
		);
380
381
		return $text;
382
	}
383
384
	/**
385
	 * Subscription source text.
386
	 *
387
	 * @param string       $text         Source text.
388
	 * @param Subscription $subscription Subscription to create the source text for.
389
	 *
390
	 * @return string
391
	 */
392
	public static function subscription_source_text( $text, Subscription $subscription ) {
393
		$text = __( 'MemberPress', 'pronamic_ideal' ) . '<br />';
394
395
		$text .= sprintf(
396
			'<a href="%s">%s</a>',
397
			add_query_arg(
398
				array(
399
					'page'         => 'memberpress-subscriptions',
400
					'subscription' => $subscription->source_id,
401
				),
402
				admin_url( 'admin.php' )
403
			),
404
			/* translators: %s: payment source id */
405
			sprintf( __( 'Subscription %s', 'pronamic_ideal' ), $subscription->source_id )
406
		);
407
408
		return $text;
409
	}
410
411
	/**
412
	 * Source description.
413
	 *
414
	 * @param string  $description Description.
415
	 * @param Payment $payment     Payment to create the description for.
416
	 *
417
	 * @return string
418
	 */
419
	public static function source_description( $description, Payment $payment ) {
420
		return __( 'MemberPress Transaction', 'pronamic_ideal' );
421
	}
422
423
	/**
424
	 * Subscription source description.
425
	 *
426
	 * @param string       $description  Description.
427
	 * @param Subscription $subscription Subscription to create the description for.
428
	 *
429
	 * @return string
430
	 */
431
	public static function subscription_source_description( $description, Subscription $subscription ) {
432
		return __( 'MemberPress Subscription', 'pronamic_ideal' );
433
	}
434
435
	/**
436
	 * Source URL.
437
	 *
438
	 * @param string  $url     URL.
439
	 * @param Payment $payment The payment to create the source URL for.
440
	 *
441
	 * @return string
442
	 */
443
	public static function source_url( $url, Payment $payment ) {
444
		$url = add_query_arg(
445
			array(
446
				'page'   => 'memberpress-trans',
447
				'action' => 'edit',
448
				'id'     => $payment->source_id,
449
			),
450
			admin_url( 'admin.php' )
451
		);
452
453
		return $url;
454
	}
455
456
	/**
457
	 * Subscription source URL.
458
	 *
459
	 * @param string       $url          URL.
460
	 * @param Subscription $subscription Subscription.
461
	 *
462
	 * @return string
463
	 */
464
	public static function subscription_source_url( $url, Subscription $subscription ) {
465
		$url = add_query_arg(
466
			array(
467
				'page'         => 'memberpress-subscriptions',
468
				'subscription' => $subscription->source_id,
469
			),
470
			admin_url( 'admin.php' )
471
		);
472
473
		return $url;
474
	}
475
476
	/**
477
	 * MemberPress update subscription.
478
	 *
479
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/controllers/MeprSubscriptionsCtrl.php#L92-L111
480
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L100-L123
481
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L112
482
	 *
483
	 * @param string           $status_old               Old status identifier.
484
	 * @param string           $status_new               New status identifier.
485
	 * @param MeprSubscription $memberpress_subscription MemberPress subscription object.
486
	 */
487
	public function memberpress_subscription_transition_status( $status_old, $status_new, $memberpress_subscription ) {
488
		$subscription = get_pronamic_subscription_by_meta( '_pronamic_subscription_source_id', $memberpress_subscription->id );
489
490
		if ( empty( $subscription ) ) {
491
			return;
492
		}
493
494
		$status = SubscriptionStatuses::transform( $status_new );
495
496
		$subscription->set_status( $status );
497
498
		$subscription->save();
499
	}
500
}
501