IPN signup vars

@openmtbmap, I went looking into the ipn_signup_vars thing, and tried a few things.

I managed to get a user without them (mostly just default empty values), demoted when ending his subscription. I’m not sure yet if those missing values would cause an issue elsewhere yet.

I may end up adding it to a release, if not as a default, maybe as an beta setting that you can enable to not require those vars in the database for the gateway notification to be acted on.

In case you want to try this for a bit…

in src/includes/classes/utils-users.inc.php find

		public static function get_user_ipn_signup_vars($user_id = 0, $subscr_txn_baid_cid_id = '')
		{
			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)))
			   || (!$user_id && !$subscr_txn_baid_cid_id && is_object($user = wp_get_current_user()) && !empty($user->ID) && ($user_id = $user->ID))
			)
			{
				$_subscr_baid = get_user_option('s2member_subscr_baid', $user_id);
				$_subscr_cid  = get_user_option('s2member_subscr_cid', $user_id);
				$_subscr_id   = get_user_option('s2member_subscr_id', $user_id);

				if($_subscr_id && (!$subscr_txn_baid_cid_id || $subscr_txn_baid_cid_id === $_subscr_id || $subscr_txn_baid_cid_id === $_subscr_baid || $subscr_txn_baid_cid_id === $_subscr_cid))
					if(is_array($ipn_signup_vars = get_user_option('s2member_ipn_signup_vars', $user_id)))
						if($ipn_signup_vars['subscr_id'] === $_subscr_id)
							return $ipn_signup_vars;
			}
			return FALSE; // Otherwise, return false.
		}

and replace it with

		public static function get_user_ipn_signup_vars($user_id = 0, $subscr_txn_baid_cid_id = '')
		{
			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)))
			   || (!$user_id && !$subscr_txn_baid_cid_id && is_object($user = wp_get_current_user()) && !empty($user->ID) && ($user_id = $user->ID))
			)
			{
				$_subscr_baid = get_user_option('s2member_subscr_baid', $user_id);
				$_subscr_cid  = get_user_option('s2member_subscr_cid', $user_id);
				$_subscr_id   = get_user_option('s2member_subscr_id', $user_id);

				if($_subscr_id)
					$ipn_signup_vars = get_user_option('s2member_ipn_signup_vars', $user_id);
					if(!is_array($ipn_signup_vars)) {
						$ipn_signup_vars = array(
							'subscr_cid' => $_subscr_cid,
							'subscr_id' => $_subscr_id,
							'custom' => $_SERVER["HTTP_HOST"],
							'period1' => '',
							'period3' => '',
							'payer_email' => '',
							'first_name' => '',
							'last_name' => '',
							'option_name1' => '',
							'option_selection1' => '',
							'option_name2' => '',
							'option_selection2' => '',
							'item_name' => '',
							'item_number' => '',
						);
					}
					return $ipn_signup_vars;
			}
			return FALSE; // Otherwise, return false.
		}

Please let me know if you try it and how it goes.

:slight_smile:

@thesimarchitect, maybe this interests you as well.

:slight_smile:

1 Like

Awesome! I will test it out as soon as I turn my computer on and run my daily collection routines!

:slightly_smiling_face:

1 Like

I’m super busy right now but thanks a lot. I will try it as soon as I can squeeze out some hours (checking things work alright is what will take time)

2 Likes

I started using it now - will report back if there are problems. I have very few users left without ipn_signup_vars because I stopped accepting subscriptions via Stripe for many many years. It will take a month or too until one of the few left old ones cancels I guess.

2 Likes

I deal with those things daily so I will do my best to test it out.

I am making a full FTP backup again today (yesterday’s failed) before changing it.

It will be nice to see how the new code performs. @clavaque is surely very welcome to exchange private messages with me on the topic as well.

1 Like

Reapplied the segment being tested after today’s update.

Keep me posted if you need anything and thanks again!

:rabbit:

1 Like

It seems to work - users get cancelled now. However there must be a second place where this is missing! And that is the API notification email if a payment is received. That one is still missing. It’s much less important but would be nice if it could be fixed as well.

Could it be that the block following line 215 needs adjustment too?

  public static function get_user_ipn_signup_var($var = '', $user_id = 0, $subscr_txn_baid_cid_id = '')
  {
  	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)))
  	{
  		if(isset($user_ipn_signup_vars[$var]))
  			return $user_ipn_signup_vars[$var];
  	}
  	return FALSE; // Otherwise, return false.
  }

So yeah would be great to get that fix (plus the additional one if it’s easy to find) into the main branch. Finally a huge worry less. This problem has been driving me nuts for years as it is really taking a lot of time/checking to filter Stripe for those cancelled customers every couple of months.

2 Likes

Oh yeah, and much more importantly, this patch enables to move stripe accounts to a new country. Still no dice for PayPal AFAIK but that’s PayPal regulations not s2. While for stripe it was s2member making it impossible to move accounts due to signup vars.

1 Like

It seems to work - users get cancelled now …
and much more importantly, this patch enables to move stripe accounts to a new country …
Finally a huge worry less …

Wonderful! That’s great! :smiley:

there must be a second place where this is missing! And that is the API notification email if a payment is received.

I’m not sure I understood that. Is the notification sent or not sent when a Stripe payment is received? Could you give me more details, and how to reproduce it?

:slight_smile:

When someone pays with Stripe, then usually there is a s2member API notification email (I have enabled this option).
(s2Member / API Notification Email) - Payment …

However this email never arrived for recurring payments for Stripe payments where the user misses the signup_vars. This is still the case. So users get demoted now correctly, but there must somewhere be a very similar check in s2member for that email - and that email isn’t sent either.
If the user has signup_vars then the email is sent.

If you need I could try to check the logs for the error message why it isn’t sent.

Gateway Core IPN log - I cannot find an entry at all.
Stripe Api log - 6 entries.
Stripe IPN debug log has 6 entries as well with the decision to ignore all of them:

        [livemode] => 1
        [pending_webhooks] => 2
        [request] => Stripe\StripeObject Object
            (
                [id] => 
                [idempotency_key] => 
            )

        [type] => invoice.created
    )

[s2member_log] => Array
    (
        [0] => Ignoring this Webhook/IPN. The event does NOT require any action on the part of s2Member.
    )

)

… another entry:
[livemode] => 1
[pending_webhooks] => 2
[request] => Stripe\StripeObject Object
(
[id] =>
[idempotency_key] =>
)

        [type] => invoice.paid
    )

[s2member_log] => Array
    (
        [0] => Ignoring this Webhook/IPN. The event does NOT require any action on the part of s2Member.
    )

)

I see…

Those entries are not for Stripe webhooks that s2 acts on, that’d be why they’re being ignored. These are the ones s2 expects:

  • invoice.payment_succeeded
  • invoice.payment_failed
  • customer.deleted
  • customer.subscription.deleted
  • charge.refunded
  • charge.dispute.created

(s2member-pro/src/includes/classes/gateways/stripe/stripe-notify-in.inc.php)

You’re talking about subscription payment notifications, right? That would be the invoice.payment_succeeded webhook. Do you have any of those that were ignored by s2?

I checked the different parts where the payment notification emails happen, and I’m guessing that it’s not getting that far because an earlier point checks that the vars are not empty, and the hack we’re trying is mostly empty values…

One thing to try would be to have dummy data there, and see if it makes a difference in the notification, which of course would then be a notification with wrong details… Or we could try filling them out with the Stripe webhook details instead of the saved signup vars from when the subscription got created…

But first please try adding dummy values to the array in the hack (especially these: payer_email, item_name, item_number), and see if then the payment notifications start going out, and then we look at populating some of that with real data.

Try this:

$userdata = get_userdata($user_id); // Fetch user data
$ipn_signup_vars = array(
    'subscr_cid' => $_subscr_cid,
    'subscr_id' => $_subscr_id,
    'custom' => $_SERVER["HTTP_HOST"],
    'period1' => '',
    'period3' => '',
    'payer_email' => $userdata->user_email ?? 'USER EMAIL',
    'first_name' => $userdata->first_name ?? 'FIRST NAME',
    'last_name' => $userdata->last_name ?? 'LAST NAME',
    'option_name1' => '',
    'option_selection1' => '',
    'option_name2' => '',
    'option_selection2' => '',
    'item_name' => 'ITEM NAME',
    'item_number' => 'ITEM NUMBER',
);

:slight_smile:

I actually found something related to this in the IPN log - is this sufficient? Do I need to insert your patch?: However I find nothing related to the users email in the gateway-core-ipn.log.

                        [subtotal] => 1300
                        [subtotal_excluding_tax] => 1300
                        [tax] => 
                        [tax_percent] => 
                        [test_clock] => 
                        [total] => 1300
                        [total_discount_amounts] => Array
                            (
                            )

                        [total_excluding_tax] => 1300
                        [total_tax_amounts] => Array
                            (
                            )

                        [transfer_data] => 
                        [webhooks_delivered_at] => 1711389033
                    )

            )

        [livemode] => 1
        [pending_webhooks] => 2
        [request] => Stripe\StripeObject Object
            (
                [id] => 
                [idempotency_key] => 
            )

        [type] => invoice.payment_succeeded
    )

[s2member_log] => Array
    (
        [0] => Stripe Webhook/IPN event type identified as: `invoice.payment_succeeded` on: Mon Mar 25, 2024 6:51:40 pm UTC
        [1] => Webhook/IPN event `invoice.payment_succeeded` reformulated. Piping through s2Member's core gateway processor as `txn_type` (`subscr_payment`).
        [2] => Please check core IPN logs for further processing details.
    )

)

That one shows that the invoice.payment_succeeded webhook was acted on. But it doesn’t mention if the emails were sent.

That would be mentioned in the corresponding core-ipn log entry, but if you didn’t apply the additional change I suggested, it likely won’t send the emails.

So you can try this:

		public static function get_user_ipn_signup_vars($user_id = 0, $subscr_txn_baid_cid_id = '')
		{
			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)))
			   || (!$user_id && !$subscr_txn_baid_cid_id && is_object($user = wp_get_current_user()) && !empty($user->ID) && ($user_id = $user->ID))
			)
			{
				$_subscr_baid = get_user_option('s2member_subscr_baid', $user_id);
				$_subscr_cid  = get_user_option('s2member_subscr_cid', $user_id);
				$_subscr_id   = get_user_option('s2member_subscr_id', $user_id);

				if($_subscr_id)
					$ipn_signup_vars = get_user_option('s2member_ipn_signup_vars', $user_id);
					if(!is_array($ipn_signup_vars)) {
						$ipn_signup_vars = array(
							'subscr_cid' => $_subscr_cid,
							'subscr_id' => $_subscr_id,
							'custom' => $_SERVER["HTTP_HOST"],
							'period1' => '',
							'period3' => '',
							'payer_email' => $userdata->user_email ?? 'USER EMAIL',
							'first_name' => $userdata->first_name ?? 'FIRST NAME',
							'last_name' => $userdata->last_name ?? 'LAST NAME',
							'option_name1' => '',
							'option_selection1' => '',
							'option_name2' => '',
							'option_selection2' => '',
							'item_name' => 'ITEM NAME',
							'item_number' => 'ITEM NUMBER',
						);
					}
					return $ipn_signup_vars;
			}
			return FALSE; // Otherwise, return false.
		}

:slight_smile: