Fix for 3D-Secure payments on cart page checkout#49
Conversation
When the 3DSecure authorization fails the received error is a complex object, not a simple string. We need to access the message property in order to show a meaningful text to the customer.
Customers needs to choose the shipping address again, once the payment request process has failed for any reason. Failing this, issues in the backend when calculating shipping rates may arise.
The service object was renamed as its main responsibility is to create the Solidus payment starting from a Stripe payment intent.
kennyadsl
left a comment
There was a problem hiding this comment.
Thanks! Have you already thought about which version release we need to do after merging this?
|
@kennyadsl I think this needs a major release, as there are many breaking changes, both in the FE and the BE. The payment flow for the cart checkout was radically changed, these changes leaked on the Payment Intents flow as well (ie. we're now using "automatic" authorization in the FE instead of "manual" auth in the BE) and some routes changed as well. |
aldesantis
left a comment
There was a problem hiding this comment.
Amazing work @spaghetticode, thanks so much.
I have left a few comments on the implementation details, especially for the Ruby part.
I was wondering if it would also be possible to add a live integration test to confirm everything is working properly, given how complex the flow is? I don't know if it's doable, but it would be awesome.
To be honest, I don't feel like I have properly understood the entire data flow, so I'll hold off on an explicit approval until someone else can also chime in on this. I don't know if @kennyadsl has a better understanding of Payment Intents? If not, we'll just have to trust you. 🙂
|
@aldesantis testing the payment request button flow would add much value, but this is not testable unless going through much setup pain (this API works only on https and the server must be accessible externally from Stripe) and I'm not even sure that capybara is able to interact with the browser payment request popup (for lack of a better name). Also, an important part of the test would be to verify the outcomes on the stripe backend account, and that again would add much more complexity and brittleness to the tests. Luckily the Payment Intents process on the payment page is already well tested, considering also some edge cases, so the weakness is confined to the payment request button only. |
So we don't need to explicitly pass `nil` when there's no user (guest checkout, cart page checkout).
The service needs to accept a payment intent id and load the corresponding payment intent via ActiveMerchant.
We need to add this method in order to retrieve a payment intent starting from an ID.
This action needs to be extracted in order to be called when creating a Solidus Payment by the cart page checkout process.
According to Stripe support this is the only correct way to authorize 3D-Secure cards when using the payment request API. The `confirm` action has been simplified and it now only creates a new payment intent. This is only the backend part of the change, JS code will need a more invasive rework.
We need to create the Solidus payment later in the flow, so the JSON returned by IntentsController#generate_payment_response needs to be changed and include the payment intent ID.
`payment` seems to be more descriptive than a generic `result`.
This is needed in order to properly manage possible 3-DSecure payments
This will be used to create a payment on Solidus backend.
This change ensures the payment intent is confirmed via JS instead of using a Solidus action. This allows to properly manage 3D-Secure card payments in the cart page checkout.
When this method is used, `this` is now the window object.
This is the iframe name when confirming 3DS cards via JS.
A few changes are required in order to properly manage the cart checkout flow in this service object. Cart checkout page sends the address attributes as a JSON that we need to manipulate before Solidus can use it to create an address record. Also, the customer full name needs to be extracted differently when checking out from the cart page.
When for some reason the Solidus payment cannot be successfully created, the service object returns `false`. This return value will be used by the controller in order to properly respond to the JS request.
When for any reason the Solidus payment cannot be created, we need to notify the JS code that something went wrong, so the error message can be shown to the customer. When the payment cannot be created, we also need to cancel the payment intent on Stripe, or it will remain pending and this will result in blocking some funds on the customer's card.
The renaming makes sense since this action now only creates a payment intents. Also, routes were updated both in the Rails router and in the JS code.
7e487c1 to
bc1d775
Compare
|
@spaghetticode understood. I think we can merge this one, it looks like all the tradeoffs that have been made were more than justified. Again, great work! 🎉 |
The cart page checkout did not work properly when paying with a 3D-Secure credit card.
This PR fixes the cart checkout payment flow by making sure that a proper payment intent is created and authorized. This requires also to manually create the corresponding Solidus payment
record, as it happens already in the regular Payment Intents flow.
The payment intent is now confirmed automatically (ie. in the browser) so there's no more need to confirm it via Ruby in the Intents controller. According to Stripe customer support, this change is required in order to be able to properly manage the 3D-Secure iframe popup after the payment request data gathering process is completed in the browser.
All these changes resulted in a significant rework of the JS code for the cart checkout flow and the shared code between the cart checkout and the regular Payment Intent flow.