Disposable cards breaks checkout process

Created on 30 March 2025, 11 days ago

Using disposable cards like cards issued by Revolut breaks the checkout process.
The issue affects both payment and card elements.
The issue can be traced to these lines which is responsible for updating the billing details of stripe payment method , needed for future use :
Stripe Payment element
https://git.drupalcode.org/project/commerce_stripe/-/blob/8.x-1.2/src/Pl...
PaymentMethod::update($stripe_payment_method->id, ['billing_details' => $payment_method_data]);
Stripe card element
https://git.drupalcode.org/project/commerce_stripe/-/blob/8.x-1.2/src/Pl...
PaymentMethod::update($stripe_payment_method_id, ['billing_details' => $payment_method_data]);
the issue can be reproduced by using a Revolut (maybe other card providers) disposable cards that are valid for only one transaction

on using Stripe payment element with future use enabled it raises WSOD and the card is charged
on Stripe card element , it raises an error that payment can't be processed and charges the card
both cases the order stays in draft

🐛 Bug report
Status

Active

Version

1.2

Component

Stripe Checkout

Created by

🇪🇬Egypt Ahmed Eldesoky

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @Ahmed Eldesoky
  • 🇪🇬Egypt Ahmed Eldesoky

    This patch catches the error , logs it , and sets the payment method to non reusable

  • 🇬🇧United Kingdom jonathanshaw Stroud, UK

    What you say makes sense to me.

    But there might be a more precise solution.

    The root problem is likely:

           $is_reusable = $this->isReusable() && $payment_method_type->isReusable();
            ...
            if ($is_reusable) {
              // We cannot attach if the payment is not reusable.
              $stripe_payment_method->attach(['customer' => $customer_id]);
            }
    

    There is one additional source of information present on the payment intent at this point that we might be able to use:
    payment_method_options.card.setup_future_usage

    Use none if you do not intend to reuse this payment method and want to override the top-level setup_future_usage value for this payment method.

    Maybe we can check this property of the payment method options and add it to the logic deciding whether or not the payment method is reusable. If we do this, I think we need to add an ::isResuablePaymentMethod(\Stripe\PaymentMethod $method) method to the gateway plugin, I'm seeing other places where this logic needs to be refined.

    Could you check what the value of this property is on one of your Revolut card payment intents that fail?

  • 🇪🇬Egypt Ahmed Eldesoky

    $payment_method_type->isReusable() returns true

    $intent->payment_method_options->card->setup_future_usage returns NULL

    I guess that stripe might not know that the card is disposable
    Maybe we can check that the card is still valid ?

  • 🇬🇧United Kingdom jonathanshaw Stroud, UK

    I guess that stripe might not know that the card is disposable

    They must know, or otherwise why do they prevent it being attached to a customer?

    (1) If you were able to provide a complete [redacted] payment intent object JSON here that might be useful.

    (2) Also, it's be good to know what the exception was that was thrown, what it's message was, and what stripes cloud logs show.

    (3) Stripe's test docs say that pm_card_visa_chargeDeclined can't be attached to a customer. So you could try creating a test customer and attaching that and seeing what error you get.

Production build 0.71.5 2024