How to prevent stripe from creating new user when receiving a payment? - laravel

I am starting with stripe and still in test mode.
I use Laravel 9 + cashier and vuejs
I also installed vue-stripe and created a view to test the checkout functionality.
Here is my vue
<template>
<div class="text-xl sass-editor-1 text-center">
<h1 class="text-2xl">Stripe Payment Gateway integration</h1>
<div class="container" v-if="payId">
<stripe-checkout
ref="checkoutRef"
:pk="publishableKey"
:sessionId="payId"
:customerEmail="customerEmail" />
<button class="border-2 rounded-lg bg-green-800 text-white p-3 mt-4" #click="submit">Pay Now!</button>
</div>
{{customerEmail}}
<div v-if="subscribeId" class="container">
<stripe-checkout
ref="subRef"
:pk="publishableKey"
:sessionId="subscribeId"
:customerEmail="customerEmail" />
<button class="border-2 rounded-lg bg-green-800 text-white p-3 mt-4" #click="submitSub">Subscribe Now!</button>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import { StripeCheckout , Stripe} from '#vue-stripe/vue-stripe';
import axios from "../axios"
import store from "../store"
export default {
components: {
StripeCheckout,
},
data() {
return {
publishableKey: 'pk_test_51M6ZtzI....................XgAyUVjlwG6MFos0AaqaQYJOf2YC3a6oWlZqMjFtTZj00Tue51qVs',
payId: null,
subscribeId: null,
customerEmail: store.state.user.email
}
},
methods: {
getSession() {
axios.get('api/getSession')
.then(res => {
console.log(res);
this.payId = res.data.oneTime.id;
this.subscribeId = res.data.sub.id;
})
.catch(err => {
console.log(err);
})
},
submit() {
this.$refs.checkoutRef.redirectToCheckout();
},
submitSub() {
this.$refs.subRef.redirectToCheckout();
}
},
mounted() {
this.getSession();
}
}
</script>
and here is the StripeController to return the sessionId in the backend
<?php
namespace App\Http\Controllers;
use Stripe\StripeClient;
use Illuminate\Http\Request;
use Laravel\Cashier\Cashier;
class StripeController extends Controller
{
public function getSession()
{
$stripe = new StripeClient(env('STRIPE_SECRET'));
$user=auth()->user();
$stripeCustomer = $user->createOrGetStripeCustomer(
['name'=>$user->name,'email'=>$user->email]);
$checkout = $stripe->checkout->sessions->create(
[
'success_url' => 'https://yahoo.com',
'cancel_url' => 'https://google.com',
'line_items' =>
[
[
'price_data' =>
[
'currency' => 'eur',
'unit_amount' => 500,
'product_data' =>
[
'name' => 'Example Stripe Checkout'
],
],
'quantity' => 1,
],
],
'mode' => 'payment',
]
);
$sub = $stripe->checkout->sessions->create(
[
'success_url' => 'https://denentzat.fr',
'cancel_url' => 'https://google.com',
'line_items' =>
[
[
'price'=>'price_1M84UNIWDjpHNQK1FXOj1k01',
'quantity' => 1,
],
],
'mode' => 'subscription',
]
);
return ['oneTime'=>$checkout, 'sub'=>$sub];
}
public function webhook(Request $request){
\Log::info($request->data);
return response()->json(['status'=>'success']);
}
}
The payment is done in both cases (pay or subscribe).
Nevertheless, when I go in the customer tab of the dashboard, I can see that sometimes (I could not find for what reason) stripe create a guest user, or a normal user and
eventually, there may be several users with the same email. How to prevent this?
I was hoping that in passing the user email to the form (Stripe-checkout component) this will prefill the email field but it doesn't happen.
Thank you for help.

You can pre-create a Customer with their email, then passing in the Customer Id when creating the Checkout Session. (see Existing Customer on Stripe Doc).

Related

Laravel client authentification with external Laravel passport lumen api

I have been looking online but i can't find any way of doing it. Let me explain, i have an API (Laravel passport on lumen), i tested it with Postman, i get my access token with oauth, everything is fine. Now i have another Laravel application and i want to know how i can keep all my authentification stuff using the API to login. I have seen lot of apps that actualy retrieve an api_token and they use 'Auth::user()->where('api_token', $token)'. But i find this wrong because i don't want my client to have access to the database, i want every request to the database to be handled by the API. Is that possible?
Let say you want to login to a laravel backend app via api. make sure you install guzzle.
Route(api): Route::POST('/login', 'AuthController#login')
Controller: AuthController.php
public function login(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|string',
]);
$http = new \GuzzleHttp\Client;
try {
$response = $http->post(config('services.passport.login_endpoint'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'your client_id',
'client_secret' => 'your client_secret',
'username' => $request->email,
'password' => $request->password,
// 'scope' => '',
],
]);
return $response->getBody();
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
if ($e->getCode() == 401) {
return response()->json(['message' => 'This action can\'t be perfomed at this time. Please try later.'], $e->getCode());
} else if ($e->getCode() == 400) {
return response()->json(['message' => 'These credentials do not match our records.'], $e->getCode());
}
return response()->json('Something went wrong on the server. Please try letar.', $e->getCode());
}
}
In your front-end app for example vuejs, even laravel using vue component. As you can see, i'm using boostrap-vue but feel free to use the regular html elements
<template>
<div>
<form #submit.prevent="login()">
<b-form-group label="Email">
<b-input placeholder="E-Mail" class="ml-1" v-model="form.email" type="email" name="email" :class="{ 'is-invalid': form.errors.has('email') }"/>
<has-error :form="form" field="email"></has-error>
</b-form-group>
<b-form-group>
<div slot="label" class="d-flex justify-content-between align-items-end">
<div>Password</div>
Forgot password?
</div>
<b-input v-model="form.password" type="password" name="password" :class="{ 'is-invalid': form.errors.has('password') }" />
<has-error :form="form" field="password"></has-error>
</b-form-group>
<div class="d-flex justify-content-between align-items-center m-0">
<b-check v-model="form.rememberMe" class="m-0">Remember me</b-check>
<b-btn type="submit" variant="primary">Sign In</b-btn>
</div>
</form>
</div>
<template>
<script>
export default ({
name: 'pages-authentication-login-v2',
metaInfo: {
title: 'Login'
},
state: {
token: localStorage.getItem('access_token'),
},
mutations: {
login(state, token) {
state.token = token
},
},
data: () => ({
form: new Form({
email: '',
password: '',
})
}),
methods: {
login(){
this.form.post('/api/login')
.then((response) =>{
const token = response.data.access_token
localStorage.setItem('access_token', token)
// console.log(response);
this.$router.push('/dashboard');
})
.catch((error)=>{
this.$toasted.error('Ooops! Something went wrong', {
icon : "warning",
theme: "bubble",
closeOnSwipe: true,
position: "top-right",
duration : 5000,
singleton: true,
})
});
},
}
})
</script>

How to validate Dynamic input in vuejs / Laravel

I'm catching errors via interceptors and a Toast for UI. Usually the error is caught by the interceptor and displayed via the toast for one-time inputs, i'm trying to catch errors for an uncertain number of inputs. So far looping through the input array and setting rules does not work.
Component.vue
<template>
<div>
<div class="form-group" v-for="(input,k) in inputs" :key="k">
<input type="text" id="name" placeholder="Name" v-model="input.name" />
<span>
<i class="fas fa-minus" #click="remove(k)" v-show="k || ( !k && inputs.length > 1)"></i>
<i class="fas fa-plus" #click="add(k)" v-show="k == inputs.length-1"></i>
</span>
</div>
<button #click="addName">Create</button>
</div>
</template>
<script>
export default {
data() {
return {
inputs: [{name: ''}]
}
},
methods: {
add(index) {
this.inputs.push({ name: ''});
console.log( this.inputs);
},
remove(index) {
this.inputs.splice(index, 1);
},
addName() {
axios.post('/user', {userinputs:this.inputs}).then(response => {})
.catch(error => {
console.log(error);
});
}
}
}
</script>
Controller:
public function store(Request $request)
{
$rules = [
'userinputs' => 'required|max:255',
];
foreach ($request->input('userinputs') as $key => $my_object_in_array) {
$rules[$my_object_in_array['name']] = 'required|max:10';
}
return $rules;
}
I hope this is what you wanted to achieve.
public function store(Request $request)
{
$rules = [
'userinputs.*.name' => 'required|max:255',
];
$validator = \Illuminate\Support\Facades\Validator::make($request->all(), $rules);
if ($validator->fails()) {
//Return the errors as JSON
return response()->json(['success' => 'false', 'errors' => $validator->errors()], 400);
}
//Do something
return ['success' => 'true'];
}

Laravel + Vue Looping a Specific ID once

Hey guys so I'm planning to have a list of my recent chats. The problem is that i just want to loop the same Id (ex. Chatroom_id), in my case its looping the same the Chatroom Id 5 times, cause im checking a db that reciever_id is mine and the result is 5, hence the 5 times it loops the same Chatroom ID. I just want it to loop once ever chatroom Id
Laravel View // where i insert my vue component
<!--- Message or online followers and followings -->
<div id="messagesidebar" class="app-sidebar-userdetails">
<h6 class="sm text-white"> <i> Recent Chat </i> </h6>
<div id="recentchat">
<recentchat-component :user_id="{{ Auth::user()->id }}"></recentchat-component>
</div>
</div>
<!-- /# Message or online followers and followings -->
</div>
Vue Template
<div class="chats" v-for="recentchat in recentchats" v-bind:key="recentchat.id">
{{ recentchat.chatroom_id}}
</div>
Laravel Controller
public function fetchrecentchat($user_id)
{
$recentchat = Chat::Where('receiver_id', $user_id)
->orderBy('created_at', 'DESC')
->get();
return ChatResource::collection($recentchat);
}
Chat Resource
public function toArray($request)
{
// return parent::toArray($request);
return [
'id' => $this->id,
'message' => $this->message,
'chatroom_id' => $this->chatroom_id,
'user_id' => $this->user_id,
'receiver_id' => $this->receiver_id,
'created_at' => $this->created_at->diffForHumans(),
'updated_at' => $this->updated_at,
];
}
Tell me if you need to review codes that i did not add above so much thanks guys!
Update
Vue script
<script>
export default {
props: [ 'user_id' ],
data() {
return {
recentchats: [],
recentchat: {
id: '',
chatroom_id: '',
user_id: '',
receiver_id: '',
created_at: '',
updated_at: '',
}
}
},
created() {
this.fetchchatrooms();
},
methods: {
fetchchatrooms(){
fetch('/api/fetchrecentchat/' +this.user_id)
.then(res => res.json())
.then(res => {
console.log(res);
this.recentchats = res.data
});
}
},
mounted() {
console.log('Recent Chat mounted.')
}
}

Foreign key on post request Laravel & Vue

I am using a larvel backend with a vue frontend. They are connected by API. I have been trying to figure out how i can correctly submit a form for a model that has a foreign_key relationship with a parent model.
Here is the relationships
A User has many Tasks
A Task belongs to User
A Client has many Engagements
A Engagement belongs to Client
Engagements and Tasks have many to many
User and Client have no direct relationship
So I know that if i wanted to submit a post request for data that had a relationship with user It would be simple to assign the foreign_key like below
*Example
public function store(Request $request)
{
$data = $request->validate([
'title' => 'required|string',
'completed' => 'required|boolean',
]);
$todo = Todo::create([
'user_id' => auth()->user()->id,
'title' => $request->title,
'completed' => $request->completed,
]);
return response($todo, 201);
}
But when i try to do this same thing for my client I get a 500 internal server error..
So here is the Workflow
On my vue frontend in the AddEngagment.vue component
<template>
<div class="page-wrapper mt-1">
<div class="add-engagement container">
<div class="card-body bg-light border-primary mb-2">
<h4 class="text-left text-primary m-0"><i class="mr-3 far fa-folder-open"></i>New Engagement</h4>
</div>
<form #submit.prevent="addEngagement" class="d-flex-column justify-content-center">
<div class="form-group">
<select class="form-control mb-3" id="type" v-model="engagement.return_type">
<option v-for="type in types" :key="type.id" :value="type">{{ type }}</option>
</select>
<input type="text" class="form-control mb-3" placeholder="Year" v-model="engagement.year">
<input type="text" class="form-control mb-3" placeholder="Assign To" v-model="engagement.assigned_to">
<input type="text" class="form-control mb-3" placeholder="Status" v-model="engagement.status">
<button type="submit" class="btn btn-lg btn-primary d-flex justify-content-start">Create</button>
</div>
</form>
</div>
</div>
</template>
<script>
export default {
name: 'add-engagement',
data() {
return {
engagement: {
return_type: null,
year: '',
assigned_to: '',
status: '',
},
types: [
'Choose Return Type...',
'1040',
'1120',
],
}
},
methods: {
addEngagement(e) {
if(!this.engagement.return_type || !this.engagement.year ){
return
} else {
this.$store.dispatch('addEngagement', {
id: this.idForEngagement,
return_type: this.engagement.return_type,
year: this.engagement.year,
assigned_to: this.engagement.assigned_to,
status: this.engagement.status,
})
e.preventDefault();
}
e.preventDefault();
this.engagement = ""
this.idForEngagement++
this.$router.push('/engagements')
},
},
created: function() {
this.engagement.return_type = this.types[0]
},
}
</script>
I am capturing the data in the input and then dispatching the store for the addEngagement action
Now in the URL for the AddEngagement.vue component I have the client_id shown like below
add-engagement/{client_id}
This is the store.js action for the addEngagement
addEngagement(context, engagement) {
axios.post(('/engagements'), {
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
.then(response => {
context.commit('getClientEngagement', response.data)
})
.catch(error => {
console.log(error)
})
},
So from here, it sends the request to my /engagement on the laravel api route file like below
Route::post('/engagements', 'EngagementsController#store');
And then goes to the EngagementsController to run the store method below
public function store($client_id, Request $request)
{
$data = $request->validate([
'return_type' => 'required|string',
'year' => 'required|string',
'status' => 'required|string',
'assigned_to' => 'required|string',
'done' => 'required|boolean'
]);
$engagement = Engagement::create([
'return_type' => $request->return_type,
'year' => $request->year,
'assigned_to' => $request->assigned_to,
'status' => $request->status,
'done' => $request->done,
+ [
'client_id' => $client_id
]
]);
return response($engagement, 201);
}
At this point is where I am getting my error and I think it has to do with the $client_id which I have tried many different ways of formatting it with no success. When the URL is storing the client_id on the front end for the post request I do not know how that data is getting shared with laravel for it to know which client the client_id foreign key will be assigned to.
First of all:
$engagement = Engagement::create([
'return_type' => $request->return_type,
'year' => $request->year,
'assigned_to' => $request->assigned_to,
'status' => $request->status,
'done' => $request->done,
+ [
'client_id' => $client_id
]
]);
why is there this +[...] array thing? Doesn't really makes sense to me...
But the 500 Server Error most probably results from your false request (which most of 500's do).
If this is your Route definition:
Route::post('/engagements', 'EngagementsController#store');
This is the method head:
public function store($client_id, Request $request)
And your Post request is the following:
axios.post(('/engagements'), {
return_type: engagement.return_type,
year: engagement.year,
assigned_to: engagement.assigned_to,
status: engagement.status,
done: false
})
You will notice, that the function definition differs from your Route. In the function you expect a parameter 'client_id' which isn't known by the route. So to resolve this issue, put your client_id into the Post request body (underneath your done: false for example) and remove the parameter from the function definition.

I can not save values using POST method - laravel vue

I'm trying to save the data I send from the Event view. in the storeEvent method of the EventController driver but it gives me error 422 and I can not find the problem so far.
The Event model has a many-to-many relationship with the Categories model, and Event also has a many-to-many relationship with the Coins model, which is why I occupy vue-multiselect since the user can select several categories or several coins for the same event
Event.vue
<template>
<form v-on:submit.prevent="createdEvent" class="form-horizontal">
<div class="form-group row">
<label>Titulo</label>
<input type="text" name="title" maxlength="25" v-model="title">
</div>
<div class="form-group row">
<label>*Cryptodivisas</label>
<multiselect v-model="coinvalue" :options="coins"
:multiple="true" label="name"
track-by="id" placeholder="Seleccione">
</multiselect>
</div>
<div class="form-group row">
<label>*Categoría</label>
<multiselect v-model="categoryvalue" :options="categories"
:multiple="true" label="name"
track-by="id" placeholder="Seleccione">
</multiselect>
</div>
<div class="col-sm-12">
<button class="btn" type="submit">Crear evento</button>
</div>
</form>
<script>
import Multiselect from 'vue-multiselect';
export default {
components: {
Multiselect,
},
props: ['auth'],
data () {
return {
user: {},
title: '',
coins: [],
coinvalue: [],
categories: [],
categoryvalue: [],
}
},
created() {
this.getCoins();
this.getCategories();
},
mounted() {
this.user = JSON.parse(this.auth);
},
methods: {
getCoins(){
let urlCoin = '/dashboard/coins';
axios.get(urlCoin)
.then((response) => {
this.coins = response.data;
})
.catch((err) => {
})
},
getCategories(){
let urlCategories = '/dashboard/categories';
axios.get(urlCategories)
.then((response) => {
this.categories = response.data;
})
.catch((err) => {
})
},
createdEvent(){
let urlEvent = '/dashboard/newEvent';
const eventData = {
'id' : this.user.id,
'title' : this.title,
'coin' : this.coinvalue,
'category' : this.categoryvalue,
}
console.log(eventData);
axios.post(urlEvent, eventData)
.then((response) => {
console.log(ok);
})
.catch((err) => {
console.log(err.response.data);
})
}
</script>
storeEvent (EventController)
public function storeEvent(Request $request)
{
$this->validate($request, [
'title' => 'required|max:25',
'coin' => 'required',
'category' => 'required',
]);
$userAuth = Auth::user()->id;
$userEvent = $request->id;
if($userAuth === $userEvent){
$event = new Event;
$event->user_id = $userEvent;
$event->title = $request->title;
if($event->save()){
$event->coins()->attach($request->coin);
$event->categories()->attach($request->category);
return response()->json([
'status' => 'Muy bien!',
'msg' => 'Tu evento ya fue creado con éxito.',
], 200);
}
else {
return response()->json([
'status' => 'Error!',
'msg' => 'No pudimos crear tu evento.',
], 401);
}
}
}
I think the problem may be when I assign the values to the coin () -> attach () and category () -> attach () section, but I have no idea how to solve it due to my inexperience in the tool.
The system was made entirely in Laravel and it worked without problems, now that it is being updated with Vue it began to give inconveniences.
Any ideas? I occupy Laravel 5,6, Vuejs 2, Axios and Vue-multiselect 2
Try sending form data
Here is the example for you.
var urlEvent = '/dashboard/newEvent';
var form_data = new FormData();
form_data.append('id',this.user.id);
form_data.append('title',this.title);
for(let coin of this.coinvalue)
form_data.append('coin[]', coin);
for(let category of this.categoryvalue)
form_data.append('category[]', category);
axios(urlEvent,{
method: "post",
data: form_data
})
.then(res => {
console.log(ok);
})
.catch(err => {
console.log(err.response.data);
});
If this stills gives you a 422 status code (Unprocessable entities). Then try returning $request in you controller. And check what data are actually send to the controller and what your validation is.
422 means validation error so do a console.log or inspect the element on axios call and check that :
'title' : this.title,
'coin' : this.coinvalue,
'category' : this.categoryvalue,
Are not empty, cause right now some data from above is missing since its a 422 validation exception.

Resources