Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

tjsherrill's avatar

How to handle exceptions in a custom PHP class

I have a controller that handles payments. A user submits a payment for something and there are multiple methods they may have used. Credit card, ACH, wire... Both Credit Cards and ACH use separate PHP classes that interact with the payment processor. For the ACH I am using Plaid which is super cool. Anyway, when the standard post request hits the controller its pretty simple logic to define which type of payment this is. Then I am injecting a PHP class to handle the payment itself. So in this case I am writing the class to handle Plaid (I know there is a library but that doesn't solve my issue).

The Class has methods to do all sorts of things.

I inject my class: use App\Library\Plaid; Then I can init it: $plaid = new Plaid; Then I can call it: $plaid->exchangeToken($request->pubtoken);

Now this is where I have my issue: If I call $plaid->exchangeToken($request->pubtoken); and there is an error, say a 400 error and Plaid shares back the error code (which will tell me what I need to do or tell the user), how do I handle that error, ultimately sending info back to the user, and how do I do it without writing endless logic in the controller?

The way I am thinking about doing it would be to create the function response and handle all the exception types in the custom class. so maybe part of my response is either "success" or "error"

Then in my controller when I call: $response = $plaid->exchangeToken($request->pubtoken); I can do a simple check on the response. Something like:

if(array_key_exists($response['error']){ return $response }

// else keep going with the rest of the function?

Is this is best way to do this?

0 likes
4 replies
martinbean's avatar

If I call $plaid->exchangeToken($request->pubtoken); and there is an error, say a 400 error and Plaid shares back the error code (which will tell me what I need to do or tell the user), how do I handle that error, ultimately sending info back to the user, and how do I do it without writing endless logic in the controller?

@tjsherrill By using Laravel’s exception handler which is for, well, handling exceptions.

Most payment gateways give you a PHP SDK to work with. Plaid has a PHP one, albeit being third-party: https://github.com/TomorrowIdeas/plaid-sdk-php

SDKs usually throw exceptions when something happens. You can then handle those exceptions in your application’s execution handler, specifying if the exception should be converted to another exception, or what HTTP response should be displayed if that exception is encountered.

You shouldn’t be handling exceptions in your application code (controllers, console commands, queued jobs, etc) because any exceptions thrown should be handled by your application’s exception handler. Otherwise your application code will just end up stuffed full of try/catch blocks with no centralised logic for how those exceptions are dealt with.

tjsherrill's avatar

@martinbean, thanks for the notes. This is helping me get to where I am going. I have a couple of follow-ups questions, but here are a couple of notes:

Yes, plaid has a third-party library but writing a simple class, and leveraging Guzzle, was helpful since I need to handle reporting things back to users in a specific way. Also, putting this in a custom class, like using the SDK, allows me to handle all of the exceptions there, keeping me from having try/catch in a bunch of places throughout my app.

First am struggling to wrap my head around how I can put this logic in the exception handler. I don't want to redirect the user to another place if there is an exception. I want to change the user's db representation of their "Account" and then return some flash to the user notifying them of the issue.

Second, since I am using Guzzle, there are only two exceptions I need to catch: ServerException, and ClientException.

My current thought for my class is to send the

$plaid->exchangeToken($request->pubtoken);  

call from my controller action where I am handling the post request to take a payment. This calls the exchangeToken() method in my Plaid class:

       function exchangeToken($token)
    {
        Log::info('exchangeToken');
        $method = 'POST';
        $url = 'item/public_token/exchange';
        return json_decode($this->request($method, $url, [
                "public_token" => $token
            ]
        ));
    }

Then in my Plaid::request method I am doing:

    function request( String $method, String $url, Array $body = NULL )
    {
  
        try {
            $client = new GuzzleHttp\Client([
                'base_uri' => $this->baseUrl,
                'timeout'  => 0
            ]);
            if($body){
                $body = json_encode($body);
            }
            $response = $client->request(
                    $method, 
                    $url, 
                    [
                        'headers' => [
                            'content-type' => 'application/json',
                            'PLAID-CLIENT-ID' => config('services.plaid.client'),
                            'PLAID-SECRET' => config('services.plaid.secret')
                        ],
                        'body' => $body
                    ]
            );
            return $response->getBody()->getContents();
        
        // Handle Errors
        } catch (ServerException $e){
            dd($e);

        } catch(ClientException $e) {
            dd($e);
        }
    }

Ignore the dd()'s for now. But in the two catch blocks, I can handle the errors, make an array and return it. First, to the Plaid::class function that called it (in this case, exchangeToken) and then return it to the controller action.

This feels the dryest and cleanest way to get this done but if there is a better, more laravel way, I'd love to do that.

Thoughts?

martinbean's avatar
Level 80

@tjsherrill Your Plaid service class here, I’d catch the Guzzle exceptions and then throw an instance of a Plaid-specific exception class. You can then catch this in your exception handler:

public function request(string $method, string $url, array $body = null)
{
    try {
        // Attempt Guzzle request here...
    } catch (ClientException | ServerException $e) {
        throw new PlaidException($e);
    }
}

I don’t really know what kind of responses Plaid’s API returns, but you’ll be able to access the contents of a JSON response like this:

$response = json_decode($e->getResponse()->getBody(), true);

You can then change your PlaidException constructor to accept any specific error information from Plaid, such as an error message or error code, and handle cases accordingly in the exception handler.

Also be wary of your scalar type-hints there. In PHP they’re all-lowercase, i.e. string and not String. String may inadvertently get treated as a different type (i.e. a Sting class if PHP or Laravel were to introduce such a thing).

kokoshneta's avatar

@martinbean Type-hints in PHP are not case-sensitive. You cannot introduce a String class: all the scalar type names that may be used in type declarations are reserved keywords, and keywords too are case-insensitive, so any case-permutation of the word string used as a class name will throw a fatal error.

Please or to participate in this conversation.