Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
cdf4b23
Add deprecated gateway sunset workflow
dparker1005 Jun 3, 2026
f95c6cc
Rework deprecated gateway migration workflow
dparker1005 Jun 10, 2026
364c6d7
Improve deprecated gateway migration feedback
dparker1005 Jun 10, 2026
cd693c7
Refine deprecated gateway migration review states
dparker1005 Jun 10, 2026
f72446f
Harden deprecated gateway migration recovery and concurrency
dparker1005 Jun 10, 2026
2cbcf74
Merge remote-tracking branch 'strangerstudios/v3.8' into feature/depr…
dparker1005 Jun 10, 2026
7150118
Clarify that unmigrated Stripe members lose their membership
dparker1005 Jun 10, 2026
c538a79
Make the billing limit bridge order check match enforcement
dparker1005 Jun 10, 2026
4007055
Fix cross-environment checks in deprecated gateway migration
dparker1005 Jun 10, 2026
2f41d25
Track migrated subscriptions awaiting a payment method
dparker1005 Jun 11, 2026
353421e
Improve deprecated gateway migration panel wording
dparker1005 Jun 11, 2026
8182fc8
Limit Stripe migration readiness to a credentials check
dparker1005 Jun 11, 2026
f975474
Normalize gateway environments in the deprecated gateway workflow
dparker1005 Jun 11, 2026
f1d2c03
Add a dry run option to the deprecated gateway migration workflow
dparker1005 Jun 12, 2026
3dd0262
Polish the deprecated gateway migration panel and emails
dparker1005 Jun 17, 2026
ffad7ea
Lower the deprecated gateway migration batch size default to 5
dparker1005 Jun 18, 2026
4a28b6b
Rework the deprecated gateway "Force Migration" setting for clarity
dparker1005 Jun 18, 2026
81d53f5
Add a per-run migration CSV to the deprecated gateway workflow
dparker1005 Jun 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 18 additions & 22 deletions adminpages/paymentsettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@
$pmpro_gateways = pmpro_gateways();
$deprecated_gateways = pmpro_get_deprecated_gateways();

// Show a confirmation after deprecated gateway data is removed.
$deprecated_gateway_removed_message = '';
if ( ! empty( $_REQUEST['deprecated_gateway_removed'] ) ) {
$removed_gateway = sanitize_key( wp_unslash( $_REQUEST['deprecated_gateway_removed'] ) );
$deprecated_gateway_removed_message = sprintf(
// translators: %s is the gateway name.
__( 'The %s gateway and its stored data have been removed from this site.', 'paid-memberships-pro' ),
pmpro_get_gateway_nicename( $removed_gateway )
);
}

require_once(dirname(__FILE__) . "/admin_header.php");
?>

Expand All @@ -106,6 +117,11 @@
// Show the table of gateways and global settings.
?>
<h1><?php esc_html_e( 'Payment Settings', 'paid-memberships-pro' );?></h1>
<?php if ( ! empty( $deprecated_gateway_removed_message ) ) { ?>
<div class="notice notice-success inline">
<p><?php echo esc_html( $deprecated_gateway_removed_message ); ?></p>
</div>
<?php } ?>
<div id="global-settings" class="pmpro_section" data-visibility="shown" data-activated="true">
<div class="pmpro_section_toggle">
<button class="pmpro_section-toggle-button" type="button" aria-expanded="true">
Expand Down Expand Up @@ -340,29 +356,9 @@
?>
</h1>
<?php
// If this gateway is deprecated, show a warning.
// If this gateway is deprecated, show the deprecation notice and migration workflow.
if ( in_array( $edit_gateway, $deprecated_gateways, true ) ) {
?>
<div class="pmpro_message pmpro_error">
<p><strong><?php esc_html_e('Notice: You Are Using a Deprecated Gateway', 'paid-memberships-pro' ); ?></strong></p>
<p>
<?php
// translators: %s is the gateway name.
printf(
esc_html__('The %s gateway has been deprecated and will not receive updates or support. To ensure your payments continue running smoothly, switch to a supported payment gateway.', 'paid-memberships-pro'),
esc_html( $pmpro_gateways[ $edit_gateway ] )
);
?>
</p>
<p>
<?php if ( 'paypalexpress' === $edit_gateway ) { ?>
<a class="button button-secondary" href="https://www.paidmembershipspro.com/paypal-express-deprecation-hub/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=blog&utm_content=paypal-express-deprecation" target="_blank" rel="nofollow noopener"><?php esc_html_e('Learn More', 'paid-memberships-pro' ); ?></a>
<?php } ?>
<a class="button button-secondary" href="https://www.paidmembershipspro.com/documentation/compatibility/incompatible-deprecated-add-ons/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=documentation&utm_content=deprecated-gateways#deprecated-payment-gateways" target="_blank" rel="nofollow noopener"><?php esc_html_e('About Deprecated Gateways', 'paid-memberships-pro' ); ?></a>
<a class="button" href="https://www.paidmembershipspro.com/switching-payment-gateways/?utm_source=plugin&utm_medium=pmpro-paymentsettings&utm_campaign=blog&utm_content=switching-payment-gateways" target="_blank" rel="nofollow noopener"><?php esc_html_e('How to Switch Payment Gateways', 'paid-memberships-pro' ); ?></a>
</p>
</div>
<?php
pmpro_deprecated_gateway_render_panel( $edit_gateway );
}

// Show the settings for the specific gateway.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<?php

class PMPro_Email_Template_Deprecated_Gateway_Checkout_Required extends PMPro_Email_Template {
/**
* The user object of the user to send the email to.
*
* @var WP_User
*/
protected $user;

/**
* The level ID.
*
* @var int
*/
protected $membership_level_id;

/**
* Unix timestamp when the membership expires.
*
* @var int
*/
protected $expiration_timestamp;

/**
* Constructor.
*
* @since TBD
*
* @param WP_User $user The user object of the user to send the email to.
* @param int $membership_level_id The ID of the membership level.
* @param int $expiration_timestamp Unix timestamp when the membership expires.
*/
public function __construct( WP_User $user, int $membership_level_id, int $expiration_timestamp ) {
$this->user = $user;
$this->membership_level_id = $membership_level_id;
$this->expiration_timestamp = $expiration_timestamp;
}

/**
* Get the email template slug.
*
* @since TBD
*
* @return string The email template slug.
*/
public static function get_template_slug() {
return 'deprecated_gateway_checkout_required';
}

/**
* Get the "nice name" of the email template.
*
* @since TBD
*
* @return string The "nice name" of the email template.
*/
public static function get_template_name() {
return esc_html__( 'Deprecated Gateway Checkout Required', 'paid-memberships-pro' );
}

/**
* Get "help text" to display to the admin when editing the email template.
*
* @since TBD
*
* @return string The help text.
*/
public static function get_template_description() {
return esc_html__( 'This email is sent when an administrator cancels a deprecated gateway subscription and sets the member\'s expiration date to the old next payment date. It tells the member they will need to check out again to continue their membership.', 'paid-memberships-pro' );
}

/**
* Get the default subject for the email.
*
* @since TBD
*
* @return string The default subject.
*/
public static function get_default_subject() {
return esc_html__( 'Action required: renew your membership at {{ sitename }}', 'paid-memberships-pro' );
}

/**
* Get the default body content for the email.
*
* @since TBD
*
* @return string The default body content.
*/
public static function get_default_body() {
return wp_kses_post( __( '<p>Your saved payment method for {{ membership_level_name }} at {{ sitename }} is no longer valid because the payment processor that stored it is no longer supported.</p>

<p>Your membership remains active until {{ expiration_date }}.</p>

<p>To continue your membership, you will need to check out again. Checking out before {{ expiration_date }} may replace your remaining access time.</p>

<p><a href="{{ checkout_url }}">Check Out Again</a></p>', 'paid-memberships-pro' ) );
}

/**
* Get the email template variables for the email paired with a description.
*
* @since TBD
*
* @return array The email template variables for the email.
*/
public static function get_email_template_variables_with_description() {
return array(
'{{ display_name }}' => esc_html__( 'The display name of the user.', 'paid-memberships-pro' ),
'{{ user_login }}' => esc_html__( 'The username of the user.', 'paid-memberships-pro' ),
'{{ user_email }}' => esc_html__( 'The email address of the user.', 'paid-memberships-pro' ),
'{{ membership_id }}' => esc_html__( 'The ID of the membership level.', 'paid-memberships-pro' ),
'{{ membership_level_name }}' => esc_html__( 'The name of the membership level.', 'paid-memberships-pro' ),
'{{ expiration_date }}' => esc_html__( 'The date when the member\'s current access expires.', 'paid-memberships-pro' ),
'{{ checkout_url }}' => esc_html__( 'The checkout URL for the member\'s membership level.', 'paid-memberships-pro' ),
);
}

/**
* Get the email address to send the email to.
*
* @since TBD
*
* @return string The email address.
*/
public function get_recipient_email() {
return $this->user->user_email;
}

/**
* Get the name of the email recipient.
*
* @since TBD
*
* @return string The name.
*/
public function get_recipient_name() {
return $this->user->display_name;
}

/**
* Get the email template variables for the email.
*
* @since TBD
*
* @return array The email template variables.
*/
public function get_email_template_variables() {
$user = $this->user;
$level = pmpro_getSpecificMembershipLevelForUser( $user->ID, $this->membership_level_id );
if ( empty( $level ) ) {
$level = pmpro_getLevel( $this->membership_level_id );
}

$expiration_date = date_i18n( get_option( 'date_format' ), $this->expiration_timestamp );

return array(
'name' => $user->display_name,
'display_name' => $user->display_name,
'user_login' => $user->user_login,
'user_email' => $user->user_email,
'membership_id' => ! empty( $level->id ) ? $level->id : $this->membership_level_id,
'membership_level_name' => ! empty( $level->name ) ? $level->name : '',
'expiration_date' => $expiration_date,
'checkout_url' => pmpro_url( 'checkout', 'pmpro_level=' . (int) $this->membership_level_id ),
);
}

/**
* Returns the arguments to send the test email from the abstract class.
*
* @since TBD
*
* @return array The arguments to send the test email from the abstract class.
*/
public static function get_test_email_constructor_args() {
global $current_user;

$levels = pmpro_getAllLevels( true );
$level = current( $levels );
$level_id = empty( $level ) ? 1 : (int) $level->id;

return array( $current_user, $level_id, strtotime( '+1 month' ) );
}
}

/**
* Register the email template.
*
* @since TBD
*
* @param array $email_templates The email templates.
* @return array The modified email templates array.
*/
function pmpro_deprecated_gateway_register_checkout_required_email_template( $email_templates ) {
if ( function_exists( 'pmpro_has_undeprecated_gateways' ) && pmpro_has_undeprecated_gateways() ) {
$email_templates['deprecated_gateway_checkout_required'] = 'PMPro_Email_Template_Deprecated_Gateway_Checkout_Required';
}

return $email_templates;
}
add_filter( 'pmpro_email_templates', 'pmpro_deprecated_gateway_register_checkout_required_email_template' );
Loading