Stripe causing double payments

Thank you so much Tim for calling my attention to this issue and all the valuable research you did on your end, and the insight you provided me with to help me understand where this issue could be coming from!

I can’t say with certainty that everyone here is experiencing the same problem, although they look similar. My guess is that the issue happens when there is a paid trial term for a subscription, and the subscription creation fails but the invoice item for the initial term succeeded. Each time the subscription is attempted, a new invoice item was created, until the subscription finally succeeds and all items from the previous attempts are charged.

Tim told me that he’s seeing the problem with a paid trial sub. I also see the invoice item in Alan’s log. I think James’ is also the case. I don’t have enough info from Tim or Corey to say if their cases are the same.

I was able to reproduce the duplicate invoice item behavior with paid trials in my tests. I found how to avoid duplicates, and fixed the problem in my tests.

I’m attaching the updated file for you to test if you can, please. 200413b-stripe-utilities.inc.zip (11.6 KB)

It’s not a full release of s2Member Pro, it’s just the relevant file for s2Member Pro v200301. It goes here: s2member-pro/src/includes/classes/gateways/stripe/stripe-utilities.inc.php

I look forward to your feedback.

:slight_smile:

I confirm that the behaviour outlined by Tim Hibberd is one of the Stripe issues we’ve seen. Thank you all for your work in resolving. The other one is the month = 30 days issue, which is clearly wrong and is causing our members a big headache.

Yes, that is what is happening for me as well. I just tested it onmy test site in test mode on Stripe and I was able to reproduce that.

UPDATE: I’m still in dialogue with Christian on this issue.

The fix Cristian provided in the zipfile above adds support for failed message retry protection which alleviates our problem in as a benign side-effect but only if the final successful payment occurs within 24 hours of the failed payments. If the failed payment subsequently succeeds more than 48 hours later then the aggregated payment bug will still occur (which is the case that occured for me…the bank must have ben having a slow day and failed the 2 payment attempts then the Stripe retry mechanism kicked 2 days later and the payment succeeded).

This bug appears to be associated with new Stripe logic introduced to address some new requirements from VISA w.r.t. trials. Basically, Stripe now considers a subscription created even if the payment fails and considers all subsequent requests to be amendments with additional payments EVEN IF THEY ARE FAILED PAYMENTS (Say WHAT! I’m not sure what the Stripe gang were thinking when they decided that logic gem). We’re still working to determine the appropriate adjustment. It may require a change to the s2member payment retry logic. Not sure. We’ll update you when we sort that out.

1 Like

@onepresstech Thank you so much for the update and your ongoing attention and effort on fixing this bug!

No worries Alan. This is taking longer to fix than planned. I had a client who had a COVID-related sale that generated a lot of traffic and managed to trip three stripe-related payment issues. The sale is over so now I can put some more time tracking these three bugs down. Summarising:

Bug1 (as stated previously): If a subscription payment fails, S2member / Stripe combo is not clearing the invoice and subsequent payment retries are aggregating the invoiced amount so when the payment finally succeeds the charge amount is a multiple of the true amount…sometimes 3-4 times the value. To fix this requires a refund to the client which looks bad and incurs Stripe fee. The code issue here is likely related to new Stripe logic for VISA trials and SCA. Solution likely involves an adjustment to the S2 payment retry logic.

Bug2: Duplicate payments 1 minute apart for the same transaction (i.e. a single subscription in the name of the user even though two payments are successfully processed). This is likely a JavaScript bug involving the multi-transaction lockout logic AND a problem with the way transaction IDs are being handled (There is no logical reason for Stripe to endup processing 2 transactions for identical subscriptions with minor ID variations and ending up with a single subscription in the user’s name)

Bug3: I had a S2member L2 transaction occur from a user (user1) and then a L3 transaction from a different user (user2) but the user profiles in WordPress were tagged with a role that was mismatched…user1 had an L3 role and user2 had a L2 role. That one’s going to be tough to reproduce / track-down. Oh well…no one every said being a coder was easy :slight_smile:

Here’s a new fix for the paid trial. The previous fix worked, but I’m afraid it may not fully take care of it under all situations. I think this new one would be more reliable.

I’m attaching the file as before, for you to test if you want before the release. 200422a-stripe-utilities.inc.zip (11.4 KB)

I look forward to your feedback. :slight_smile:

Cheers @clavaque - did you end up having to modify the payment retry logic?

No. s2Member doesn’t touch the payment retry, that’s done by Stripe. That wasn’t touched at all by this.

Cool. I’ll download it and review the change and let you know if I see anything.

The fix is a good idea but like the other fix it doesn’t fully solve the problem.

The flaw is based on a fundamental change in how Stripe handles subscriptions as of release 2019-03-14

[Major] Creating a subscription succeeds even when the first payment fails. The subscription will be created in an incomplete status, where it will remain for up to 23 hours. During that time period, it can be moved into an active state by paying the first invoice. If no successful payment is made, the subscription will move into a final incomplete_expired state. Updates to a non-incomplete subscription that require a payment will also succeed regardless of the payment status. Prior to this version, all creations or updates would fail if the corresponding payment failed.

See https://stripe.com/docs/billing/subscriptions/overview#incomplete

If you delete the subscription object in s2member on exception (which is the fix you propose), Stripe still thinks this is an active subscription request and I am not convinced that subsequent attempts by the user won’t just get re-absorbed by the pending Stripe subscription. We have an unrelated second bug that looks like a JavaScript error whereby two sequential requests with the same information and different IDs is getting translated by Stripe into two payments and one combined subscription at the single rate!

I’ll read through the Stripe logic and the s2member logic further to deduce how the logic needs to be adjusted to deal with this new Stripe subscription reality.

UPDATE:

The best “quick fix” appears to be to add payment_behavior=error_if_incomplete `` flag on all API calls that create and update subscriptions. This would restore Stripe behavior to a legacy s2member-stripe-logic compliant form. CAVEAT: This obviates mandatory EEA support for SCA but the current s2member-stripe-logic is not SCA compliant anyway. So once this fix is put in place to get everyone’s Stripe working correctly again, we can table an enhancement for a subsequent release for SCA support.

What I’m fixing with that, is avoiding the duplicate charge for a paid trial because of accumulated invoice items after failed subscription attempts.

I’m removing the invoice item if the subscription was not created, which means that despite a failed payment, the subscription was not created.

So the possible scenario you describe, doesn’t seem to be the case or a problem here.

:slight_smile:

Please re-read my detailed description of the problem and solution above.

Summary:

  • Stripe subscription remains active on failure unless payment_behavior=error_if_incomplete `` flag is added on all API calls that create and update subscriptions to restore operations to be compliant with current s2 logic until SCA-compliant loigc is added to S2.
  • S2 / Stripe has a Javascript bug that allows two payments to be made with independent IDs yet somehow manage to end up with only one subscription. So if you delete the s2 object on subscription failure subseuqnet retry on the S2 side may get attached to that pending subscription…because that is what the SCA is all about…a failed subscription that is subsequently corrected.
  • SCA requires new UI back to the user to prompt them for manual authorisation…that is new logic for S2 is it not?

Have a read of https://stripe.com/docs/strong-customer-authentication/migration and https://stripe.com/docs/strong-customer-authentication and https://stripe.com/au/guides/strong-customer-authentication. This looks to me like it is going to require adjustments on the s2member side to comply. And it’s not a future issue…SCA legislation passed last Sept.

S2member was designed assuming that a failed Stripe subscription failed if the initial payment failed. Now the Stripe logic is that the transaction remains pending and associates subsequent payment requests to the pending subscription. That’s the bug we have.

So glad that powerful minds are working on this. I’ve just been threatened with legal action by a member who was still being charged when I thought I’d cancelled their trial subscription. It turns out that two subscriptions were made for the same s2_plan_ identification. I’ve calmed the person down, but I’d have been angry too, if I was them and being charged twice!

I hope this will get sorted out super quickly as it’s causing major on-going problems and is losing us reputation and actual money and actual members.

Any updates to this. It would also be good if there was a way to block duplicate payments - e.g. payment for the same amount within XX minutes. That also happens sometimes and since April stripe does not refund the fixed payment fee anymore in Europe just like paypal - so it’s more annoying than before.

I can’t speak for @clavaque but I got caught up with client needs and EOY tax filing. I won’t get a chance to look at this again for 2 weeks.

Undoubtedly related, I’m seeing payments in Stripe which are status “Retrying”, and have tried over multiple days (eg. 24 apr, 27 apr, 2 may) and each time they’re saying “failed” but the person is still an active member on the site.

Thanks for the update Tim. All the issues relate to a failed payment attempt. If the user puts in their card info properly and the card is valid then the transactions work fine. If the person puts in their details incorrectly or the card is not valid then we see the problems.

1 Like

@clavaque Are there any plans to make a new version of stripe-utilities.inc based on onepresstech’s advice, or do you believe that the [200422a-stripe-utilities.inc.zip] file will solve the problem? Thank you for working on this issue!