Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Adams_'s avatar

This customer has no attached payment source or default payment method.

when I try to create a new subscription I get this error (This customer has no attached payment source or default payment method. ) so I checked the PaymentController with dd($paymentMethod) which returned null

so I don't know why the variable $paymentMethod in store method is returning NULL from the $request but the request, for the price is returning the price_id. Please any help is appreciated

but when console.log() setupIntent.payment_method it returned the payment_method in the console

here is my PaymentController

         public function index()
{
    $availablePlans = [
        'price_1HnIiLLzAo4pwMcyh2aGaznB'  => 'Monthly',
        'price_1HnJ2vLzAo4pwMcygQT66juk'  => 'Yearly',
        'price_1HnIhILzAo4pwMcy9iH3j30L'  => 'Free Membership'
    ];

    $user = auth()->user();
    $data = [
        'intent' => $user->createSetupIntent(),
        'plans' =>  $availablePlans

    ];
    
    return view('payments.checkout')->with($data);
}
     

       public function store(Request $request)
    {

  


             $user = auth()->user();

              $paymentMethod = $request->payment_method;
             //  dd($paymentMethod);
    
    
             $planId = $request->plan;

             $user->newSubscription('premium', $planId)->create($paymentMethod);

             return response(['status' => 'success']);
       }

This is the Javascript

         window.addEventListener('load', function (){


      // Create a Stripe client.
        const stripe = Stripe('pk_test_51H2OqqLzAo4pwMcyT4h405wpFRAn3FWhvByfvmVnW6tabrIsDoU1dBXJ0UaWexUJeacCJ9uKpb5OBmmA2KaCg4sd00ZZ5tj2q8');

        // Create an instance of Elements.
        const elements = stripe.elements();

        // Custom styling can be passed to options when creating an Element.
        // (Note that this demo uses a wider set of styles than the guide below.)
         
         // const cardElement = elements.create('card', {style: style});

        // Create an instance of the card Element.
        const cardElement = elements.create('card');

        // Add an instance of the card Element into the `card-element` <div>.
        cardElement.mount('#card-element');

            const cardHolderName = document.getElementById('card-holder-name');
            const cardButton = document.getElementById('card-button');
            const clientSecret = cardButton.dataset.secret;

            const plan = document.getElementById('subscription-plan').value;

            cardButton.addEventListener('click', async (e) => {
                const { setupIntent, error } = await stripe.handleCardSetup(
                    clientSecret, cardElement, {
                        payment_method_data: { 
                        billing_details: { name: cardHolderName.value }
                        }
                    }
                );

                if (error) {
                    // Display "error.message" to the user...
                } else {
                    // The card has been verified successfully...
                   // console.log('handling success', setupIntent.payment_method);

                    axios.post('/subscribe', {
                      payment_method: setupIntent.payment_method,
                      plan: plan
                    })
                }
            });

         });                  

here is the form

         <form action="{{ route('subscribe')}}" method="POST" id="">
@csrf
  <div class="form-content">

      <div class="field">
        <select class="form-control" name="plan" id="subscription-plan">
          @foreach ($plans as $key=>$plan )
            <option value="{{$key}}">{{$plan}}</option>
          @endforeach
        </select>
      </div>

    <div class="field">
      <input type="text" autocorrect="off" spellcheck="false" id="card-holder-name" maxlength="25" />
      <span class="focus-bar"></span>
      <label for="cardholder">Card holder (Name on card)</label>
    </div>
        
        <div  class="field mb-5" id="card-element">
        <!-- Stripe Elements Placeholder -->
        </div>

    <button id="card-button" data-secret="{{ $intent->client_secret }}"><span>Pay</span></button>
    
  </div>
</form>

The Route

     Route::resource('payments', 'PaymentsController', [
         'names'=> [
              'index'     => 'checkout',
               'store'     => 'subscribe',
   
          ]
      ]);
0 likes
2 replies
Adams_'s avatar
Adams_
OP
Best Answer
Level 2

Adding a hidden input field in the form and setting the value to setupIntent.payment_method passed the payment_method id to the controller which is used to create the subscription so the problem is solved.

A few modifications and adding a hidden input field to the JS

        // Handle form submission.
              var form = document.getElementById('payment-form');
              form.addEventListener('submit', async (e) => {
              e.preventDefault();
            //cardButton.addEventListener('click', async (e) => {
                   //e.preventDefault()
                const { setupIntent, error } = await stripe.handleCardSetup(
                    clientSecret, cardElement, {
                        payment_method_data: { 
                        billing_details: { name: cardHolderName.value }
                        }
                    }
                );
               
                if (error) {
                    // Display "error.message" to the user...
                } else {
                    // The card has been verified successfully...
                   //console.log('handling success', setupIntent.payment_method);
                     axios.post('/subscribe',{
                     plan : plan
                })
                   var paymentMethod  = setupIntent.payment_method;

                    var form = document.getElementById('payment-form');
                    var hiddenInput = document.createElement('input');
                    hiddenInput.setAttribute('type', 'hidden');
                    hiddenInput.setAttribute('name', 'payment_method');
                    hiddenInput.setAttribute('value', paymentMethod);
                    form.appendChild(hiddenInput);

                    // Submit the form
                    form.submit();

      
                }
Top-Master's avatar

To fix the error in the title:

  • You need to first have the "payment method", if you haven't already, create one:
    • Maybe using Stripe.js and it's elements API.
    • Which nowadays has "payment" element, allowing users to choose their payment-method.
    • And supports Setup-Intent's "client_secret" (beside Payment-Intent's), submittable using confirmSetup(...) method.

Then either:

  • While creating the subscription (using Stripe API), pass the default_payment_method (with said "payment method" as value).

  • Or, attach said "payment method" to the Customer:

    • And set it as their default for invoices (with invoice_settings.default_payment_method).

See also related StackOverflow post

Please or to participate in this conversation.