PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / trunk
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions vtrunk
260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 120219 120301 All 187 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 trunk, at src/includes/classes/utils-users.inc.php

825 lines 44.9 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 && (!user_can($user->ID, 'access_s2member_level1') || c_ws_plugin__s2member_user_access::user_access_role($user) === $demotion_role)
471 && !c_ws_plugin__s2member_user_access::user_access_ccaps($user) // And no CCAPs either?
472 ) return array('type' => 'fixed', 'time' => $last_auto_eot_time, 'tense' => $last_auto_eot_time <= $now ? 'past' : 'future',
473 'debug' => 'This is an archived/fixed EOT time recorded by s2Member; i.e., the date this customer\'s access expired.');
474
475 if(!$subscr_gateway || !$subscr_id || !is_array($ipn_signup_vars) || !$ipn_signup_vars)
476 return array_merge($empty_response, array('debug' => 'This user has no subscription; i.e., missing `subscr_id`, `subscr_gateway` or `ipn_signup_vars`.'));
477
478 $ipn_signup_vars_fallback = !empty($ipn_signup_vars['s2member_ipn_signup_vars_fallback']);
479 //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.
480 $can_recover_paypal_fallback = ($check_gateway && $subscr_gateway === 'paypal' && $ipn_signup_vars_fallback);
481
482 if((empty($ipn_signup_vars['txn_type']) || $ipn_signup_vars['txn_type'] !== 'subscr_signup') && !$can_recover_paypal_fallback)
483 return array_merge($empty_response, array('debug' => 'This user has no subscription; i.e., `txn_type` != `subscr_signup`.'));
484
485 //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.
486 $auto_eot_time = ($ipn_signup_vars_fallback && empty($ipn_signup_vars['period3']))
487 ? 0
488 : c_ws_plugin__s2member_utils_time::auto_eot_time($user->ID, $ipn_signup_vars['period1'], $ipn_signup_vars['period3']);
489
490 //260828.0710 !!! TO-DO: Track a gateway-independent `s2member_access_through_time`, meaning the end of the latest confirmed entitlement period, before EOT grace.
491 // 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.
492 // 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.
493 if($check_gateway) switch($subscr_gateway) // A bit different for each payment gateway.
494 {
495 case 'paypal': // PayPal (legacy Pro NVP/Payflow + PayPal Checkout REST).
496
497 $ppco_creds = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_creds();
498 $can_try_ppco = (!empty($ppco_creds['client_id']) && !empty($ppco_creds['secret']));
499 $can_try_standard = (!empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_username'])
500 && !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_password'])
501 && !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_api_signature']));
502 $can_try_payflow = (c_ws_plugin__s2member_utils_conds::pro_is_installed()
503 && class_exists('c_ws_plugin__s2member_pro_paypal_utilities')
504 && !empty($GLOBALS['WS_PLUGIN__']['s2member']['o']['paypal_payflow_api_username']));
505
506 //260819.0543 Prefer positive product provenance, then fall through to other configured PayPal APIs when lookup fails.
507 // Classic Recurring Payments and REST Subscriptions can both use I-* IDs; Payflow profile IDs use RP/RT prefixes.
508 $ppco_hint = ((!empty($ipn_signup_vars['s2member_paypal_proxy_use']) && $ipn_signup_vars['s2member_paypal_proxy_use'] === 'paypal_checkout')
509 || (!empty($ipn_signup_vars['invoice']) && strpos((string)$ipn_signup_vars['invoice'], 's2mpf-') === 0));
510 $payflow_hint = (bool)preg_match('/^R[PT]/i', $subscr_id);
511 if($ppco_hint)
512 $paypal_routes = array('ppco', 'standard', 'payflow');
513 else if($payflow_hint)
514 $paypal_routes = array('payflow', 'standard', 'ppco');
515 else
516 $paypal_routes = array('standard', 'ppco', 'payflow');
517
518 foreach($paypal_routes as $_paypal_route)
519 {
520 switch($_paypal_route)
521 {
522 case 'ppco':
523 if(!$can_try_ppco)
524 break;
525
526 $subscription = c_ws_plugin__s2member_paypal_utilities::paypal_checkout_subscription_details($subscr_id);
527 if(!$subscription || !empty($subscription['__error']))
528 break; // Try another configured PayPal API family.
529
530 $status = !empty($subscription['status']) ? strtoupper((string)$subscription['status']) : '';
531 $next = !empty($subscription['billing_info']['next_billing_time']) ? (string)$subscription['billing_info']['next_billing_time'] : '';
532
533 if($status && $status !== 'ACTIVE')
534 {
535 if($auto_eot_time)
536 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
537 '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.');
538
539 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.'));
540 }
541
542 if($next && ($time = strtotime($next)) > $now)
543 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
544 'debug' => 'PayPal Checkout says this is the next payment time.');
545
546 return array_merge($empty_response, array(
547 'debug' => 'PayPal Checkout says this subscription is active; no next billing time was returned.',
548 ));
549
550 case 'payflow':
551 if(!$can_try_payflow)
552 break;
553
554 if(!($api_response = c_ws_plugin__s2member_pro_paypal_utilities::payflow_get_profile($subscr_id)) || !empty($api_response['__error']))
555 break; // Try another configured PayPal API family.
556
557 if(preg_match('/^(?:Pending|PendingProfile)$/i', $api_response['STATUS']))
558 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.'));
559
560 if(!preg_match('/^(?:Active|ActiveProfile)$/i', $api_response['STATUS']))
561 {
562 if($auto_eot_time)
563 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
564 '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.');
565
566 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.'));
567 }
568
569 if($api_response['TERM'] > 0 && $api_response['PAYMENTSLEFT'] <= 0)
570 {
571 if($auto_eot_time)
572 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
573 '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.');
574
575 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.'));
576 }
577
578 if($api_response['TERM'] <= 0 || $api_response['PAYMENTSLEFT'] > 0)
579 if($api_response['NEXTPAYMENT'] && strlen($api_response['NEXTPAYMENT']) === 8) // MMDDYYYY format is not `strtotime()` compatible.
580 if(($time = strtotime(substr($api_response['NEXTPAYMENT'], -4).'-'.substr($api_response['NEXTPAYMENT'], 0, 2).'-'.substr($api_response['NEXTPAYMENT'], 2, 2))) > $now)
581 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
582 'debug' => 'The PayPal Pro API says this is the next payment time.');
583
584 return array_merge($empty_response, array('debug' => 'No fixed EOT, and there are no more payments needed from this user.'));
585
586 case 'standard':
587 if(!$can_try_standard)
588 break;
589
590 $api_args = array(
591 'METHOD' => 'GetRecurringPaymentsProfileDetails',
592 'PROFILEID' => $subscr_id,
593 );
594 if(!($api_response = c_ws_plugin__s2member_paypal_utilities::paypal_api_response($api_args)) || !empty($api_response['__error']))
595 break; // 11592 and other profile-family misses may resolve through REST.
596
597 if(preg_match('/^(?:Pending|PendingProfile)$/i', $api_response['STATUS']))
598 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.'));
599
600 if(!preg_match('/^(?:Active|ActiveProfile)$/i', $api_response['STATUS']))
601 {
602 if($auto_eot_time)
603 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
604 '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.');
605
606 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.'));
607 }
608
609 if($api_response['TOTALBILLINGCYCLES'] > 0 && $api_response['NUMCYCLESREMAINING'] <= 0)
610 {
611 if($auto_eot_time)
612 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
613 '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.');
614
615 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.'));
616 }
617
618 if($api_response['TOTALBILLINGCYCLES'] <= 0 || $api_response['NUMCYCLESREMAINING'] > 0)
619 if($api_response['NEXTBILLINGDATE'] && ($time = strtotime($api_response['NEXTBILLINGDATE'])) > $now)
620 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
621 'debug' => 'The PayPal Pro API says this is the next payment time.');
622
623 return array_merge($empty_response, array('debug' => 'No fixed EOT, and there are no more payments needed from this user.'));
624 }
625 }
626
627 if(!$can_try_ppco && !$can_try_standard && !$can_try_payflow)
628 return array_merge($empty_response, array('debug' => 'PayPal subscription API credentials missing in your s2Member configuration.'));
629
630 return array_merge($empty_response, array(
631 'debug' => 'No fixed EOT, and no configured PayPal API could resolve this subscription. Older recurring profiles may still require legacy PayPal API credentials.',
632 ));
633
634 break; // Break switch.
635
636 case 'authnet': // Authorize.Net (EOT only; w/ limited functionality).
637
638 if(!c_ws_plugin__s2member_utils_conds::pro_is_installed()
639 || !class_exists('c_ws_plugin__s2member_pro_authnet_utilities')
640 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_authnet_api_login_id']
641 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_authnet_api_trans_key']
642 ) return array_merge($empty_response, array('debug' => 'Authorize.Net API credentials missing in your s2Member configuration.'));
643
644 $api_args = array(
645 'x_method' => 'status',
646 'x_subscription_id' => $subscr_id,
647 );
648 if(!($api_response = c_ws_plugin__s2member_pro_authnet_utilities::authnet_arb_response($api_args)) || !empty($api_response['__error']))
649 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the Authorize.Net API says there is no subscription for this user.'));
650
651 if(!preg_match('/^(?:active)$/i', $api_response['subscription_status']))
652 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
653 '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.');
654
655 // Next payment time not possible with Authorize.Net at this time.
656 // Fixed recurring intervals not possible to query with Authorize.Net at this time.
657 return array_merge($empty_response, array('debug' => 'Partially-supported payment gateway; unable to determine.'));
658
659 break; // Break switch.
660
661 case 'stripe': // Stripe payment gateway (best).
662
663 if(!c_ws_plugin__s2member_utils_conds::pro_is_installed()
664 || !class_exists('c_ws_plugin__s2member_pro_stripe_utilities')
665 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_stripe_api_publishable_key']
666 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_stripe_api_secret_key']
667 ) return array_merge($empty_response, array('debug' => 'Stripe API credentials missing in your s2Member configuration.'));
668
669 if(!$subscr_cid) return array_merge($empty_response, array('debug' => 'No fixed EOT, and no `subscr_cid` on file. Unable to determine.'));
670
671 if(!is_object($stripe_subscription = c_ws_plugin__s2member_pro_stripe_utilities::get_customer_subscription($subscr_cid, $subscr_id)) || empty($stripe_subscription->id))
672 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the Stripe API says there is no subscription for this user.'));
673
674 if((int)$stripe_subscription->ended_at > 0) // Done?
675 {
676 $time = $stripe_subscription->ended_at + $grace_time;
677 return array('type' => 'fixed', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
678 'debug' => 'The Stripe API says this subscription reached an expiration on this date + grace time.');
679 }
680 if(in_array($stripe_subscription->status, array('canceled', 'unpaid'), TRUE) || $stripe_subscription->cancel_at_period_end)
681 {
682 $time = $stripe_subscription->current_period_end + $grace_time;
683 return array('type' => 'fixed', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
684 'debug' => 'The Stripe API says this subscription was cancelled, and thus, should EOT on this date + grace time.');
685 }
686 if(isset($stripe_subscription->plan->metadata->recurring, $stripe_subscription->plan->metadata->recurring_times)
687 && !$stripe_subscription->plan->metadata->recurring) // Non-recurring subscription?
688 {
689 $time = (int)$stripe_subscription->start;
690 $time += $stripe_subscription->plan->trial_period_days * DAY_IN_SECONDS;
691
692 switch($stripe_subscription->plan->interval)
693 {
694 case 'day': // Every X days in this case.
695 $time += (DAY_IN_SECONDS * $stripe_subscription->plan->interval_count) * 1;
696 break; // Break switch now.
697
698 case 'week': // Every X weeks in this case.
699 $time += (WEEK_IN_SECONDS * $stripe_subscription->plan->interval_count) * 1;
700 break; // Break switch now.
701
702 case 'month': // Every X months in this case.
703 $time += ((WEEK_IN_SECONDS * 4) * $stripe_subscription->plan->interval_count) * 1;
704 break; // Break switch now.
705
706 case 'year': // Every X years in this case.
707 $time += (YEAR_IN_SECONDS * $stripe_subscription->plan->interval_count) * 1;
708 break; // Break switch now.
709 }
710 if($favor === 'next' && $stripe_subscription->current_period_end + 1 < $time)
711 {
712 if($stripe_subscription->current_period_end + 1 > $now)
713 {
714 $time = $stripe_subscription->current_period_end + 1;
715 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
716 'debug' => 'The Stripe API says this is the next payment time.');
717 }
718 return array_merge($empty_response, array('debug' => 'Stripe says no more payments needed from this user.'));
719 }
720 $time += $grace_time; // Now add grace to the final EOT time.
721 return array('type' => 'fixed', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
722 'debug' => 'The Stripe API says this subscription will be completely over on this date + grace time.');
723 }
724 if(isset($stripe_subscription->plan->metadata->recurring, $stripe_subscription->plan->metadata->recurring_times)
725 && $stripe_subscription->plan->metadata->recurring && $stripe_subscription->plan->metadata->recurring_times <= 0)
726 {
727 if($stripe_subscription->current_period_end + 1 > $now)
728 {
729 $time = $stripe_subscription->current_period_end + 1;
730 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
731 'debug' => 'The Stripe API says this is the next payment time.');
732 }
733 return array_merge($empty_response, array('debug' => 'Stripe says no more payments needed from this user.'));
734 }
735 if(isset($stripe_subscription->plan->metadata->recurring, $stripe_subscription->plan->metadata->recurring_times)
736 && $stripe_subscription->plan->metadata->recurring && $stripe_subscription->plan->metadata->recurring_times > 0)
737 {
738 $time = (int)$stripe_subscription->start;
739 $time += $stripe_subscription->plan->trial_period_days * DAY_IN_SECONDS;
740
741 switch($stripe_subscription->plan->interval)
742 {
743 case 'day': // Every X days in this case.
744 $time += (DAY_IN_SECONDS * $stripe_subscription->plan->interval_count)
745 * $stripe_subscription->plan->metadata->recurring_times;
746 break; // Break switch now.
747
748 case 'week': // Every X weeks in this case.
749 $time += (WEEK_IN_SECONDS * $stripe_subscription->plan->interval_count)
750 * $stripe_subscription->plan->metadata->recurring_times;
751 break; // Break switch now.
752
753 case 'month': // Every X months in this case.
754 $time += ((WEEK_IN_SECONDS * 4) * $stripe_subscription->plan->interval_count)
755 * $stripe_subscription->plan->metadata->recurring_times;
756 break; // Break switch now.
757
758 case 'year': // Every X years in this case.
759 $time += (YEAR_IN_SECONDS * $stripe_subscription->plan->interval_count)
760 * $stripe_subscription->plan->metadata->recurring_times;
761 break; // Break switch now.
762 }
763 if($favor === 'next' && $stripe_subscription->current_period_end + 1 < $time)
764 {
765 if($stripe_subscription->current_period_end + 1 > $now)
766 {
767 $time = $stripe_subscription->current_period_end + 1;
768 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
769 'debug' => 'The Stripe API says this is the next payment time.');
770 }
771 return array_merge($empty_response, array('debug' => 'Stripe says no more payments needed from this user.'));
772 }
773 $time += $grace_time; // Now add grace to the final EOT time.
774 return array('type' => 'fixed', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
775 'debug' => 'The Stripe API says this subscription will be completely over on this date + grace time.');
776 }
777 if($stripe_subscription->current_period_end + 1 > $now)
778 {
779 $time = $stripe_subscription->current_period_end + 1;
780 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
781 'debug' => 'The Stripe API says this is the next payment time.');
782 }
783 return array_merge($empty_response, array('debug' => 'No fixed EOT, and Stripe says there are no more payments needed from this user.'));
784
785 break; // Break switch.
786
787 case 'clickbank': // ClickBank (limited functionality).
788
789 if(!c_ws_plugin__s2member_utils_conds::pro_is_installed()
790 || !class_exists('c_ws_plugin__s2member_pro_clickbank_utilities')
791 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_clickbank_username']
792 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_clickbank_clerk_key']
793 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_clickbank_developer_key']
794 || !$GLOBALS['WS_PLUGIN__']['s2member']['o']['pro_clickbank_secret_key']
795 ) return array_merge($empty_response, array('debug' => 'ClickBank API credentials missing in your s2Member configuration.'));
796
797 if(empty($ipn_signup_vars['txn_id'])) // ClickBank receipt number.
798 return array_merge($empty_response, array('debug' => 'No fixed EOT, and no `txn_id` on file. Unable to determine.'));
799
800 if(!($api_response = c_ws_plugin__s2member_pro_clickbank_utilities::clickbank_api_order($ipn_signup_vars['txn_id'])))
801 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the ClickBank API says there is no subscription for this user.'));
802
803 if(!preg_match('/^(?:TEST_)?SALE$/i', $api_response['txnType']) || !$api_response['recurring'])
804 return array_merge($empty_response, array('debug' => 'No fixed EOT, and the ClickBank API says there is no recurring subscription for this user.'));
805
806 if(strcasecmp($api_response['status'], 'active') !== 0 || $api_response['futurePayments'] <= 0)
807 return array('type' => 'fixed', 'time' => $auto_eot_time, 'tense' => $auto_eot_time <= $now ? 'past' : 'future',
808 '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.');
809
810 if($api_response['nextPaymentDate'] && ($time = strtotime($api_response['nextPaymentDate'])) > $now)
811 return array('type' => 'next', 'time' => $time, 'tense' => $time <= $now ? 'past' : 'future',
812 'debug' => 'The ClickBank API says this is the next payment time.');
813
814 return array_merge($empty_response, array('debug' => 'No fixed EOT, and there are no more payments needed from this user.'));
815
816 break; // Break switch.
817
818 default: // Default case handler.
819 return array_merge($empty_response, array('debug' => 'Partially-supported payment gateway; unable to determine.'));
820 }
821 return array_merge($empty_response, array('debug' => 'Payment gateway check disabled; unable to determine.'));
822 }
823 }
824 }
825