Problem/Motivation
When a user registers after anonymous checkout, the payment method they used is then attached to their new account. However, guest checkout through Stripe does not allow payment methods to be attached (they must be associated with a Customer). Attempting to reuse this payment method will fail.
Steps to reproduce
- Place an order anonymously. Use an email that is not associated with an existing user so that the Registration after checkout pane appears
- Register a new user AFTER checkout using the checkout pane that is shown on the Complete page
- That order should now be in your history. The payment method you used should now also be saved under your new account.
- Start another cart. Attempt to checkout.
- Upon selecting your existing payment method and attempting to go to the Review page, you will see a WSOD. The error in watchdog says "The provided PaymentMethod was previously used with a PaymentIntent without Customer attachment, shared with a connected account without Customer attachment, or was detached from a Customer. It may not be used again. To use a PaymentMethod multiple times, you must attach it to a Customer first."
Proposed resolution
Mark the payment method as un-reusable if registering after checkout. I'm not sure if there is a way to retroactively associate the payment method with a customer after the transaction has taken place but this could be ideal so that the payment method is able to be reused without having to input it again.
Remaining tasks
tbd
User interface changes
tbd
API changes
tbd
Data model changes
tbd
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | 3266382-checkout-register-17.patch | 5.49 KB | alex.bukach |
Issue fork commerce_stripe-3266382
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
cchoe1 commentedComment #3
cchoe1 commentedComment #4
alex.bukach commentedComment #5
alex.bukach commentedComment #6
alex.bukach commentedCorrected files paths.
Comment #7
alex.bukach commentedWe should also update the existing payment methods (mark relevant ones as non-reusable).
Comment #8
johnpitcairn commentedThere is a possible alternative, not retroactive but proactive. Using the
PaymentMethodCreateEventfrom #3191993: Add event to allow customising payment method and customer creation allows a user to be created early in the process, before the customer and payment method are registered at Stripe. That user can be set as the payment method owner.We are using this to provide the appearance of a fully anonymous checkout (no post-checkout registration), with re-usable payment methods if required. We create the user with a "pending" role to allow us to clean up if we get a lot of old uncompleted transactions/users. I can provide the code we are using for the event subscriber if anyone wants it. The whole thing could be provided by a separate module, with a cron cleanup to remove "pending" users older than a given threshold (we haven't needed that yet).
You'd still have to handle getting the user to "activate" their account, either via sending an email or by logging them in and asking them to reset their password or such. So it's not a solution for this issue, it's an alternative.
Comment #11
gcbI've submitted an MR with a solution to this issue, which makes sure customer ID's are associated with payment methods that we are saving, then retains them and assigns them to the User that gets created by Commerce.
I'm attaching a patch of the current MR for convenience.
Comment #13
gcbDiscovered an issue with the latest solution, which is that a payment intent can't use a payment with a different customer ID. You can see this issue emerge if you select saved payment method associated with a different customer ID (one using the field added in this patch), then go back to change payment to a different or new payment method.
This is feeling like a generally bad approach to solving this problem. Instead, we should probably be smart about associating the new user with the customer record in Stripe.
However, here's a workaround which refreshes the payment intent.
Comment #14
gcbUpdated patch & PR to prevent overwriting of a user's existing customer ID. This commit seems relevant beyond this issue's specific problems, as it's certainly possible for a user to have a customer ID, then make an anonymous purchase using their email address, which will then break their existing saved cards without a fix like this.
Comment #18
alex.bukach commentedRe-rolled patch #7 against the head of 8.x.1-x and created MR!101.
Comment #19
alex.bukach commentedComment #20
alex.bukach commentedComment #24
tomtech commentedAn enhancement has been added to assign the payment method when the customer has orders assigned to them. (Note: this also handles other scenarios, besides the "Register after Checkout" pane, such as checkout flow with create account for guest user or checkout flow with sign to existing user options enabled.)
This allows the payment method to properly be reusable.
Some notes:
1. This works for the Stripe Payment Element. If needed for Card Element, please create a separate issue to port similar logic, as it would require some refactoring in the Stripe.php class.
2. If backfill is needed, the database update in this thread might work on smaller sites, but not for larger sites with 100k/millions of records. A more targeted and performant query would be needed, along with the ability to defer/skip this revision, to be performed offline. A drush command might be a better option. That should also be a separate issue.
Comment #25
tomtech commentedComment #27
gcbReroll of my previous patch against the latest version. However, I don't like this solution. Saving a card for an anonymous user is not desirable.