'.esc_html__('Unable to create/update PayPal Checkout webhook. PayPal requires an HTTPS (SSL) webhook URL, but your site URL appears to be configured without HTTPS. Please enable SSL and update your WordPress Site URL/Home URL to use https://, then try again.', 's2member').'

'."\n"; else { //260216 IMPORTANT: // Do not block webhook creation/update based on a server-side "loopback" probe. // Many shared hosts/WAFs return false negatives for loopback requests (e.g., HEAD blocked, // ModSecurity rules, disabled loopback/outbound HTTP). PayPal can still deliver webhooks // successfully from the outside. $r = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_webhook_upsert($env); if(!empty($r['id'])) { $ppco_webhook_notice = '

'.sprintf(esc_html__('PayPal Checkout %1$s webhook created/updated successfully.', 's2member'), $env_label).'

'."\n"; //260824.0507 The persistent warning was rendered before this page action ran; remove that now-resolved copy immediately. $ppco_webhook_notice .= ''."\n"; } else $ppco_webhook_notice = '

'.sprintf(esc_html__('Unable to create/update PayPal Checkout %1$s webhook. See the s2Member paypal-checkout.log for details.', 's2member'), $env_label).' '.esc_html__('Log Viewer', 's2member').'

'."\n"; } } else $ppco_webhook_notice = '

'.sprintf(esc_html__('PayPal Checkout %1$s credentials are not configured.', 's2member'), $env_label).'

'."\n"; } else $ppco_webhook_notice = '

'.esc_html__('Invalid request. Please try again.', 's2member').'

'."\n"; } // No redirect; display notice inline. if(!empty($ppco_r['s2member_ppco_creds_test']) && empty($ppco_r['s2member_ppco_webhook']) && empty($ppco_r['s2member_ppco_clear_cache']) && current_user_can('manage_options')) { if(!empty($ppco_r['_wpnonce']) && wp_verify_nonce((string)$ppco_r['_wpnonce'], 's2member_ppco_creds_test')) { $env = (!empty($ppco_r['ppco_creds_env']) && $ppco_r['ppco_creds_env'] === 'sandbox') ? 'sandbox' : 'live'; $env_label = ($env === 'sandbox') ? esc_html__('Sandbox', 's2member') : esc_html__('Live', 's2member'); $client_id = ''; $secret = ''; if($env === 'sandbox') { $client_id = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_id"]; $secret = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_sandbox_client_secret"]; } else // live { $client_id = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_id"]; $secret = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["paypal_checkout_client_secret"]; } if(!empty($client_id) && !empty($secret)) { $ok = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_creds_test($env); if($ok) $ppco_creds_notice = '

'.sprintf(esc_html__('PayPal Checkout %1$s credentials test OK.', 's2member'), $env_label).'

'."\n"; else $ppco_creds_notice = '

'.sprintf(esc_html__('PayPal Checkout %1$s credentials test failed. Please verify that the Client ID and Secret were copied from the same PayPal %1$s REST app, paste them again, save options, and test again. See the s2Member paypal-checkout.log for details.', 's2member'), $env_label).' '.esc_html__('Log Viewer', 's2member').'

'."\n"; } else $ppco_creds_notice = '

'.sprintf(esc_html__('PayPal Checkout %1$s credentials are not configured.', 's2member'), $env_label).'

'."\n"; } else $ppco_creds_notice = '

'.esc_html__('Invalid request. Please try again.', 's2member').'

'."\n"; } // No redirect; display notice inline. if(!empty($ppco_r['s2member_ppco_clear_cache']) && empty($ppco_r['s2member_ppco_webhook']) && empty($ppco_r['s2member_ppco_creds_test']) && current_user_can('manage_options')) { if(!empty($ppco_r['_wpnonce']) && wp_verify_nonce((string)$ppco_r['_wpnonce'], 's2member_ppco_clear_cache')) { $env = (!empty($ppco_r['ppco_clear_env']) && $ppco_r['ppco_clear_env'] === 'sandbox') ? 'sandbox' : 'live'; $env_label = ($env === 'sandbox') ? esc_html__('Sandbox', 's2member') : esc_html__('Live', 's2member'); c_ws_plugin__s2member_paypal_utilities::paypal_checkout_clear_cache($env); $ppco_cache_notice = '

'.sprintf(esc_html__('PayPal Checkout cache cleared for %1$s.', 's2member'), $env_label).'

'."\n"; } else $ppco_cache_notice = '

'.esc_html__('Invalid request. Please try again.', 's2member').'

'."\n"; } // No redirect; display notice inline. echo '
'."\n"; echo $ppco_webhook_notice; echo $ppco_creds_notice; echo $ppco_cache_notice; echo '
'."\n"; echo '
'."\n"; c_ws_plugin__s2member_menu_pages_tb::display(); echo '
'."\n"; echo '

PayPal Options

'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_before_left_sections_form", get_defined_vars()); echo '
'."\n"; echo ''."\n"; echo ''."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_before_left_sections", get_defined_vars()); if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_paypal_account_details", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_paypal_account_details", get_defined_vars()); //260106 New PayPal Checkout panel. //260824.0507 Allow administrative notices to open and jump directly to the PayPal Checkout panel. echo '
'."\n"; echo '
'."\n"; echo ''."\n"; // .ws-menu-page-section echo '
'."\n"; // .ws-menu-page-group echo '
'."\n"; echo ''."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_paypal_account_details", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_paypal_payflow_account_details", c_ws_plugin__s2member_utils_conds::pro_is_installed(), get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_paypal_payflow_account_details", get_defined_vars()); echo '
'."\n"; echo ''."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_paypal_payflow_account_details", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_paypal_ipn", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_paypal_ipn", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

PayPal IPN / Instant Payment Notifications (required, please enable)

'."\n"; echo '

Log into your PayPal account and navigate to this section:
Account Settings → Website Payments → Instant Payment Notifications

'."\n"; echo '

Edit your IPN settings & turn IPN Notifications: On

'."\n"; echo '

You\'ll need your IPN URL, which is:
'.esc_html(home_url("/?s2member_paypal_notify=1", "https")).'

'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_ipn", get_defined_vars()); echo '

Note: SSL is required by PayPal

'."\n"; echo '

If you configure your PayPal.com account using the URL above, your site must support SSL (i.e., the https:// protocol). In other words, PayPal\'s system will refuse to accept any URL that does not begin with https://. The IPN URL that s2Member provides (see above) does start with https://. However, that doesn\'t necessarily mean that the URL actually works. Please be sure that your hosting account is configured with a valid SSL certificate before giving this URL to PayPal.

'."\n"; //250426 Fallback for IPN Signup Vars. echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '    
'."\n"; echo 'Enable fallback behavior when the user\'s IPN Signup Variables are missing during the IPN event processing (useful for migrated or imported subscriptions, for example).
'."\n"; echo '
'."\n"; //250606 Skip Custom Value Domain Validation. echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '    
'."\n"; echo 'This allows continued processing of notifications even if the domain in the `custom` value doesn\'t match the current '.esc_html($_SERVER["HTTP_HOST"]).'. Useful for subscriptions originated outside of s2Member, or under a different domain.
'."\n"; echo '
'."\n"; echo '
'."\n"; echo '

More Information (click here)

'."\n"; echo ''."\n"; echo '
'."\n"; echo '

IPN w/ Proxy Key (optional, for 3rd-party integrations)

'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_paypal_ipn", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_paypal_pdt", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_paypal_pdt", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

PayPal PDT Identity Token (required, please enable)

'."\n"; echo '

Log into your PayPal account and navigate to this section:
Account Settings → Website Payments → Website Preferences

'."\n"; echo '

Turn the Auto-Return feature: On

'."\n"; echo '

You\'ll need your Auto-Return URL, which is:
'.esc_html(home_url("/?s2member_paypal_return=1")).'

'."\n"; echo '

You must also enable PDT (Payment Data Transfer): On
You\'ll be issued an Identity Token that you can enter below.

'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_pdt", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Your PDT Identity Token will appear under Account Settings → Website Payments → Website Preferences in your PayPal account.'."\n"; echo '
'."\n"; echo '
'."\n"; echo '

More Information (click here)

'."\n"; echo ''."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_paypal_pdt_after_more_info", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_paypal_pdt", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_signup_confirmation_email", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_signup_confirmation_email", get_defined_vars()); echo '
'."\n"; echo ''."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_signup_confirmation_email", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_modification_confirmation_email", c_ws_plugin__s2member_utils_conds::pro_is_installed(), get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_modification_confirmation_email", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Modification Confirmation Email (required, but the default works fine)

'."\n"; echo '

This email is sent to existing Users after they complete an upgrade/downgrade (if and when you make this possible). For instance, if a Free Subscriber upgrades to a paid Membership Level, s2Member considers this a Modification (NOT a Signup; a Signup is associated only with someone completely new). The primary purpose of this email is to provide the Customer with a confirmation that their account was updated. You may also customize this further by providing details that are specifically geared to your site.

'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_modification_confirmation_email", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:
'."\n"; echo '"%%full_name%%" <%%payer_email%%>; admin@example.com; "Webmaster" <webmaster@example.com>'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Subject Line used in the email sent to a Customer after a successful modification has occurred through PayPal.'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; //250615 Visual editor for HTML emails. if (empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['html_emails_enabled'])) { echo '
'."\n"; } else { c_ws_plugin__s2member_utilities::editor('modification_email_message'); } echo 'Message Body used in the email sent to a Customer after a successful modification has occurred through PayPal.

'."\n"; echo 'You can also use these special Replacement Codes if you need them:'."\n"; echo '
    '."\n"; echo '
  • %%subscr_id%% = The PayPal Subscription ID, which remains constant throughout any & all future payments. [ ? ]
  • '."\n"; echo '
  • %%subscr_baid%% = Applicable only with PayPal Pro (Payflow Edition); and only for Express Checkout transactions that require a Billing Agreement. This is the Subscription\'s Billing Agreement ID, which remains constant throughout any & all future payments. [ ? ]
  • '."\n"; echo '
  • %%currency%% = Three-character currency code (uppercase); e.g., USD
  • '."\n"; echo '
  • %%currency_symbol%% = Currency code symbol; e.g., $
  • '."\n"; echo '
  • %%initial%% = The Initial Fee. If you offered a 100% Free Trial, this will be 0. [ ? ]
  • '."\n"; echo '
  • %%regular%% = The Regular Amount of the Subscription. If you offer something 100% free, this will be 0. [ ? ]
  • '."\n"; echo '
  • %%recurring%% = This is the amount that will be charged on a recurring basis, or 0 if non-recurring. [ ? ]
  • '."\n"; echo '
  • %%first_name%% = The First Name of the Customer who purchased the Membership Subscription.
  • '."\n"; echo '
  • %%last_name%% = The Last Name of the Customer who purchased the Membership Subscription.
  • '."\n"; echo '
  • %%full_name%% = The Full Name (First & Last) of the Customer who purchased the Membership Subscription.
  • '."\n"; echo '
  • %%payer_email%% = The Email Address of the Customer who purchased the Membership Subscription.
  • '."\n"; echo '
  • %%item_number%% = The Item Number (colon separated level:custom_capabilities:fixed term) that the Subscription is for.
  • '."\n"; echo '
  • %%item_name%% = The Item Name (as provided by the desc="" attribute in your Shortcode, which briefly describes the Item Number).
  • '."\n"; echo '
  • %%initial_term%% = This is the term length of the Initial Period. This will be a numeric value, followed by a space, then a single letter. [ ? ]
  • '."\n"; echo '
  • %%initial_cycle%% = This is the %%initial_term%% from above, converted to a cycle representation of: X days/weeks/months/years.
  • '."\n"; echo '
  • %%regular_term%% = This is the term length of the Regular Period. This will be a numeric value, followed by a space, then a single letter. [ ? ]
  • '."\n"; echo '
  • %%regular_cycle%% = This is the %%regular_term%% from above, converted to a cycle representation of: [every] X days/weeks/months/years—OR daily, weekly, bi-weekly, monthly, bi-monthly, quarterly, yearly, or lifetime. This is a very useful Replacment Code. Its value is dynamic; depending on term length, recurring status, and period/term lengths configured.
  • '."\n"; echo '
  • %%recurring/regular_cycle%% = Example (14.95 / Monthly), or ... (0 / non-recurring); depending on the value of %%recurring%%.
  • '."\n"; echo '
  • %%user_first_name%% = The First Name listed on their User account. This might be different than what is on file with PayPal.
  • '."\n"; echo '
  • %%user_last_name%% = The Last Name listed on their User account. This might be different than what is on file with PayPal.
  • '."\n"; echo '
  • %%user_full_name%% = The Full Name listed on their User account. This might be different than what is on file with PayPal.
  • '."\n"; echo '
  • %%user_email%% = The Email Address associated with their User account. This might be different than what is on file with PayPal.
  • '."\n"; echo '
  • %%user_login%% = The Username associated with their account. The Customer created this during registration.
  • '."\n"; echo '
  • %%user_ip%% = The Customer\'s original IP Address, during checkout/registration via $_SERVER["REMOTE_ADDR"].
  • '."\n"; echo '
  • %%user_id%% = A unique WordPress User ID that references this account in the WordPress database.
  • '."\n"; echo '
'."\n"; if(c_ws_plugin__s2member_utils_conds::pro_is_installed()) { echo 'Coupon Replacement Codes (applicable only w/ s2Member Pro-Forms):'."\n"; echo '
    '."\n"; echo '
  • %%full_coupon_code%% = A full Coupon Code—if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e., the full Coupon Code).
  • '."\n"; echo '
  • %%coupon_code%% = A Coupon Code—if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).
  • '."\n"; echo '
  • %%coupon_affiliate_id%% = This is the end of an Affiliate Coupon Code (i.e., the referring affiliate\'s ID). This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.
  • '."\n"; echo '
'."\n"; } echo 'Custom Registration/Profile Fields are also supported in this email:'."\n"; echo '
    '."\n"; echo '
  • %%date_of_birth%% would be valid; if you have a Custom Registration/Profile Field with the ID date_of_birth.
  • '."\n"; echo '
  • %%street_address%% would be valid; if you have a Custom Registration/Profile Field with the ID street_address.
  • '."\n"; echo '
  • %%country%% would be valid; if you have a Custom Registration/Profile Field with the ID country.
  • '."\n"; echo '
  • %%etc, etc...%% see: s2Member → General Options → Registration/Profile Fields.
  • '."\n"; echo '
'."\n"; echo 'Custom Replacement Codes can also be inserted using these instructions:'."\n"; echo '
    '."\n"; echo '
  • %%cv0%% = The domain of your site, which is passed through the `custom` attribute in your Shortcode.
  • '."\n"; echo '
  • %%cv1%% = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3". You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.
  • '."\n"; echo '
'."\n"; echo 'This example uses cv1 to record a special marketing campaign:
'."\n"; echo '(The campaign (i.e., christmas-promo) could be referenced using %%cv1%%)
'."\n"; echo 'custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"'."\n"; echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '
'."\n". '

Advanced Customization: Further customization is possible with the ws_plugin__s2member_modification_email_sbj and ws_plugin__s2member_modification_email_msg filters from a plugin or theme.

'."\n" : ''; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_modification_confirmation_email", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_ccap_confirmation_email", c_ws_plugin__s2member_utils_conds::pro_is_installed(), get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_ccap_confirmation_email", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Capability Confirmation Email (required, but the default works fine)

'."\n"; echo '

This email is sent to existing Users after they complete a Buy Now purchase for one or more Custom Capabilities (if and when you make this possible); see: Dashboard → s2Member → PayPal Buttons/Forms → Capability (Buy Now). The primary purpose of this email is to provide the Customer with a confirmation that their account was updated. You may also customize this further by providing details that are specifically geared to your site.

'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_ccap_confirmation_email", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:
'."\n"; echo '"%%full_name%%" <%%payer_email%%>; admin@example.com; "Webmaster" <webmaster@example.com>'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Subject Line used in the email sent to a Customer after a purchase is completed through PayPal.'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; //250615 Visual editor for HTML emails. if (empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['html_emails_enabled'])) { echo '
'."\n"; } else { c_ws_plugin__s2member_utilities::editor('ccap_email_message'); } echo 'Message Body used in the email sent to a Customer after a purchase is completed through PayPal.

'."\n"; echo 'You can also use these special Replacement Codes if you need them:'."\n"; echo '
    '."\n"; echo '
  • %%txn_id%% = The PayPal Transaction ID. PayPal assigns a unique identifier for every purchase.
  • '."\n"; echo '
  • %%currency%% = Three-character currency code (uppercase); e.g., USD
  • '."\n"; echo '
  • %%currency_symbol%% = Currency code symbol; e.g., $
  • '."\n"; echo '
  • %%amount%% = The full Amount that you charged for Custom Capability access.
  • '."\n"; echo '
  • %%first_name%% = The First Name of the Customer who completed the purchase.
  • '."\n"; echo '
  • %%last_name%% = The Last Name of the Customer who completed the purchase.
  • '."\n"; echo '
  • %%full_name%% = The Full Name (First & Last) of the Customer who completed the purchase.
  • '."\n"; echo '
  • %%payer_email%% = The Email Address of the Customer who completed the purchase.
  • '."\n"; echo '
  • %%item_number%% = The Item Number (colon separated *:custom_capabilities); where custom_capabilities is a comma-delimited list of the Custom Capabilities they purchased.
  • '."\n"; echo '
  • %%item_name%% = The Item Name (as provided by the desc="" attribute in your Shortcode, which briefly describes the Item Number).
  • '."\n"; echo '
  • %%user_first_name%% = The First Name listed on their User account. This might be different than what is on file with PayPal.
  • '."\n"; echo '
  • %%user_last_name%% = The Last Name listed on their User account. This might be different than what is on file with PayPal.
  • '."\n"; echo '
  • %%user_full_name%% = The Full Name listed on their User account. This might be different than what is on file with PayPal.
  • '."\n"; echo '
  • %%user_email%% = The Email Address associated with their User account. This might be different than what is on file with PayPal.
  • '."\n"; echo '
  • %%user_login%% = The Username associated with their account. The Customer created this during registration.
  • '."\n"; echo '
  • %%user_ip%% = The Customer\'s original IP Address, during checkout/registration via $_SERVER["REMOTE_ADDR"].
  • '."\n"; echo '
  • %%user_id%% = A unique WordPress User ID that references this account in the WordPress database.
  • '."\n"; echo '
'."\n"; if(c_ws_plugin__s2member_utils_conds::pro_is_installed()) { echo 'Coupon Replacement Codes (applicable only w/ s2Member Pro-Forms):'."\n"; echo '
    '."\n"; echo '
  • %%full_coupon_code%% = A full Coupon Code—if one is accepted by your configuration of s2Member. This may indicate an Affiliate Coupon Code, which will include your Affiliate Suffix Chars too (i.e., the full Coupon Code).
  • '."\n"; echo '
  • %%coupon_code%% = A Coupon Code—if one is accepted by your configuration of s2Member. This will NOT include any Affiliate Suffix Chars. This indicates the actual Coupon Code accepted by your configuration of s2Member (excluding any Affiliate ID).
  • '."\n"; echo '
  • %%coupon_affiliate_id%% = This is the end of an Affiliate Coupon Code (i.e., the referring affiliate\'s ID). This is only applicable if an Affiliate Coupon Code is accepted by your configuration of s2Member.
  • '."\n"; echo '
'."\n"; } echo 'Custom Registration/Profile Fields are also supported in this email:'."\n"; echo '
    '."\n"; echo '
  • %%date_of_birth%% would be valid; if you have a Custom Registration/Profile Field with the ID date_of_birth.
  • '."\n"; echo '
  • %%street_address%% would be valid; if you have a Custom Registration/Profile Field with the ID street_address.
  • '."\n"; echo '
  • %%country%% would be valid; if you have a Custom Registration/Profile Field with the ID country.
  • '."\n"; echo '
  • %%etc, etc...%% see: s2Member → General Options → Registration/Profile Fields.
  • '."\n"; echo '
'."\n"; echo 'Custom Replacement Codes can also be inserted using these instructions:'."\n"; echo '
    '."\n"; echo '
  • %%cv0%% = The domain of your site, which is passed through the `custom` attribute in your Shortcode.
  • '."\n"; echo '
  • %%cv1%% = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3". You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.
  • '."\n"; echo '
'."\n"; echo 'This example uses cv1 to record a special marketing campaign:
'."\n"; echo '(The campaign (i.e., christmas-promo) could be referenced using %%cv1%%)
'."\n"; echo 'custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"'."\n"; echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '
'."\n". '

Advanced Customization: Further customization is possible with the ws_plugin__s2member_capabilities_email_sbj and ws_plugin__s2member_capabilities_email_msg filters from a plugin or theme.

'."\n" : ''; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_ccap_confirmation_email", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_sp_confirmation_email", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_sp_confirmation_email", get_defined_vars()); echo '
'."\n"; echo '
'."\n"; echo '

Specific Post/Page Confirmation Email (required, but the default works fine)

'."\n"; echo '

This email is sent to new Customers after they return from a successful purchase at PayPal, for Specific Post/Page Access. (see: s2Member → Restriction Options → Specific Post/Page Access). This is NOT used for Membership sales, only for Specific Post/Page Access. The primary purpose of this email is to provide the Customer with instructions, along with a link to access the Specific Post/Page they\'ve purchased access to. If you\'ve created a Specific Post/Page Package (with multiple Posts/Pages bundled together into one transaction), this ONE link (%%sp_access_url%%) will automatically authenticate them for access to ALL of the Posts/Pages included in their transaction. You may customize this email further by providing details that are specifically geared to your site.

'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_sp_confirmation_email", get_defined_vars()); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'This is a semicolon ( ; ) delimited list of Recipients. Here is an example:
'."\n"; echo '"%%full_name%%" <%%payer_email%%>; admin@example.com; "Webmaster" <webmaster@example.com>'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Subject Line used in the email sent to a Customer after a successful purchase has occurred through PayPal, for Specific Post/Page Access.'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; //250615 Visual editor for HTML emails. if (empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['html_emails_enabled'])) { echo '
'."\n"; } else { c_ws_plugin__s2member_utilities::editor('sp_email_message'); } echo 'Message Body used in the email sent to a Customer after a successful purchase has occurred through PayPal, for Specific Post/Page Access.

'."\n"; echo 'You can also use these special Replacement Codes if you need them:'."\n"; echo '
    '."\n"; echo '
  • %%sp_access_url%% = The full URL (generated by s2Member) where the Customer can gain access.
  • '."\n"; echo '
  • %%sp_access_exp%% = Human readable expiration for %%sp_access_url%%. Ex: (link expires in %%sp_access_exp%%).
  • '."\n"; echo '
  • %%txn_id%% = The PayPal Transaction ID. PayPal assigns a unique identifier for every purchase.
  • '."\n"; echo '
  • %%currency%% = Three-character currency code (uppercase); e.g., USD
  • '."\n"; echo '
  • %%currency_symbol%% = Currency code symbol; e.g., $
  • '."\n"; echo '
  • %%amount%% = The full Amount that you charged for Specific Post/Page Access.
  • '."\n"; echo '
  • %%first_name%% = The First Name of the Customer who purchased Specific Post/Page Access.
  • '."\n"; echo '
  • %%last_name%% = The Last Name of the Customer who purchased Specific Post/Page Access.
  • '."\n"; echo '
  • %%full_name%% = The Full Name (First & Last) of the Customer who purchased Specific Post/Page Access.
  • '."\n"; echo '
  • %%payer_email%% = The Email Address of the Customer who purchased Specific Post/Page Access.
  • '."\n"; echo '
  • %%user_ip%% = The Customer\'s IP Address, detected during checkout via $_SERVER["REMOTE_ADDR"].
  • '."\n"; echo '
  • %%item_number%% = The Item Number. Ex: sp:13,24,36:72 (translates to: sp:comma-delimited IDs:expiration hours).
  • '."\n"; echo '
  • %%item_name%% = The Item Name (as provided by the desc="" attribute in your Shortcode, which briefly describes the Item Number).
  • '."\n"; echo '
'."\n"; echo 'Custom Replacement Codes can also be inserted using these instructions:'."\n"; echo '
    '."\n"; echo '
  • %%cv0%% = The domain of your site, which is passed through the `custom` attribute in your Shortcode.
  • '."\n"; echo '
  • %%cv1%% = If you need to track additional custom variables, you can pipe delimit them into the `custom` attribute; inside your Shortcode, like this: custom="'.esc_html($_SERVER["HTTP_HOST"]).'|cv1|cv2|cv3". You can have an unlimited number of custom variables. Obviously, this is for advanced webmasters; but the functionality has been made available for those who need it.
  • '."\n"; echo '
'."\n"; echo 'This example uses cv1 to record a special marketing campaign:
'."\n"; echo '(The campaign (i.e., christmas-promo) could be referenced using %%cv1%%)
'."\n"; echo 'custom="'.esc_html($_SERVER["HTTP_HOST"]).'|christmas-promo"'."\n"; echo (!is_multisite() || !c_ws_plugin__s2member_utils_conds::is_multisite_farm() || is_main_site()) ? '
'."\n". '

Advanced Customization: Further customization is possible with the ws_plugin__s2member_sp_email_sbj and ws_plugin__s2member_sp_email_msg filters from a plugin or theme.

'."\n" : ''; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_sp_confirmation_email", get_defined_vars()); } if(apply_filters("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_display_eot_behavior", TRUE, get_defined_vars())) { do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_before_eot_behavior", get_defined_vars()); //260908.2031 Allow diagnostic/admin-notice links to open this collapsed panel before the shared menu-page JavaScript scrolls to a setting inside it. echo '
'."\n"; echo '
'."\n"; echo '

PayPal End-of-Term Behavior (required, please choose)

'."\n"; echo '

EOT = End Of Term. By default, s2Member will demote a paid Member to the role selected under Demote To Role whenever their Subscription term has ended (i.e., expired), been cancelled, refunded, charged back to you, etc. s2Member assigns the role selected under Demote To Role; resulting access depends on that role and on any unrelated WordPress roles you choose to preserve. However, in some cases, you may prefer to have Customer accounts moved to Pending Deletion for administrator review, instead of just being demoted. This is where you choose which method works best for your site. If you don\'t want s2Member to take ANY action at all, you can disable s2Member\'s EOT System temporarily, or even completely. There are also a few other configurable options here, so please read carefully. These options are all very important.

'."\n"; echo '

PayPal IPNs: The PayPal IPN service will notify s2Member whenever a Member\'s payments have been failing, and/or whenever a Member\'s Subscription has expired for any reason. Even refunds & chargeback reversals are supported through the IPN service. For example, if you issue a refund to an unhappy Customer through PayPal, s2Member will eventually be notified, and the account for that Customer will either be assigned the role selected under Demote To Role, or have the configured Delete behavior applied automatically. The communication from PayPal → s2Member is seamless.

'."\n"; echo '

Some Hairy Details: There might be times whenever you notice that a Member\'s Subscription has been cancelled through PayPal... but, s2Member continues allowing the User access to your site as a paid Member. Please don\'t be confused by this... in 99.9% of these cases, the reason for this is legitimate. s2Member will only process the membership EOT when an EOT (End Of Term) is reached, a refund occurs, a chargeback occurs, or when a cancellation later results in a delayed Auto-EOT by s2Member.

'."\n"; echo '

s2Member will not process an EOT until the User has completely used up the time they paid for. In other words, if a User signs up for a monthly Subscription on Jan 1st, and then cancels their Subscription on Jan 15th; technically, they should still be allowed to access the site for another 15 days, and then on Feb 1st, the time they paid for has completely elapsed. At that time, s2Member will process their membership EOT by either assigning the role selected under Demote To Role or applying the configured Delete behavior. s2Member also calculates one extra day (24 hours) into its equation, just to make sure access is not removed sooner than a Customer might expect.

'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_during_eot_behavior", get_defined_vars()); //260820.0306 Show current processing health where Auto-EOT is configured, so pending/stalled work is visible before it becomes a support issue. $auto_eot_health = c_ws_plugin__s2member_auto_eots::auto_eot_system_health(TRUE); $auto_eot_legacy_cap = c_ws_plugin__s2member_auto_eots::auto_eot_system_legacy_cap_info(); $auto_eot_mode = (string)$GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["auto_eot_system_enabled"]; $auto_eot_runtime_target = c_ws_plugin__s2member_auto_eots::auto_eot_system_runtime_budget($auto_eot_mode === '2'); $php_execution_limit = (int)ini_get('max_execution_time'); $auto_eot_status_labels = array('healthy' => 'Healthy', 'processing' => 'Processing', 'catching_up' => 'Catching up', 'attention' => 'Attention', 'error' => 'Needs attention', 'disabled' => 'Disabled'); $auto_eot_status_label = isset($auto_eot_status_labels[$auto_eot_health['status']]) ? $auto_eot_status_labels[$auto_eot_health['status']] : ucfirst($auto_eot_health['status']); $auto_eot_next_run = 0; if($auto_eot_mode === '1') { $_scheduled = array_filter(array((int)$auto_eot_health['recurring_at'], (int)$auto_eot_health['continuation_at'])); $auto_eot_next_run = $_scheduled ? min($_scheduled) : 0; unset($_scheduled); } echo '
'."\n"; echo '

Automatic Behavior Status: '.esc_html($auto_eot_status_label).'

'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; //260828.0231 Keep current EOT work and previous EOT history separate while retaining WordPress' native Users table, filters, bulk actions, and Screen Options. echo ''."\n"; echo '
Pending overdue EOTs:'.number_format_i18n((int)$auto_eot_health['pending_count']).'
Oldest overdue EOT:'.($auto_eot_health['oldest_due_at'] ? esc_html(human_time_diff((int)$auto_eot_health['oldest_due_at'], time()).' ago') : 'None').'
Last completed run:'.($auto_eot_health['last_completed_at'] ? esc_html(human_time_diff((int)$auto_eot_health['last_completed_at'], time()).' ago') : 'Not recorded yet').'
Last run:'.($auto_eot_health['last_completed_at'] ? number_format_i18n((int)$auto_eot_health['last_processed']).' EOT(s) in '.esc_html(number_format_i18n((float)$auto_eot_health['last_runtime'], 2)).' seconds' : 'Not recorded yet').'
Next run:'.($auto_eot_mode === '1' ? ($auto_eot_next_run ? esc_html(($auto_eot_next_run <= time() ? human_time_diff($auto_eot_next_run, time()).' overdue' : 'in '.human_time_diff(time(), $auto_eot_next_run))) : 'Not scheduled') : ($auto_eot_mode === '2' ? 'Controlled by your external cron service' : 'Disabled')).'
Current runtime target:About '.esc_html(number_format_i18n($auto_eot_runtime_target, 0)).' seconds'.($php_execution_limit > 0 ? ' (PHP limit: '.esc_html($php_execution_limit).')' : ' (no set PHP limit)').'
End-of-Term users:Current | Previous
'."\n"; echo '
'."\n"; //260820.0306 Legacy filters remain authoritative for compatibility, but explain when an inherited fixed-batch limit can now reduce adaptive throughput. if(!empty($auto_eot_legacy_cap['detected'])) { echo '
'."\n"; echo '

Legacy processing limit detected. A developer customization is using ws_plugin__s2member_auto_eot_system_per_process. This filter was useful with s2Member\'s older fixed-batch processor, but the new runtime-adaptive engine can usually process more efficiently without it. Unless it exists for a specific site reason, this legacy limit is probably unhelpful now.

'."\n"; if($auto_eot_legacy_cap['last_hard_cap_source'] === 'filter' && $auto_eot_legacy_cap['last_hard_cap'] !== NULL) echo '

Last effective legacy limit: '.number_format_i18n((int)$auto_eot_legacy_cap['last_hard_cap']).' EOT(s) per pass.

'."\n"; if($auto_eot_legacy_cap['last_hard_cap_source'] === 'filter' && $auto_eot_legacy_cap['last_stop_reason'] === 'legacy_item_cap' && $auto_eot_legacy_cap['estimated_additional'] > 0) echo '

On the last constrained run, s2Member estimates that approximately '.number_format_i18n((int)$auto_eot_legacy_cap['estimated_additional']).' additional EOT(s) could have fit within the remaining safe runtime without this hard limit.

'."\n"; if(!empty($auto_eot_legacy_cap['sources'])) { echo '

Detected source'.(count($auto_eot_legacy_cap['sources']) === 1 ? '' : 's').':
'."\n"; foreach($auto_eot_legacy_cap['sources'] as $_source) { $_source_location = !empty($_source['file']) ? $_source['file'].(!empty($_source['line']) ? ':'.(int)$_source['line'] : '') : (!empty($_source['callback']) ? $_source['callback'] : 'Unknown callback'); echo ''.esc_html($_source_location).'
'."\n"; } echo '

'."\n"; unset($_source, $_source_location); } echo '
'."\n"; } echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Recommended setting: (Yes / enable via WP-Cron)'."\n"; echo '
When disabled, automatic End-of-Term processing is paused. s2Member can still record End-of-Term dates, and s2Member Pro reminders based on stored End-of-Term dates can continue independently. Overdue End-of-Term actions remain pending and will be processed if the Auto-EOT System is enabled again.'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Automatic mode adapts the number of EOTs processed to the wall-clock time available in each run. The current target for this configuration is approximately '.esc_html(number_format_i18n($auto_eot_runtime_target, 1)).' seconds; it automatically leaves additional headroom below a finite PHP execution limit.'."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Used only in Custom mode. If PHP reports a finite execution limit, s2Member uses the lower of this value and 90% of PHP\'s limit. For example, if you enter 100 seconds and PHP\'s limit is 30 seconds, s2Member uses 27 seconds. Developers can still override the final runtime with ws_plugin__s2member_auto_eot_system_runtime.'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; //260916.1840 Keep the global EOT role destination/scope controls identical across every gateway page. c_ws_plugin__s2member_menu_pages::eot_demotion_options(); echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; //260822.0614 Keep the stored `delete` value for compatibility; irreversible deletion is an explicit developer opt-in rather than the product default. echo 'Delete removes s2Member membership access and moves the account to the Pending Deletion role for administrator review. To restore historical automatic irreversible deletion, a developer must explicitly allow it with the ws_plugin__s2member_allow_eot_user_deletion filter.'."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'NOTE: If Refunds/Reversals trigger an Immediate EOT (see setting below); Custom Capabilities will always be removed when/if a Refund or Reversal occurs. In other words, this setting is ignored for Refunds/Reversals (IF they trigger an Immediate EOT—based on your configuration below). If you prefer to review all Refunds/Reversals for yourself, please choose that option below.'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'This is represented in seconds. For example, a value of: 86400 = 1 day. Your EOT Grace Time; is the amount of time you will offer as a grace period (if any). Most site owners will give customers an additional 24 hours of access; just to help avoid any negativity that may result from a customer losing access sooner than they might expect. You can disable EOT Grace Time by setting this to: 0. Note: there is NO Grace Time applied when/if a Refund or Reversal occurs. If Refunds/Reversals trigger an Immediate EOT (see setting below); there is never any Grace Time applied in that scenario.'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; //260824.1957 Clarify that disputes/chargebacks are normalized into the existing Reversals policy without changing stored option values. echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'Disputes/Chargebacks: When a supported gateway reports a dispute or chargeback, s2Member treats it as a Reversal for this setting. Note: s2Member is not equipped to detect partial refunds against multi-payment Subscriptions reliably. Therefore, all refunds processed against Subscriptions (of any kind) are considered Partial Refunds. Full refunds (in the eyes of s2Member) occur only against Buy Now transactions where it is easy for s2Member to see that the refund amount is >= the original Buy Now purchase price (i.e., a Full Refund). Also Note: This setting (no matter what you choose) will NOT impact s2Member\'s internal API Notifications for Refund/Reversal events. Click here for details.'."\n"; echo '
'."\n"; echo '
'."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo ''."\n"; echo '
'."\n"; echo ''."\n"; echo '
'."\n"; echo '
'."\n"; echo 'This setting will only affect Buy Now transactions for fixed-term lengths. By default, s2Member will automatically extend any existing EOT Time that a Customer may have. For example, if I buy one year of access, and then I buy another year of access (before my first year is totally used up); I end up with everything I paid you for (now over 1 year of access) if this is set to Yes. If this was set to No, the EOT Time would be reset when I make the second purchase; leaving me with only 1 year of access, starting the date of my second purchase.'."\n"; echo '
'."\n"; echo '
'."\n"; echo '
'."\n"; do_action("ws_plugin__s2member_during_paypal_ops_page_during_left_sections_after_eot_behavior", get_defined_vars()); } do_action("ws_plugin__s2member_during_paypal_ops_page_after_left_sections", get_defined_vars()); echo '

'."\n"; echo '
'."\n"; echo '
'."\n"; c_ws_plugin__s2member_menu_pages_rs::display(); echo '
'."\n"; echo '
'."\n"; } } } new c_ws_plugin__s2member_menu_page_paypal_ops();