Passed
Push — develop ( 8818fa...832d85 )
by Remco
07:20
created

table_joins_nested_query_fix()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 28
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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