Failed Conditions
Push — develop ( bdd916...26c591 )
by Remco
06:44 queued 13s
created

src/Extension.php (8 issues)

1
<?php
2
/**
3
 * Extension
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2021 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.2.3
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
	 * @param array $args Arguments.
45
	 */
46
	public function __construct( $args = array() ) {
47
		$args['name'] = __( 'MemberPress', 'pronamic_ideal' );
48
49
		parent::__construct( $args );
50
51
		// Dependencies.
52
		$dependencies = $this->get_dependencies();
53
54
		$dependencies->add( new MemberPressDependency() );
55
56
		// Upgrades.
57
		$upgrades = $this->get_upgrades();
58
59
		$upgrades->set_executable( true );
60
61
		$upgrades->add( new Upgrade310() );
62
	}
63
64
	/**
65
	 * Setup.
66
	 */
67
	public function setup() {
68
		\add_filter( 'pronamic_subscription_source_description_memberpress_transaction', array( $this, 'subscription_source_description' ), 10, 2 );
69
		\add_filter( 'pronamic_payment_source_description_memberpress_transaction', array( $this, 'source_description' ), 10, 2 );
70
71
		// Check if dependencies are met and integration is active.
72
		if ( ! $this->is_active() ) {
73
			return;
74
		}
75
76
		// @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/lib/MeprGatewayFactory.php#L48-50
77
		\add_filter( 'mepr-gateway-paths', array( $this, 'gateway_paths' ) );
78
79
		\add_filter( 'pronamic_payment_redirect_url_memberpress_transaction', array( $this, 'redirect_url' ), 10, 2 );
80
		\add_action( 'pronamic_payment_status_update_memberpress_transaction', array( $this, 'status_update' ), 10, 1 );
81
82
		\add_action( 'pronamic_pay_new_payment', array( $this, 'maybe_create_memberpress_transaction' ), 10, 1 );
83
84
		\add_filter( 'pronamic_subscription_source_text_memberpress_subscription', array( $this, 'subscription_source_text' ), 10, 2 );
85
		\add_filter( 'pronamic_subscription_source_url_memberpress_subscription', array( $this, 'subscription_source_url' ), 10, 2 );
86
		\add_filter( 'pronamic_payment_source_text_memberpress_transaction', array( $this, 'source_text' ), 10, 2 );
87
		\add_filter( 'pronamic_payment_source_url_memberpress_transaction', array( $this, 'source_url' ), 10, 2 );
88
89
		\add_action( 'mepr_subscription_pre_delete', array( $this, 'subscription_pre_delete' ), 10, 1 );
90
91
		\add_action( 'mepr_subscription_transition_status', array( $this, 'memberpress_subscription_transition_status' ), 10, 3 );
92
93
		// MemberPress subscription email parameters.
94
		\add_filter( 'mepr_subscription_email_params', array( $this, 'subscription_email_params' ), 10, 2 );
95
		\add_filter( 'mepr_transaction_email_params', array( $this, 'transaction_email_params' ), 10, 2 );
96
97
		// Hide MemberPress columns for payments and subscriptions.
98
		\add_action( 'registered_post_type', array( $this, 'post_type_columns_hide' ), 15, 1 );
99
100
		if ( \is_admin() ) {
101
			$admin_subscriptions = new Admin\AdminSubscriptions();
102
			$admin_transactions  = new Admin\AdminTransactions();
103
104
			$admin_subscriptions->setup();
105
			$admin_transactions->setup();
106
		}
107
	}
108
109
	/**
110
	 * Gateway paths.
111
	 *
112
	 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/lib/MeprGatewayFactory.php#L49
113
	 * @param string[] $paths Array with gateway paths.
114
	 * @return string[]
115
	 */
116
	public function gateway_paths( $paths ) {
117
		$paths[] = dirname( __FILE__ ) . '/../gateways/';
118
119
		return $paths;
120
	}
121
122
	/**
123
	 * Hide MemberPress columns for payments and subscriptions.
124
	 *
125
	 * @link https://gitlab.com/pronamic/memberpress/blob/1.2.4/app/controllers/MeprAppCtrl.php#L129-146
126
	 *
127
	 * @param string $post_type Registered post type.
128
	 *
129
	 * @return void
130
	 */
131
	public function post_type_columns_hide( $post_type ) {
132
		if ( ! in_array( $post_type, array( 'pronamic_payment', 'pronamic_pay_subscr' ), true ) ) {
133
			return;
134
		}
135
136
		remove_filter( 'manage_edit-' . $post_type . '_columns', 'MeprAppCtrl::columns' );
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 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 PaymentStatus::CANCELLED:
158
			case PaymentStatus::EXPIRED:
159
			case PaymentStatus::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 PaymentStatus::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 PaymentStatus::OPEN:
192
			default:
193
				break;
194
		}
195
196
		return $url;
197
	}
198
199
	/**
200
	 * Maybe create create MemberPress transaction for the Pronamic payment.
201
	 * 
202
	 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/models/MeprSubscription.php
203
	 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/gateways/MeprStripeGateway.php#L587-L714
204
	 * @param Payment $payment Payment.
205
	 * @return void
206
	 * @throws \Exception Throws an exception when the MemberPress subscription cannot be found.
207
	 */
208
	public function maybe_create_memberpress_transaction( Payment $payment ) {
209
		if ( 'memberpress_subscription' !== $payment->get_source() ) {
210
			return;
211
		}
212
213
		$memberpress_subscription_id = $payment->get_source_id();
214
215
		$memberpress_subscription = MemberPress::get_subscription_by_id( $memberpress_subscription_id );
216
217
		if ( null === $memberpress_subscription ) {
218
			throw new \Exception(
219
				\sprintf(
220
					'Could not find MemberPress subscription with ID: %s.',
221
					$memberpress_subscription_id
222
				)
223
			);
224
		}
225
226
		/**
227
		 * If the payment method is changed we have to update the MemberPress
228
		 * subscription.
229
		 * 
230
		 * @link https://github.com/wp-pay-extensions/memberpress/commit/3631bcb24f376fb637c1317e15f540cb1f9136f4#diff-6f62438f6bf291e85f644dbdbb14b2a71a9a7ed205b01ce44290ed85abe2aa07L259-L290
231
		 */
232
		$memberpress_gateways = MeprOptions::fetch()->payment_methods();
233
234
		foreach ( $memberpress_gateways as $memberpress_gateway ) {
235
			if ( ! $memberpress_gateway instanceof Gateway ) {
236
				continue;
237
			}
238
239
			if ( $memberpress_gateway->get_payment_method() === $payment->get_method() ) {
240
				$memberpress_subscription->gateway = $memberpress_gateway->id;
241
			}
242
		}
243
244
		/**
245
		 * Payment method.
246
		 * 
247
		 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/models/MeprTransaction.php#L634-L637
248
		 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/models/MeprOptions.php#L798-L811
249
		 */
250
		$memberpress_gateway = $memberpress_subscription->payment_method();
251
252
		if ( ! $memberpress_gateway instanceof Gateway ) {
253
			return;
254
		}
255
256
		/**
257
		 * At this point we should call `MeprBaseRealGateway->record_subscription_payment`.
258
		 * 
259
		 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/gateways/MeprStripeGateway.php#L587-L714
260
		 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/gateways/MeprAuthorizeGateway.php#L205-L255
261
		 */
262
		$memberpress_gateway->record_subscription_payment();
263
264
		$memberpress_transaction = new MeprTransaction();
265
266
		$memberpress_transaction->user_id         = $memberpress_subscription->user_id;
267
		$memberpress_transaction->product_id      = $memberpress_subscription->product_id;
0 ignored issues
show
Bug Best Practice introduced by
The property product_id does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
268
		$memberpress_transaction->txn_type        = MeprTransaction::$payment_str;
0 ignored issues
show
Bug Best Practice introduced by
The property txn_type does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
269
		$memberpress_transaction->status          = MeprTransaction::$pending_str;
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
270
		$memberpress_transaction->coupon_id       = $memberpress_subscription->coupon_id;
0 ignored issues
show
Bug Best Practice introduced by
The property coupon_id does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
271
		$memberpress_transaction->trans_num       = $payment->get_transaction_id();
0 ignored issues
show
Bug Best Practice introduced by
The property trans_num does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
272
		$memberpress_transaction->subscription_id = $memberpress_subscription->id;
0 ignored issues
show
Bug Best Practice introduced by
The property subscription_id does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
273
		$memberpress_transaction->gateway         = $memberpress_gateway->id;
0 ignored issues
show
Bug Best Practice introduced by
The property gateway does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
274
275
		/**
276
		 * Gross.
277
		 * 
278
		 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/models/MeprTransaction.php#L1013-L1021
279
		 */
280
		$memberpress_transaction->set_gross( $payment->get_total_amount()->get_value() );
281
282
		$memberpress_transaction_id = $memberpress_transaction->store();
283
284
		MeprUtils::send_transaction_receipt_notices( $memberpress_transaction );
285
286
		/**
287
		 * Store the MemberPress subscription in case of gateway changes.
288
		 */
289
		$memberpress_subscription->store();
290
291
		/**
292
		 * Update payment source.
293
		 * 
294
		 * @link https://github.com/wp-pay-extensions/restrict-content-pro/blob/3.0.0/src/Extension.php#L770-L776
295
		 */
296
		$payment->source    = 'memberpress_transaction';
297
		$payment->source_id = $memberpress_transaction_id;
298
299
		$payment->save();
300
	}
301
302
	/**
303
	 * Update lead status of the specified payment.
304
	 *
305
	 * @param Payment $payment The payment whose status is updated.
306
	 * @return void
307
	 */
308
	public function status_update( Payment $payment ) {
309
		$payment_source_id = $payment->get_source_id();
310
311
		$memberpress_transaction = MemberPress::get_transaction_by_id( $payment_source_id );
312
313
		/**
314
		 * If we can't find a MemberPress transaction by the payment source ID
315
		 * we can't update the MemberPress transaction, bail out early.
316
		 */
317
		if ( null === $memberpress_transaction ) {
318
			return;
319
		}
320
321
		/**
322
		 * We don't update MemberPress transactions that already have the
323
		 * status 'failed' or 'complete'.
324
		 */
325
		if ( MemberPress::transaction_has_status(
326
			$memberpress_transaction,
327
			array(
328
				MeprTransaction::$failed_str,
329
				MeprTransaction::$complete_str,
330
			)
331
		) ) {
332
			return;
333
		}
334
335
		/**
336
		 * Payment method.
337
		 * 
338
		 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/models/MeprTransaction.php#L634-L637
339
		 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/models/MeprOptions.php#L798-L811
340
		 */
341
		$memberpress_gateway = $memberpress_transaction->payment_method();
342
343
		if ( ! $memberpress_gateway instanceof Gateway ) {
344
			return;
345
		} 
346
347
		$memberpress_gateway->set_record_data( $payment, $memberpress_transaction );
348
349
		switch ( $payment->get_status() ) {
350
			case PaymentStatus::FAILURE:
351
			case PaymentStatus::CANCELLED:
352
			case PaymentStatus::EXPIRED:
353
				$memberpress_gateway->record_payment_failure();
354
355
				break;
356
			case PaymentStatus::SUCCESS:
357
				$memberpress_gateway->record_payment();
358
359
				break;
360
			case PaymentStatus::OPEN:
361
			default:
362
				break;
363
		}
364
	}
365
366
	/**
367
	 * Subscription deleted.
368
	 *
369
	 * @param int $subscription_id MemberPress subscription id.
370
	 * @return void
371
	 */
372
	public function subscription_pre_delete( $subscription_id ) {
373
		$subscription = get_pronamic_subscription_by_meta( '_pronamic_subscription_source_id', $subscription_id );
374
375
		if ( ! $subscription ) {
376
			return;
377
		}
378
379
		// Add note.
380
		$note = sprintf(
381
			/* translators: %s: MemberPress */
382
			__( '%s subscription deleted.', 'pronamic_ideal' ),
383
			__( 'MemberPress', 'pronamic_ideal' )
384
		);
385
386
		$subscription->add_note( $note );
387
388
		// The status of canceled or completed subscriptions will not be changed automatically.
389
		if ( ! in_array( $subscription->get_status(), array( SubscriptionStatus::CANCELLED, SubscriptionStatus::COMPLETED ), true ) ) {
390
			$subscription->set_status( SubscriptionStatus::CANCELLED );
391
392
			$subscription->save();
393
		}
394
	}
395
396
	/**
397
	 * Subscription email parameters.
398
	 *
399
	 * @param array<string, string> $params                   Email parameters.
400
	 * @param MeprSubscription      $memberpress_subscription MemberPress subscription.
401
	 * @return array<string, string>
402
	 */
403
	public function subscription_email_params( $params, MeprSubscription $memberpress_subscription ) {
404
		$subscriptions = \get_pronamic_subscriptions_by_source( 'memberpress', $memberpress_subscription->id );
405
406
		if ( empty( $subscriptions ) ) {
407
			return $params;
408
		}
409
410
		$subscription = reset( $subscriptions );
411
412
		// Add parameters.
413
		$next_payment_date = $subscription->get_next_payment_date();
414
415
		return \array_merge(
416
			$params,
417
			array(
418
				'pronamic_subscription_id'           => (string) $subscription->get_id(),
419
				'pronamic_subscription_cancel_url'   => $subscription->get_cancel_url(),
420
				'pronamic_subscription_renewal_url'  => $subscription->get_renewal_url(),
421
				'pronamic_subscription_renewal_date' => null === $next_payment_date ? '' : \date_i18n( \get_option( 'date_format' ), $next_payment_date->getTimestamp() ),
0 ignored issues
show
It seems like get_option('date_format') can also be of type false; however, parameter $format of date_i18n() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

421
				'pronamic_subscription_renewal_date' => null === $next_payment_date ? '' : \date_i18n( /** @scrutinizer ignore-type */ \get_option( 'date_format' ), $next_payment_date->getTimestamp() ),
Loading history...
422
			)
423
		);
424
	}
425
426
	/**
427
	 * Transaction email parameters.
428
	 *
429
	 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/helpers/MeprTransactionsHelper.php#L233
430
	 * @param array<string, string> $params      Parameters.
431
	 * @param MeprTransaction       $transaction MemberPress transaction.
432
	 * @return array<string, string>
433
	 */
434
	public function transaction_email_params( $params, MeprTransaction $transaction ) {
435
		$payments = \get_pronamic_payments_by_source( 'memberpress', $transaction->id );
436
437
		if ( null === $payments ) {
438
			return $params;
439
		}
440
441
		$payment = \reset( $payments );
442
443
		if ( false === $payment ) {
444
			return $params;
445
		}
446
447
		// Get subscription.
448
		$periods = $payment->get_periods();
449
450
		if ( null === $periods ) {
451
			return $params;
452
		}
453
454
		$period = \reset( $periods );
455
456
		if ( false === $period ) {
457
			return $params;
458
		}
459
460
		$subscription = $period->get_phase()->get_subscription();
461
462
		// Add parameters.
463
		$memberpress_subscription = new MeprSubscription( $subscription->get_source_id() );
464
465
		return $this->subscription_email_params( $params, $memberpress_subscription );
466
	}
467
468
	/**
469
	 * Source text.
470
	 *
471
	 * @param string  $text    Source text.
472
	 * @param Payment $payment Payment to create the source text for.
473
	 *
474
	 * @return string
475
	 */
476
	public function source_text( $text, Payment $payment ) {
477
		$text = __( 'MemberPress', 'pronamic_ideal' ) . '<br />';
478
479
		$text .= sprintf(
480
			'<a href="%s">%s</a>',
481
			add_query_arg(
482
				array(
483
					'page'   => 'memberpress-trans',
484
					'action' => 'edit',
485
					'id'     => $payment->source_id,
486
				),
487
				admin_url( 'admin.php' )
488
			),
489
			/* translators: %s: payment source id */
490
			sprintf( __( 'Transaction %s', 'pronamic_ideal' ), $payment->source_id )
491
		);
492
493
		return $text;
494
	}
495
496
	/**
497
	 * Subscription source text.
498
	 *
499
	 * @param string       $text         Source text.
500
	 * @param Subscription $subscription Subscription to create the source text for.
501
	 *
502
	 * @return string
503
	 */
504
	public function subscription_source_text( $text, Subscription $subscription ) {
505
		$text = __( 'MemberPress', 'pronamic_ideal' ) . '<br />';
506
507
		$text .= sprintf(
508
			'<a href="%s">%s</a>',
509
			add_query_arg(
510
				array(
511
					'page'         => 'memberpress-subscriptions',
512
					'subscription' => $subscription->source_id,
513
				),
514
				admin_url( 'admin.php' )
515
			),
516
			/* translators: %s: payment source id */
517
			sprintf( __( 'Subscription %s', 'pronamic_ideal' ), $subscription->source_id )
518
		);
519
520
		return $text;
521
	}
522
523
	/**
524
	 * Source description.
525
	 *
526
	 * @param string  $description Description.
527
	 * @param Payment $payment     Payment to create the description for.
528
	 *
529
	 * @return string
530
	 */
531
	public function source_description( $description, Payment $payment ) {
532
		return __( 'MemberPress Transaction', 'pronamic_ideal' );
533
	}
534
535
	/**
536
	 * Subscription source description.
537
	 *
538
	 * @param string       $description  Description.
539
	 * @param Subscription $subscription Subscription to create the description for.
540
	 *
541
	 * @return string
542
	 */
543
	public function subscription_source_description( $description, Subscription $subscription ) {
544
		return __( 'MemberPress Subscription', 'pronamic_ideal' );
545
	}
546
547
	/**
548
	 * Source URL.
549
	 *
550
	 * @param string  $url     URL.
551
	 * @param Payment $payment The payment to create the source URL for.
552
	 *
553
	 * @return string
554
	 */
555
	public function source_url( $url, Payment $payment ) {
556
		$url = add_query_arg(
557
			array(
558
				'page'   => 'memberpress-trans',
559
				'action' => 'edit',
560
				'id'     => $payment->source_id,
561
			),
562
			admin_url( 'admin.php' )
563
		);
564
565
		return $url;
566
	}
567
568
	/**
569
	 * Subscription source URL.
570
	 *
571
	 * @param string       $url          URL.
572
	 * @param Subscription $subscription Subscription.
573
	 *
574
	 * @return string
575
	 */
576
	public function subscription_source_url( $url, Subscription $subscription ) {
577
		$url = add_query_arg(
578
			array(
579
				'page'         => 'memberpress-subscriptions',
580
				'subscription' => $subscription->source_id,
581
			),
582
			admin_url( 'admin.php' )
583
		);
584
585
		return $url;
586
	}
587
588
	/**
589
	 * MemberPress update subscription.
590
	 *
591
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/controllers/MeprSubscriptionsCtrl.php#L92-L111
592
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L100-L123
593
	 * @link https://github.com/wp-premium/memberpress-basic/blob/1.3.18/app/models/MeprSubscription.php#L112
594
	 * @link https://github.com/wp-premium/memberpress/blob/1.9.21/app/models/MeprSubscription.php#L122
595
	 * @param string           $status_old               Old status identifier.
596
	 * @param string           $status_new               New status identifier.
597
	 * @param MeprSubscription $memberpress_subscription MemberPress subscription object.
598
	 * @return void
599
	 */
600
	public function memberpress_subscription_transition_status( $status_old, $status_new, $memberpress_subscription ) {
601
		$subscription = get_pronamic_subscription_by_meta( '_pronamic_subscription_source_id', $memberpress_subscription->id );
602
603
		if ( empty( $subscription ) ) {
604
			return;
605
		}
606
607
		$status = SubscriptionStatuses::transform( $status_new );
608
609
		if ( null === $status ) {
610
			return;
611
		}
612
613
		$subscription->set_status( $status );
614
615
		$subscription->save();
616
	}
617
}
618