Skip to content

Commit 65996ad

Browse files
committed
fix(gateways): add reactivation cart type handling to all payment gateways
PayPal Express, PayPal REST, PayPal webhook handler, and Stripe webhook handler now call Membership::reactivate() instead of renew() when processing cancelled/expired memberships. This ensures: - wu_membership_pre_reactivate / wu_membership_post_reactivate hooks fire - Cancellation metadata is cleared correctly via the reactivate() path - Consistent behavior across all gateways (Stripe checkout already handled) Also converts PayPal gateway conditions to Yoda form per coding standards. Closes #752
1 parent 54e2589 commit 65996ad

4 files changed

Lines changed: 126 additions & 55 deletions

File tree

inc/gateways/class-base-stripe-gateway.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3061,11 +3061,22 @@ public function process_webhooks() {
30613061
}
30623062
}
30633063

3064-
/**
3065-
* Renewals the membership
3064+
/*
3065+
* Renew or reactivate the membership depending on its current state.
3066+
*
3067+
* Use reactivate() when the membership is currently cancelled or expired
3068+
* so that wu_membership_pre_reactivate / wu_membership_post_reactivate
3069+
* hooks fire and cancellation metadata is cleared correctly.
3070+
*
3071+
* @since 2.5.0
30663072
*/
30673073
$membership->add_to_times_billed(1);
3068-
$membership->renew($membership->is_recurring(), 'active', $expiration);
3074+
3075+
if (Membership_Status::CANCELLED === $membership->get_status() || Membership_Status::EXPIRED === $membership->get_status()) {
3076+
$membership->reactivate($membership->is_recurring(), $expiration);
3077+
} else {
3078+
$membership->renew($membership->is_recurring(), 'active', $expiration);
3079+
}
30693080

30703081
/**
30713082
* We need to save here to ensure that we are not saving more than once.
@@ -3238,9 +3249,16 @@ public function process_webhooks() {
32383249
$expiration = '';
32393250
}
32403251

3241-
$new_status = 'trialing' === $stripe_status ? Membership_Status::TRIALING : Membership_Status::ACTIVE;
3252+
$new_status = 'trialing' === $stripe_status ? Membership_Status::TRIALING : Membership_Status::ACTIVE;
32423253

3243-
$membership->renew($membership->is_recurring(), $new_status, $expiration);
3254+
/*
3255+
* Use reactivate() for expired memberships so that
3256+
* wu_membership_pre_reactivate / wu_membership_post_reactivate
3257+
* hooks fire and cancellation metadata is cleared.
3258+
*
3259+
* @since 2.5.0
3260+
*/
3261+
$membership->reactivate($membership->is_recurring(), $expiration);
32443262

32453263
$membership->add_note(
32463264
[

inc/gateways/class-paypal-gateway.php

Lines changed: 60 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,41 +1387,50 @@ protected function create_recurring_profile($details, $cart, $payment, $membersh
13871387
$membership->set_gateway_customer_id($details['PAYERID']);
13881388
$membership->set_gateway('paypal');
13891389

1390-
if (Payment_Status::COMPLETED === $payment_status) {
1391-
$membership->add_to_times_billed(1);
1390+
if (Payment_Status::COMPLETED === $payment_status) {
1391+
$membership->add_to_times_billed(1);
13921392

1393-
/*
1394-
* Lets deal with upgrades, downgrades and addons
1395-
*
1396-
* Here, we just need to make sure we process
1397-
* a membership swap.
1398-
*/
1399-
if ($cart->get_cart_type() === 'upgrade' || $cart->get_cart_type() === 'addon') {
1400-
$membership->swap($cart);
1393+
/*
1394+
* Lets deal with upgrades, downgrades and addons
1395+
*
1396+
* Here, we just need to make sure we process
1397+
* a membership swap.
1398+
*/
1399+
if ('upgrade' === $cart->get_cart_type() || 'addon' === $cart->get_cart_type()) {
1400+
$membership->swap($cart);
14011401

1402-
$membership->renew(true);
1403-
} elseif ($cart->get_cart_type() === 'downgrade') {
1404-
$membership->set_auto_renew(true);
1402+
$membership->renew(true);
1403+
} elseif ('downgrade' === $cart->get_cart_type()) {
1404+
$membership->set_auto_renew(true);
14051405

1406-
$membership->schedule_swap($cart);
1406+
$membership->schedule_swap($cart);
14071407

1408-
$membership->save();
1409-
} elseif ( ! $is_trial_setup) {
1410-
$membership->renew(true);
1411-
} else {
1412-
$membership->save();
1413-
}
1408+
$membership->save();
1409+
} elseif ('reactivation' === $cart->get_cart_type()) {
1410+
/*
1411+
* Use reactivate() for reactivation carts so that the
1412+
* wu_membership_pre_reactivate / wu_membership_post_reactivate
1413+
* hooks fire and cancellation metadata is cleared correctly.
1414+
*
1415+
* @since 2.5.0
1416+
*/
1417+
$membership->reactivate(true);
1418+
} elseif ( ! $is_trial_setup) {
1419+
$membership->renew(true);
14141420
} else {
14151421
$membership->save();
14161422
}
1423+
} else {
1424+
$membership->save();
1425+
}
14171426

1418-
$this->payment = $payment;
1419-
$redirect_url = $this->get_return_url();
1427+
$this->payment = $payment;
1428+
$redirect_url = $this->get_return_url();
14201429

1421-
wp_safe_redirect($redirect_url);
1430+
wp_safe_redirect($redirect_url);
14221431

1423-
exit;
1424-
}
1432+
exit;
1433+
}
14251434
} else {
14261435
wp_die(
14271436
esc_html__('Something has gone wrong, please try again', 'ultimate-multisite'),
@@ -1531,27 +1540,36 @@ protected function complete_single_payment($details, $cart, $payment, $membershi
15311540

15321541
$is_trial_setup = $membership->is_trialing() && empty($payment->get_total());
15331542

1543+
/*
1544+
* Lets deal with upgrades, downgrades and addons
1545+
*
1546+
* Here, we just need to make sure we process
1547+
* a membership swap.
1548+
*/
1549+
if ('upgrade' === $cart->get_cart_type() || 'addon' === $cart->get_cart_type()) {
1550+
$membership->swap($cart);
1551+
1552+
$membership->renew(false);
1553+
} elseif ('downgrade' === $cart->get_cart_type()) {
1554+
$membership->schedule_swap($cart);
1555+
1556+
$membership->save();
1557+
} elseif ('reactivation' === $cart->get_cart_type()) {
15341558
/*
1535-
* Lets deal with upgrades, downgrades and addons
1559+
* Use reactivate() for reactivation carts so that the
1560+
* wu_membership_pre_reactivate / wu_membership_post_reactivate
1561+
* hooks fire and cancellation metadata is cleared correctly.
15361562
*
1537-
* Here, we just need to make sure we process
1538-
* a membership swap.
1563+
* @since 2.5.0
15391564
*/
1540-
if ($cart->get_cart_type() === 'upgrade' || $cart->get_cart_type() === 'addon') {
1541-
$membership->swap($cart);
1542-
1543-
$membership->renew(false);
1544-
} elseif ($cart->get_cart_type() === 'downgrade') {
1545-
$membership->schedule_swap($cart);
1546-
1547-
$membership->save();
1548-
} elseif ( ! $is_trial_setup) {
1549-
$membership->renew(false);
1550-
} else {
1551-
$membership->save();
1552-
}
1565+
$membership->reactivate(false);
1566+
} elseif ( ! $is_trial_setup) {
1567+
$membership->renew(false);
1568+
} else {
1569+
$membership->save();
1570+
}
15531571

1554-
$this->payment = $payment;
1572+
$this->payment = $payment;
15551573
$redirect_url = $this->get_return_url();
15561574

15571575
wp_safe_redirect($redirect_url);

inc/gateways/class-paypal-rest-gateway.php

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,9 +1290,20 @@ protected function confirm_subscription(string $subscription_id): void {
12901290
$payment->set_status(Payment_Status::PENDING);
12911291
}
12921292

1293-
// Always renew regardless of ACTIVE vs APPROVED — this activates the membership
1294-
// and fires wu_membership_post_renew which triggers site creation.
1295-
$membership->renew(false);
1293+
/*
1294+
* Use reactivate() when the membership is currently cancelled or expired
1295+
* so that wu_membership_pre_reactivate / wu_membership_post_reactivate
1296+
* hooks fire and cancellation metadata is cleared correctly.
1297+
*
1298+
* @since 2.5.0
1299+
*/
1300+
$inactive_statuses = [Membership_Status::CANCELLED, Membership_Status::EXPIRED];
1301+
1302+
if (in_array($membership->get_status(), $inactive_statuses, true)) {
1303+
$membership->reactivate(false);
1304+
} else {
1305+
$membership->renew(false);
1306+
}
12961307

12971308
$payment->set_gateway('paypal-rest');
12981309
$payment->save();
@@ -1366,7 +1377,21 @@ protected function confirm_order(string $token): void {
13661377
$membership->set_gateway('paypal-rest');
13671378
$membership->set_gateway_customer_id($capture['payer']['payer_id'] ?? '');
13681379
$membership->add_to_times_billed(1);
1369-
$membership->renew(false);
1380+
1381+
/*
1382+
* Use reactivate() when the membership is currently cancelled or expired
1383+
* so that wu_membership_pre_reactivate / wu_membership_post_reactivate
1384+
* hooks fire and cancellation metadata is cleared correctly.
1385+
*
1386+
* @since 2.5.0
1387+
*/
1388+
$inactive_statuses = [Membership_Status::CANCELLED, Membership_Status::EXPIRED];
1389+
1390+
if (in_array($membership->get_status(), $inactive_statuses, true)) {
1391+
$membership->reactivate(false);
1392+
} else {
1393+
$membership->renew(false);
1394+
}
13701395

13711396
$this->log(sprintf('Order captured: %s, Transaction: %s', $token, $transaction_id));
13721397

inc/gateways/class-paypal-webhook-handler.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,10 +314,20 @@ protected function handle_subscription_activated(array $event_data): void {
314314
return;
315315
}
316316

317-
// Update membership status if needed — use renew() so wu_membership_post_renew
318-
// fires and triggers site creation / all post-activation hooks.
319-
if ($membership->get_status() !== Membership_Status::ACTIVE) {
320-
$membership->renew(true);
317+
// Update membership status if needed.
318+
if (Membership_Status::ACTIVE !== $membership->get_status()) {
319+
/*
320+
* Use reactivate() when the membership is currently cancelled or expired
321+
* so that wu_membership_pre_reactivate / wu_membership_post_reactivate
322+
* hooks fire and cancellation metadata is cleared correctly.
323+
*
324+
* @since 2.5.0
325+
*/
326+
if (Membership_Status::CANCELLED === $membership->get_status() || Membership_Status::EXPIRED === $membership->get_status()) {
327+
$membership->reactivate(true);
328+
} else {
329+
$membership->renew(true);
330+
}
321331

322332
$this->log(sprintf('Membership %d activated via webhook', $membership->get_id()));
323333
}

0 commit comments

Comments
 (0)