Account balance system with Stripe - laravel

For the past two days I've been trying to understand how Stripe works.. What I'm trying to build is a simple system that lets the user to add funds to his account on the site.
I followed a tutorial I found on the internet that uses Laravel Cashier but as I've read on the laravel documentation if I need to perform single charges I should use directly Stripe. The thing is, there are not many tutorials on how this should be done with laravel..
Here's what I have so far:
VIEW:
<form class="app-form" style="margin-bottom: 0px;" action="/add-funds" method="POST">
{{ csrf_field() }}
<select id="funds-options" style="width: 20%; margin-bottom: 20px;" name="add-funds-select">
<option value="30">$30</option>
<option value="50">$50</option>
<option value="100">$100</option>
<option value="200">$200</option>
<option value="300">$300</option>
<option value="500">$500</option>
</select>
<p style="margin-bottom: 0px;">
<script src="https://checkout.stripe.com/checkout.js"></script>
<button id="customButton">Purchase</button>
<script>
var handler = StripeCheckout.configure({
key: '{{ getenv('STRIPE_KEY') }}',
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function(token) {
// You can access the token ID with `token.id`.
// Get the token ID to your server-side code for use.
}
});
document.getElementById('customButton').addEventListener('click', function(e) {
// Open Checkout with further options:
var userAmount = $("#funds-options").val();
handler.open({
name: 'Demo Site',
description: '2 widgets',
amount: userAmount*100
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
</p>
</form>
I have this select tag where the user can select the amount he wants to add to his account. Now, this opens the widget from Stripe but as soon as I hit pay, I'm getting that info: "You did not set a valid publishable key".
I tried this using the publishable key directly and I'm able to pass this but as soon as it gets into the controller it throws pretty much the same error, something like API key was not set.
I set the keys in the env file and I also reference them in the services.php..
ENV:
STRIPE_KEY=pk_test_....
STRIPE_SECRET=sk_test_...
SERVICES:
'stripe' => [
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
Anyway, even if I pass this "error" I'm still not sure if I'm doing this right.
Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
class WalletController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('user.wallet.index');
}
public function postPayWithStripe(Request $request)
{
return $this->chargeCustomer($request->input('add-funds-select'), $request->input('stripeToken'));
}
public function chargeCustomer($amount, $token)
{
\Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET'));
if (!$this->isStripeCustomer())
{
$customer = $this->createStripeCustomer($token);
}
else
{
$customer = \Stripe\Customer::retrieve(Auth::user()->stripe_id);
}
return $this->createStripeCharge($amount, $customer);
}
public function createStripeCharge($amount, $customer)
{
try {
$charge = \Stripe\Charge::create(array(
"amount" => $amount,
"currency" => "brl",
"customer" => $customer->id,
"description" => "Add funds to your account"
));
} catch(\Stripe\Error\Card $e) {
return redirect()
->route('index')
->with('error', 'Your credit card was been declined. Please try again or contact us.');
}
return $this->postStoreAmount($amount);
}
public function createStripeCustomer($token)
{
\Stripe\Stripe::setApiKey(getenv('STRIPE_SECRET'));
$customer = \Stripe\Customer::create(array(
"description" => Auth::user()->email,
"source" => $token
));
Auth::user()->stripe_id = $customer->id;
Auth::user()->save();
return $customer;
}
/**
* Check if the Stripe customer exists.
*
* #return boolean
*/
public function isStripeCustomer()
{
return Auth::user() && \App\User::where('id', Auth::user()->id)->whereNotNull('stripe_id')->first();
}
public function postStoreAmount($amount)
{
$userBalance = Auth::user()->balance;
$userBalance = $userBalance + $amount;
Auth::user()->save();
session()->flash('message', 'You just added funds to your account.');
return redirect()->route('index');
}
}
I have a field in the users table that holds the user balance.
As I mentioned, I followed a tutorial I found on the internet.. I'm not sure how this should work. Any suggestions?

You will follow this tutorial. I am integrated it in my cart functionality previous week. Its very easy to integrate ...have Fun :)
http://justlaravel.com/integrate-stripe-payment-gateway-laravel/

For anyone else looking for how to retrieve account balance with laravel cashier, I found it like this:
$user = App\User::first();
echo $user->asStripeCustomer()->account_balance;
This returns the account balance in cents.

Related

How to Add Roles and Permission to Laravel Fortify + Inertia + vue?

my project use laravel fortify, inertia with vue. i have to add role based permissions (just like spatie permissions package). i'm still a beginner to fortify and inertia. but i have experience in spatie package. i'm stuck with how add roles and permission to fortify. currently i'm planning to create table structure like spatie package have(roles, permssions, roles_has_permissions ,etc). is there per-build package or better way to implement roles and permissions ? and use '#can' in vue files? thank you.
edit
hello guys,this is i'm currently did(im using this now ). it is working but still need some improvements, (any better solution i really appreciate it)
1)installed and configured as usual spatie/laravel-permission
2)added predefined permissions and roles to tables using seeder
created function in user model to get permission array list
// user model function
public function getPermissionArray()
{
return $this->getAllPermissions()->mapWithKeys(function($pr){
return [$pr['name'] => true];
});
}
and added that function to inertia middleware
//App\Http\Middleware\HandleInertiaRequests
public function share(Request $request)
{
return array_merge(parent::share($request), [
'auth'=>['user' => $request->user() ? $request->user()->only('id', 'name', 'email') : null,
'can' =>$request->user() ? $request->user()->getPermissionArray() : []
],
]);
}
now $page.props.auth.can can access globally
added permission check in vue file
<div class="row">
<div class="col-sm-12 col-md-6" v-if="$page.props.auth.can['user_create']">
<inertia-link
class="btn btn-primary"
:href="$route('admin.user.create')"
>Create New
</inertia-link>
</div>
</div>
I resolved issue like below,
first I'm sending permission array to UI.
in user model
<?php
// user model function
public function getPermissionArray()
{
return $this->getAllPermissions()->mapWithKeys(function($pr){
return [$pr['name'] => true];
});
}
in inertia share middleware
<?php
//App\Http\Middleware\HandleInertiaRequests
public function share(Request $request)
{
return array_merge(parent::share($request), [
'auth'=>['user' => $request->user() ? $request->user()->only('id', 'name', 'email') : null,
'can' =>$request->user() ? $request->user()->getPermissionArray() : []
],
]);
}
in app js file, I have added global function to check one or many permssion have user
import Vue from 'vue'
Vue.mixin({
methods: {
hasAnyPermission: function (permissions) {
var allPermissions = this.$page.props.auth.can;
var hasPermission = false;
permissions.forEach(function(item){
if(allPermissions[item]) hasPermission = true;
});
return hasPermission;
},
},
})
in vue components :
<script>
export default {
data() {
return {};
},
mounted: function () {},
methods: {},
};
</script>
<template>
<div>
<li v-if="hasAnyPermission(['testiml_view', 'testiml_edit', 'testiml_create'])">
<inertia-link
:href="$route('admin.testimonial.index')"
class="side-nav-link-a-ref"
>
<i class="fas fa-feather"></i>
<span>Testimonial</span>
</inertia-link>
</li>
</div>
</template>
in the share method on the Inertia Middleware: HandleInertiaRequest.php, I passed the permissions and the roles array to Vue using:
$permissions = $user->getAllPermissions()->pluck('name');
$roles = $user->roles()->pluck('name');
return array_merge(parent::share($request), [
'auth.user' => fn() => $request->user() ?
$request->user()->only('id', 'name', 'email', 'roles')
: null,
'auth.user.permissions' => $permissions,
'auth.user.roles' => $roles
]);

Assign specific roles from where the user clicks

I would like to assign roles depending on which button the user clicks:
For example:
- If you click on I want to be an Advisor, redirect to the Laravel registration form and assign the role of advisor.
- If the user clicks on I want to be a Buyer, they redirect to the Laravel registration form and assign the buyer role.
But I do not know how to do it.
I have this code in my 'RegisterController':
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
//'password' => Hash::make($data['password']), //mutador en User model
'password' => $data['password'],
'surname1' => $data['surname1'],
'surname2' => $data['surname2'],
'comunidad_id' => $data['cbx_comunidad'],
'provincia_id' => $data['cbx_provincia'],
'municipio_id' => $data['cbx_municipio'],
]);
//dd(Request::url());
// $user->assignRole('Asesor');
//quiero asignar varios roles depende de el botón que clicken
return $user;
}
For now, what I have done is to add such a parameter, in the view that calls the view 'register':
href="{{ route('register','Asesor') }}"
and in the view 'register' send it by post in a hidden:
<div class="form-group">
<?php
$pos = strpos(Request::fullUrl(), '?');
$cadena = substr (Request::fullUrl() , $pos+1, strlen(Request::fullUrl()) );
?>
<input type="hidden" name="role" id="role" value="{{ $cadena }}">
</div>
Then in the controller I do this:
if ($data['role'] == 'Asesor=')
{
$user->assignRole('Asesor');
}
return $user;
But I don't know if it's the right way to act.
I think, you could work with events like this:
In your EventServiceProvider class, create an item inside your property $listen:
'App\Events\User\Created' => ['App\Listeners\User\AssignRoles'],
After that, you going to run the command:
php artisan event:generate
Now, you need to turn on this event in your User class declaring protected property $dispatchesEvents, like this:
protected $dispatchesEvents = ['created' => 'App\Events\User\Created'];
After all call create method in your User class, the created event is going to be called and run AssignRoles logic.
In your App\Events\User\Created class you need to inject User into __construct method, like this:
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
Remember to put the complete path of User class!
This is the object that is going to be filled with data coming from User::create method.
Inside your Listener AssignRoles you have the event linked with the user filled and you can get it this way:
public function handle(Created $event)
{
$event->user;
// ...
}
Inside your Listener AssignRoles you can get all Requested params in your __construct method:
private $request;
public function __construct(Illuminate\Http\Request $request)
{
$this->request = $request;
}
With requested params in your hand you can apply the logic depending on the clicked button inside handle method:
public function handle(Created $event)
{
$event->user;
// here is the best place to do all the logic about roles that is going to be attached in this user. E.g:
switch($role = $this->request->role) {
case $role == 'Asesor':
$event->user->roles()->assignRole('Asesor');
break;
case $role == 'Buyer':
$event->user->roles()->assignRole('Buyer');
break;
}
}
To send role param into Request you need to create a form with hidden element,
<input type='hidden' name='role' />
create more than one submit button to fill role hidden element
<input type='submit' value='I want to be an Advisor' onClick='setRole("Advisor")' />
<input type='submit' value='I want to be a Buyer' onClick='setRole("Buyer")' />
And, finally you need a logic to setRole js method. Good Look. ;-)
For assign Role to user.
Controller function will be like.
/* assign role */
if(is_array($request['role']))
{
foreach($request['role'] as $d)
{
$user->roles()->attach(Role::where('id',$d)->first());
}
}
return redirect()->route();

How to login with google using socialite in laravel 5.8 with different types of users

I want to login with google socialite for two types of user.
1- Company Users.
2- Individuals users
In database users table i add a field sign_up_as if 1 then company users if 2 then individuals. so how i paas 1 and 2 value to signin with google link.
In the login view i add this code
<li>
<i class="fa fa-google"></i>
</li>
Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Socialite;
use App\Services\SocialGoogleAccountService;
use Illuminate\Routing\UrlGenerator;
class SocialAuthGoogleController extends Controller
{
public function redirect()
{
return Socialite::driver('google')->redirect();
}
public function callback(SocialGoogleAccountService $service)
{
$user = $service->createOrGetUser(Socialite::driver('google')->user());
auth()->login($user);
return redirect()->to('/home');
}
}
createOrGetUser function
public function createOrGetUser(ProviderUser $providerUser)
{
// dd('now here');
$account = SocialGoogleAccount::whereProvider('google')
->whereProviderUserId($providerUser->getId())
->first();
// dd($account);
if ($account) {
return $account->user;
} else {
$account = new SocialGoogleAccount([
'provider_user_id' => $providerUser->getId(),
'provider' => 'google'
]);
$user = User::whereEmail($providerUser->getEmail())->first();
if (!$user) {
$user = User::create([
'email' => $providerUser->getEmail(),
'name' => $providerUser->getName(),
'password' => md5(rand(1, 10000)),
'sign_up_as' => 2, // currently sign_up-as static
]);
}
$account->user()->associate($user);
$account->save();
return $user;
}
}
I got the solution myself rather then passing values simple set the values in the session and get in the controller then save in the database like.
#php
session(['sign_up_as' => '2']);
#endphp
and in the controller
$sign_up_as = session('sign_up_as');

sending an email address with axios within Vue.Js component to a laravel backend is failing

I am trying to make a vue component that checks if a given input is available or not in the database. The calls to the server i am doing with axios inside the vue component. for some reason, when i type the # symbol in the email, it breaks.here is my code so far:
this is the backend.
class TestController extends Controller
{
public function verifyName($name){
if (request()->wantsJson()){
$names=User::where('name', $name)->count();
if ($names>0)
return 1;
if ($names==0)
return 0;
}
}
public function verifyEmail($email){
if (request()->wantsJson()){
$emails=User::where('email', $email)->count();
if ($emails>0)
return 1;
if ($emails==0)
return 0;
}
}
}
here is the vue script:
<script>
export default {
data(){
return{
name: '',
email: '',
nameTaken: false,
emailTaken: false,
nameAvailable:false,
emailAvailable:false,
nameIsTooShort:false
}
},
methods:{
verifyName(){
axios.get('/verify/'+this.name)
.then((response) => {
if (response.data){
this.nameTaken=true;
this.nameAvailable=false
}
if(!response.data){
this.nameTaken = false;
this.nameAvailable=true
}
});
},
verifyEmail(){
axios.get('/verify/'+this.email)
.then((response) => {
if (response.data){
this.emailTaken=true;
this.emailAvailable=false
}
if(!response.data){
this.emailTaken = false;
this.emailAvailable=true
}
});
}
}
}
</script>
and here are my routes:
Route::get('/verify/{name}','TestController#verifyName');
Route::get('/verify/{email}','TestController#verifyEmail');
and here is a small gif to show it.
as requested, here is the html part where i show the message.
<div class="field-body">
<div class="field">
<p class="control">
<input class="input" id="email" type="email" name="email" value="{{ old('email') }}" v-model="email" #change="verifyEmail">
<div class="help is-danger" v-show="emailTaken" v-cloak>This e mail is taken</div>
<div class="help is-success" v-show="emailAvailable" v-cloak>Great. this email is available</div>
</p>
</div>
</div>
Thanks.
Try using laravel's validator instead.
One way to go about it, in your "App\Http\Controllers\Auth\RegisterController"
use Illuminate\Support\Facades\Validator;
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255|unique:users',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
}
Make note of the unique:xxxxx rule where xxxxx is your table. https://laravel.com/docs/5.5/validation
If the email or name is not unique the validator will return the error response. So you would use it at the beginning of your register function.
public function register(Request $request)
{
$this->validator($request->all())->validate();
// rest of registration routine here....
}
I think its happens because of you are using "#" symbol in url string. This is reserved character. (Which characters make a URL invalid?)
Solution: send email to verification on server in json body of post request.

Keeping modal dialog open after validation error laravel

So basically I have a blade.php, controller page and a form request page(validation). I'm trying to keep my modal dialog open if there is an error but I just cant figure it out, what part of code am I missing out on or needs to be changed?
blade.php
<div id="register" class="modal fade" role="dialog">
...
<script type="text/javascript">
if ({{ Input::old('autoOpenModal', 'false') }}) {
//JavaScript code that open up your modal.
$('#register').modal('show');
}
</script>
Controller.php
class ManageAccountsController extends Controller
{
public $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function index()
{
$users = User::orderBy('name')->get();
$roles = Role::all();
return view('manage_accounts', compact('users', 'roles'));
}
public function register(StoreNewUserRequest $request)
{
// process the form here
$this->userRepository->upsert($request);
Session::flash('flash_message', 'User successfully added!');
//$input = Input::except('password', 'password_confirm');
//$input['autoOpenModal'] = 'true'; //Add the auto open indicator flag as an input.
return redirect()->back();
}
}
class UserRepository {
public function upsert($data)
{
// Now we can separate this upsert function here
$user = new User;
$user->name = $data['name'];
$user->email = $data['email'];
$user->password = Hash::make($data['password']);
$user->mobile = $data['mobile'];
$user->role_id = $data['role_id'];
// save our user
$user->save();
return $user;
}
}
request.php
class StoreNewUserRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
// create the validation rules ------------------------
return [
'name' => 'required', // just a normal required validation
'email' => 'required|email|unique:users', // required and must be unique in the user table
'password' => 'required|min:8|alpha_num',
'password_confirm' => 'required|same:password', // required and has to match the password field
'mobile' => 'required',
'role_id' => 'required'
];
}
}
Laravel automatically checks for errors in the session data and so, an $errors variable is actually always available on all your views. If you want to display a modal when there are any errors present, you can try something like this:
<script type="text/javascript">
#if (count($errors) > 0)
$('#register').modal('show');
#endif
</script>
Put If condition outside from script. This above is not working in my case
#if (count($errors) > 0)
<script type="text/javascript">
$( document ).ready(function() {
$('#exampleModal2').modal('show');
});
</script>
#endif
for possibly multiple modal windows you can expand Thomas Kim's code like following:
<script type="text/javascript">
#if ($errors->has('email_dispatcher')||$errors->has('name_dispatcher')|| ... )
$('#register_dispatcher').modal('show');
#endif
#if ($errors->has('email_driver')||$errors->has('name_driver')|| ... )
$('#register_driver').modal('show');
#endif
...
</script>
where email_dispatcher, name_dispatcher, email_driver, name_driver
are your request names being validated
just replace the name of your modal with "login-modal". To avoid error put it after the jquery file you linked or jquery initialized.
<?php if(count($login_errors)>0) : ?>
<script>
$( document ).ready(function() {
$('#login-modal').modal('show');
});
</script>
<?php endif ?>

Resources