Reputation: 3573
I am creating a new app using Cashier 10.1. In the past I would send a token from Stripe to the subscription function when registering a user with a subscription. Now it says it takes a Payment Method (id). I am registering a payment method with Stripe and passing that to my controller but it returns the error "This customer has no attached payment source". I read the docs but it only gives an example of how to add a subscription to a current user by passing to the view $user->createSetupIntent()
. Below is the code for when a user registers:
Component
pay() {
this.isLoading = true;
if (this.form.payment_method == "cc") {
let that = this;
this.stripe
.createPaymentMethod({
type: "card",
card: that.card
})
.then(result => {
this.form.stripePayment = result.paymentMethod.id;
this.register();
})
.catch(e => {
console.log(e);
});
} else {
this.register();
}
},
register() {
this.form
.post("/register")
.then(data => {
this.isLoading = false;
if (data.type == "success") {
this.$swal({
type: "success",
title: "Great...",
text: data.message,
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 2000
});
setTimeout(() => {
// window.location.replace(data.url);
}, 2000);
}
})
.catch(error => {
this.isLoading = false;
});
}
RegisterController
protected function create(request $request)
{
$request->validate([
'name' => 'required',
'email' => 'required|unique:users',
'username' => 'required|alpha_dash|unique:users',
'phone' => 'required',
'city' => 'required',
'state' => 'required',
'password' => 'required|confirmed',
'agree' => 'required',
]);
$user = User::create([
'username' => $request['username'],
'name' => $request['name'],
'email' => $request['email'],
'phone' => $request['phone'],
'city' => $request['city'],
'state' => $request['state'],
'password' => Hash::make($request['password']),
]);
if ($request['subscription_type'] == 'premier' && $request['payment_method'] == 'cc') {
$user->newSubscription('default', env('STRIPE_PLAN_ID'))->create($request->input('stripePayment'), [
'email' => $request['email'],
]);
}
if ($user) {
$user->assignRole('subscriber');
Auth::login($user);
return response()->json([
'type' => 'success',
'message' => 'you are all set.',
'url' => '/dashboard'
]);
}
Upvotes: 1
Views: 965
Reputation: 3573
I found a good article explaining the new setup finally. Basically when I show my registration view I now create a new User and pass the intent there. I kept thinking the user had to be saved already, not just created. So if anyone else wants to do it:
Show the view
Registration Controller
public function show()
{
$user = new User;
return view('auth.register', [
'intent' => $user->createSetupIntent()
]);
}
Pass the intent to my Vue component
<register-form stripe-key="{{ env('STRIPE_KEY') }}" stripe-intent="{{ $intent->client_secret }}">
</register-form>
Add stripe elements to a div:
mounted() {
// Create a Stripe client.
this.stripe = Stripe(this.stripeKey);
// Create an instance of Elements.
var elements = this.stripe.elements();
var style = {
base: {
color: "#32325d",
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#aab7c4"
}
},
invalid: {
color: "#fa755a",
iconColor: "#fa755a"
}
};
// Create an instance of the card Element.
this.card = elements.create("card", { style: style });
// Add an instance of the card Element into the `card-element` <div>.
this.card.mount("#card-element");
},
process the data
pay() {
this.isLoading = true;
if (this.form.payment_method == "cc") {
this.setupCard();
} else {
this.register();
}
},
setupCard() {
this.stripe
.handleCardSetup(this.stripeIntent, this.card, {
payment_method_data: {
billing_details: { name: this.form.name }
}
})
.then(data => {
this.form.stripePayment = data.setupIntent.payment_method;
if (this.form.stripePayment) this.register();
})
.catch(error => {
this.isLoading = false;
console.log(error);
});
},
register(setupIntent) {
this.form
.post("/register")
.then(data => {
this.isLoading = false;
if (data.type == "success") {
this.$swal({
type: "success",
title: "Great...",
text: data.message,
toast: true,
position: "top-end",
showConfirmButton: false,
timer: 2000
});
setTimeout(() => {
window.location.replace(data.url);
}, 2000);
}
})
.catch(error => {
this.isLoading = false;
});
}
Save the user
Registration Controller
protected function create(request $request)
{
$request->validate([
'name' => 'required',
'email' => 'required|unique:users',
'username' => 'required|alpha_dash|unique:users',
'phone' => 'required',
'city' => 'required',
'state' => 'required',
'password' => 'required|confirmed',
'agree' => 'required',
]);
$user = User::create([
'username' => $request['username'],
'name' => $request['name'],
'email' => $request['email'],
'phone' => $request['phone'],
'city' => $request['city'],
'state' => $request['state'],
'password' => Hash::make($request['password']),
]);
if ($request['subscription_type'] == 'premier' && $request['payment_method'] == 'cc') {
$user->newSubscription('default', env('STRIPE_PLAN_ID'))->create($request->input('stripePayment'), [
'email' => $request['email'],
]);
}
if ($user) {
$user->assignRole('subscriber');
Auth::login($user);
return response()->json([
'type' => 'success',
'message' => 'you are all set.',
'url' => '/dashboard'
]);
}
}
Upvotes: 2
Reputation: 92
Cashier has updated the way it interacts with Stripe due to Strong Customer Authentication (SCA). This means card payments require a different user experience, namely 3D Secure, in order to meet SCA requirements. It may be beneficial to read-up on the Payment Intents API on Stripe and you may be able to work around this by first creating the payment intent with a direct interaction with Stripe and then attaching it to your Laravel user.
The simple solution may be to have a multi step registration process:
$user->createSetupIntent()
and collect payment details and save to customer.Upvotes: 0