The issue is you are trying to append the payment method to the form then submit it in the else but the redirect to the return_url happens before that and adds the setup intent id as a query param. After submitting the form confirmSetup calls stripe to check the card is valid then if no error redirects you immediately to the return_url with the setup intent id added to the params. So the return url should go to a route to a controller method something like
$stripe = Cashier::stripe();
$setupIntent = $stripe->setupIntents->retrieve(
$request->setup_intent,
['expand' => ['payment_method']]
);
$paymentMethod = $setupIntent->payment_method;
if ($paymentMethod) {
$this->User->addPaymentMethod($paymentMethod->id);
$this->User->updateDefaultPaymentMethod($paymentMethod->id);
}
You handleSubmit should be like
async function handleSubmit(e) {
e.preventDefault();
const { error } = await stripe.confirmSetup({
elements,
confirmParams: {
return_url: "{{ url('/handle-payment') }}" //Should redirect to a controller method that handles the card attachment server side
},
redirect: 'if_required'
});
if (error) {
// do error stuff here...
} else {
// If confirmSetup is successful you should never get here, you should have been redirec ted to your return url and server side should use the $request->setup_intent to retrieve the setup intent from stripe and get the payment method from it
}
}