Stripe Payment Element: Restrict to “Card Only” and Show Saved Payment Methods (Undocumented Edge Case)

stripe-payment-element:-restrict-to-“card-only”-and-show-saved-payment-methods-(undocumented-edge-case)

Problem:
Stripe’s Payment Element allows multiple payment types and shows a Saved tab for logged-in users with saved payment methods.
But if you want to restrict the Payment Element to “card” only (no ACH, no Link, etc.) and show the user’s saved cards, Stripe doesn’t officially document how to do it.

The issue:

  • Using a SetupIntent with payment_method_types: ['card'] restricts to card, but the Saved tab won’t appear.
  • Using a CustomerSession enables the Saved tab, but it shows all your enabled payment methods, not just cards.

The Solution (SetupIntent + CustomerSession Hack)

  1. Create a SetupIntent for your customer with payment_method_types: ['card'].
  2. Also create a CustomerSession for that customer.
  3. Initialize the Payment Element with both the SetupIntent’s clientSecret and the CustomerSession’s customerSessionClientSecret.

Result:

  • Only “Card” is available for new payment methods.
  • The Saved tab appears with any saved cards.

Image description

Laravel Example

Route (web.php):

Route::get('/stripe-element-test', function () {
    Stripe::setApiKey(config('services.stripe.secret'));
    Stripe::setApiVersion('2024-06-20');

    $user             = auth()->user();
    $isLoggedIn       = !is_null($user);
    $stripeCustomerId = ($isLoggedIn && $user->stripe_customer_id) ? $user->stripe_customer_id : null;

    // Create SetupIntent for 'card' only
    $setupIntentParams = [
        'usage'                  => 'off_session',
        'payment_method_types'   => ['card'],
        'payment_method_options' => [
            'card' => ['request_three_d_secure' => 'automatic'],
        ],
    ];

    // Attach customer only if available
    if ($stripeCustomerId) {
        $setupIntentParams['customer'] = $stripeCustomerId;
    }

    $setupIntent = SetupIntent::create($setupIntentParams);

    $customerSessionClientSecret = null;
    if ($stripeCustomerId) {
        $customerSession = CustomerSession::create([
            'customer'   => $stripeCustomerId,
            'components' => [
                'payment_element' => [
                    'enabled'  => true,
                    'features' => [
                        'payment_method_redisplay'  => 'enabled',
                        'payment_method_save'       => 'enabled',
                        'payment_method_save_usage' => 'off_session',
                        'payment_method_remove'     => 'disabled',
                    ],
                ],
            ],
        ]);
        $customerSessionClientSecret = $customerSession->client_secret;
    }

    return View::make('stripe-test', [
        'stripePublishableKey'        => config('services.stripe.key'),
        'setupIntentClientSecret'     => $setupIntent->client_secret,
        'customerSessionClientSecret' => $customerSessionClientSecret, // null for guest
    ]);
});

View (resources/views/stripe-test.blade.php):


 lang="en">

     charset="UTF-8">
    </span>Stripe Payment Element Test – Card Only w/ Saved Methods<span class="nt">
    
    


Stripe Payment Element Test
(Card Only, Shows Saved Cards if logged in)

id="payment-form"> id="payment-element">
id="submit-button" type="submit">Confirm Card id="error-message">
id="success-message">

Bonus:
This works for ACH or any payment method you might need to isolate in certain scenarios. IE, If you use ['us_bank_account'] for payment_method_types, users will see and be able to select their saved bank accounts.

Summary:
No official Stripe docs for this, but combining a SetupIntent (to restrict methods) and a CustomerSession (to show saved methods) gets you a Payment Element limited to card only, with the Saved tab.
Use at your own risk—Stripe could change this behavior, but it works today.

Hope someone finds this useful.
Cheers!

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
is-react-beginner-friendly?

Is React beginner-friendly?

Next Post
-replacing-the-base-url-of-a-path-in-javascript

🔄 Replacing the Base URL of a Path in JavaScript

Related Posts