Failed Conditions
Push — develop ( 99696b...c8c077 )
by Reüel
05:11
created

Extension::source_text()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 2
dl 0
loc 18
ccs 0
cts 15
cp 0
crap 2
rs 9.9
c 0
b 0
f 0
1
<?php
2
/**
3
 * Extension
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2020 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\AbstractPluginIntegration;
20
use Pronamic\WordPress\Pay\Payments\PaymentStatus;
21
use Pronamic\WordPress\Pay\Extensions\MemberPress\Gateways\Gateway;
22
use Pronamic\WordPress\Pay\Payments\Payment;
23
use Pronamic\WordPress\Pay\Subscriptions\Subscription;
24
use Pronamic\WordPress\Pay\Subscriptions\SubscriptionStatus;
25
26
/**
27
 * WordPress pay MemberPress extension
28
 *
29
 * @author  Remco Tolsma
30
 * @version 2.0.4
31
 * @since   1.0.0
32
 */
33
class Extension extends AbstractPluginIntegration {
34
	/**
35
	 * The slug of this addon
36
	 *
37
	 * @var string
38
	 */
39
	const SLUG = 'memberpress';
40
41
	/**
42
	 * Construct MemberPress plugin integration.
43
	 */
44
	public function __construct() {
45
		parent::__construct();
46
47
		// Dependencies.
48
		$dependencies = $this->get_dependencies();
49
50
		$dependencies->add( new MemberPressDependency() );
51
	}
52
53
	/**
54
	 * Setup.
55
	 */
56
	public function setup() {
57
		\add_filter( 'pronamic_subscription_source_description_' . self::SLUG, array( $this, 'subscription_source_description' ), 10, 2 );
58
		\add_filter( 'pronamic_payment_source_description_' . self::SLUG, array( $this, 'source_description' ), 10, 2 );
59
60
		// Check if dependencies are met and integration is active.
61
		if ( ! $this->is_active() ) {
62
			return;
63
		}
64
65
		// @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/lib/MeprGatewayFactory.php#L48-50
66
		\add_filter( 'mepr-gateway-paths', array( $this, 'gateway_paths' ) );
67
68
		\add_filter( 'pronamic_payment_redirect_url_' . self::SLUG, array( $this, 'redirect_url' ), 10, 2 );
69
		\add_action( 'pronamic_payment_status_update_' . self::SLUG, array( $this, 'status_update' ), 10, 1 );
70
71
		\add_filter( 'pronamic_subscription_source_text_' . self::SLUG, array( $this, 'subscription_source_text' ), 10, 2 );
72
		\add_filter( 'pronamic_subscription_source_url_' . self::SLUG, array( $this, 'subscription_source_url' ), 10, 2 );
73
		\add_filter( 'pronamic_payment_source_text_' . self::SLUG, array( $this, 'source_text' ), 10, 2 );
74
		\add_filter( 'pronamic_payment_source_url_' . self::SLUG, array( $this, 'source_url' ), 10, 2 );
75
76
		\add_action( 'mepr_subscription_pre_delete', array( $this, 'subscription_pre_delete' ), 10, 1 );
77
78
		\add_action( 'mepr_subscription_transition_status', array( $this, 'memberpress_subscription_transition_status' ), 10, 3 );
79
80
		// Hide MemberPress columns for payments and subscriptions.
81
		\add_filter( 'registered_post_type', array( $this, 'post_type_columns_hide' ), 15, 1 );
82
83
		if ( \is_admin() ) {
84
			$this->admin_subscriptions = new Admin\AdminSubscriptions();
0 ignored issues
show
Bug Best Practice introduced by
The property admin_subscriptions does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
85
			$this->admin_transactions  = new Admin\AdminTransactions();
0 ignored issues
show
Bug Best Practice introduced by
The property admin_transactions does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
86
87
			$this->admin_subscriptions->setup();
88
			$this->admin_transactions->setup();
89
		}
90
	}
91
92
	/**
93
	 * Gateway paths.
94
	 *
95
	 * @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/lib/MeprGatewayFactory.php#L48-50
96
	 *
97
	 * @param array $paths Array with gateway paths.
98
	 * @return array
99
	 */
100
	public function gateway_paths( $paths ) {
101
		$paths[] = dirname( __FILE__ ) . '/../gateways/';
102
103
		return $paths;
104
	}
105
106
	/**
107
	 * Hide MemberPress columns for payments and subscriptions.
108
	 *
109
	 * @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/controllers/MeprAppCtrl.php#L129-146
110
	 *
111
	 * @param string $post_type Registered post type.
112
	 *
113
	 * @return void
114
	 */
115
	public function post_type_columns_hide( $post_type ) {
116
		if ( ! in_array( $post_type, array( 'pronamic_payment', 'pronamic_pay_subscr' ), true ) ) {
117
			return;
118
		}
119
120
		remove_filter( 'manage_edit-' . $post_type . '_columns', 'MeprAppCtrl::columns' );
121
	}
122
123
	/**
124
	 * Payment redirect URL filter.
125
	 *
126
	 * @since 1.0.1
127
	 *
128
	 * @param string  $url     Payment redirect URL.
129
	 * @param Payment $payment Payment to redirect for.
130
	 *
131
	 * @return string
132
	 */
133
	public function redirect_url( $url, Payment $payment ) {
134
		global $transaction;
135
136
		$transaction_id = $payment->get_source_id();
137
138
		$transaction = new MeprTransaction( $transaction_id );
139
140
		switch ( $payment->get_status() ) {
141
			case PaymentStatus::CANCELLED:
142
			case PaymentStatus::EXPIRED:
143
			case PaymentStatus::FAILURE:
144
				$product = $transaction->product();
145
146
				$url = add_query_arg(
147
					array(
148
						'action'   => 'payment_form',
149
						'txn'      => $transaction->trans_num,
150
						'errors'   => array(
151
							__( 'Payment failed. Please try again.', 'pronamic_ideal' ),
152
						),
153
						'_wpnonce' => wp_create_nonce( 'mepr_payment_form' ),
154
					),
155
					$product->url()
156
				);
157
158
				break;
159
			case PaymentStatus::SUCCESS:
160
				// @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/models/MeprOptions.php#L768-782
161
				$mepr_options = MeprOptions::fetch();
162
163
				$product         = new MeprProduct( $transaction->product_id );
164
				$sanitized_title = sanitize_title( $product->post_title );
165
166
				$args = array(
167
					'membership_id' => $product->ID,
168
					'membership'    => $sanitized_title,
169
					'trans_num'     => $transaction->trans_num,
170
				);
171
172
				$url = $mepr_options->thankyou_page_url( http_build_query( $args ) );
173
174
				break;
175
			case PaymentStatus::OPEN:
176
			default:
177
				break;
178
		}
179
180
		return $url;
181
	}
182
183
	/**
184
	 * Update lead status of the specified payment.
185
	 *
186
	 * @link https://github.com/Charitable/Charitable/blob/1.1.4/includes/gateways/class-charitable-gateway-paypal.php#L229-L357
187
	 *
188
	 * @param Payment $payment The payment whose status is updated.
189
	 */
190
	public function status_update( Payment $payment ) {
191
		$transaction = new MeprTransaction( $payment->get_source_id() );
192
193
		if ( $payment->get_recurring() ) {
194
			$subscription_id = $payment->get_subscription()->get_source_id();
195
			$subscription    = new MeprSubscription( $subscription_id );
196
197
			// Same source ID and first transaction ID for recurring payment means we need to add a new transaction.
198
			if ( $payment->get_source_id() === $subscription->id ) {
199
				// First transaction.
200
				$first_txn = $subscription->first_txn();
201
202
				if ( false === $first_txn || ! ( $first_txn instanceof MeprTransaction ) ) {
203
					$first_txn             = new MeprTransaction();
204
					$first_txn->user_id    = $subscription->user_id;
205
					$first_txn->product_id = $subscription->product_id;
206
					$first_txn->coupon_id  = $subscription->coupon_id;
207
					$first_txn->gateway    = null;
208
				}
209
210
				// Transaction number.
211
				$trans_num = $payment->get_transaction_id();
212
213
				if ( empty( $trans_num ) ) {
214
					$trans_num = uniqid();
215
				}
216
217
				// New transaction.
218
				$transaction                  = new MeprTransaction();
219
				$transaction->created_at      = $payment->post->post_date_gmt;
220
				$transaction->user_id         = $first_txn->user_id;
221
				$transaction->product_id      = $first_txn->product_id;
222
				$transaction->coupon_id       = $first_txn->coupon_id;
223
				$transaction->gateway         = $first_txn->gateway;
224
				$transaction->trans_num       = $trans_num;
225
				$transaction->txn_type        = MeprTransaction::$payment_str;
226
				$transaction->status          = MeprTransaction::$pending_str;
227
				$transaction->expires_at      = MeprUtils::ts_to_mysql_date( $payment->get_end_date()->getTimestamp(), '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...
228
				$transaction->subscription_id = $subscription->id;
229
230
				$transaction->set_gross( $payment->get_total_amount()->get_value() );
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
					/*
241
					 * We create a 'confirmed' 'subscription_confirmation'
242
					 * transaction for a grace period of 15 days.
243
					 *
244
					 * Transactions of type "subscription_confirmation" with a
245
					 * status of "confirmed" are hidden in the UI, and are used
246
					 * as a way to provide free trial periods and the 24 hour
247
					 * grace period on a recurring subscription signup.
248
					 *
249
					 * @link https://docs.memberpress.com/article/219-where-is-data-stored.
250
					 */
251
					$subscription_confirmation                  = new MeprTransaction();
252
					$subscription_confirmation->created_at      = $payment->post->post_date_gmt;
253
					$subscription_confirmation->user_id         = $first_txn->user_id;
254
					$subscription_confirmation->product_id      = $first_txn->product_id;
255
					$subscription_confirmation->coupon_id       = $first_txn->coupon_id;
256
					$subscription_confirmation->gateway         = $first_txn->gateway;
257
					$subscription_confirmation->trans_num       = $trans_num;
258
					$subscription_confirmation->txn_type        = MeprTransaction::$subscription_confirmation_str;
259
					$subscription_confirmation->status          = MeprTransaction::$confirmed_str;
260
					$subscription_confirmation->subscription_id = $subscription->id;
261
					$subscription_confirmation->expires_at      = MeprUtils::ts_to_mysql_date( strtotime( $payment->post->post_date_gmt ) + MeprUtils::days( 15 ), 'Y-m-d 23:59:59' );
262
263
					$subscription_confirmation->set_subtotal( 0.00 );
264
265
					$subscription_confirmation->store();
266
				}
267
			}
268
		}
269
270
		$should_update = ! MemberPress::transaction_has_status(
271
			$transaction,
272
			array(
273
				MeprTransaction::$failed_str,
274
				MeprTransaction::$complete_str,
275
			)
276
		);
277
278
		// Allow successful recurring payments to update failed transaction.
279
		if ( $payment->get_recurring() && PaymentStatus::SUCCESS === $payment->get_status() && MeprTransaction::$failed_str === $transaction->status ) {
280
			$should_update = true;
281
		}
282
283
		if ( $should_update ) {
284
			$gateway = new Gateway();
285
286
			$gateway->pronamic_payment = $payment;
287
			$gateway->mp_txn           = $transaction;
288
289
			switch ( $payment->get_status() ) {
290
				case PaymentStatus::CANCELLED:
291
				case PaymentStatus::EXPIRED:
292
				case PaymentStatus::FAILURE:
293
					$gateway->record_payment_failure();
294
295
					break;
296
				case PaymentStatus::SUCCESS:
297
					if ( $payment->get_recurring() ) {
298
						$gateway->record_subscription_payment();
299
					} else {
300
						$gateway->record_payment();
301
					}
302
303
					break;
304
				case PaymentStatus::OPEN:
305
				default:
306
					break;
307
			}
308
		}
309
	}
310
311
	/**
312
	 * Subscription deleted.
313
	 *
314
	 * @param int $subscription_id MemberPress subscription id.
315
	 */
316
	public function subscription_pre_delete( $subscription_id ) {
317
		$subscription = get_pronamic_subscription_by_meta( '_pronamic_subscription_source_id', $subscription_id );
318
319
		if ( ! $subscription ) {
320
			return;
321
		}
322
323
		// Add note.
324
		$note = sprintf(
325
			/* translators: %s: MemberPress */
326
			__( '%s subscription deleted.', 'pronamic_ideal' ),
327
			__( 'MemberPress', 'pronamic_ideal' )
328
		);
329
330
		$subscription->add_note( $note );
331
332
		// The status of canceled or completed subscriptions will not be changed automatically.
333
		if ( ! in_array( $subscription->get_status(), array( SubscriptionStatus::CANCELLED, SubscriptionStatus::COMPLETED ), true ) ) {
334
			$subscription->set_status( SubscriptionStatus::CANCELLED );
335
336
			$subscription->save();
337
		}
338
	}
339
340
	/**
341
	 * Source text.
342
	 *
343
	 * @param string  $text    Source text.
344
	 * @param Payment $payment Payment to create the source text for.
345
	 *
346
	 * @return string
347
	 */
348
	public function source_text( $text, Payment $payment ) {
349
		$text = __( 'MemberPress', 'pronamic_ideal' ) . '<br />';
350
351
		$text .= sprintf(
352
			'<a href="%s">%s</a>',
353
			add_query_arg(
354
				array(
355
					'page'   => 'memberpress-trans',
356
					'action' => 'edit',
357
					'id'     => $payment->source_id,
358
				),
359
				admin_url( 'admin.php' )
360
			),
361
			/* translators: %s: payment source id */
362
			sprintf( __( 'Transaction %s', 'pronamic_ideal' ), $payment->source_id )
363
		);
364
365
		return $text;
366
	}
367
368
	/**
369
	 * Subscription source text.
370
	 *
371
	 * @param string       $text         Source text.
372
	 * @param Subscription $subscription Subscription to create the source text for.
373
	 *
374
	 * @return string
375
	 */
376
	public function subscription_source_text( $text, Subscription $subscription ) {
377
		$text = __( 'MemberPress', 'pronamic_ideal' ) . '<br />';
378
379
		$text .= sprintf(
380
			'<a href="%s">%s</a>',
381
			add_query_arg(
382
				array(
383
					'page'         => 'memberpress-subscriptions',
384
					'subscription' => $subscription->source_id,
385
				),
386
				admin_url( 'admin.php' )
387
			),
388
			/* translators: %s: payment source id */
389
			sprintf( __( 'Subscription %s', 'pronamic_ideal' ), $subscription->source_id )
390
		);
391
392
		return $text;
393
	}
394
395
	/**
396
	 * Source description.
397
	 *
398
	 * @param string  $description Description.
399
	 * @param Payment $payment     Payment to create the description for.
400
	 *
401
	 * @return string
402
	 */
403
	public function source_description( $description, Payment $payment ) {
404
		return __( 'MemberPress Transaction', 'pronamic_ideal' );
405
	}
406
407
	/**
408
	 * Subscription source description.
409
	 *
410
	 * @param string       $description  Description.
411
	 * @param Subscription $subscription Subscription to create the description for.
412
	 *
413
	 * @return string
414
	 */
415
	public function subscription_source_description( $description, Subscription $subscription ) {
416
		return __( 'MemberPress Subscription', 'pronamic_ideal' );
417
	}
418
419
	/**
420
	 * Source URL.
421
	 *
422
	 * @param string  $url     URL.
423
	 * @param Payment $payment The payment to create the source URL for.
424
	 *
425
	 * @return string
426
	 */
427
	public function source_url( $url, Payment $payment ) {
428
		$url = add_query_arg(
429
			array(
430
				'page'   => 'memberpress-trans',
431
				'action' => 'edit',
432
				'id'     => $payment->source_id,
433
			),
434
			admin_url( 'admin.php' )
435
		);
436
437
		return $url;
438
	}
439
440
	/**
441
	 * Subscription source URL.
442
	 *
443
	 * @param string       $url          URL.
444
	 * @param Subscription $subscription Subscription.
445
	 *
446
	 * @return string
447
	 */
448
	public function subscription_source_url( $url, Subscription $subscription ) {
449
		$url = add_query_arg(
450
			array(
451
				'page'         => 'memberpress-subscriptions',
452
				'subscription' => $subscription->source_id,
453
			),
454
			admin_url( 'admin.php' )
455
		);
456
457
		return $url;
458
	}
459
460
	/**
461
	 * MemberPress update subscription.
462
	 *
463
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/controllers/MeprSubscriptionsCtrl.php#L92-L111
464
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L100-L123
465
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L112
466
	 *
467
	 * @param string           $status_old               Old status identifier.
468
	 * @param string           $status_new               New status identifier.
469
	 * @param MeprSubscription $memberpress_subscription MemberPress subscription object.
470
	 */
471
	public function memberpress_subscription_transition_status( $status_old, $status_new, $memberpress_subscription ) {
472
		$subscription = get_pronamic_subscription_by_meta( '_pronamic_subscription_source_id', $memberpress_subscription->id );
473
474
		if ( empty( $subscription ) ) {
475
			return;
476
		}
477
478
		$status = SubscriptionStatuses::transform( $status_new );
479
480
		$subscription->set_status( $status );
481
482
		$subscription->save();
483
	}
484
}
485