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

  1. Place an order anonymously. Use an email that is not associated with an existing user so that the Registration after checkout pane appears
  2. Register a new user AFTER checkout using the checkout pane that is shown on the Complete page
  3. That order should now be in your history. The payment method you used should now also be saved under your new account.
  4. Start another cart. Attempt to checkout.
  5. 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

Command icon 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

cchoe1 created an issue. See original summary.

cchoe1’s picture

Issue summary: View changes
cchoe1’s picture

StatusFileSize
new1.93 KB
alex.bukach’s picture

Version: 8.x-1.0-rc6 » 8.x-1.x-dev
Status: Active » Needs review
alex.bukach’s picture

Version: 8.x-1.x-dev » 8.x-1.0-rc6
alex.bukach’s picture

Version: 8.x-1.0-rc6 » 8.x-1.x-dev
StatusFileSize
new1.71 KB

Corrected files paths.

alex.bukach’s picture

StatusFileSize
new5.44 KB

We should also update the existing payment methods (mark relevant ones as non-reusable).

johnpitcairn’s picture

There is a possible alternative, not retroactive but proactive. Using the PaymentMethodCreateEvent from #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.

gcb made their first commit to this issue’s fork.

gcb’s picture

I'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.

Status: Needs review » Needs work

The last submitted patch, 11: 3266382-anonymous-checkout-customer-11.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

gcb’s picture

Discovered 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.

gcb’s picture

StatusFileSize
new16.34 KB

Updated 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.

Alex Bukach changed the visibility of the branch 3266382-register-after-checkout to hidden.

Alex Bukach changed the visibility of the branch 3266382-register-after-checkout to active.

alex.bukach’s picture

Re-rolled patch #7 against the head of 8.x.1-x and created MR!101.

alex.bukach’s picture

Status: Needs work » Needs review
alex.bukach’s picture

StatusFileSize
new5.49 KB

TomTech made their first commit to this issue’s fork.

  • TomTech committed 2f7bf090 on 8.x-1.x
    Issue #3266382 by gcb, Alex Bukach, cchoe1, TomTech, John Pitcairn: "...
tomtech’s picture

Assigned: Unassigned » tomtech
Status: Needs review » Fixed

An 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.

tomtech’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

gcb’s picture

StatusFileSize
new7.78 KB

Reroll 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.