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