L5 Paypal Integration - Finding Controller method - laravel

I am trying to integrate Paypal into my Laravel 5 site using this package:
http://packalyst.com/packages/package/netshell/paypal
When I go to: /paypal/checkout though, I get this error:
InvalidArgumentException in UrlGenerator.php line 561: Action
App\Http\Controllers\PayPalController#getDone not defined.
This is my route:
Route::get('/paypal/checkout', [
'as' => 'get-paypal-checkout', 'uses' => 'PayPalController#getCheckout'
]);
And this is the PayPalController:
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Paypal;
use Redirect;
use Illuminate\Http\Request;
class PayPalController extends Controller {
private $_apiContext;
public function __construct() {
$this->_apiContext = PayPal::ApiContext(
config('services.paypal.client_id'),
config('services.paypal.secret'));
$this->_apiContext->setConfig(array(
'mode' => 'sandbox',
'service.EndPoint' => 'https://api.sandbox.paypal.com',
'http.ConnectionTimeOut' => 30,
'log.LogEnabled' => true,
'log.FileName' => storage_path('logs/paypal.log'),
'log.LogLevel' => 'FINE'
));
}
public function getCheckout() {
$payer = PayPal::Payer();
$payer->setPaymentMethod('paypal');
$amount = PayPal:: Amount();
$amount->setCurrency('EUR');
$amount->setTotal(42); // This is the simple way,
// you can alternatively describe everything in the order separately;
// Reference the PayPal PHP REST SDK for details.
$transaction = PayPal::Transaction();
$transaction->setAmount($amount);
$transaction->setDescription('What are you selling?');
$redirectUrls = PayPal:: RedirectUrls();
$redirectUrls->setReturnUrl(action('PayPalController#getDone'));
$redirectUrls->setCancelUrl(action('PayPalController#getCancel'));
$payment = PayPal::Payment();
$payment->setIntent('sale');
$payment->setPayer($payer);
$payment->setRedirectUrls($redirectUrls);
$payment->setTransactions(array($transaction));
$response = $payment->create($this->_apiContext);
$redirectUrl = $response->links[1]->href;
return Redirect::to( $redirectUrl );
}
public function getDone(Request $request) {
$id = $request->get('paymentId');
$token = $request->get('token');
$payer_id = $request->get('PayerID');
$payment = PayPal::getById($id, $this->_apiContext);
$paymentExecution = PayPal::PaymentExecution();
$paymentExecution->setPayerId($payer_id);
$executePayment = $payment->execute($paymentExecution, $this->_apiContext);
// Clear the shopping cart, write to database, send notifications, etc.
// Thank the user for the purchase
return view('checkout.done');
}
public function getCancel() {
// Curse and humiliate the user for cancelling this most sacred payment (yours)
return view('checkout.cancel');
}
}
Any help would be appreciated.
Thanks

Although laravel allows you to generate url's to actions. I researched what happened in the code.
In the UrlGenerator class:
/**
* Get the URL to a controller action.
*
* #param string $action
* #param mixed $parameters
* #param bool $absolute
* #return string
*
* #throws \InvalidArgumentException
*/
public function action($action, $parameters = [], $absolute = true)
{
if ($this->rootNamespace && !(strpos($action, '\\') === 0)) {
$action = $this->rootNamespace.'\\'.$action;
} else {
$action = trim($action, '\\');
}
if (!is_null($route = $this->routes->getByAction($action))) {
return $this->toRoute($route, $parameters, $absolute);
}
throw new InvalidArgumentException("Action {$action} not defined.");
}
It will search for a defined route with the specified action and in fact returns a link based on that route. So you would have to define the route to PayPayController#getDone for it to work.

Related

Checking Authorization From Laravel Controller

I'm making a website project using Laravel Jetstream with stack inertia. In this project, I added the package spatie/laravel-permission. When I try to authorize the controller to protect against malicious users from accessing the URL directly I create a trait that I can call for any controller I want and here is the code
trait Authorizable
{
private $abilities = [
'index' => 'view',
'edit' => 'edit',
'show' => 'view',
'update' => 'edit',
'create' => 'add',
'store' => 'add',
'destroy' => 'delete',
];
/**
* Override of callAction to perform the authorization before
*
* #param $method
* #param $parameters
* #return mixed
*/
public function callAction($method, $parameters)
{
if ($ability = $this->getAbility($method)) {
$this->authorize($ability);
}
return parent::callAction($method, $parameters);
}
public function getAbility($method)
{
$routeName = explode('.', \Request::route()->getName());
$action = Arr::get($this->getAbilities(), $method);
return $action ? $action . '_' . $routeName[0] : null;
}
private function getAbilities()
{
return $this->abilities;
}
public function setAbilities($abilities)
{
$this->abilities = $abilities;
}
}
after that, I call the trait on the controller
use App\Http\Controllers\Controller;
use App\Traits\Authorizable;
use Illuminate\Http\Request;
use Inertia\Inertia;
class UsersController extends Controller
{
use Authorizable;
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return Inertia::render('Admin/Users/Index');
}
this is my route
Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified',])->group(function () {
Route::middleware(['role:Admin'])->prefix('admin')->group(function () {
Route::resource('users', AdminUsers::class, ['as' => 'admin']);
});
});
but unfortunately, after I try to access the URL /admin/users, I get a message 403: THIS ACTION IS UNAUTHORIZED.
I have no idea to solve this problem, could anyone here to help me to solve this problem or give me some advice? it will save me.
Thank You

Back end to Back end API request and response

I have an app built on laravel 8 with a Vue Spa front end, using Sanctum.
I have a controller method that requests from another Laravel project (using its Sanctum API) so essentially, the Spa requests from Laravel 1, which requests from Laravel 2 project.
Following the responses from L2 project back, the Controller method on L2 is:
public function popular(Request $request)
{
$limit = 20;
if ($request->has('limit')) {
$limit = $request->limit;
}
$perPage = 20;
if ($request->has('per_page')) {
$limit = $request->per_page;
}
if ($request->has('page')) {
$articles = $request
->user()
->articles()
->activeArticles()
->select('articles.uuid', 'articles.title')
->orderBy('articles.views', 'DESC')
->simplePaginate($perPage);
} else {
$articles = $request
->user()
->articles()
->activeArticles()
->select('articles.uuid', 'articles.title')
->orderBy('articles.views', 'DESC')
->limit($limit)
->get();
}
return $articles;
}
This response is received by L1 Controller method, and sent back to the Spa like this:
public function popular(Request $request)
{
$apiEndPoint = self::$apiBaseUrl . '/article/popular';
$response = self::$httpRequest->get($apiEndPoint, $request->query());
if (!$response->successful()) {
return $this->setMessage(trans('help.api_error'))->send();
}
$body = $response->getBody();
return response(['data' => $body]);
}
With this return:
return response(['data' => $body]);
I get and empty data object:
{
data: {}
}
And with this return:
return response($body);
I get the payload as text / string:
[{"id":15,"uuid":"c6082143-0f34-443b-9447-3fa57ed73f48","name":"dashboard","icon":"database","active":1,"owned_by":2,"product_id":4,"created_at":"2021-12-23T11:46:35.000000Z","updated_at":"2021-12-23T11:46:35.000000Z"},{"id":16,
How do I return the $body as JSON to the Spa?
UPDATE: I tried suggestions below, but the result is still exception.
return response()->json($body);
Returns:
"message": "json_decode(): Argument #1 ($json) must be of type string, GuzzleHttp\\Psr7\\Stream given",
So getting the body in getBody() returns a string I understood.
If I Log the $body I get:
$body = $response->getBody();
Log::info($body);
[2021-12-25 23:15:36] local.INFO: {"current_page":2,"data":[{"uuid":"aa4a47bf-4975-4e78-868a-103398934504","title":"Ritchie-Hoeger"},
Thanks for any help and happy festive season.
API Responser
First create a trait in laravel in 'app\Traits\ApiResponser.php'
<?php
namespace App\Traits;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
trait ApiResponser{
public function set_response($data, $status_code, $status, $details)
{
$resData = response(json_encode(
[
'status' => $status, // true or false
'code' => $status_code,
'data' => $data,
'message' => $details
]
), 200)
->header('Content-Type', 'application/json');
$data = [];
return $resData;
}
}
Second in any controller call this trait's function set_response()
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Traits\ApiResponser;
use Illuminate\Http\Request;
class ListController extends Controller
{
use ApiResponser;
public function getAllUserList(Request $request)
{
$data = User::select('id', 'name', 'email')->get();
return $this->set_response(['data' => $data], 200,'success', ['User list']);
}
}
Output will be like this
use the json helper function
return response()->json($body);
or
use Response;
return Response::json($body);
This will create an instance of \Illuminate\Routing\ResponseFactory. See the phpDocs for possible parameters below:
/**
* Return a new JSON response from the application.
*
* #param string|array $data
* #param int $status
* #param array $headers
* #param int $options
* #return \Symfony\Component\HttpFoundation\Response
* #static
*/
public static function json($data = array(), $status = 200, $headers = array(), $options = 0){
return \Illuminate\Routing\ResponseFactory::json($data, $status, $headers, $options);
}
I needed to ->getContents() after the ->getBody()
$body = $response->getBody()->getContents();
All good again...

Access Controller resources based on Laravel/Spatie Permissions

I am working on Laravel passport api in which i am using spatie package for user role's and permission's. I have to perform certain operation ('store','view','update','delete') based on user permission's.
For this purpose i have created a trait and used in controller but it is not working correctly.
On every api request it throw's an exception "This action is unauthorized" either the user has permission or not.
Authorize Trait :
<?php
namespace App;
/*
* A trait to handle authorization based on users permissions for given controller
*/
trait Authorizable
{
/**
* Abilities
*
* #var array
*/
private $abilities = [
'index' => 'view',
'edit' => 'edit',
'show' => 'view',
'update' => 'edit',
'create' => 'add',
'store' => 'add',
'destroy' => 'delete'
];
/**
* Override of callAction to perform the authorization before it calls the action
*
* #param $method
* #param $parameters
* #return mixed
*/
public function callAction($method, $parameters)
{
if( $ability = $this->getAbility($method) ) {
$this->authorize($ability);
}
return parent::callAction($method, $parameters);
}
/**
* Get ability
*
* #param $method
* #return null|string
*/
public function getAbility($method)
{
$routeName = explode('.', \Request::route()->getName());
$action = array_get($this->getAbilities(), $method);
return $action ? $action . '_' . $routeName[0] : null;
}
/**
* #return array
*/
private function getAbilities()
{
return $this->abilities;
}
/**
* #param array $abilities
*/
public function setAbilities($abilities)
{
$this->abilities = $abilities;
}
}
Routes:
Route::middleware('auth:api')->group(function () {
Route::post('user', 'ApiController#user');
Route::post('view_department', 'DepartmentController#index');
Route::post('add_department', 'DepartmentController#store');
Route::post('edit_department', 'DepartmentController#update');
Route::post('delete_department', 'DepartmentController#destroy');
Route::post('/logout', 'ApiController#logout');
}); // auth middleware ends
Controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\User;
use App\Authorizable;
use Illuminate\Support\Facades\Validator;
use App\Department;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class DepartmentController extends Controller
{
use Authorizable;
//
public function index(Request $request) {
// return response
return response()->json([
'success' => 'You have the permission to view departments!']);
}
//
public function store(Request $request) {
// validate the posted data
$validator = Validator::make($request->all(), [
'name' => 'required|string|unique:departments',
]);
// return errors
if ($validator->fails())
{
return response(['errors'=>$validator->errors()->all()]);
}
$department = new Department;
$department->name = $request->name;
$department->save();
// return response
return response()->json([
'success' => 'Successfully created department!']);
}
}
I am badly stack at it, don't know where i am going wrong. I would highly appreciate if anyone guide me through this.
Thanks,

Laravel testing Failed asserting that two strings are equal

I am very new to testing, but have now found it essential to automate my testing.
I have a test that is working fine up until it gets to the link '/cart' it gets to the link '/cart' no problem, but any other link I try to click afterwards always ends up back at the cart.
here is my error after trying to navigate away from the cart.
Failed asserting that two strings are equal.
--- Expected
+++ Actual
## ##
-'http://ngwenya-mtb.dev/events'
+'http://ngwenya-mtb.dev/cart'
And here is my test script
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase {
//use Illuminate\Foundation\Testing\WithoutMiddleware;
//use DatabaseTransactions;
//use withoutMiddleware;
//use DatabaseMigrations;
/**
*
* A basic functional test example.
* Please choose a unique email address for your new participant
* #return void
*/
public function testNewUserRegistration() {
$this->visit('http://ngwenya-mtb.dev/')
// View Event
->click('View event details')
->seePageIs('/event?id=30')
->click('#enter-race47')
->press('Enter yourself to this race')
->seePageIs('/events/courses/register/addtocart')
//->withSession(['This email is already registered' => 'alert-danger'])
/////////////////////////////////////////////
// Fill the register for for new user
/////////////////////////////////////////////
->type('Bingo', 'first_name')
->type('11111111', 'password')
->type('11111111', 'password_confirmation')
->type(''.substr(md5(time()), 0, 12).'#tesing.com', 'email')
//->check('terms')
->select('Male', 'gender')
->select('1985', 'year')
->select('07', 'month')
->select('21', 'day')
->select('Small', 'shirt_size')
->select('Swaziland ID', 'id_type')
->type('badassnumber', 'id_number')
->select('Swazi', 'nationality')
//Contact details Physical
->type('Dawlish', 'town_physical')
->select('Swaziland', 'country_physical')
->type('864741', 'phone_cell')
//Emergency contact details 1
->type('Simon', 'emergency_contact_1')
->type('Brother', 'emergency_relationship_1')
->type('864741', 'emergency_phone_1');
$this->press('Register');
$this->seePageIs('/cart');
/////////////////////////////////////////////
// Add a new user
/////////////////////////////////////////////
$this->visit('http://ngwenya-mtb.dev/');
$this->click('#events-link')
->seePageIs('/events');
dd($this->response->getContent());exit;
$this->click('#event-30');
$this->seePageIs('/event?id=30');
$this->click('#enter-race48');
$this->press('Enter someone else to this race');
$this->seePageIs('/events/courses/register/addtocart');
}
}
Everything is working fine up until this comment
/////////////////////////////////////////////
// Add a new user
/////////////////////////////////////////////
Here is my Registration controller
<?php
namespace App\Http\Controllers;
use Vinkla\Hashids\HashidsManager;
use Illuminate\Routing\Controller as BaseController;
use Sentinel\FormRequests\RegisterRequest;
use Sentinel\FormRequests\EmailRequest;
use Sentinel\FormRequests\ResetPasswordRequest;
use Sentinel\Repositories\Group\SentinelGroupRepositoryInterface;
use Sentinel\Repositories\User\SentinelUserRepositoryInterface;
use Sentinel\Traits\SentinelRedirectionTrait;
use Sentinel\Traits\SentinelViewfinderTrait;
use Sentry;
use View;
use Request;
use Event;
use Redirect;
use Session;
use Config;
use App\Models\Users;
use Illuminate\Support\Facades\Input;
use Gloudemans\Shoppingcart\Facades\Cart;
class RegistrationController extends BaseController
{
/**
* Traits
*/
use SentinelRedirectionTrait;
use SentinelViewfinderTrait;
/**
* Constructor
*/
public function __construct(
SentinelUserRepositoryInterface $userRepository,
SentinelGroupRepositoryInterface $groupRepository,
HashidsManager $hashids
) {
$this->userRepository = $userRepository;
$this->groupRepository = $groupRepository;
$this->hashids = $hashids;
}
/**
* Show the registration form, if registration is allowed
*
* #return Response
*/
public function registration()
{
// Is this user already signed in? If so redirect to the post login route
if (Sentry::check()) {
return $this->redirectTo('session_store');
}
//If registration is currently disabled, show a message and redirect home.
if (! config('sentinel.registration', false)) {
return $this->redirectTo(['route' => 'home'], ['error' => trans('Sentinel::users.inactive_reg')]);
}
// All clear - show the registration form.
return $this->viewFinder(config('sentinel.view.user_register', 'Sentinel::users.register'));
}
/**
* Process a registration request
*
* #return Response
*/
public function register(RegisterRequest $request)
{
// Gather input
$data = $request->all();
// collect cart items
$email = Input::get('email');
$course_id = Input::get('course_id');
$event_name = Input::get('event_name');
$entry_fee = Input::get('entry_fee');
// check user exists
if (Users::where('email', '=', $email)->exists()) {
// user found
$request->session()->flash('alert-danger', 'Warning: This email is already registered.');
Input::flash();
return View::make('sentinel.users.register')
->with('course_id',$course_id)
->with('event_name',$event_name)
->with('entry_fee',$entry_fee);
}
// Add user and course to cart
if ($course_id) {
$firstUserRowId = Cart::add($course_id, $event_name , 1, $entry_fee, [
'first_name' => Input::get('first_name'),
'last_name' => Input::get('last_name'),
'email' => Input::get('email'),
'no_email' => 0,
'master_user' => 1,
'gender' => Input::get('gender'),
'dob' => Input::get('dob'),
'shirt_size' => Input::get('shirt_size'),
'id_type' => Input::get('id_type'),
'id_number' => Input::get('id_number'),
'nationality' => Input::get('nationality'),
'phone_cell' => Input::get('phone_cell'),
'town_physical' => Input::get('town_physical'),
'country_physical' => Input::get('country_physical'),
'emergency_contact_1' => Input::get('emergency_contact_1'),
'emergency_relationship_1' => Input::get('emergency_relationship_1'),
'emergency_phone_1' => Input::get('emergency_phone_1'),
]);
}
// get email from request
$email = $request->only('email');
foreach ($email as $userModel) {}
// Edit date of birth from request
$year = Input::get('year');
$month = Input::get('month');
$day = Input::get('day');
$dob = $year.'-'.$month.'-'.$day;
$data['dob'] = $dob;
// Attempt Registration
$result = $this->userRepository->store($data);
// Log user in
FunctionsController::loginUser($userModel);
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_complete', $result);
}
/**
* Activate a new user
*
* #param int $id
* #param string $code
*
* #return Response
*/
public function activate($hash, $code)
{
// Decode the hashid
$id = $this->hashids->decode($hash)[0];
// Attempt the activation
$result = $this->userRepository->activate($id, $code);
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_activated', $result);
}
/**
* Show the 'Resend Activation' form
*
* #return View
*/
public function resendActivationForm()
{
return $this->viewFinder('Sentinel::users.resend');
}
/**
* Process resend activation request
* #return Response
*/
public function resendActivation(EmailRequest $request)
{
// Resend the activation email
$result = $this->userRepository->resend(['email' => e($request->get('email'))]);
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_resend', $result);
}
/**
* Display the "Forgot Password" form
*
* #return \Illuminate\View\View
*/
public function forgotPasswordForm()
{
return $this->viewFinder('Sentinel::users.forgot');
}
/**
* Process Forgot Password request
* #return Response
*/
public function sendResetPasswordEmail(EmailRequest $request)
{
// Send Password Reset Email
$result = $this->userRepository->triggerPasswordReset(e($request->get('email')));
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_reset_triggered', $result);
}
/**
* A user is attempting to reset their password
*
* #param $id
* #param $code
*
* #return Redirect|View
*/
public function passwordResetForm($hash, $code)
{
// Decode the hashid
$id = $this->hashids->decode($hash)[0];
// Validate Reset Code
$result = $this->userRepository->validateResetCode($id, $code);
if (! $result->isSuccessful()) {
return $this->redirectViaResponse('registration_reset_invalid', $result);
}
return $this->viewFinder('Sentinel::users.reset', [
'hash' => $hash,
'code' => $code
]);
}
/**
* Process a password reset form submission
*
* #param $hash
* #param $code
* #return Response
*/
public function resetPassword(ResetPasswordRequest $request, $hash, $code)
{
// Decode the hashid
$id = $this->hashids->decode($hash)[0];
// Gather input data
$data = $request->only('password', 'password_confirmation');
// Change the user's password
$result = $this->userRepository->resetPassword($id, $code, e($data['password']));
// It worked! Use config to determine where we should go.
return $this->redirectViaResponse('registration_reset_complete', $result);
}
}
It seems that when you click and a your link "Register" your redirection fail, so check if you have multiple "Register" links/buttons, and if they are pointing to the right URL
And for easiest debugging, you should make less assertions per Test, you will gain in visibility :)

L5 Using Hashid in middleware returns null

I am using hashid to hash the id parameters in url. I have it set up in my model to automatically hash the id. This is working fine. My problem is decoding the hash in a middleware returns null. I'm not sure if this is a problem with my middleware or because of the hashing.
Model:
public function getIdAttribute($value)
{
$hashids = new \Hashids\Hashids(env('APP_KEY'),10);
return $hashids->encode($value);
}
Middleware:
<?php
namespace App\Http\Middleware;
use Closure;
class HashIdsDecode
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
dd($request->id); //Returns null on show method - example localhost:8000/invoices/afewRfshTl
if($request->has('id'))
{
$hashids = new \Hashids\Hashids(env('APP_KEY'),10);
dd($hashids->decode($request->input('id')));
}
return $next($request);
}
}
Route:
Route::resource('invoices','InvoiceController');
Controller:
public function show($id)
{
$invoice = Invoice::find($id);
return view('invoices.show', [
'invoice' => $invoice,
'page_title' => ' Invoices',
'page_description' => 'View Invoice',
]);
}
NOTE: if I bypass the middleware and do it directly in my controller like this it works but it requires me to repeat myself over and over and probably not the best way to do it.
public function show($id)
{
$hashids = new \Hashids\Hashids(env('APP_KEY'),10);
$invoiceId = $hashids->decode($id)[0];
$invoice = Invoice::find($invoiceId);
return view('invoices.show', [
'invoice' => $invoice,
'page_title' => ' Invoices',
'page_description' => 'View Invoice',
]);
}
Personally, I would be more inclined to write a model trait. You can then use the trait on only the models required, rather than assuming every ID argument in a request is a Hash ID.
E.g.
namespace App\Models\Traits;
use Hashids\Hashids;
use Illuminate\Database\Eloquent\Builder;
trait HashedId
{
public function scopeHashId(Builder $query, $id)
{
$hashIds = new Hashids(env('APP_KEY'), 10);
$id = $hashIds->decode($id)[0];
return $query->where('id', $id);
}
}
Then to use it, you'd use the trait on your Invoice model (edit):
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
use \App\Models\Traits\HashedId;
// ...
}
And execute the following query in your controller:
public function show($id)
{
$invoice = Invoice::hashId($id)->firstOrFail();
return view('invoices.show', [
'invoice' => $invoice,
'page_title' => ' Invoices',
'page_description' => 'View Invoice',
]);
}

Resources