Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ability to perform refunds after a first failed attempt #3831

Merged
merged 2 commits into from
Nov 12, 2020

Conversation

kennyadsl
Copy link
Member

@kennyadsl kennyadsl commented Nov 5, 2020

Description

With #1415 we introduced a bug in the Admin:

When attempting to create a refund for a payment that fails with a gateway error, it's no more possible to create refunds for that payment.

To reproduce:

  1. Click the refund link on any payment

Screenshot 2020-11-05 at 15 41 27

  1. Refund credit allowed is correct, let's proceed with the refund using a gateway that will fail

Screenshot 2020-11-05 at 15 41 38

At this point, we have the flash message correctly printed with the error:

Screenshot 2020-11-05 at 15 42 02

  1. A refund is actually created and we can't create new refunds:

Screenshot 2020-11-05 at 15 42 16

This is due to the split of record creation and calling perform! in Spree::Admin::RefundsController#create. In fact, when we create a refund in the first place, it is added to the database but when we call perform! right after the creation, and it fails, the record created is not marked as failed anyhow. The system assumes all refunds are successful and consider them done if when they failed, disabling us to create new ones for those payments.

In fact, the refundable amount is calculated with

payment.credit_allowed = payment total - refunds amount

and this formula is true also when the refund has not been successful.

This comes from our past behavior of creating and calling perform! on refunds at the same time. Records weren't created if the refund failed to perform.

A more clean solution would be considering in that formula only refunds that are completed but at the moment there's no way to do that. This way we can also keep track of the failed attempts to perform refunds, saving all the information we get back from the gateway. But this is way more invasive and we can't do this in a patch level fix.

This change is supposed to be merged in master (for 3.0) and backported to v2.11, which is the version that introduces the bug.

Checklist:

  • I have followed Pull Request guidelines
  • I have added a detailed description into each commit message
  • I have updated Guides and README accordingly to this change (if needed)
  • I have added tests to cover this change (if needed)
  • I have attached screenshots to this PR for visual changes (if needed)

The first line of perform! avoid the method to be executed under
certain circumstances (which are actually happening in the spec
context). Due to this, we are not testing the current behavior
since perform! should be skipped and in the spec it's raising
an exception.

To fix this, it's better to stub process!, which is where the
simulated exception can happen. We now have a failing spec but
it's reflecting what happens in the real world.
@kennyadsl kennyadsl added this to the 3.0.0 milestone Nov 5, 2020
@kennyadsl kennyadsl self-assigned this Nov 5, 2020
@kennyadsl kennyadsl added the type:bug Error, flaw or fault label Nov 5, 2020
Copy link
Member

@jarednorman jarednorman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we initialize but don't save the refund before calling perform! and have perform! save the record (if it doesn't already)?

This would avoid this weird code that recreates the refund.

Regardless, I agree that tracing the success/fail state would be a better solution in the long run. Would probably be worth having three states: pending (default state), complete and failed.

@aldesantis
Copy link
Member

I would also prefer not to save the refund at all unless it's been successfully performed. Would that be possible?

@kennyadsl
Copy link
Member Author

I think it's a great idea, working on that and will let you know, thanks!

If we save unsuccessful refund on payment gateway errors
we'll end up having a refund associated to the payment without
the actual refund processed.

Since at the moment refunds have no states to indicate whether
is successful or not, the system is considering it as done and
doesn't allow to perform a new one since the credit allowed amount
for the payment becomes 0:

payment.credit_allowed = payment total - refund amount

and this formula is true also when the refund has not been succesful.

This comes from our past behavior of creating and calling perform! on
refunds at the same time.

By just validating the refund we have the same result. The
"perform_after_create: false" attribute setting has been
moved into the perform! itself. When called on a record still
unpersisted, it will set its value to false to avoid deprecation
warnings and well.. to call perform! again!

A more clean solution would be considering in that formula only
refunds that are completed but at the moment there's no way to
do that. This way we could also keep track of the failed attempts
to perform refunds, saving all the information we get back from the
gateway.
@kennyadsl kennyadsl force-pushed the kennyadsl/refund-perform-fix branch from 70dcce9 to ea7cbe0 Compare November 6, 2020 17:34
@kennyadsl
Copy link
Member Author

@aldesantis @jarednorman please review again when you have time, and thanks again for pointing in the right direction. The first attempt was so bad!

@kennyadsl kennyadsl requested a review from jarednorman November 9, 2020 11:01
@kennyadsl kennyadsl added the changelog:solidus_backend Changes to the solidus_backend gem label Nov 9, 2020
Copy link
Member

@spaghetticode spaghetticode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kennyadsl makes sense to me, thanks 👍

Copy link
Member

@aldesantis aldesantis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, thanks @kennyadsl!

@kennyadsl
Copy link
Member Author

@jarednorman will wait for your review before merging if you have some time. Please let me know!

@kennyadsl kennyadsl merged commit 3c8ffcc into solidusio:master Nov 12, 2020
@kennyadsl kennyadsl deleted the kennyadsl/refund-perform-fix branch November 12, 2020 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog:solidus_backend Changes to the solidus_backend gem type:bug Error, flaw or fault
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants