How to store user details after paypal payment approval? - php

I'm building an online courses website, where students can buy a course via PayPal. The payment process is working successfully, But I'm confused on where or how to store course and user data after PayPal Payment Approval.
I have a users table, courses table and pivot table: course_students where I store the id of the course and the id of the student:
-------------
course_students
--------------
student_id course_id
1 2
----------
This is PayPalService Class:
class PayPalService
{
use ConsumesExternalServices;
protected $baseUri;
protected $clientId;
protected $clientSecret;
public function __construct()
{
$this->baseUri = config('services.paypal.base_uri');
$this->clientId = config('services.paypal.client_id');
$this->clientSecret = config('services.paypal.client_secret');
}
public function resolveAuthorization(&$queryParams, &$formParams, &$headers)
{
$headers['Authorization'] = $this->resolveAccessToken();
}
public function decodeResponse($response)
{
return json_decode($response);
}
public function resolveAccessToken()
{
$credentials = base64_encode("{$this->clientId}:{$this->clientSecret}");
return "Basic {$credentials}";
}
public function handlePayment(Request $request)
{
$order = $this->createOrder($request->value, $request->currency);
$orderLinks = collect($order->links);
$approve = $orderLinks->where('rel', 'approve')->first();
session()->put('approvalId', $order->id);
return redirect($approve->href);
}
public function handleApproval()
{
if (session()->has('approvalId')) {
$approvalId = session()->get('approvalId');
$payment = $this->capturePayment($approvalId);
$name = $payment->payer->name->given_name;
$payment = $payment->purchase_units[0]->payments->captures[0]->amount;
$amount = $payment->value;
$currency = $payment->currency_code;
return redirect()
->route('success')
->with('payment', "Thanks, {$name}. We received your {$amount}{$currency} payment.");
}
// $errorMessage = 'We cannot capture the payment. Try again, please';
return redirect()
->route('paymentform')
->with('error','We cannot capture the payment. Try again, please');
}
public function createOrder($value, $currency)
{
return $this->makeRequest(
'POST',
'/v2/checkout/orders',
[],
[
'intent' => 'CAPTURE',
'purchase_units' => [
0 => [
'amount' => [
'currency_code' =>strtoupper($currency),
'value' => round($value * $factor = $this->resolveFactor($currency)) / $factor,
]
]
],
'application_context' => [
'brand_name' => config('app.name'),
'shipping_preference' => 'NO_SHIPPING',
'user_action' => 'PAY_NOW',
'return_url' => route('approval'),
'cancel_url' => route('cancelled'),
]
],
[],
$isJsonRequest = true,
);
}
public function capturePayment($approvalId)
{
return $this->makeRequest(
'POST',
"/v2/checkout/orders/{$approvalId}/capture",
[],
[],
[
'Content-Type' => 'application/json'
],
);
}
public function resolveFactor($currency)
{
$zeroDecimalCurrencies = ['JPY'];
if (in_array(strtoupper($currency), $zeroDecimalCurrencies)) {
return 1;
}
return 100;
}
}
PaymentController:
public function paymentForm($course_uuid)
{
$currencies = Currency::all();
$platforms = PaymentPlatform::get();
$course = Course::where('uuid', $course_uuid)->where('status',1)->where('availability',1)->first();
return view('public.payment.paypalform', compact('currencies','platforms','course'));
}
/**
* implment Payemnt process
*
* #param Request $request
*/
public function pay(Request $request)
{
// dd($request->all());
$rules = [
'value' => ['required', 'numeric', 'min:5'],
'currency' => ['required', 'exists:currencies,iso'],
'payment_platform' => ['required', 'exists:payment_platforms,id'],
];
$request->validate($rules);
$paymentPlatform = $this->paymentPlatformResolver
->resolveService($request->payment_platform);
session()->put('paymentPlatformId', $request->payment_platform);
return $paymentPlatform->handlePayment($request);
}
protected function approval()
{
if (session()->has('paymentPlatformId')) {
$paymentPlatform = $this->paymentPlatformResolver
->resolveService(session()->get('paymentPlatformId'));
return $paymentPlatform->handleApproval();
}else{
return redirect()
->route('courses.levels')
->withErrors('We cannot retrieve your payment platform. Try again, please.');
}
}
protected function canceled()
{
return redirect()
->route('courses.levels')
->withErrors('You cancelled the payment.');
}

In your payment controller you can save you the course information in session. After payment when user will redirect, save the information to your desired database table..
//save course info or other info
session()->put('courseInfo', [your data]);
your controller function:
/**
* implment Payemnt process
*
* #param Request $request
*/
public function pay(Request $request)
{
// dd($request->all());
$rules = [
'value' => ['required', 'numeric', 'min:5'],
'currency' => ['required', 'exists:currencies,iso'],
'payment_platform' => ['required', 'exists:payment_platforms,id'],
];
$request->validate($rules);
$paymentPlatform = $this->paymentPlatformResolver
->resolveService($request->payment_platform);
session()->put('paymentPlatformId', $request->payment_platform);
//save course info or other info
session()->put('courseInfo', [your data]);
return $paymentPlatform->handlePayment($request);
}
you another function: approval() call another function $paymentPlatform->handleApproval(); inside of it after payment done.
Go to handleApproval() function and hopefully you will find there user account creation codes. Now get the session value session()->get('courseInfo') and save in your desired table.

Related

Laravel: Set custom Authentication user as the logged in user

I have created a custom authentication class which get's called from middleware, this will forward the user to AWS Cognito Hosted UI, the user will login then get returned to the application with a authorization code. This code is then exchanged for the access_token, id_token and refresh_token.
Once the access_token and id_token has been validated using the jwks keys I can extract the users email address and then return this within the User model which extends Authenticatable. Then i'd like to use $user within my Twig templates the same way i can with the default login system for Laravel.
Below is my code:
// config/auth.php
'guards' => [
'web' => [
'driver' => 'cognito',
'provider' => 'users',
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User\User::class,
],
// app/Providers/AuthServiceProvider.php
public function boot()
{
$this->registerPolicies();
Auth::extend('cognito', static function ($app, $name, array $config) {
return new CognitoUserProvider(new User());
});
}
// app/Auth/CognitoUserProvider.php
<?php
namespace App\Auth;
use App\Models\User;
use Firebase\JWT\JWT;
use GuzzleHttp\Client;
use CoderCat\JWKToPEM\JWKConverter;
class CognitoUserProvider
{
private $user;
private $clientId;
private $clientSecret;
private $poolId;
private $region;
private $domain;
private $guzzle;
public function __construct(User $user)
{
$this->clientSecret = config('app.aws_cognito_client_secret');
$this->poolId = config('app.aws_cognito_pool_id');
$this->clientId = config('app.aws_cognito_client_id');
$this->region = config('app.aws_region');
$this->domain = implode('.', [
config('app.aws_cognito_domain'),
'auth',
config('app.aws_region'),
'amazoncognito.com'
]);
$this->guzzle = new Client();
$this->user = $user;
}
/**
* #return User|bool|void
*/
public function authenticate()
{
if (request()->input('code')) {
$tokens = $this->exchangeTokens();
$accessToken = $this->validateToken($tokens['access_token']);
$userToken = $this->validateToken($tokens['id_token']);
if (!$accessToken || !$userToken) {
dd('JsonException Exception Occurred.');
}
request()->session()->put('access_token', $accessToken);
request()->session()->put('id_token', $userToken);
request()->session()->put('refresh_token', $tokens['refresh_token']);
$this->user->setAttribute('email', $userToken->email);
$this->user->setAttribute('first_name', 'Martyn');
$this->user->setAttribute('last_name', 'Ball');
$this->user->setAttribute('id', 5);
return $this->user;
}
if (request()->session()->has('refresh_token')) {
return $this->refreshToken();
}
$this->sendToLoginPage();
}
/**
* #param $token
*
* #return false|object
*/
private function validateToken($token)
{
$domain = implode('.', [ 'cognito-idp', $this->region, 'amazonaws.com' ]);
$jwks = file_get_contents("https://$domain/{$this->poolId}/.well-known/jwks.json");
try {
$jwks = collect(json_decode($jwks, true, 512, JSON_THROW_ON_ERROR)['keys']);
$jwks = $jwks->keyBy('kid')->map(static function ($current) {
return (new JWKConverter())->toPEM($current);
})->toArray();
$jwt = new JWT();
$jwt::$leeway = config('app.jwt_leeway');
return $jwt::decode($token, $jwks, ['RS256']);
} catch (\JsonException $e) {
return false;
}
}
/**
* #return false|mixed
*/
private function exchangeTokens()
{
$params = [
'grant_type' => 'authorization_code',
'client_id' => $this->clientId,
'code' => request()->input('code'),
'redirect_uri' => 'https://localhost/',
];
$result = $this->guzzle->post("https://{$this->domain}/oauth2/token?".http_build_query($params), [
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'Basic '.base64_encode("{$this->clientId}:{$this->clientSecret}")
]
]);
if ($result->getStatusCode() === 200) {
try {
return json_decode($result->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
return false;
}
}
return false;
}
/**
* #return void
*/
private function sendToLoginPage(): void
{
$query = http_build_query([
'client_id' => $this->clientId,
'response_type' => 'code',
'redirect_uri' => 'https://localhost/'
]);
header("Location: https://{$this->domain}/login?$query&scope=email+openid");
exit;
}
/**
* #return void
*/
public function logout(): void
{
dd(debug_backtrace());
}
}

Hi i get this "Error Call to a member function update() on boolean" when updating image in Laravel

Can someone help me with this Laravel problem?
I get Error Call to a member function update() on boolean, when I edit and update Image for an ad.
So when I create a new Ad the image will store but when updating the Ad with a new image the old image stays and I get this error for this line in storeImage.
private function storeImage($ad){
if (request()->has('image')) {
$ad->update([
'image' => request()->image->store('uploads', 'public'),
]);
$image = Image::make(public_path('storage/' . $ad->image))->fit(300, 300, null, 'top-left');
$image->save();
}
}
This is my AdsController
public function edit($id)
{
$ad = Ad::find($id);
return view('ads.edit_ad', compact('ad', 'id'));
}
public function update(Request $request, $id)
{
$ad = Ad::find($id);
$user_id = Auth::user()->id;
$rules = [
'ad_title' => 'required',
'ad_description' => 'required',
'purpose' => 'required',
'image' => 'sometimes|file|image|max:5000',
];
$this->validate($request, $rules);
$title = $request->ad_title;
$is_negotialble = $request->negotiable ? $request->negotiable : 0;
$data = [
'title' => $request->ad_title,
'description' => $request->ad_description,
'type' => $request->type,
'price' => $request->price,
'purpose' => $request->purpose,
'address' => $request->address,
'user_id' => $user_id,
];
$updated_ad = $ad->update($data);
if ($updated_ad){
$this->storeImage($updated_ad);
}
return redirect()->back()->with('success','Ad Updated');
}
public function destroy(Ad $ad)
{
$ad->delete();
return redirect()->back()->with('success','Ad Deleted');
}
/**
* Listing
*/
public function listing(Request $request){
$ads = Ad::all();
$roles = Role::all();
return view('listing', compact('ads'));
}
public function myAds(){
$user = Auth::user();
$ads = $user->ads()->orderBy('id', 'desc')->paginate(20);
return view('ads.my_ads', compact('ads'));
}
private function storeImage($ad){
if (request()->has('image')) {
$ad->update([
'image' => request()->image->store('uploads', 'public'),
]);
$image = Image::make(public_path('storage/' . $ad->image))->fit(300, 300, null, 'top-left');
$image->save();
}
}
At the bottom of your update() function, try to change this:
if ($updated_ad){
$this->storeImage($updated_ad);
}
to this:
if ($updated_ad){
$this->storeImage($ad);
}
The Eloquent models update() function returns a boolean of whether the update completed successfully. After running the update() function on the $ad variable, it is mutated and contains the updated data, and you can use it as an argument in your storeImage() function.

How create validation of nested model to call in request in Laravel

I have a database table structure like the following (in laravel):
user 1-1 profile
partner 1-1 profile
user 1-N department
I want to send a save request (post) and have the user validated in UserRequest and have this class call a ProfileRequest.
Is this possible to do?
Is there any way to perform validations of related models?
Class of User request example:
class UserRequest extends FormRequest
{
/**
* 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()
{
return [
'name' => 'required|string',
'lastname' => 'required|string',
'user' => [
'required',
Rule::unique('users')->ignore($this),
],
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed',
'headquarter_id' => 'required'
//Validation of profile
];
}
}
Example of controller User
public function store(AdAszaUserRequest $request)
{
$input = $request->all();
$validated = $request->validated();
$input['password'] = \Hash::make($request['password']);
//
$departmentidList = array_column($input['departments'], 'id');
$AszaUser = AdAszaUser::create($input);
$models = [];
foreach ($input['departments'] as $model) {
$models[] = new AdDepartment($model);
}
///important: this line add departments without validation
$AszaUser->departments()->saveMany($models);
$AszaUser->departments()->sync($departmentidList);
return response($AszaUser, 201);
}
And Request Of deparment:
<?php
namespace App\Http\Requests\AD;
use Illuminate\Foundation\Http\FormRequest;
class AdDepartmentRequest extends FormRequest
{
/**
* 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()
{
return [
'name' => 'required|string|unique:ad_departments',
'internal_name' => 'required|string|unique:ad_departments'
];
}
}
Example of Json send in post:
{
"id":2,
"name": "Admin2",
"email": "test#gmail.com",
"lastname": "test",
"user": "test",
"password": "test",
"password_confirmation": "test",
"headquarter_id": 1,
"lastname":"test",
"remember_token": "1",
"email_verified_at": "test",
"headquarter": {
"id": 1,
"name": "ASZA ZARAGOZA",
"description": "Sede en Zaragoza",
},
"departments": [
{
"id": 1,
"name": "Intérpretes",
"internal_name": "Interprete",
"description": "Departamento de Intérpretes",
"display_id": "01",
"pivot": {
"user_id": 1,
"department_id": 1
}
},
{
"id": 10,
"name": "Psicología"
}
]
}
Can I call the DepartmentRequest to validate the elements passed in the department array?
UPDATE: 1
I don't think it is necessary, but of course it is possible
public function store(AdAszaUserRequest $request)
{
$input = $request->all();
$validated = $request->validated();
$input['password'] = \Hash::make($request['password']);
//
$departmentidList = array_column($input['departments'], 'id');
$AszaUser = AdAszaUser::create($input);
$models = [];
foreach ($input['departments'] as $model) {
/** To check validation for single item */
$validator = Validator::make($model, (new StoreEventRequest)->rules());
if (!$validator->fails()) {
$models[] = new AdDepartment($model);
} else {
/** Something wrong */
/** $errors = $validator->errors(); */
}
}
/** To check validation for array of data
$validator = Validator::make($request->only(['departments']), collect(array_map(function ($rules, $field): array {
return ['departments.*.' . $field => $rules];
}, (new StoreEventRequest)->rules()))
->collapse()
->toArray()); */
/**
* And then do what you want to do with this object
* $errors = $validator->errors();
*
if ($validator->fails()) {
return redirect('some_url')
->withErrors($validator);
} */
$AszaUser->departments()->saveMany($models);
$AszaUser->departments()->sync($departmentidList);
return response($AszaUser, 201);
}
For more information see documentation https://laravel.com/docs/6.x/validation#manually-creating-validators
UPDATE: 2
If you need to separate your request classes, you also can do it like so
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()
{
return array_merge([
'name' => 'required|string',
'lastname' => 'required|string',
'user' => [
'required',
Rule::unique('users')->ignore($this),
],
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed',
'headquarter_id' => 'required'
//Validation of profile
/** Validate of departments */
'departments' => 'nullable|array',
], collect(array_map(function ($rules, $field): array {
return ['departments.*.' . $field => $rules];
}, (new StoreEventRequest)->rules()))
->collapse()
->toArray())
->toArray();
}
}
Yes you can do it like this
public function rules() {
return [
'name' => 'required|string',
'lastname' => 'required|string',
'user' => [ 'required', Rule::unique('users')->ignore($this), ],
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed',
'headquarter_id' => 'required',
//Validation of profile
'profile.some_field' => 'required',
//For array of objects
'profile.*.some_field' => 'required',
];
}

how to logout from rest api in yii2

I m calling logout api in yii2 framework using logout action
url :http://localhost/mobile/public/api/v1/logout
Code:
public function actionLogout()
{
$user_t = Yii::$app->user->logout();
return $this->apiItem(array(),'Logout Successfully');
}
but after calling logout api
when after this i calling view profile api it returns user data
public function actionViewprofile()
{
$user = Yii::$app->user->identity;
$profile_fetch = [
'firstname' => $user['member_fname'],
'lastname' => $user['member_lname'],
'gender' => $user['member_gender'],
'dateofbirth' => $user['member_dob']
];
return $this->apiItem($profile_fetch);
}
where apitem is a function for json parameter format
/**
* Api Item response
*/
public function apiItem($data, $message = false,$flag = false )
{
Yii::$app->response->statusCode = 200;
return [
'statusCode' => 200,
'message' => $message ? $message : 'Data retrieval successful',
'data' => $data,
'flag' => $flag
];
}
Clear the token from DB and clear the user session
$userID = Yii::$app->session->get('userID');
$userModel = User::find()->where(['id'=>$userID])->one();
if(!empty($userModel))
{
$userModel->token=NULL;
$userModel->save(false);
}
Yii::app()->user->logout(false);

Updating Data to db in Laravel

I have a problem while updating data to the database in laravel. Normally the data gets inserted to the database with store function(POST request). But the update POST request doesn't make a update to the database. And my code is
routes.php
Route::resource('Customer','CustomerDetailController');
Route::get('/', function () {
return view('welcome');
});
Model
CustomerDetail.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class CustomerDetail extends Model
{
//
public $table = "customer_details";
protected $fillable = [
'Name', 'Contact_Number','Address','NoOfCans','Price'
];
}
Controller
CustomerDetailController.php
<?php
namespace App\Http\Controllers;
use App\CustomerDetail;
use Illuminate\Http\Request;
use App\Http\Requests;
class CustomerDetailController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
//
$Customers=CustomerDetail::all();
if($Customers){
$response = [
'msg' => 'Customer Found',
'Customer' => $Customers
];
return response()->json($response, 201);
}
else {
$response=[
'msg'=>'No Customer Found',
];
return response()->json($response, 404);
}
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
$this->validate($request, [
'Name' => 'required',
'Contact_Number' => 'required',
'Address' => 'required',
'NoOfCans' => 'required',
'Price' => 'required',
]);
$Name = $request->input('Name');
$Contact_Number = $request->input('Contact_Number');
$Address = $request->input('Address');
$NoOfCans = $request->input('NoOfCans');
$Price = $request->input('Price');
$CustomerDetail = new CustomerDetail([
'Name' => $Name,
'Contact_Number' => $Contact_Number,
'Address' => $Address,
'NoOfCans' => $NoOfCans,
'Price' => $Price
]);
if ($CustomerDetail->save()) {
$response = [
'msg' => 'Customer Created',
'Customer' => $CustomerDetail
];
return response()->json($response, 201);
}
$response = [
'msg' => 'An Error Occured',
];
return response()->json($response, 502);
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
$Customer=CustomerDetail::find($id);
if($Customer){
$response = [
'msg' => 'Customer Found',
'Customer' => $Customer
];
return response()->json($response, 201);
}
else {
$response=[
'msg'=>'Customer Not Found',
];
return response()->json($response, 404);
}
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
$this->validate($request, [
'Name' => 'required',
'Contact_Number' => 'required',
'Address' => 'required',
'NoOfCans' => 'required',
'Price' => 'required',
]);
$CustomerUpdate = CustomerDetail::find($id);
$CustomerUpdate->Name = $request->input('Name');
$CustomerUpdate->Contact_Number = $request->input('Contact_Number');
$CustomerUpdate->Address = $request->input('Address');
$CustomerUpdate->NoOfCans = $request->input('NoOfCans');
$CustomerUpdate->Price = $request->input('Price');
if ($CustomerUpdate->save()) {
$response = [
'msg' => 'Customer Updated',
'Customer' => $CustomerUpdate
];
return response()->json($response, 201);
}
$response = [
'msg' => 'An Error Occured',
];
return response()->json($response, 502);
}
}
And the data gets inserted when i make a post request for store as
{
"Name":"MRK",
"Contact_Number":"1234567890",
"Address":"newyork",
"NoOfCans":"3",
"Price":"20"
}
with Output
{
"msg": "Customer Created",
"Customer": {
"Name": "MRK",
"Contact_Number": "1234567890",
"Address": "newyork",
"NoOfCans": "3",
"Price": "20",
"updated_at": "2016-11-20 17:17:55",
"created_at": "2016-11-20 17:17:55",
"id": 4
}
}
But when I try to Update the same data with PUT or PATCH request
Input
{
"Name":"MRK",
"Contact_Number":"9876543210",
"Address":"Chennai",
"NoOfCans":"2",
"Price":"20"
}
I get the JSON Response as Updated but I don't see the values got Updated in the database
{
"msg": "Customer Updated",
"Customer": {
"ID": 4,
"created_at": "2016-11-20 17:17:55",
"updated_at": "2016-11-20 17:20:44",
"Name": "MRK",
"Contact_Number": "9876543210",
"Address": "Chennai",
"NoOfCans": "2",
"Price": "20"
}
}
The database contains the values which are originally inserted at first. No updated values are inserted into the database
enter image description here
Update code
public function update(Request $request, $id)
{
//
$this->validate($request, [
'Name' => 'required',
'Contact_Number' => 'required',
'Address' => 'required',
'NoOfCans' => 'required',
'Price' => 'required',
]);
$CustomerUpdate = CustomerDetail::find($id);
$CustomerUpdate->Name = $request->input('Name');
$CustomerUpdate->Contact_Number = $request->input('Contact_Number');
$CustomerUpdate->Address = $request->input('Address');
$CustomerUpdate->NoOfCans = $request->input('NoOfCans');
$CustomerUpdate->Price = $request->input('Price');
if ($CustomerUpdate->save()) {
$response = [
'msg' => 'Customer Updated',
'Customer' => $CustomerUpdate
];
return response()->json($response, 201);
}
$response = [
'msg' => 'An Error Occured',
];
return response()->json($response, 502);
}
The problem is with the model. I have not mentioned the primary key in my model so the database is not updated
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class CustomerDetail extends Model
{
//
public $table = "customer_details";
Protected $primaryKey = "ID";
protected $fillable = [
'Name', 'Contact_Number','Address','NoOfCans','Price'
];
}

Categories