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