2

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',
   
          ]
      ]);
Adam
  • 85
  • 2
  • 12

2 Answers2

0

Looks like there is something wrong with how you're using axios. Have you tried taking a look at laravel simple axios with argument

karbi
  • 1,770
  • 5
  • 8
  • thank you, I just tried that but it's not making any post request only get request – Adam Nov 16 '20 at 22:26
  • Hmm, what is the line `$user->newSubscription('premium', $planId)->create($paymentMethod);` doing? – karbi Nov 17 '20 at 00:50
  • creating a new subscription with the auth user for the product premium with $planId which is the price plan for the product it can yearly or monthly depending on the selected plan from the form, the selected plan was passed in the request successfully is just the payment_plan that is not coming from the form, I can access it from the console.log but is not passed to the Paymentcontroller. and in order to create a new subscription, u must provide the payment method generated from the stripe API. – Adam Nov 17 '20 at 06:49
  • Gotcha - updated my answer since what I initially said was wrong. I haven't used axios, but I think the answer I linked to should get you on the right track – karbi Nov 17 '20 at 18:08
  • Thank You for your help I really appreciate it – Adam Nov 17 '20 at 22:37
0

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();

          
                    }
Adam
  • 85
  • 2
  • 12