PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 260917
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v260917
260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 All 189 releases
← All changes | src/includes/classes/utils-users.inc.php +48 -15 260829260917 View file →
@@ -189,8 +189,9 @@
189 189 // Do not guess term values in fallback vars (risk of incorrect EOT decisions).
190 190 if (!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['ipn_signup_vars_fallback'])) {
191 191 $userdata = get_userdata((int)$user_id);
192 192 $ipn_signup_vars = array(
193 + 's2member_ipn_signup_vars_fallback' => '1', //260829.1709 Mark generated fallback vars explicitly so gateway-aware EOT logic can distinguish missing local signup metadata from malformed stored signup data.
193 194 'subscr_cid' => !empty($_subscr_cid) ? $_subscr_cid : '',
194 195 'subscr_id' => $_subscr_id,
195 196 'custom' => esc_html($_SERVER["HTTP_HOST"]),
196 197 'period1' => '',
@@ -465,21 +466,28 @@
465 466 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
466 467 'debug' => 'This is a fixed EOT time recorded by s2Member. It can be altered in the WordPress Dashboard for this user.');
467 468
468 469 if(!$subscr_gateway && !$subscr_id && !$subscr_cid && $last_auto_eot_time // EOTd?
469 - && (!user_can($user->ID, 'access_s2member_level1') || c_ws_plugin__s2member_user_access::user_access_role($user) === $demotion_role)
470 + //260916.1840 A preserved unrelated role may sort before the destination role, so inspect all assigned roles instead of only s2Member's historical first-role result.
471 + && (!user_can($user->ID, 'access_s2member_level1') || in_array($demotion_role, (array)$user->roles, TRUE))
470 472 && !c_ws_plugin__s2member_user_access::user_access_ccaps($user) // And no CCAPs either?
471 473 ) return array('type' => 'fixed', 'time' => $last_auto_eot_time, 'tense' => $last_auto_eot_time <= $now ? 'past' : 'future',
472 - 'debug' => 'This is an archived/fixed EOT time recorded by s2Member; i.e., the date this customer\'s access expired.');
474 + 'debug' => 'This is an archived/fixed EOT time recorded by s2Member; i.e., the date this customer\'s membership EOT was processed.');
473 475
474 476 if(!$subscr_gateway || !$subscr_id || !is_array($ipn_signup_vars) || !$ipn_signup_vars)
475 477 return array_merge($empty_response, array('debug' => 'This user has no subscription; i.e., missing `subscr_id`, `subscr_gateway` or `ipn_signup_vars`.'));
476 478
477 - if(empty($ipn_signup_vars['txn_type']) || $ipn_signup_vars['txn_type'] !== 'subscr_signup')
479 + $ipn_signup_vars_fallback = !empty($ipn_signup_vars['s2member_ipn_signup_vars_fallback']);
480 + //260829.1709 A PayPal fallback still has a bound local subscription ID, so allow the configured PayPal APIs to recover authoritative timing before rejecting it for missing legacy signup metadata.
481 + $can_recover_paypal_fallback = ($check_gateway && $subscr_gateway === 'paypal' && $ipn_signup_vars_fallback);
482 +
483 + if((empty($ipn_signup_vars['txn_type']) || $ipn_signup_vars['txn_type'] !== 'subscr_signup') && !$can_recover_paypal_fallback)
478 484 return array_merge($empty_response, array('debug' => 'This user has no subscription; i.e., `txn_type` != `subscr_signup`.'));
479 485
480 - $auto_eot_time // Update this now; i.e., build a new EOT time based on IPN signup vars.
481 - = c_ws_plugin__s2member_utils_time::auto_eot_time($user->ID, $ipn_signup_vars['period1'], $ipn_signup_vars['period3']);
486 + //260829.1709 Never turn deliberately blank fallback billing terms into an estimated EOT of "now"; PayPal API recovery below must succeed or the existing empty/fallback behavior remains.
487 + $auto_eot_time = ($ipn_signup_vars_fallback && empty($ipn_signup_vars['period3']))
488 + ? 0
489 + : c_ws_plugin__s2member_utils_time::auto_eot_time($user->ID, $ipn_signup_vars['period1'], $ipn_signup_vars['period3']);
482 490
483 491 //260828.0710 !!! TO-DO: Track a gateway-independent `s2member_access_through_time`, meaning the end of the latest confirmed entitlement period, before EOT grace.
484 492 // 1. On signup/received payment, save the provider's then-current next billing/period-end time as access-through. Failed payments never advance it. 2. Cancellation/EOT should use this value when available, even if already past.
485 493 // 3. For older users without it, estimate conservatively from provider billing schedule + signup vars/last successful payment when reliable; otherwise keep the existing estimated EOT fallback.
@@ -523,11 +531,16 @@
523 531 $status = !empty($subscription['status']) ? strtoupper((string)$subscription['status']) : '';
524 532 $next = !empty($subscription['billing_info']['next_billing_time']) ? (string)$subscription['billing_info']['next_billing_time'] : '';
525 533
526 534 if($status && $status !== 'ACTIVE')
527 - return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
528 - 'debug' => 'This is the estimated EOT time. PayPal Checkout says this subscription is no longer active, and thus, access should be terminated at this time.');
535 + {
536 + if($auto_eot_time)
537 + return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
538 + 'debug' => 'This is the estimated EOT time. PayPal Checkout says this subscription is no longer active, and thus, access should be terminated at this time.');
529 539
540 + return array_merge($empty_response, array('debug' => 'PayPal Checkout says this subscription is no longer active, but local signup billing terms are unavailable; refusing to guess an EOT.'));
541 + }
542 +
530 543 if($next && ($time = strtotime($next)) > $now)
531 544 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
532 545 'debug' => 'PayPal Checkout says this is the next payment time.');
533 546
@@ -545,15 +558,25 @@
545 558 if(preg_match('/^(?:Pending|PendingProfile)$/i', $api_response['STATUS']))
546 559 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the PayPal Pro API says the subscription for this user is currently pending changes. Unable to determine at this moment. Please try again in 15 minutes.'));
547 560
548 561 if(!preg_match('/^(?:Active|ActiveProfile)$/i', $api_response['STATUS']))
549 - return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
550 - 'debug' => 'This is the estimated EOT time. The PayPal Pro API says this subscription is no longer active, and thus, access should be terminated at this time.');
562 + {
563 + if($auto_eot_time)
564 + return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
565 + 'debug' => 'This is the estimated EOT time. The PayPal Pro API says this subscription is no longer active, and thus, access should be terminated at this time.');
551 566
567 + return array_merge($empty_response, array('debug' => 'The PayPal Pro API says this subscription is no longer active, but local signup billing terms are unavailable; refusing to guess an EOT.'));
568 + }
569 +
552 570 if($api_response['TERM'] > 0 && $api_response['PAYMENTSLEFT'] <= 0)
553 - return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
554 - 'debug' => 'This is the estimated EOT time. The PayPal Pro API says this subscription has reached its last payment, and thus, access should be terminated at this time.');
571 + {
572 + if($auto_eot_time)
573 + return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
574 + 'debug' => 'This is the estimated EOT time. The PayPal Pro API says this subscription has reached its last payment, and thus, access should be terminated at this time.');
555 575
576 + return array_merge($empty_response, array('debug' => 'The PayPal Pro API says this subscription has reached its last payment, but local signup billing terms are unavailable; refusing to guess an EOT.'));
577 + }
578 +
556 579 if($api_response['TERM'] <= 0 || $api_response['PAYMENTSLEFT'] > 0)
557 580 if($api_response['NEXTPAYMENT'] && strlen($api_response['NEXTPAYMENT']) === 8) // MMDDYYYY format is not `strtotime()` compatible.
558 581 if(($time = strtotime(substr($api_response['NEXTPAYMENT'], -4).'-'.substr($api_response['NEXTPAYMENT'], 0, 2).'-'.substr($api_response['NEXTPAYMENT'], 2, 2))) > $now)
559 582 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
@@ -575,14 +598,24 @@
575 598 if(preg_match('/^(?:Pending|PendingProfile)$/i', $api_response['STATUS']))
576 599 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the PayPal Pro API says the subscription for this user is currently pending changes. Unable to determine at this moment. Please try again in 15 minutes.'));
577 600
578 601 if(!preg_match('/^(?:Active|ActiveProfile)$/i', $api_response['STATUS']))
579 - return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
580 - 'debug' => 'This is the estimated EOT time. The PayPal Pro API says this subscription is no longer active, and thus, access should be terminated at this time.');
602 + {
603 + if($auto_eot_time)
604 + return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
605 + 'debug' => 'This is the estimated EOT time. The PayPal Pro API says this subscription is no longer active, and thus, access should be terminated at this time.');
581 606
607 + return array_merge($empty_response, array('debug' => 'The PayPal Pro API says this subscription is no longer active, but local signup billing terms are unavailable; refusing to guess an EOT.'));
608 + }
609 +
582 610 if($api_response['TOTALBILLINGCYCLES'] > 0 && $api_response['NUMCYCLESREMAINING'] <= 0)
583 - return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
584 - 'debug' => 'This is the estimated EOT time. The PayPal Pro API says this subscription has reached its last payment, and thus, access should be terminated at this time.');
611 + {
612 + if($auto_eot_time)
613 + return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
614 + 'debug' => 'This is the estimated EOT time. The PayPal Pro API says this subscription has reached its last payment, and thus, access should be terminated at this time.');
615 +
616 + return array_merge($empty_response, array('debug' => 'The PayPal Pro API says this subscription has reached its last payment, but local signup billing terms are unavailable; refusing to guess an EOT.'));
617 + }
585 618
586 619 if($api_response['TOTALBILLINGCYCLES'] <= 0 || $api_response['NUMCYCLESREMAINING'] > 0)
587 620 if($api_response['NEXTBILLINGDATE'] && ($time = strtotime($api_response['NEXTBILLINGDATE'])) > $now)
588 621 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',