---
title: "Handle Jovvie PHP SDK errors"
description: "The exceptions the Jovvie PHP SDKs throw, what each one means, and what your integration should do about it."
url: https://docs.jovvie.com/developers/errors/
section: "Developers"
last_updated: 2026-10-27
---

# Handle Jovvie PHP SDK errors

Section: Developers

> The exceptions the Jovvie PHP SDKs throw, what each one means, and what your integration should do about it.

This page lists the exceptions the Jovvie SDKs throw and what your integration should do when it catches each one. Both SDKs use the same error classes. Only the namespace is different.

## Error classes

| Class                      | When it is thrown                                                                                                                                   |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BadRequestError`          | Jovvie rejected the request as invalid (HTTP 400)                                                                                                   |
| `UnauthorizedError`        | Jovvie did not accept your keys (HTTP 401)                                                                                                          |
| `ForbiddenError`           | Your keys are valid but cannot do this (HTTP 403)                                                                                                   |
| `NotFoundError`            | The payment, device, or other item was not found (HTTP 404)                                                                                         |
| `ResponseError`            | Any other error response from Jovvie. `BadRequestError`, `UnauthorizedError`, `ForbiddenError`, and `NotFoundError` extend it                       |
| `WebhookError`             | A webhook could not be verified or read                                                                                                             |
| `InvalidArgumentException` | The Stripe client was created with a mode other than `'live'` or `'test'`, or in live mode with a platform provider ID that works in test mode only |
| `Exception`                | The request could not be completed, for example because of a network problem or a timeout                                                           |

The namespaces are:

|                | Stripe                                | NMI                                          |
| -------------- | ------------------------------------- | -------------------------------------------- |
| Request errors | `JovviePayments\Http\Error\`          | `JovviePaymentsGateway\Http\Error\`          |
| Webhook errors | `JovviePayments\WebhookEvents\Error\` | `JovviePaymentsGateway\WebhookEvents\Error\` |

`InvalidArgumentException` and `Exception` are PHP’s built-in classes.

## Catch errors

Catch the most specific classes first. Every request method can throw `ResponseError` or one of its subclasses, and also a plain `Exception` when the request does not complete.

```php
use JovviePayments\Http\Error\BadRequestError;
use JovviePayments\Http\Error\NotFoundError;
use JovviePayments\Http\Error\ResponseError;
use JovviePayments\Http\Error\UnauthorizedError;

try {
    $payment = $jovvie->payments()->get($jovviePaymentId);
} catch (NotFoundError $e) {
    // The payment does not exist in this mode for this account.
} catch (UnauthorizedError $e) {
    // Check the keys and platform provider ID. Alert an administrator.
} catch (BadRequestError $e) {
    // Check the values you sent. Do not retry unchanged.
} catch (ResponseError $e) {
    // Another error from Jovvie. See the "Read the error details" section.
} catch (\Exception $e) {
    // Network problem or timeout. See the "When to try again" section.
}
```

## Read the error details

`ResponseError` and its subclasses have two methods that help you find the cause:

- `$e->getCode()` returns the HTTP status code, such as `400`.
- `$e->getResponse()->getBody()` returns the response body as an object. It usually has a `code` property with an error code, and an `error` or `message` property with a short description. The body can be `null`, so check it before you read it.

```php
} catch (ResponseError $e) {
    $body = $e->getResponse()->getBody();
    $errorCode = is_object($body) && isset($body->code) ? $body->code : null;

    yourApp_log('Jovvie request failed', [
        'status' => $e->getCode(),
        'code'   => $errorCode,
    ]);
}
```

Exception messages can include technical details about the request. Log them on your server, but show staff a plain message instead, such as “We could not confirm whether the payment started. Check the reader and the payment processor before trying again, or contact your administrator.” Never show exception messages to customers.

## What to do for each error

### `BadRequestError`

Something in the request is not valid, or the merchant’s account is not ready. Do not retry the same request without changing it.

| Error code                         | What it means                                                                                                                                 | What to do                                                                                                  |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `ERR_BAD_REQUEST`                  | A value is missing or not valid. For example, the amount is not a positive whole number, a URL is not a full URL, or the description is empty | Check the values you send. If the `message` is `Invalid platform provider`, check your platform provider ID |
| `ERR_INVALID_STRIPE_CONFIGURATION` | The merchant’s Stripe account is not connected                                                                                                | Ask the merchant to connect Stripe in the Payments Dashboard                                                |
| `ERR_STRIPE_UNSUPPORTED_CURRENCY`  | The currency is not supported for in-person payments on this Stripe account                                                                   | Use the currency of the merchant’s Stripe account                                                           |
| `ERR_MISSING_NMI_ACCOUNT`          | The merchant has no NMI account set up (NMI only)                                                                                             | Ask the merchant to finish setting up NMI                                                                   |
| `ERR_DEVICE_NOT_FOUND`             | The device ID you sent is not one of the merchant’s terminals (NMI only)                                                                      | List the merchant’s devices again and use a current device ID                                               |

### `UnauthorizedError`

Jovvie did not accept the request’s credentials. Check that:

- The public key and secret key come from the same application, and you copied them in full.
- The keys were not regenerated since you saved them.
- The application is not disabled.
- With Stripe, the platform provider ID is the one Jovvie support gave you, and the application is not already used by another integration.
- Your server’s clock is set correctly.

Do not retry until you fix the cause. Alert an administrator instead of showing the error to staff.

### `ForbiddenError`

Your keys work, but they cannot do what you asked. Common causes:

- The keys belong to an application that is not a server application. Create one on the **Applications** page. See [Get started](https://docs.jovvie.com/developers/get-started/).
- You asked for a payment that belongs to a different merchant account.

### `NotFoundError`

The item does not exist for this account and mode. Check that:

- You saved the correct payment ID when you created the payment.
- You are using a client in the same mode as the payment. A test payment cannot be fetched with a live client, and a live payment cannot be fetched with a test client.

### Other `ResponseError` errors

These include:

- `429`: too many requests.
- `500` and higher: an error on Jovvie’s side.
- Card errors, such as a declined card. Staff usually see these on the payment page instead. See [Card declined and payment error messages](https://docs.jovvie.com/troubleshooting/payment-errors/).

Whether you can try again depends on the request. See [When to try again](https://docs.jovvie.com/developers/errors/#when-to-try-again).

### `WebhookError`

The SDK throws `WebhookError` from `WebhookEvent::constructEvent()` when a webhook cannot be trusted or read. For example, the signature does not match your webhook key, the request is too old, or the body is not valid.

When you catch it:

1. Respond with an HTTP error status, such as `400`.
2. Do not change any order.
3. Log that it happened, without the request headers or your webhook key.

The SDK also throws `WebhookError` for an event type it does not know, and from an event’s getter methods when a field is missing from the event. See [Handle webhooks](https://docs.jovvie.com/developers/webhooks/).

### `InvalidArgumentException`

The Stripe client throws `InvalidArgumentException` when you create it in two cases:

- **The mode is not valid.** The client only accepts `'live'` or `'test'`. Check the mode setting in your integration.
- **The platform provider ID only works in test mode.** This is why an integration can work in test mode and then fail as soon as you switch to live mode. Pass the platform provider ID that Jovvie support gave you. If you do not have one, contact [Jovvie support](https://jovvie.com/support/) to get it. Do not switch back to test mode to get around the error, because test mode does not take real payments.

See [Install the SDK](https://docs.jovvie.com/developers/install-the-sdk/).

## When to try again

Only retry read-only requests automatically, such as `payments()->get()`, or `devices()->list()` and `devices()->get()` with NMI. Retry them after a `429`, a status of `500` or higher, or a plain `Exception`. Wait before each attempt, wait longer after each one, and stop after a few attempts.

Never retry creating a payment automatically. If creating a payment fails with any error, including a status of `500` or higher or a plain `Exception`, a payment may still have started. Before anyone tries again:

1. Check the card reader or terminal to see whether a payment is in progress or went through.
2. Check the payment in the Stripe Dashboard or the NMI portal.
3. If you are still not sure, contact [Jovvie support](https://jovvie.com/support/).

Create a new payment only after you have confirmed that no payment for the order went through and none is still processing. With Stripe, only send staff to the checkout link of the payment you saved for the order.

Never retry a payment in a loop.

## Errors on the payment page

Errors that staff see on the Jovvie payment page are not thrown by the SDK. For those, see:

- [Checkout payment page errors](https://docs.jovvie.com/troubleshooting/checkout-errors/)
- [Card declined and payment error messages](https://docs.jovvie.com/troubleshooting/payment-errors/)
- [Reader not showing on the payment page](https://docs.jovvie.com/troubleshooting/reader-connection/)

## Need help?

When you contact support, include the class name, the HTTP status, and the error code. Never send your keys.

Contact [Jovvie support](https://jovvie.com/support/).
