I used Laravel Cashier to handle my user's subscription, however when I try to do the basic cancellation $user->subscription('main')->cancel(), an exception is being thrown
BadMethodCallException: Call to undefined method Illuminate\Database\Query\Builder::asStripeCustomer() in
\try\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php:2483
Stack trace:
\try\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1470): Illuminate\Database\Eloquent\Builder->__call('asStripeCustome...', Array)
\try\vendor\laravel\cashier\src\Subscription.php(345): Illuminate\Database\Eloquent\Model->__call('asStripeCustome...', Array)
\try\vendor\laravel\cashier\src\Subscription.php(256): Laravel\Cashier\Subscription->asStripeSubscription()
I setup the Model correctly and I uses the Billable trait so I really have no idea what's really causing this error
App\User.php
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Cashier\Billable;
class User extends Authenticatable{
use Billable;
...
}
App\Http\Controllers\UserController.php
public function cancelSubscription(Request $request)
{
$user = $request->user();
try {
if ($user->subscription('main')->onTrial()) {
$user->subscription('main')->cancelNow();
} else {
$user->subscription('main')->cancel();
}
} catch (\Exception $e) {
\Log::error($e);
return [
'success' => 0,
'message' => "Something went wrong while trying cancel your subscription. Please try again later."
];
}
Any help and hints will be greatly appreciated, thanks in advance!
My bad, I just found out that it was actually with my stripe configuration on /config/services.php as I have two models for my Users (because I'm also using another package other than laravel-cashier to handle payments via Authorize.net on which I ended up creating different Models for them to work)
'stripe' => [
// 'model' => App\AnetUser::class, => this actually caused the error as
// ->asStripeCustomer() doesn't exists on an Authorize.net's Billable trait
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
],
I feel so stupid. XD
Hi I've never worked with Laravel Cashier before but, I think the root of your problem might be that you are accessing user from the request, therefore it's not a user instance thats why it triggers undefined methods errors.
So creating a user instance should probably work out for you:
Note: I don't know if $request->user is primary key or whole user instance so I added different solutions
public function cancelSubscription(Request $request)
{
// if $request->user is the user instance you can do this:
$user = App\User::findOrFail($request->user->id);
// if $request->user was any other field from user you could retrieve
// the user using something like->
// App\User::where('fieldName', 'LIKE', $request->user)->firstOrFail();
try {
if ($user->subscription('main')->onTrial()) {
$user->subscription('main')->cancelNow();
} else {
$user->subscription('main')->cancel();
}
} catch (\Exception $e) {
\Log::error($e);
return [
'success' => 0,
'message' => "Something went wrong while trying cancel your subscription. Please try again later."
];
}
}
Related
Hello i am having xero API
i am trying to integrate it with my laravel project , i am getting above error I am using following laravel package for same.
github package link : https://github.com/webfox/laravel-xero-oauth2/
----------------routes-----------------
Route::get('/manage/xero', [XeroController::class, 'index'])->name('xero.auth.success');
Route::get('xero/auth/callback', [XeroController::class, 'data'])->name('xero.auth.callback');
---controller-----------------
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Webfox\Xero\OauthCredentialManager;
class XeroController extends Controller
{
public function index(Request $request, OauthCredentialManager $xeroCredentials)
{
try {
// Check if we've got any stored credentials
if ($xeroCredentials->exists()) {
/*
* We have stored credentials so we can resolve the AccountingApi,
* If we were sure we already had some stored credentials then we could just resolve this through the controller
* But since we use this route for the initial authentication we cannot be sure!
*/
$xero = resolve(\XeroAPI\XeroPHP\Api\AccountingApi::class);
$organisationName = $xero->getOrganisations($xeroCredentials->getTenantId())->getOrganisations()[0]->getName();
$user = $xeroCredentials->getUser();
$username = "{$user['given_name']} {$user['family_name']} ({$user['username']})";
}
} catch (\throwable $e) {
// This can happen if the credentials have been revoked or there is an error with the organisation (e.g. it's expired)
$error = $e->getMessage();
}
return view('xero', [
'connected' => $xeroCredentials->exists(),
'error' => $error ?? null,
'organisationName' => $organisationName ?? null,
'username' => $username ?? null
]);
}
}
Your xero/auth/callback route is routed to the XeroController::data() function, which does not exist.
Looking at that package, it looks like it already registers a route for xero/auth/callback, pointing to the AuthorizationCallbackController in the package. I'm assuming you just need to remove your manually defined route.
I have added the Saptie Laravel Permission Package in a Laravel 5.8 API application. Every works fine and I get exception when a non admin user tries to access admin specific routes.
However the default exception is rendered as HTML 403 User does not have the right roles. Considering I am using this inside an API application, I would like to return my own custom message for such exceptions.
I tried checking if the auth()->user()->hasRole('admin') but still got the same default exception page. Here's my code
route
Route::post('products', 'ProductController#store')->middleware('role:super-admin|admin'); // create a new product
Controller method
if (auth()->user()->hasRole('admin')) {
// create & store the product
$product = Product::create($request->all())
// return new product
$responseMessage = 'Successful operation';
$responseStatus = 200;
$productResource = new ProductResource($product);
return response()->json([
'responseMessage' => $responseMessage,
'responseStatus' => $responseStatus,
'product' => $productResource
]);
} else {
return response()->json([
'responseMessage' => 'You do not have required authorization.',
'responseStatus' => 403,
]);
}
Why is my custom message not showing?
Because you are protecting your routes through the role middleware the UnauthorizedException will be thrown before your controller code is ever reached.
What you can do is use laravels exception handler render method and check the exception type and return your own response:
from the docs:
The render method is responsible for converting a given exception into
an HTTP response that should be sent back to the browser. By default,
the exception is passed to the base class which generates a response
for you. However, you are free to check the exception type or return
your own custom response
app/Exceptions/Handler.php
use Spatie\Permission\Exceptions\UnauthorizedException;
public function render($request, Exception $exception)
{
if ($exception instanceof UnauthorizedException) {
return response()->json([
'responseMessage' => 'You do not have required authorization.',
'responseStatus' => 403,
]);
}
return parent::render($request, $exception);
}
I want to use Eloquent to create a DB entry like this:
MFUser::create(array(
'user_reference' => $this->userReference,
'first_name' => $this->firstName,
'last_name' => $this->lastName,
'user_type_id' => $this->userTypeId,
'email' => $this->email,
'password' => $this->password
));
It works well, except for the case, when exactly the same data is put into the fields, which is expected, as there should be no duplicate. I get the QueryExecption then.
But how do I properly perform error handling here to check if this query exception occurs so I can return a response from my server to the client via json then?
Just wrap that code in a try-catch block. Something like this:
try {
MFUser::create(array(
'user_reference' => $this->userReference,
'first_name' => $this->firstName,
'last_name' => $this->lastName,
'user_type_id' => $this->userTypeId,
'email' => $this->email,
'password' => $this->password
));
} catch (\Illuminate\Database\QueryException $exception) {
// You can check get the details of the error using `errorInfo`:
$errorInfo = $exception->errorInfo;
// Return the response to the client..
}
I prefer to reserve try/catch's for unexpected events that can't be handled elsewhere. In this case, you can utilise validation as a first measure, and the exception handler as a backup measure.
If the form request fails, the error messages are flashed and the user is returned to the previous page (where the form was submitted) and you can handle the messages gracefully. Validation requests
// First line of defence - gracefully handle
// Controller
public function store(MFUserRequest $request)
{
// The incoming request is valid...
MFUser::create(array(...));
}
// Form Request
class MFUserRequest extends Request
{
public function rules()
{
return [
'email' => 'required|email|unique:users,email',
];
}
}
Elsewhere, in your App\Exceptions directory you have the exception handler class that can be a catch all for various errors. Use this, when you haven't been able to gracefully handle it further down.
// Second line of defence - something was missed, and a model was
// created without going via the above form request
namespace App\Exceptions;
class Handler extends ExceptionHandler
{
public function render($request, Exception $e)
{
if($e instanceof QueryException) {
// log it or similar. then dump them back on the dashboard or general something bad
// has happened screen
return redirect()->route('/dashboard');
}
}
}
Simply make use of try / catch block.
use Illuminate\Database\QueryException;
// ...
try {
// DB query goes here.
} catch (QueryException $e) {
// Logics in case there are QueryException goes here
}
// ...
Try and Catch something might help u and imagine for create try catch for all of them, but have best practice to handle all of QueryException, my recommend is use Laravel Exceptions Handler because it's make u easy for make execption global!
let's try it!, open App\Exception\Handler.php and at render method u can write like this one
public function render($request, Throwable $exception)
{
if ($request->ajax() || $request->wantsJson()) {
if ($exception instanceof QueryException) {
// example algo for make response
return response()->json(['message' => 'xxx'], 403);
}
}
return parent::render($request, $exception);
}
after that, u can get xxx json for every error request triggered by Query
After I upgraded to Laravel 5.2 I encountered a problem with the laravel validator. When I want to validate data in a controller take for example this code.
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class ContactController extends Controller
{
public function storeContactRequest(Request $request)
{
$this->validate($request, [
'_token' => 'required',
'firstname' => 'required|string'
'lastname' => 'required|string'
'age' => 'required|integer',
'message' => 'required|string'
]);
// Here to store the message.
}
}
But somehow when I enter unvalid data it will not redirect me back to the previous page and flash some messages to the session but it will trigger an exception and gives me a 500 error page back.
This is the exception I get.
I have read in the documentation that the ValidationException is new instead of the HttpResponseException but I don't know if it has anything to do with this.
[2016-01-05 11:49:49] production.ERROR: exception 'Illuminate\Foundation\Validation\ValidationException' with message 'The given data failed to pass validation.' in /home/vagrant/Code/twentyre-webshop/vendor/laravel/framework/src/Illuminate/Foundation/Validation/ValidatesRequests.php:70
And when I use a seperate request class it will just redirect back with the error messages. It seems to me only the validate method used in a controller is affected by this behaviour.
Update your App\Exceptions\Handler class
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Foundation\Validation\ValidationException;
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];
I also recommend you to read the docs how to migrate to laravel 5.2, because there were some breaking changes. For example this, ValidatesRequests trait throws
Illuminate\Foundation\Validation\ValidationException instead of Illuminate\Http\Exception\HttpResponseException
Documentation how to migrate from Laravel 5.1 to 5.2
Example from laravel docs. You can use Validator facade, for custom validation fails behaviour
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Store the blog post...
}
This is how I handle it in Laravel 5.3 (by modifying Handler.php)
https://stackoverflow.com/a/42852358/3107185
For laravel 5.2 I had to add this line:
if ($e instanceof ValidationException)
{
return redirect()->back()->withInput();
}
In App\Exceptions\Handler.php,and the following headers:
use Illuminate\Session\TokenMismatchException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\AuthenticationException;
For my purpose, I was bulding a fully API based application in Laravel 5.3 which I had manually upgraded from Laravel 5.1. and I just needed Laravel to respond back with the validation errors that needed fixing on my FormRequest.
Adding this line:
elseif ($e instanceof ValidationException)
{
return $this->convertValidationExceptionToResponse($e, $request);
}
after this one:
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
}
In App\Exceptions\Handler.php did the trick for me and returned expected validation errors when using FormRequest validation.
Please see my comments here: #ratatatKE's comments on github
might save someone time, Another issue is that you are calling validator->validate() in the view, not in the controller
i was calling in the view because i have a lazy load component that triggered on the view
I had the same problem when upgrading 4.2 to 5.3.
This answer worked for me.
Override the method in app/Exceptions/Handler.php
protected function convertExceptionToResponse(Exception $e)
{
if (config('app.debug')) {
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
return response()->make(
$whoops->handleException($e),
method_exists($e, 'getStatusCode') ? $e->getStatusCode() : 500,
method_exists($e, 'getHeaders') ? $e->getHeaders() : []
);
}
return parent::convertExceptionToResponse($e);
}
Answer found here: https://laracasts.com/discuss/channels/laravel/whoops-20-laravel-52
I'm very new to Laravel and unit testing in general. I'm trying to write some tests for my AccountController and I've run into a road block.
I'm using Sentry to handle users and groups in the site. I'm trying to test that my controller is handling exceptions thrown by Sentry properly. So my controller method that handles the login POST looks like this:
public function postLogin(){
$credentials = array(
'email' => Input::get('email'),
'password' => Input::get('password')
);
try{
$user = $this->authRepo->authenticate($credentials, true);
return Redirect::route('get_posts');
}
catch (Exception $e){
$message = $this->getLoginErrorMessage($e);
return View::make('login', array('errorMsg' => $message));
}
}
authRepository is just a repository that uses Sentry to handle authentication. Now I want to test that when an email address is not specified a LoginRequiredException is thrown and the user sees the error message. Here is my test:
public function testPostLoginNoEmailSpecified(){
$args = array(
'email' => 'test#test.com'
);
$this->authMock
->shouldReceive('authenticate')
->once()
->andThrow(new Cartalyst\Sentry\Users\LoginRequiredException);
$this->action('POST', 'MyApp\Controllers\AccountController#postLogin', $args);
$this->assertViewHas('errorMsg', 'Please enter your email address.');
}
However, the test is not passing. For some reason all it spits out is:
There was 1 error:
1) AccountControllerTest::testPostLoginNoEmailSpecified
Cartalyst\Sentry\Users\LoginRequiredException:
Am I using the andThrow() method incorrectly? If anyone can shed any light on what is going on it would be much appreciated.
Thanks in advance!
So I actually just figured the problem out. Turns out it wasn't a problem with my unit tests at all but was actually just a namespacing issue. I forgot the backslash on the Exception class. So in my controller it should have been:
try{
$user = $this->authRepo->authenticate($credentials, true);
return Redirect::route('get_posts');
}
catch (\Exception $e){
$message = $this->getLoginErrorMessage($e);
return View::make('account.login', array('errorMsg' => $message));
}