Failed Conditions
Push — develop ( a8466e...4dad4c )
by Remco
23:54
created

src/Extension.php (3 issues)

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