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
s2member / src / includes / classes / utils-users.inc.php

utils-users.inc.php in s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions 260917, at src/includes/classes/utils-users.inc.php

826 lines 45.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // @codingStandardsIgnoreFile
3 /**
4 * User utilities.
5 *
6 * Copyright: © 2009-2011
7 * {@link http://websharks-inc.com/ WebSharks, Inc.}
8 * (coded in the USA)
9 *
10 * Released under the terms of the GNU General Public License.
11 * You should have received a copy of the GNU General Public License,
12 * along with this software. In the main directory, see: /licensing/
13 * If not, see: {@link http://www.gnu.org/licenses/}.
14 *
15 * @package s2Member\Utilities
16 * @since 3.5
17 */
18 if(!defined('WPINC')) // MUST have WordPress.
19 exit ('Do not access this file directly.');
20
21 if(!class_exists('c_ws_plugin__s2member_utils_users'))
22 {
23 /**
24 * User utilities.
25 *
26 * @package s2Member\Utilities
27 * @since 3.5
28 */
29 class c_ws_plugin__s2member_utils_users
30 {
31 /**
32 * Determines the total Users/Members in the database.
33 *
34 * @package s2Member\Utilities
35 * @since 3.5
36 *
37 * @return int Number of Users in the database, total.
38 */
39 public static function users_in_database()
40 {
41 global $wpdb;
42 /** @var wpdb $wpdb */
43
44 $wpdb->query("SELECT SQL_CALC_FOUND_ROWS `".$wpdb->users."`.`ID` FROM `".$wpdb->users."`, `".$wpdb->usermeta."` WHERE `".$wpdb->users."`.`ID` = `".$wpdb->usermeta."`.`user_id` AND `".$wpdb->usermeta."`.`meta_key` = '".esc_sql($wpdb->prefix."capabilities")."' LIMIT 1");
45 $users = (int)$wpdb->get_var("SELECT FOUND_ROWS()");
46
47 return $users;
48 }
49
50 /**
51 * Obtains Custom String for an existing Member, referenced by a Subscr. or Transaction ID.
52 *
53 * A second lookup parameter can be provided as well *(optional)*.
54 *
55 * @package s2Member\Utilities
56 * @since 3.5
57 *
58 * @param string $subscr_txn_baid_cid_id Either a Paid Subscr. ID, or a Paid Transaction ID.
59 * @param string $os0 Optional. A second lookup parameter, usually the `os0` value for PayPal integrations.
60 *
61 * @return string|bool The Custom String value on success, else false on failure.
62 */
63 public static function get_user_custom_with($subscr_txn_baid_cid_id = '', $os0 = '')
64 {
65 global $wpdb;
66 /** @var wpdb $wpdb */
67
68 if($subscr_txn_baid_cid_id && $os0) // This case includes some additional routines that can use the ``$os0`` value.
69 {
70 if(($q = $wpdb->get_row("SELECT `user_id` FROM `".$wpdb->usermeta."` WHERE (`meta_key` = '".$wpdb->prefix."s2member_subscr_id' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_baid' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_cid' OR `meta_key` = '".$wpdb->prefix."s2member_first_payment_txn_id') AND (`meta_value` = '".esc_sql($subscr_txn_baid_cid_id)."' OR `meta_value` = '".esc_sql($os0)."') LIMIT 1"))
71 || ($q = $wpdb->get_row("SELECT `ID` AS `user_id` FROM `".$wpdb->users."` WHERE `ID` = '".esc_sql($os0)."' LIMIT 1"))
72 ) if(($custom = get_user_option('s2member_custom', $q->user_id)))
73 return $custom;
74 }
75 else if($subscr_txn_baid_cid_id) // Otherwise, if all we have is a Subscr./Txn. ID value.
76 {
77 if(($q = $wpdb->get_row("SELECT `user_id` FROM `".$wpdb->usermeta."` WHERE (`meta_key` = '".$wpdb->prefix."s2member_subscr_id' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_baid' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_cid' OR `meta_key` = '".$wpdb->prefix."s2member_first_payment_txn_id') AND `meta_value` = '".esc_sql($subscr_txn_baid_cid_id)."' LIMIT 1")))
78 if(($custom = get_user_option('s2member_custom', $q->user_id)))
79 return $custom;
80 }
81 return FALSE; // Otherwise, return false.
82 }
83
84 /**
85 * Obtains the User ID for an existing Member, referenced by a Subscr. or Transaction ID.
86 *
87 * A second lookup parameter can be provided as well *(optional)*.
88 *
89 * @package s2Member\Utilities
90 * @since 3.5
91 *
92 * @param string $subscr_txn_baid_cid_id Either a Paid Subscr. ID, or a Paid Transaction ID.
93 * @param string $os0 Optional. A second lookup parameter, usually the `os0` value for PayPal integrations.
94 *
95 * @return integer|bool A WordPress User ID on success, else false on failure.
96 */
97 public static function get_user_id_with($subscr_txn_baid_cid_id = '', $os0 = '')
98 {
99 global $wpdb;
100 /** @var wpdb $wpdb */
101
102 if($subscr_txn_baid_cid_id && $os0) // This case includes some additional routines that can use the ``$os0`` value.
103 {
104 if(($q = $wpdb->get_row("SELECT `user_id` FROM `".$wpdb->usermeta."` WHERE (`meta_key` = '".$wpdb->prefix."s2member_subscr_id' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_baid' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_cid' OR `meta_key` = '".$wpdb->prefix."s2member_first_payment_txn_id') AND (`meta_value` = '".esc_sql($subscr_txn_baid_cid_id)."' OR `meta_value` = '".esc_sql($os0)."') LIMIT 1"))
105 || ($q = $wpdb->get_row("SELECT `ID` AS `user_id` FROM `".$wpdb->users."` WHERE `ID` = '".esc_sql($os0)."' LIMIT 1"))
106 ) return $q->user_id;
107 }
108 else if($subscr_txn_baid_cid_id) // Otherwise, if all we have is a Subscr./Txn. ID value.
109 {
110 if(($q = $wpdb->get_row("SELECT `user_id` FROM `".$wpdb->usermeta."` WHERE (`meta_key` = '".$wpdb->prefix."s2member_subscr_id' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_baid' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_cid' OR `meta_key` = '".$wpdb->prefix."s2member_first_payment_txn_id') AND `meta_value` = '".esc_sql($subscr_txn_baid_cid_id)."' LIMIT 1")))
111 return $q->user_id;
112 }
113 return FALSE; // Otherwise, return false.
114 }
115
116 /**
117 * Obtains the Email Address for an existing Member, referenced by a Subscr. or Transaction ID.
118 *
119 * A second lookup parameter can be provided as well *(optional)*.
120 *
121 * @package s2Member\Utilities
122 * @since 3.5
123 *
124 * @param string $subscr_txn_baid_cid_id Either a Paid Subscr. ID, or a Paid Transaction ID.
125 * @param string $os0 Optional. A second lookup parameter, usually the `os0` value for PayPal integrations.
126 *
127 * @return int|bool A User's Email Address on success, else false on failure.
128 */
129 public static function get_user_email_with($subscr_txn_baid_cid_id = '', $os0 = '')
130 {
131 global $wpdb;
132 /** @var wpdb $wpdb */
133
134 if($subscr_txn_baid_cid_id && $os0) // This case includes some additional routines that can use the ``$os0`` value.
135 {
136 if(($q = $wpdb->get_row("SELECT `user_id` FROM `".$wpdb->usermeta."` WHERE (`meta_key` = '".$wpdb->prefix."s2member_subscr_id' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_baid' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_cid' OR `meta_key` = '".$wpdb->prefix."s2member_first_payment_txn_id') AND (`meta_value` = '".esc_sql($subscr_txn_baid_cid_id)."' OR `meta_value` = '".esc_sql($os0)."') LIMIT 1"))
137 || ($q = $wpdb->get_row("SELECT `ID` AS `user_id` FROM `".$wpdb->users."` WHERE `ID` = '".esc_sql($os0)."' LIMIT 1"))
138 ) if(is_object($user = new WP_User ($q->user_id)) && !empty($user->ID) && ($email = $user->user_email))
139 return $email;
140 }
141 else if($subscr_txn_baid_cid_id) // Otherwise, if all we have is a Subscr./Txn. ID value.
142 {
143 if(($q = $wpdb->get_row("SELECT `user_id` FROM `".$wpdb->usermeta."` WHERE (`meta_key` = '".$wpdb->prefix."s2member_subscr_id' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_baid' OR `meta_key` = '".$wpdb->prefix."s2member_subscr_cid' OR `meta_key` = '".$wpdb->prefix."s2member_first_payment_txn_id') AND `meta_value` = '".esc_sql($subscr_txn_baid_cid_id)."' LIMIT 1")))
144 if(is_object($user = new WP_User ($q->user_id)) && !empty($user->ID) && ($email = $user->user_email))
145 return $email;
146 }
147 return FALSE; // Otherwise, return false.
148 }
149
150 /**
151 * Retrieves IPN Signup Vars & validates their Subscription ID.
152 *
153 * @package s2Member\Utilities
154 * @since 3.5
155 *
156 * @param integer|string $user_id Optional. A numeric WordPress User ID.
157 *
158 * @param string $subscr_txn_baid_cid_id Optional. Can be used instead of passing in a ``$user_id``.
159 * If ``$subscr_txn_baid_cid_id`` is passed in, it has to match the one found inside the resulting IPN Signup Vars collected by this routine.
160 * If neither of these parameters are passed in, the current User is assumed instead, obtained through ``wp_get_current_user()``.
161 *
162 * @return array|bool A User's IPN Signup Vars on success, else false on failure.
163 * If no IPN Signup Vars is found for this user, a makeshift array may be returned if fallback behavior is enabled.
164 */
165 public static function get_user_ipn_signup_vars($user_id = 0, $subscr_txn_baid_cid_id = '')
166 {
167 if($user_id || ($subscr_txn_baid_cid_id && ($user_id = c_ws_plugin__s2member_utils_users::get_user_id_with($subscr_txn_baid_cid_id)))
168 || (!$user_id && !$subscr_txn_baid_cid_id && is_object($user = wp_get_current_user()) && !empty($user->ID) && ($user_id = $user->ID))
169 )
170 {
171 $_subscr_baid = get_user_option('s2member_subscr_baid', $user_id);
172 $_subscr_cid = get_user_option('s2member_subscr_cid', $user_id);
173 $_subscr_id = get_user_option('s2member_subscr_id', $user_id);
174
175 if ($_subscr_id) {
176 $ipn_signup_vars = get_user_option('s2member_ipn_signup_vars', $user_id);
177 if (is_array($ipn_signup_vars)) {
178 // If we have ipn_signup_vars, subscr_id should match.
179 if (!empty($ipn_signup_vars['subscr_id']) && $ipn_signup_vars['subscr_id'] == $_subscr_id) {
180 return $ipn_signup_vars;
181 } else {
182 return FALSE;
183 }
184 }
185 //250426 Fall back to a makeshift ipn_signup_vars array.
186 //!!! TO-DO: Gateway enrichment for missing term details (period1/period3) when signup vars are missing.
187 // PayPal Checkout subs: use REST Subscriptions API.
188 // Legacy PayPal recurring profiles: use legacy NVP/Payflow APIs.
189 // Do not guess term values in fallback vars (risk of incorrect EOT decisions).
190 if (!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['ipn_signup_vars_fallback'])) {
191 $userdata = get_userdata((int)$user_id);
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.
194 'subscr_cid' => !empty($_subscr_cid) ? $_subscr_cid : '',
195 'subscr_id' => $_subscr_id,
196 'custom' => esc_html($_SERVER["HTTP_HOST"]),
197 'period1' => '',
198 'period3' => '',
199 'payer_email' => !empty($userdata->user_email) ? $userdata->user_email : 'USER EMAIL',
200 'first_name' => !empty($userdata->first_name) ? $userdata->first_name : 'FIRST NAME',
201 'last_name' => !empty($userdata->last_name) ? $userdata->last_name : 'LAST NAME',
202 'option_name1' => '',
203 'option_selection1' => '',
204 'option_name2' => '',
205 'option_selection2' => '',
206 'item_name' => 'FALLBACK IPN SIGNUP VARS',
207 'item_number' => (string) c_ws_plugin__s2member_user_access::user_access_level($userdata), //250916
208 );
209 return $ipn_signup_vars;
210 }
211 }
212 }
213 return FALSE; // Otherwise, return false.
214 }
215
216 /**
217 * Retrieves IPN Signup Var & validates their Subscription ID.
218 *
219 * The ``$user_id`` can be passed in directly; or a lookup can be performed with ``$subscr_id``.
220 *
221 * @package s2Member\Utilities
222 * @since 110912
223 *
224 * @param string $var Required. The requested Signup Var.
225 * @param int|string $user_id Optional. A numeric WordPress User ID.
226 * @param string $subscr_txn_baid_cid_id Optional. Can be used instead of passing in a ``$user_id``.
227 * If ``$subscr_id`` is passed in, it has to match the one found inside the resulting IPN Signup Vars collected by this routine.
228 * If neither of these parameters are passed in, the current User is assumed instead, obtained through ``wp_get_current_user()``.
229 *
230 * @return mixed|bool A User's IPN Signup Var on success, else false on failure.
231 */
232 public static function get_user_ipn_signup_var($var = '', $user_id = 0, $subscr_txn_baid_cid_id = '')
233 {
234 if(!empty($var) && is_array($user_ipn_signup_vars = c_ws_plugin__s2member_utils_users::get_user_ipn_signup_vars($user_id, $subscr_txn_baid_cid_id)))
235 {
236 if(isset($user_ipn_signup_vars[$var]))
237 return $user_ipn_signup_vars[$var];
238 }
239 return FALSE; // Otherwise, return false.
240 }
241
242 /**
243 * Obtains a User's Paid Subscr. ID *(if available)*; otherwise their WP User ID.
244 *
245 * If ``$user`` IS passed in, this function will return data from a specific ``$user``, or fail if not possible.
246 * If ``$user`` is NOT passed in, check the current User/Member.
247 *
248 * @package s2Member\Utilities
249 * @since 3.5
250 *
251 * @param object $user Optional. A `WP_User` object.
252 * In order to check the current User, you must call this function with no arguments/parameters.
253 *
254 * @return int|string|bool If possible, the User's Paid Subscr. ID, else their WordPress User ID, else false.
255 */
256 public static function get_user_subscr_or_wp_id($user = NULL)
257 {
258 if((func_num_args() && (!is_object($user) || empty($user->ID)))
259 || (!func_num_args() && (!is_object($user = (is_user_logged_in()) ? wp_get_current_user() : FALSE) || empty($user->ID)))
260 ) return FALSE; // The ``$user`` was passed in but is NOT an object; or nobody is logged in.
261
262 return ($subscr_id = get_user_option('s2member_subscr_id', $user->ID)) ? $subscr_id : $user->ID;
263 }
264
265 /**
266 * Determines whether or not a Username/Email is already in the database.
267 *
268 * Returns the WordPress User ID if they exist.
269 *
270 * @package s2Member\Utilities
271 * @since 3.5
272 *
273 * @param string $user_login A User's Username.
274 * @param string $user_email A User's Email Address.
275 *
276 * @return int|bool If exists, a WordPress User ID, else false.
277 */
278 public static function user_login_email_exists($user_login = '', $user_email = '')
279 {
280 global $wpdb;
281 /** @var wpdb $wpdb */
282
283 if($user_login && $user_email) // Only if we have both of these.
284 if(($user_id = $wpdb->get_var("SELECT `ID` FROM `".$wpdb->users."` WHERE `user_login` LIKE '".esc_sql(c_ws_plugin__s2member_utils_strings::like_escape($user_login))."' AND `user_email` LIKE '".esc_sql(c_ws_plugin__s2member_utils_strings::like_escape($user_email))."' LIMIT 1")))
285 return $user_id; // Return the associated WordPress ID.
286
287 return FALSE; // Otherwise, return false.
288 }
289
290 /**
291 * Determines whether or not a Username/Email is already in the database for this Blog.
292 *
293 * Returns the WordPress User ID if they exist.
294 *
295 * @package s2Member\Utilities
296 * @since 3.5
297 *
298 * @param string $user_login A User's Username.
299 * @param string $user_email A User's Email Address.
300 * @param int|string $blog_id A numeric WordPress Blog ID.
301 *
302 * @return int|bool If exists *(but not on Blog)*, a WordPress User ID, else false.
303 */
304 public static function ms_user_login_email_exists_but_not_on_blog($user_login = '', $user_email = '', $blog_id = 0)
305 {
306 if($user_login && $user_email) // Only if we have both of these.
307 if(is_multisite() && ($user_id = c_ws_plugin__s2member_utils_users::user_login_email_exists($user_login, $user_email)) && !is_user_member_of_blog($user_id, $blog_id))
308 return $user_id;
309
310 return FALSE; // Otherwise, return false.
311 }
312
313 /**
314 * Determines whether or not a Username/Email is already in the database for this Blog.
315 *
316 * This is an alias for: `c_ws_plugin__s2member_utils_users::ms_user_login_email_exists_but_not_on_blog()`.
317 *
318 * Returns the WordPress User ID if they exist.
319 *
320 * @package s2Member\Utilities
321 * @since 3.5
322 *
323 * @param string $user_login A User's Username.
324 * @param string $user_email A User's Email Address.
325 * @param int|string $blog_id A numeric WordPress Blog ID.
326 *
327 * @return int|bool If exists *(but not on Blog)*, a WordPress User ID, else false.
328 */
329 public static function ms_user_login_email_can_join_blog($user_login = '', $user_email = '', $blog_id = 0)
330 {
331 return c_ws_plugin__s2member_utils_users::ms_user_login_email_exists_but_not_on_blog($user_login, $user_email, $blog_id);
332 }
333
334 /**
335 * Retrieves a field value. Also supports Custom Fields.
336 *
337 * @package s2Member\Utilities
338 * @since 3.5
339 *
340 * @param string $field_id Required. A unique Custom Registration/Profile Field ID, that you configured with s2Member.
341 * Or, this could be set to any property that exists on the WP_User object for a particular User;
342 * ( i.e., `id`, `ID`, `user_login`, `user_email`, `first_name`, `last_name`, `display_name`, `ip`, `IP`,
343 * `s2member_registration_ip`, `s2member_custom`, `s2member_subscr_id`, `s2member_subscr_or_wp_id`,
344 * `s2member_subscr_gateway`, `s2member_custom_fields`, `s2member_file_download_access_[log|arc]`,
345 * `s2member_auto_eot_time`, `s2member_last_payment_time`, `s2member_paid_registration_times`,
346 * `s2member_access_role`, `s2member_access_level`, `s2member_access_label`,
347 * `s2member_access_ccaps`, etc, etc. ).
348 * @param int|string $user_id Optional. Defaults to the current User's ID.
349 * @param array $args Any additional behavioral args. Since v150717.
350 *
351 * @return mixed The value of the requested field, or false if the field does not exist.
352 */
353 public static function get_user_field($field_id = '', $user_id = 0, $args = array())
354 {
355 global $wpdb; /** @var wpdb $wpdb Reference for IDEs. */
356
357 $current_user = wp_get_current_user(); // Current user.
358
359 if(is_object($user = $user_id ? new WP_User($user_id) : $current_user)
360 && !empty($user->ID) && ($user_id = $user->ID))
361 {
362 $args = (array)$args; // Force array.
363
364 if(isset($user->{$field_id}))
365 return $user->{$field_id};
366
367 else if(isset($user->data->{$field_id}))
368 return $user->data->{$field_id};
369
370 else if(isset($user->{$wpdb->prefix.$field_id}))
371 return $user->{$wpdb->prefix.$field_id};
372
373 else if(isset($user->data->{$wpdb->prefix.$field_id}))
374 return $user->data->{$wpdb->prefix.$field_id};
375
376 else if(strcasecmp($field_id, 'full_name') === 0)
377 return trim($user->first_name.' '.$user->last_name);
378
379 else if(preg_match('/^(?:email|user_email)$/i', $field_id))
380 return $user->user_email;
381
382 else if(preg_match('/^(?:login|user_login)$/i', $field_id))
383 return $user->user_login;
384
385 else if(preg_match('/^(?:s2member_)?registration_time$/i', $field_id))
386 return $user->user_registered;
387
388 else if(strcasecmp($field_id, 's2member_access_role') === 0)
389 return c_ws_plugin__s2member_user_access::user_access_role($user);
390
391 else if(strcasecmp($field_id, 's2member_access_level') === 0)
392 return c_ws_plugin__s2member_user_access::user_access_level($user);
393
394 else if(strcasecmp($field_id, 's2member_access_label') === 0)
395 return c_ws_plugin__s2member_user_access::user_access_label($user);
396
397 else if(strcasecmp($field_id, 's2member_access_ccaps') === 0)
398 return c_ws_plugin__s2member_user_access::user_access_ccaps($user);
399
400 else if(strcasecmp($field_id, 'ip') === 0 && !empty($current_user->ID) && $current_user->ID === $user_id)
401 return c_ws_plugin__s2member_utils_ip::current(); // Current IP address.
402
403 else if(strcasecmp($field_id, 's2member_registration_ip') === 0 || strcasecmp($field_id, 'reg_ip') === 0 || strcasecmp($field_id, 'ip') === 0)
404 return get_user_option('s2member_registration_ip', $user_id);
405
406 else if(strcasecmp($field_id, 's2member_subscr_or_wp_id') === 0)
407 return ($subscr_id = get_user_option('s2member_subscr_id', $user_id)) ? $subscr_id : $user_id;
408
409 else if(strcasecmp($field_id, 'avatar') === 0) // Avatar with a specific size?
410 return get_avatar($user_id, !empty($args['size']) ? $args['size'] : 96);
411
412 else if(is_array($fields = get_user_option('s2member_custom_fields', $user_id)))
413 {
414 $field_var = preg_replace('/[^a-z0-9]/i', '_', strtolower($field_id));
415 if(isset($fields[$field_var])) return $fields[$field_var];
416 }
417 }
418 return FALSE; // Otherwise, return false.
419 }
420
421 /**
422 * Auto EOT time, else NPR (next payment time).
423 *
424 * @package s2Member\Utilities
425 * @since 150713
426 *
427 * @param int|string $user_id Optional. Defaults to the current User's ID.
428 * @param bool $check_gateway Defaults to a true value. If this is false, it is only possible to return a fixed EOT time.
429 * In other words, if this is false and there is no EOT time, empty values will be returned. Be careful with this, because not checking
430 * the payment gateway can result in an inaccurate return value. Only set to false if you want to limit the check to a fixed hard-coded EOT time.
431 * @param string $favor Defaults to a value of `fixed`; i.e., if a fixed EOT time is available, that is returned in favor of a next payment time.
432 * You can set this to `next` if you'd like to favor a next payment time (when applicable) instead of returning a fixed EOT time.
433 *
434 * @return array An associative array of EOT details; with the following elements.
435 *
436 * - `type` One of `fixed` (a fixed EOT time), `next` (next payment time; i.e., an ongoing recurring subscription); or an empty string if there is no EOT for the user.
437 * - `time` The timestamp (UTC time) that represents the EOT (End Of Term); else `0` if there is no EOT time.
438 * - `tense` If time is now (or earlier) this will be `past`. If time is in the future, this will be `future`. If there is no time, this is an empty string.
439 * - `debug` A string of details that explain to a developer what was returned. For debugging only.
440 */
441 public static function get_user_eot($user_id = 0, $check_gateway = TRUE, $favor = 'fixed')
442 {
443 if(!($user_id = (int)$user_id)) // Empty user ID in this call?
444 $user_id = get_current_user_id(); // Assume current user.
445
446 if(!$favor || !in_array($favor, array('fixed', 'next'), TRUE))
447 $favor = 'fixed'; // Default behavior.
448
449 $now = time(); // Current timestamp.
450 $grace_time = (int)$GLOBALS['WS_PLUGIN__']['s2member']['o']['eot_grace_time'];
451 $grace_time = (int)apply_filters('ws_plugin__s2member_eot_grace_time', $grace_time);
452 $demotion_role = c_ws_plugin__s2member_option_forces::force_demotion_role('subscriber');
453 $empty_response = array('type' => '', 'time' => 0, 'tense' => '', 'debug' => '');
454
455 if(!$user_id || !($user = new WP_User($user_id)) || !$user->ID)
456 return array_merge($empty_response, array('debug' => 'Invalid user ID.'));
457
458 $ipn_signup_vars = self::get_user_ipn_signup_vars($user->ID);
459 $subscr_gateway = (string)get_user_option('s2member_subscr_gateway', $user->ID);
460 $subscr_id = (string)get_user_option('s2member_subscr_id', $user->ID);
461 $subscr_cid = (string)get_user_option('s2member_subscr_cid', $user->ID);
462 $last_auto_eot_time = (int)get_user_option('s2member_last_auto_eot_time', $user->ID);
463 $auto_eot_time = (int)get_user_option('s2member_auto_eot_time', $user->ID);
464
465 if($auto_eot_time) // They have a hard-coded EOT time at present?
466 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
467 'debug' => 'This is a fixed EOT time recorded by s2Member. It can be altered in the WordPress Dashboard for this user.');
468
469 if(!$subscr_gateway && !$subscr_id && !$subscr_cid && $last_auto_eot_time // EOTd?
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))
472 && !c_ws_plugin__s2member_user_access::user_access_ccaps($user) // And no CCAPs either?
473 ) return array('type' => 'fixed', 'time' => $last_auto_eot_time, 'tense' => $last_auto_eot_time <= $now ? 'past' : 'future',
474 'debug' => 'This is an archived/fixed EOT time recorded by s2Member; i.e., the date this customer\'s membership EOT was processed.');
475
476 if(!$subscr_gateway || !$subscr_id || !is_array($ipn_signup_vars) || !$ipn_signup_vars)
477 return array_merge($empty_response, array('debug' => 'This user has no subscription; i.e., missing `subscr_id`, `subscr_gateway` or `ipn_signup_vars`.'));
478
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)
484 return array_merge($empty_response, array('debug' => 'This user has no subscription; i.e., `txn_type` != `subscr_signup`.'));
485
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']);
490
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.
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.
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.
494 if($check_gateway) switch($subscr_gateway) // A bit different for each payment gateway.
495 {
496 case 'paypal': // PayPal (legacy Pro NVP/Payflow + PayPal Checkout REST).
497
498 $ppco_creds = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_creds();
499 $can_try_ppco = (!empty($ppco_creds['client_id']) && !empty($ppco_creds['secret']));
500 $can_try_standard = (!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_username'])
501 && !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_password'])
502 && !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_signature']));
503 $can_try_payflow = (c_ws_plugin__s2member_utils_conds::pro_is_installed()
504 && class_exists('c_ws_plugin__s2member_pro_paypal_utilities')
505 && !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_payflow_api_username']));
506
507 //260819.0543 Prefer positive product provenance, then fall through to other configured PayPal APIs when lookup fails.
508 // Classic Recurring Payments and REST Subscriptions can both use I-* IDs; Payflow profile IDs use RP/RT prefixes.
509 $ppco_hint = ((!empty($ipn_signup_vars['s2member_paypal_proxy_use']) && $ipn_signup_vars['s2member_paypal_proxy_use'] === 'paypal_checkout')
510 || (!empty($ipn_signup_vars['invoice']) && strpos((string)$ipn_signup_vars['invoice'], 's2mpf-') === 0));
511 $payflow_hint = (bool)preg_match('/^R[PT]/i', $subscr_id);
512 if($ppco_hint)
513 $paypal_routes = array('ppco', 'standard', 'payflow');
514 else if($payflow_hint)
515 $paypal_routes = array('payflow', 'standard', 'ppco');
516 else
517 $paypal_routes = array('standard', 'ppco', 'payflow');
518
519 foreach($paypal_routes as $_paypal_route)
520 {
521 switch($_paypal_route)
522 {
523 case 'ppco':
524 if(!$can_try_ppco)
525 break;
526
527 $subscription = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_subscription_details($subscr_id);
528 if(!$subscription || !empty($subscription['__error']))
529 break; // Try another configured PayPal API family.
530
531 $status = !empty($subscription['status']) ? strtoupper((string)$subscription['status']) : '';
532 $next = !empty($subscription['billing_info']['next_billing_time']) ? (string)$subscription['billing_info']['next_billing_time'] : '';
533
534 if($status && $status !== 'ACTIVE')
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.');
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
543 if($next && ($time = strtotime($next)) > $now)
544 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
545 'debug' => 'PayPal Checkout says this is the next payment time.');
546
547 return array_merge($empty_response, array(
548 'debug' => 'PayPal Checkout says this subscription is active; no next billing time was returned.',
549 ));
550
551 case 'payflow':
552 if(!$can_try_payflow)
553 break;
554
555 if(!($api_response = c_ws_plugin__s2member_pro_paypal_utilities::payflow_get_profile($subscr_id)) || !empty($api_response['__error']))
556 break; // Try another configured PayPal API family.
557
558 if(preg_match('/^(?:Pending|PendingProfile)$/i', $api_response['STATUS']))
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.'));
560
561 if(!preg_match('/^(?:Active|ActiveProfile)$/i', $api_response['STATUS']))
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.');
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
570 if($api_response['TERM'] > 0 && $api_response['PAYMENTSLEFT'] <= 0)
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.');
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
579 if($api_response['TERM'] <= 0 || $api_response['PAYMENTSLEFT'] > 0)
580 if($api_response['NEXTPAYMENT'] && strlen($api_response['NEXTPAYMENT']) === 8) // MMDDYYYY format is not `strtotime()` compatible.
581 if(($time = strtotime(substr($api_response['NEXTPAYMENT'], -4).'-'.substr($api_response['NEXTPAYMENT'], 0, 2).'-'.substr($api_response['NEXTPAYMENT'], 2, 2))) > $now)
582 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
583 'debug' => 'The PayPal Pro API says this is the next payment time.');
584
585 return array_merge($empty_response, array('debug' => 'No fixed EOT, and there are no more payments needed from this user.'));
586
587 case 'standard':
588 if(!$can_try_standard)
589 break;
590
591 $api_args = array(
592 'METHOD' => 'GetRecurringPaymentsProfileDetails',
593 'PROFILEID' => $subscr_id,
594 );
595 if(!($api_response = c_ws_plugin__s2member_paypal_utilities::paypal_api_response($api_args)) || !empty($api_response['__error']))
596 break; // 11592 and other profile-family misses may resolve through REST.
597
598 if(preg_match('/^(?:Pending|PendingProfile)$/i', $api_response['STATUS']))
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.'));
600
601 if(!preg_match('/^(?:Active|ActiveProfile)$/i', $api_response['STATUS']))
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.');
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
610 if($api_response['TOTALBILLINGCYCLES'] > 0 && $api_response['NUMCYCLESREMAINING'] <= 0)
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 }
618
619 if($api_response['TOTALBILLINGCYCLES'] <= 0 || $api_response['NUMCYCLESREMAINING'] > 0)
620 if($api_response['NEXTBILLINGDATE'] && ($time = strtotime($api_response['NEXTBILLINGDATE'])) > $now)
621 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
622 'debug' => 'The PayPal Pro API says this is the next payment time.');
623
624 return array_merge($empty_response, array('debug' => 'No fixed EOT, and there are no more payments needed from this user.'));
625 }
626 }
627
628 if(!$can_try_ppco && !$can_try_standard && !$can_try_payflow)
629 return array_merge($empty_response, array('debug' => 'PayPal subscription API credentials missing in your s2Member configuration.'));
630
631 return array_merge($empty_response, array(
632 'debug' => 'No fixed EOT, and no configured PayPal API could resolve this subscription. Older recurring profiles may still require legacy PayPal API credentials.',
633 ));
634
635 break; // Break switch.
636
637 case 'authnet': // Authorize.Net (EOT only; w/ limited functionality).
638
639 if(!c_ws_plugin__s2member_utils_conds::pro_is_installed()
640 || !class_exists('c_ws_plugin__s2member_pro_authnet_utilities')
641 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_authnet_api_login_id']
642 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_authnet_api_trans_key']
643 ) return array_merge($empty_response, array('debug' => 'Authorize.Net API credentials missing in your s2Member configuration.'));
644
645 $api_args = array(
646 'x_method' => 'status',
647 'x_subscription_id' => $subscr_id,
648 );
649 if(!($api_response = c_ws_plugin__s2member_pro_authnet_utilities::authnet_arb_response($api_args)) || !empty($api_response['__error']))
650 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the Authorize.Net API says there is no subscription for this user.'));
651
652 if(!preg_match('/^(?:active)$/i', $api_response['subscription_status']))
653 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
654 'debug' => 'This is the estimated EOT time. The Authorize.Net API says this subscription is no longer active, and thus, access should be terminated at this time.');
655
656 // Next payment time not possible with Authorize.Net at this time.
657 // Fixed recurring intervals not possible to query with Authorize.Net at this time.
658 return array_merge($empty_response, array('debug' => 'Partially-supported payment gateway; unable to determine.'));
659
660 break; // Break switch.
661
662 case 'stripe': // Stripe payment gateway (best).
663
664 if(!c_ws_plugin__s2member_utils_conds::pro_is_installed()
665 || !class_exists('c_ws_plugin__s2member_pro_stripe_utilities')
666 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_stripe_api_publishable_key']
667 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_stripe_api_secret_key']
668 ) return array_merge($empty_response, array('debug' => 'Stripe API credentials missing in your s2Member configuration.'));
669
670 if(!$subscr_cid) return array_merge($empty_response, array('debug' => 'No fixed EOT, and no `subscr_cid` on file. Unable to determine.'));
671
672 if(!is_object($stripe_subscription = c_ws_plugin__s2member_pro_stripe_utilities::get_customer_subscription($subscr_cid, $subscr_id)) || empty($stripe_subscription->id))
673 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the Stripe API says there is no subscription for this user.'));
674
675 if((int)$stripe_subscription->ended_at > 0) // Done?
676 {
677 $time = $stripe_subscription->ended_at + $grace_time;
678 return array('type' => 'fixed', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
679 'debug' => 'The Stripe API says this subscription reached an expiration on this date + grace time.');
680 }
681 if(in_array($stripe_subscription->status, array('canceled', 'unpaid'), TRUE) || $stripe_subscription->cancel_at_period_end)
682 {
683 $time = $stripe_subscription->current_period_end + $grace_time;
684 return array('type' => 'fixed', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
685 'debug' => 'The Stripe API says this subscription was cancelled, and thus, should EOT on this date + grace time.');
686 }
687 if(isset($stripe_subscription->plan->metadata->recurring, $stripe_subscription->plan->metadata->recurring_times)
688 && !$stripe_subscription->plan->metadata->recurring) // Non-recurring subscription?
689 {
690 $time = (int)$stripe_subscription->start;
691 $time += $stripe_subscription->plan->trial_period_days * DAY_IN_SECONDS;
692
693 switch($stripe_subscription->plan->interval)
694 {
695 case 'day': // Every X days in this case.
696 $time += (DAY_IN_SECONDS * $stripe_subscription->plan->interval_count) * 1;
697 break; // Break switch now.
698
699 case 'week': // Every X weeks in this case.
700 $time += (WEEK_IN_SECONDS * $stripe_subscription->plan->interval_count) * 1;
701 break; // Break switch now.
702
703 case 'month': // Every X months in this case.
704 $time += ((WEEK_IN_SECONDS * 4) * $stripe_subscription->plan->interval_count) * 1;
705 break; // Break switch now.
706
707 case 'year': // Every X years in this case.
708 $time += (YEAR_IN_SECONDS * $stripe_subscription->plan->interval_count) * 1;
709 break; // Break switch now.
710 }
711 if($favor === 'next' && $stripe_subscription->current_period_end + 1 < $time)
712 {
713 if($stripe_subscription->current_period_end + 1 > $now)
714 {
715 $time = $stripe_subscription->current_period_end + 1;
716 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
717 'debug' => 'The Stripe API says this is the next payment time.');
718 }
719 return array_merge($empty_response, array('debug' => 'Stripe says no more payments needed from this user.'));
720 }
721 $time += $grace_time; // Now add grace to the final EOT time.
722 return array('type' => 'fixed', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
723 'debug' => 'The Stripe API says this subscription will be completely over on this date + grace time.');
724 }
725 if(isset($stripe_subscription->plan->metadata->recurring, $stripe_subscription->plan->metadata->recurring_times)
726 && $stripe_subscription->plan->metadata->recurring && $stripe_subscription->plan->metadata->recurring_times <= 0)
727 {
728 if($stripe_subscription->current_period_end + 1 > $now)
729 {
730 $time = $stripe_subscription->current_period_end + 1;
731 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
732 'debug' => 'The Stripe API says this is the next payment time.');
733 }
734 return array_merge($empty_response, array('debug' => 'Stripe says no more payments needed from this user.'));
735 }
736 if(isset($stripe_subscription->plan->metadata->recurring, $stripe_subscription->plan->metadata->recurring_times)
737 && $stripe_subscription->plan->metadata->recurring && $stripe_subscription->plan->metadata->recurring_times > 0)
738 {
739 $time = (int)$stripe_subscription->start;
740 $time += $stripe_subscription->plan->trial_period_days * DAY_IN_SECONDS;
741
742 switch($stripe_subscription->plan->interval)
743 {
744 case 'day': // Every X days in this case.
745 $time += (DAY_IN_SECONDS * $stripe_subscription->plan->interval_count)
746 * $stripe_subscription->plan->metadata->recurring_times;
747 break; // Break switch now.
748
749 case 'week': // Every X weeks in this case.
750 $time += (WEEK_IN_SECONDS * $stripe_subscription->plan->interval_count)
751 * $stripe_subscription->plan->metadata->recurring_times;
752 break; // Break switch now.
753
754 case 'month': // Every X months in this case.
755 $time += ((WEEK_IN_SECONDS * 4) * $stripe_subscription->plan->interval_count)
756 * $stripe_subscription->plan->metadata->recurring_times;
757 break; // Break switch now.
758
759 case 'year': // Every X years in this case.
760 $time += (YEAR_IN_SECONDS * $stripe_subscription->plan->interval_count)
761 * $stripe_subscription->plan->metadata->recurring_times;
762 break; // Break switch now.
763 }
764 if($favor === 'next' && $stripe_subscription->current_period_end + 1 < $time)
765 {
766 if($stripe_subscription->current_period_end + 1 > $now)
767 {
768 $time = $stripe_subscription->current_period_end + 1;
769 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
770 'debug' => 'The Stripe API says this is the next payment time.');
771 }
772 return array_merge($empty_response, array('debug' => 'Stripe says no more payments needed from this user.'));
773 }
774 $time += $grace_time; // Now add grace to the final EOT time.
775 return array('type' => 'fixed', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
776 'debug' => 'The Stripe API says this subscription will be completely over on this date + grace time.');
777 }
778 if($stripe_subscription->current_period_end + 1 > $now)
779 {
780 $time = $stripe_subscription->current_period_end + 1;
781 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
782 'debug' => 'The Stripe API says this is the next payment time.');
783 }
784 return array_merge($empty_response, array('debug' => 'No fixed EOT, and Stripe says there are no more payments needed from this user.'));
785
786 break; // Break switch.
787
788 case 'clickbank': // ClickBank (limited functionality).
789
790 if(!c_ws_plugin__s2member_utils_conds::pro_is_installed()
791 || !class_exists('c_ws_plugin__s2member_pro_clickbank_utilities')
792 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_clickbank_username']
793 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_clickbank_clerk_key']
794 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_clickbank_developer_key']
795 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_clickbank_secret_key']
796 ) return array_merge($empty_response, array('debug' => 'ClickBank API credentials missing in your s2Member configuration.'));
797
798 if(empty($ipn_signup_vars['txn_id'])) // ClickBank receipt number.
799 return array_merge($empty_response, array('debug' => 'No fixed EOT, and no `txn_id` on file. Unable to determine.'));
800
801 if(!($api_response = c_ws_plugin__s2member_pro_clickbank_utilities::clickbank_api_order($ipn_signup_vars['txn_id'])))
802 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the ClickBank API says there is no subscription for this user.'));
803
804 if(!preg_match('/^(?:TEST_)?SALE$/i', $api_response['txnType']) || !$api_response['recurring'])
805 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the ClickBank API says there is no recurring subscription for this user.'));
806
807 if(strcasecmp($api_response['status'], 'active') !== 0 || $api_response['futurePayments'] <= 0)
808 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
809 'debug' => 'This is the estimated EOT time. The ClickBank API says this subscription no longer active, or it has reached its last payment, and thus, access should be terminated at this time.');
810
811 if($api_response['nextPaymentDate'] && ($time = strtotime($api_response['nextPaymentDate'])) > $now)
812 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
813 'debug' => 'The ClickBank API says this is the next payment time.');
814
815 return array_merge($empty_response, array('debug' => 'No fixed EOT, and there are no more payments needed from this user.'));
816
817 break; // Break switch.
818
819 default: // Default case handler.
820 return array_merge($empty_response, array('debug' => 'Partially-supported payment gateway; unable to determine.'));
821 }
822 return array_merge($empty_response, array('debug' => 'Payment gateway check disabled; unable to determine.'));
823 }
824 }
825 }
826