PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 260927
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v260927
260927 260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 All 190 releases
← All changes | src/includes/classes/paypal-notify-in.inc.php +12 -1 260829 → 260927 View file →
@@ -27,8 +27,9 @@
27 27 * @since 3.5
28 28 */
29 29 class c_ws_plugin__s2member_paypal_notify_in
30 30 {
31 + //260907.2110 TO-DO: Before final PayPal Standard deprecation, review IPN/return replay protection against the shared fulfillment/dedupe invariants; provider-hosted button creation itself does not need Gateway Checkout creation orchestration.
31 32 /**
32 33 * Handles PayPal IPN processing.
33 34 *
34 35 * These same routines also handle s2Member Pro/PayPal Pro operations;
@@ -51,8 +52,14 @@
51 52 global $current_site, $current_blog;
52 53
53 54 do_action('ws_plugin__s2member_before_paypal_notify', get_defined_vars());
54 55
56 + //260831.0135 Normalize optional s2Member proxy fields once; direct PayPal IPNs omit them, while proxied gateway integrations provide strings.
57 + if(!isset($_REQUEST['s2member_paypal_proxy']) || !is_string($_REQUEST['s2member_paypal_proxy']))
58 + $_REQUEST['s2member_paypal_proxy'] = '';
59 + if(!isset($_REQUEST['s2member_paypal_proxy_use']) || !is_string($_REQUEST['s2member_paypal_proxy_use']))
60 + $_REQUEST['s2member_paypal_proxy_use'] = '';
61 +
55 62 if(!empty($_GET['s2member_paypal_notify']) && ($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_business'] || !empty($_REQUEST['s2member_paypal_proxy'])))
56 63 {
57 64 @ignore_user_abort(TRUE); // Important. Continue processing even if/when the connection is broken by the sending party.
58 65
@@ -75,8 +82,9 @@
75 82 $paypal['subscr_gateway'] = (!empty($_REQUEST['s2member_paypal_proxy'])) ? esc_html(trim(stripslashes($_REQUEST['s2member_paypal_proxy']))) : 'paypal';
76 83
77 84 $coupon = (!empty($_REQUEST['s2member_paypal_proxy_coupon']) && is_array($_REQUEST['s2member_paypal_proxy_coupon'])) ? stripslashes_deep($_REQUEST['s2member_paypal_proxy_coupon']) : array();
78 85 $coupon = (isset($coupon['full_coupon_code'], $coupon['coupon_code'], $coupon['affiliate_id']) && is_string($coupon['full_coupon_code']) && is_string($coupon['coupon_code']) && is_string($coupon['affiliate_id'])) ? $coupon : array('full_coupon_code' => '', 'coupon_code' => '', 'affiliate_id' => '');
86 + $coupon = c_ws_plugin__s2member_utils_strings::strip_php_tags_deep($coupon); //260910.2249 Normalize proxy coupon data consistently with other notification values.
79 87
80 88 if(!empty($paypal['txn_type']) && $paypal['txn_type'] === 'merch_pmt')
81 89 // This is mostly irrelevant, but it helps to keep the logs cleaner.
82 90 sleep(15); // Wait for Pro-Form procesing to complete.
@@ -90,11 +98,14 @@
90 98 //250522 At least set it to an empty string.
91 99 else if(empty($paypal['custom']))
92 100 $paypal['custom'] = '';
93 101
102 + //260910.0356 Resolve the expected host consistently across different web-server request environments.
103 + $ipn_host = (!empty($_SERVER['HTTP_HOST']) && is_string($_SERVER['HTTP_HOST'])) ? preg_replace('/\:([0-9]+)$/', '', $_SERVER['HTTP_HOST']) : (string)parse_url(home_url('/'), PHP_URL_HOST);
104 +
94 105 //250606 Added option to skip domain validation.
95 106 if ($GLOBALS['WS_PLUGIN__']['s2member']['o']['skip_ipn_domain_validation']
96 - || (!empty($paypal['custom']) && preg_match('/^'.preg_quote(preg_replace('/\:([0-9]+)$/', '', $_SERVER['HTTP_HOST']), '/').'/i', $paypal['custom'])))
107 + || ($ipn_host !== '' && !empty($paypal['custom']) && preg_match('/^'.preg_quote($ipn_host, '/').'/i', $paypal['custom'])))
97 108 {
98 109 if ($GLOBALS['WS_PLUGIN__']['s2member']['o']['skip_ipn_domain_validation'])
99 110 $paypal['s2member_log'][] = 's2Member originating domain validation was skipped.';
100 111 else