Paypal customers not demoted after failed payment

I have just noticed that Paypal customers on my site do not get demoted after their subscription ends due to a failed payment. Some of my customers have kept access to the site for over a year without paying.

If a customer cancels their subscription normally through unsubscribing they get their EOT set properly and get demoted, so this issue only occurs when their payment fails.

1 Like

Hi Miro.

Thanks for reporting that. Do you have logging enabled? What do the logs say when you reproduce that behavior on your installation? (core/paypal ipn and api logs)

Is your EOT engine enabled and configured properly? I ask because in some rare/random cases, this setting gets reset. I’d like to confirm that that’s not part of the problem here.

Did you confirm that the PayPal subscription was ended after the failed payments and is not just waiting for a retry? Using the subscr ID, can you search for the relevant entries in the s2 logs?

Hi! I have the same issue that @Komar mentions and I have to demote my users manually whenever I receive PayPal’s email letting me know a payment didn’t go through as well.

Does PayPal send an IPN notification when that happens? I can also try to look at my IPN logs if you let me know how to see those, since I forgot the page on PayPal that has that information and I gave up last time I was trying to find it.

Hi Sim.

s2Member doesn’t act on a failed payment for a subscriptions, it acts when the subscription is ended, based on your configuration. See: WP Admin > s2Member > PayPal Options > Automatic EOT Behavior

Is this a subscription? Is the subscription ended or still active? If ended, did the user’s profile get an EOT time set by s2Member?

You can enable and view the logs from here: WP Admin > s2Member > Log Files


1 Like

Thanks for your reply.

Yes, it’s for subscriptions using PayPal Standard.

User pays the first month, gets upgraded.

Second month pay day comes, user does not have money in their account or their card is declined etc.

I receive an email from PayPal letting me know the payment didn’t go through and that they will try to charge the user again. s2Member does NOT demote the user instantly as it should, since the user didn’t pay from that moment onwards (it should demote the user and re-promote the user if payment happens later, IMHO).

Five days later, in 99% of those cases the user does NOT have funds again, PayPal emails me letting me know that it was not possible to charge the subscriber and that they’ll stop trying. Subscription is suspended on PayPal’s side. Nothing happens on s2Member again. If the user didn’t get demoted on the “first event” they should be on the second one, otherwise they’ll be able to continue accessing content that should not be available to them.

My only workaround has been editing user by user when that happens (which is something that happens quite often, since many users subscribe just to access certain content, they download whatever they want and leave after, but many re-subscribe once I demote them manually, but only when I do that).

I don’t know if PayPal sends a signal to the return URL in those cases, but I guess it should, since it does when a user cancels a subscription instead of “not paying” for it or when I refund a subscriber for any reason.

I am grateful for the plugin no matter what, of course. Hopefully we can have that feature at some point…?

Thanks Sim.

Actually s2Member would act when the subscription ends, not when a payment fails but subscription is active. I do have in my feature requests an option to remove access when payment fails and restore it if paid for an active subscription. I’m adding your vote to it.

Subscription is suspended on PayPal’s side. Nothing happens on s2Member again. If the user didn’t get demoted on the “first event” they should be on the second one, otherwise they’ll be able to continue accessing content that should not be available to them.

Exactly. If the paypal subscription was ended, then s2Member should get a notification from PayPal behind the scenes, and s2Member’s EOT would kick in based on your configuration for it.

Those notifications would show up in at least one of the s2Member logs. Did you look for them? We need to see if s2 is receiving it and not acting on it so we can look deeper into it, or if it’s just not getting told about it. WP Admin > s2Member > Log Files

My only workaround has been editing user by user when that happens

That’s a good way to handle it in the meantime.

1 Like

Hi! Finally got do test it. Yes, PayPal IPN sends a subscr_failed event to s2Member, therefore it needs to process that and demote the user right away, like when there’s a refund. Anyway I can hook the event that way by changing something in the code somewhere?

[txn_type] => subscr_failed

[s2member_log] => Array
        [0] => IPN received on: Tue Oct 6, 2020 11:38:18 am UTC
        [1] => s2Member POST vars verified through a POST back to PayPal.
        [2] => s2Member originating domain (`$_SERVER["HTTP_HOST"]`) validated.
        [3] => s2Member `txn_type` identified as ( `subscr_failed|recurring_payment_failed|recurring_payment_skipped` ).
        [4] => This `txn_type` does not require any action on the part of s2Member.
        [5] => s2Member does NOT respond to individual failed payments, only multiple consecutive failed payments.
        [6] => When multiple consecutive payments fail, a special IPN response will be triggered.

[subscr_gateway] => paypal


This is the file that handles that notification: s2member/src/includes/classes/

There are a few hooks there, and you could customize the behavior with a hack in the must-use plugins folder, for example.

If you remove access for the user but the subscription is still active, you will also want to restore it if a payment comes in later. See: s2member/src/includes/classes/

I hope that helps! :slight_smile:

1 Like

Yes! Very much! Thank you so much!!!

1 Like

I just changed the file at its line 44 from:

if ((!empty($paypal["txn_type"]) && preg_match ("/^(subscr_cancel|recurring_payment_profile_cancel|mp_cancel)$/i", $paypal["txn_type"]))


if ((!empty($paypal["txn_type"]) && preg_match ("/^(subscr_cancel|recurring_payment_profile_cancel|mp_cancel|subscr_failed|recurring_payment_failed|recurring_payment_skipped)$/i", $paypal["txn_type"]))

Changing also Line 61 do add the new types and renaming the php file you mentioned to something else so it won’t run.

I’ll see what happens when another payment fails, then I’ll let you know :wink:

1 Like

I suggest using the hooks, though.

Those files will be replaced next time you update s2.

1 Like

Thanks! Good to know! I am still learning (and quite clueless yet) :grin:

I thought I’d had to hold back updating until I changed the new files to match my modifications, LOL


My experiment did not work as I expected it was unlikely, but it was worth giving it a try. I’ll have to do it properly by adding code to the file you mentioned or by using the hooks you linked. I’ll study a bit more and let you know what I get from it when I make progress.

Thanks a lot for always being available to help us! :smiling_face_with_three_hearts:

1 Like

One idea for a future addon would be to use PayPal’s API to retry collecting outstanding amounts every x hours:,next%20billing%20cycle%20is%20reached.

Since it seems we can run it as frequently as we want, it would be nice to be able to have your plugin do that instead of using their interface to try to collect one by one manually. I didn’t know where to post this idea, so I felt it would fit here better than in a loose topic because of the relation.