Completed
Push — develop ( a22b60...304faf )
by Reüel
11:40
created

Extension   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 422
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 172
dl 0
loc 422
rs 9.1199
c 0
b 0
f 0
wmc 41

14 Methods

Rating   Name   Duplication   Size   Complexity  
A memberpress_subscription_transition_status() 0 12 2
A gateway_paths() 0 4 1
A subscription_source_text() 0 14 1
A subscriptions_table_joins_nested_query_fix() 0 28 5
B redirect_url() 0 45 6
A source_url() 0 8 1
A __construct() 0 18 1
A bootstrap() 0 2 1
A subscription_source_url() 0 7 1
A source_description() 0 2 1
C status_update() 0 104 16
A subscription_pre_delete() 0 21 3
A subscription_source_description() 0 2 1
A source_text() 0 15 1

How to fix   Complexity   

Complex Class

Complex classes like Extension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Extension, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Extension
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2018 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.1
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
		// @see 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
		add_filter( 'mepr_recurring_subscriptions_table_joins', array( $this, 'subscriptions_table_joins_nested_query_fix' ) );
52
53
		add_filter( 'pronamic_payment_redirect_url_' . self::SLUG, array( __CLASS__, 'redirect_url' ), 10, 2 );
54
		add_action( 'pronamic_payment_status_update_' . self::SLUG, array( __CLASS__, 'status_update' ), 10, 1 );
55
56
		add_filter( 'pronamic_payment_source_text_' . self::SLUG, array( __CLASS__, 'source_text' ), 10, 2 );
57
		add_filter( 'pronamic_payment_source_description_' . self::SLUG, array( __CLASS__, 'source_description' ), 10, 2 );
58
		add_filter( 'pronamic_payment_source_url_' . self::SLUG, array( __CLASS__, 'source_url' ), 10, 2 );
59
		add_filter( 'pronamic_subscription_source_text_' . self::SLUG, array( __CLASS__, 'subscription_source_text' ), 10, 2 );
60
		add_filter( 'pronamic_subscription_source_description_' . self::SLUG, array( __CLASS__, 'subscription_source_description' ), 10, 2 );
61
		add_filter( 'pronamic_subscription_source_url_' . self::SLUG, array( __CLASS__, 'subscription_source_url' ), 10, 2 );
62
63
		add_action( 'mepr_subscription_pre_delete', array( $this, 'subscription_pre_delete' ), 10, 1 );
64
65
		add_action( 'mepr_subscription_transition_status', array( $this, 'memberpress_subscription_transition_status' ), 10, 3 );
66
	}
67
68
	/**
69
	 * Gateway paths.
70
	 *
71
	 * @see https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/lib/MeprGatewayFactory.php#L48-50
72
	 *
73
	 * @param array $paths Array with gateway paths.
74
	 * @return array
75
	 */
76
	public function gateway_paths( $paths ) {
77
		$paths[] = dirname( __FILE__ ) . '/../gateways/';
78
79
		return $paths;
80
	}
81
82
	/**
83
	 * Fix to display correct active status and expiry date on account page,
84
	 * based on the subscription confirmation transaction.
85
	 *
86
	 * @param array $joins SQL joins.
87
	 *
88
	 * @return array
89
	 */
90
	public function subscriptions_table_joins_nested_query_fix( $joins ) {
91
		if ( ! is_array( $joins ) ) {
0 ignored issues
show
introduced by
The condition is_array($joins) is always true.
Loading history...
92
			return $joins;
93
		}
94
95
		// Loop joins.
96
		foreach ( $joins as &$join ) {
97
			// Determine if fix needs to be applied.
98
			if ( ! strpos( $join, 'expiring_txn' ) && ! strpos( $join, 't2.subscription_id=sub.id' ) ) {
99
				continue;
100
			}
101
102
			$mepr_db = new MeprDb();
103
104
			/**
105
			 * Add `wp_mepr_subscriptions AS sub` to SELECT query, so `sub.id` in subquery
106
			 * contains the subscription ID from outer query instead of NULL.
107
			 *
108
			 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L565
109
			 */
110
			$join = str_replace(
111
				' AS t',
112
				sprintf( ' AS t, %s AS sub', $mepr_db->subscriptions ),
113
				$join
114
			);
115
		}
116
117
		return $joins;
118
	}
119
120
	/**
121
	 * Payment redirect URL filter.
122
	 *
123
	 * @since 1.0.1
124
	 *
125
	 * @param string  $url     Payment redirect URL.
126
	 * @param Payment $payment Payment to redirect for.
127
	 *
128
	 * @return string
129
	 */
130
	public static function redirect_url( $url, Payment $payment ) {
131
		global $transaction;
132
133
		$transaction_id = $payment->get_source_id();
134
135
		$transaction = new MeprTransaction( $transaction_id );
136
137
		switch ( $payment->get_status() ) {
138
			case Statuses::CANCELLED:
139
			case Statuses::EXPIRED:
140
			case Statuses::FAILURE:
141
				$product = $transaction->product();
142
143
				$url = add_query_arg(
144
					array(
145
						'action'   => 'payment_form',
146
						'txn'      => $transaction->trans_num,
147
						'_wpnonce' => wp_create_nonce( 'mepr_payment_form' ),
148
					),
149
					$product->url()
150
				);
151
152
				break;
153
			case Statuses::SUCCESS:
154
				// @see https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/models/MeprOptions.php#L768-782
155
				$mepr_options = MeprOptions::fetch();
156
157
				$product         = new MeprProduct( $transaction->product_id );
158
				$sanitized_title = sanitize_title( $product->post_title );
159
160
				$args = array(
161
					'membership_id' => $product->ID,
162
					'membership'    => $sanitized_title,
163
					'trans_num'     => $transaction->trans_num,
164
				);
165
166
				$url = $mepr_options->thankyou_page_url( http_build_query( $args ) );
167
168
				break;
169
			case Statuses::OPEN:
170
			default:
171
				break;
172
		}
173
174
		return $url;
175
	}
176
177
	/**
178
	 * Update lead status of the specified payment.
179
	 *
180
	 * @see https://github.com/Charitable/Charitable/blob/1.1.4/includes/gateways/class-charitable-gateway-paypal.php#L229-L357
181
	 *
182
	 * @param Payment $payment The payment whose status is updated.
183
	 */
184
	public static function status_update( Payment $payment ) {
185
		$transaction_id = $payment->get_source_id();
186
187
		$transaction = new MeprTransaction( $transaction_id );
188
189
		if ( $payment->get_recurring() ) {
190
			$subscription = $transaction->subscription();
191
192
			if ( empty( $subscription ) || empty( $subscription->id ) ) {
193
				$subscription_id = $payment->get_subscription()->get_source_id();
194
195
				$subscription = new MeprSubscription( $subscription_id );
196
			}
197
198
			// Same source ID and first transaction ID for recurring payment means we need to add a new transaction.
199
			if ( $payment->get_source_id() === $subscription->id ) {
200
				// First transaction.
201
				$first_txn = $subscription->first_txn();
202
203
				if ( false === $first_txn || ! ( $first_txn instanceof MeprTransaction ) ) {
204
					$first_txn             = new MeprTransaction();
205
					$first_txn->user_id    = $subscription->user_id;
206
					$first_txn->product_id = $subscription->product_id;
207
					$first_txn->coupon_id  = $subscription->coupon_id;
208
					$first_txn->gateway    = null;
209
				}
210
211
				// Transaction number.
212
				$trans_num = $payment->get_transaction_id();
213
214
				if ( empty( $trans_num ) ) {
215
					$trans_num = uniqid();
216
				}
217
218
				// New transaction.
219
				$transaction                  = new MeprTransaction();
220
				$transaction->created_at      = $payment->post->post_date_gmt;
221
				$transaction->user_id         = $first_txn->user_id;
222
				$transaction->product_id      = $first_txn->product_id;
223
				$transaction->coupon_id       = $first_txn->coupon_id;
224
				$transaction->gateway         = $first_txn->gateway;
225
				$transaction->trans_num       = $trans_num;
226
				$transaction->txn_type        = MeprTransaction::$payment_str;
227
				$transaction->status          = MeprTransaction::$pending_str;
228
				$transaction->subscription_id = $subscription->id;
229
230
				$transaction->set_gross( $payment->get_amount()->get_amount() );
231
232
				$transaction->store();
233
234
				// Set source ID.
235
				$payment->set_meta( 'source_id', $transaction->id );
236
237
				$payment->source_id = $transaction->id;
238
239
				if ( MeprSubscription::$active_str === $subscription->status ) {
240
					// New transaction and subscription confirmation.
241
					$subscription_confirmation                  = new MeprTransaction();
242
					$subscription_confirmation->created_at      = $payment->post->post_date_gmt;
243
					$subscription_confirmation->user_id         = $first_txn->user_id;
244
					$subscription_confirmation->product_id      = $first_txn->product_id;
245
					$subscription_confirmation->coupon_id       = $first_txn->coupon_id;
246
					$subscription_confirmation->gateway         = $first_txn->gateway;
247
					$subscription_confirmation->trans_num       = $trans_num;
248
					$subscription_confirmation->txn_type        = MeprTransaction::$subscription_confirmation_str;
249
					$subscription_confirmation->status          = MeprTransaction::$confirmed_str;
250
					$subscription_confirmation->subscription_id = $subscription->id;
251
					$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...
252
253
					$subscription_confirmation->set_subtotal( 0.00 );
254
255
					$subscription_confirmation->store();
256
				}
257
			}
258
		}
259
260
		$should_update = ! MemberPress::transaction_has_status( $transaction, array(
261
			MeprTransaction::$failed_str,
262
			MeprTransaction::$complete_str,
263
		) );
264
265
		if ( $should_update ) {
266
			$gateway = new Gateway();
267
268
			$gateway->mp_txn = $transaction;
269
270
			switch ( $payment->get_status() ) {
271
				case Statuses::CANCELLED:
272
				case Statuses::EXPIRED:
273
				case Statuses::FAILURE:
274
					$gateway->record_payment_failure();
275
276
					break;
277
				case Statuses::SUCCESS:
278
					if ( $payment->get_recurring() ) {
279
						$gateway->record_subscription_payment();
280
					} else {
281
						$gateway->record_payment();
282
					}
283
284
					break;
285
				case Statuses::OPEN:
286
				default:
287
					break;
288
			}
289
		}
290
	}
291
292
	/**
293
	 * Subscription deleted.
294
	 *
295
	 * @param int $subscription_id MemberPress subscription id.
296
	 */
297
	public function subscription_pre_delete( $subscription_id ) {
298
		$subscription = get_pronamic_subscription_by_meta( '_pronamic_subscription_source_id', $subscription_id );
299
300
		if ( ! $subscription ) {
301
			return;
302
		}
303
304
		// Add note.
305
		$note = sprintf(
306
			/* translators: %s: MemberPress */
307
			__( '%s subscription deleted.', 'pronamic_ideal' ),
308
			__( 'MemberPress', 'pronamic_ideal' )
309
		);
310
311
		$subscription->add_note( $note );
312
313
		// The status of canceled or completed subscriptions will not be changed automatically.
314
		if ( ! in_array( $subscription->get_status(), array( Statuses::CANCELLED, Statuses::COMPLETED ), true ) ) {
315
			$subscription->set_status( Statuses::CANCELLED );
316
317
			$subscription->save();
318
		}
319
	}
320
321
	/**
322
	 * Source text.
323
	 *
324
	 * @param string  $text    Source text.
325
	 * @param Payment $payment Payment to create the source text for.
326
	 *
327
	 * @return string
328
	 */
329
	public static function source_text( $text, Payment $payment ) {
330
		$text = __( 'MemberPress', 'pronamic_ideal' ) . '<br />';
331
332
		$text .= sprintf(
333
			'<a href="%s">%s</a>',
334
			add_query_arg( array(
335
				'page'   => 'memberpress-trans',
336
				'action' => 'edit',
337
				'id'     => $payment->source_id,
338
			), admin_url( 'admin.php' ) ),
339
			/* translators: %s: payment source id */
340
			sprintf( __( 'Transaction %s', 'pronamic_ideal' ), $payment->source_id )
341
		);
342
343
		return $text;
344
	}
345
346
	/**
347
	 * Subscription source text.
348
	 *
349
	 * @param string       $text         Source text.
350
	 * @param Subscription $subscription Subscription to create the source text for.
351
	 *
352
	 * @return string
353
	 */
354
	public static function subscription_source_text( $text, Subscription $subscription ) {
355
		$text = __( 'MemberPress', 'pronamic_ideal' ) . '<br />';
356
357
		$text .= sprintf(
358
			'<a href="%s">%s</a>',
359
			add_query_arg( array(
360
				'page'         => 'memberpress-subscriptions',
361
				'subscription' => $subscription->source_id,
362
			), admin_url( 'admin.php' ) ),
363
			/* translators: %s: payment source id */
364
			sprintf( __( 'Subscription %s', 'pronamic_ideal' ), $subscription->source_id )
365
		);
366
367
		return $text;
368
	}
369
370
	/**
371
	 * Source description.
372
	 *
373
	 * @param string  $description Description.
374
	 * @param Payment $payment     Payment to create the description for.
375
	 *
376
	 * @return string
377
	 */
378
	public static function source_description( $description, Payment $payment ) {
379
		return __( 'MemberPress Transaction', 'pronamic_ideal' );
380
	}
381
382
	/**
383
	 * Subscription source description.
384
	 *
385
	 * @param string       $description  Description.
386
	 * @param Subscription $subscription Subscription to create the description for.
387
	 *
388
	 * @return string
389
	 */
390
	public static function subscription_source_description( $description, Subscription $subscription ) {
391
		return __( 'MemberPress Subscription', 'pronamic_ideal' );
392
	}
393
394
	/**
395
	 * Source URL.
396
	 *
397
	 * @param string  $url     URL.
398
	 * @param Payment $payment The payment to create the source URL for.
399
	 *
400
	 * @return string
401
	 */
402
	public static function source_url( $url, Payment $payment ) {
403
		$url = add_query_arg( array(
404
			'page'   => 'memberpress-trans',
405
			'action' => 'edit',
406
			'id'     => $payment->source_id,
407
		), admin_url( 'admin.php' ) );
408
409
		return $url;
410
	}
411
412
	/**
413
	 * Subscription source URL.
414
	 *
415
	 * @param string       $url          URL.
416
	 * @param Subscription $subscription Subscription.
417
	 *
418
	 * @return string
419
	 */
420
	public static function subscription_source_url( $url, Subscription $subscription ) {
421
		$url = add_query_arg( array(
422
			'page'         => 'memberpress-subscriptions',
423
			'subscription' => $subscription->source_id,
424
		), admin_url( 'admin.php' ) );
425
426
		return $url;
427
	}
428
429
	/**
430
	 * MemberPress update subscription.
431
	 *
432
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/controllers/MeprSubscriptionsCtrl.php#L92-L111
433
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L100-L123
434
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L112
435
	 *
436
	 * @param string           $status_old               Old status identifier.
437
	 * @param string           $status_new               New status identifier.
438
	 * @param MeprSubscription $memberpress_subscription MemberPress subscription object.
439
	 */
440
	public function memberpress_subscription_transition_status( $status_old, $status_new, $memberpress_subscription ) {
441
		$subscription = get_pronamic_subscription_by_meta( '_pronamic_subscription_source_id', $memberpress_subscription->id );
442
443
		if ( empty( $subscription ) ) {
444
			return;
445
		}
446
447
		$status = SubscriptionStatuses::transform( $status_new );
448
449
		$subscription->set_status( $status );
450
451
		$subscription->save();
452
	}
453
}
454