Test Failed
Push — master ( 41b3f4...4592c2 )
by Remco
35:19 queued 13:41
created

src/Extension.php (3 issues)

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