Developers · Hashry Pay
Errors
Every error code, and what to do about it. Looking for a symptom, such as a card form that never loads? Start with Troubleshooting.
In the browser: HashryPayError
The browser packages throw a HashryPayError, or pass it to onError.
Check for one with isHashryPayError(error), then read its code. Its
message never holds the embed_url or a token, so it is safe to log.
| code | Meaning |
|---|---|
secret_in_browser | An option looks like a secret key. Move it to your server and rotate the key. |
invalid_option | An option is missing, or has the wrong type or range. |
invalid_embed_url | embedUrl is not a valid URL. |
host_not_allowed | embedUrl is not on a Hashry Pay host. Mount only the embed_url Hashry returned. |
load_timeout | The card form sent no ready message in time. Arrives in onError. |
status_not_found | The status token is wrong, expired or for another payment (404). |
status_failed | The status endpoint refused the request for another reason. |
status_timeout | pollStatus ran longer than timeoutMs. |
aborted | Your AbortSignal stopped pollStatus. |
In React, a bad option throws during mount. Catch it with an error boundary.
From the API
A refusal has a JSON body. Read it like this:
- The content type is
application/problem+jsonorapplication/json. Read both the same way. - The reason code is in
code. If there is nocode, it is intype: a short code, or a path that ends in one. - A validation error has no
detail. Each bad field is a key that holds a list of messages.
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"type": "embed_origin_not_allowed",
"title": "Payment request refused.",
"status": 400,
"detail": "That embed origin is not on this integration's allowed list."
}HTTP/1.1 403 Forbidden
{ "detail": "...", "code": "secret_key_used_in_browser" }| Code | Status | What to do |
|---|---|---|
secret_key_used_in_browser | 403 | Your client secret was sent from a web browser. Call Hashry only from your server, and rotate the key. |
key_revoked_browser_use | 403 | The key was revoked because it was used from a browser. Ask the merchant for a new key. |
authentication_error | 401 | The client ID or secret is wrong, or the key was revoked. Check the key and the environment. |
idempotency_conflict | 409 | You reused an idempotency key with different details. Use a new key for a new payment. |
idempotency_key_invalid | 400 | The idempotency key is missing, or not 8 to 100 characters. |
embed_origin_not_allowed | 400 | embed_origin is not on Allowed embed origins for this key's mode. Add the exact origin. |
embedded_checkout_not_enabled | 409 | The embedded card form is off for this account. Ask Hashry to turn it on, or use the redirect. |
origin_not_allowed | 400 | The return_url or cancel_url origin is not on Allowed return origins for this key's mode. |
url_invalid | 400 | A URL is not valid. Use https (http only for localhost), with no user name or #fragment. |
currency_mismatch | 400 | The currency is not the store's currency. |
limit_exceeded | 409 | Over the limit for one payment, or for 24 hours, set on the channel. |
intent_retry_window_closed | 409 | It is too late to retry this payment. Start a new payment with a new key. |
gateway_unavailable | 502 | Nothing happened. Try again: a repeat is safe. |
gateway_outcome_unknown | 502 | Hashry may have acted. Repeat with the same key, or read the payment. Never start a new one. |
(none) | 429 | Too many requests. Wait, then try again. |
In the server SDKs: Node and Python
Both server SDKs throw the same error classes. In Node, import them from @hashrytech/pay-node. In Python, import them from hashry_pay. Every
one is a HashryError. Their messages never hold a secret, a token or an embed_url, so they are safe to log.
| Error | When |
|---|---|
ApiError | The API refused the call or failed. The errors below say how. |
InvalidRequestError | 400. The request is not valid. See the field errors. |
AuthenticationError | 401. The client ID or secret is wrong, or the key was revoked. |
PermissionDeniedError | 403. The key may not do this, for example a browser call. |
NotFoundError | 404. No such object for this key and mode. |
ConflictError | 409. The object is not in a state that allows this. |
IdempotencyError | The idempotency key is missing, not valid, or reused with new details. |
RateLimitError | 429. Too many requests. Wait the retry-after time, then try again. |
ServerError | 5xx. Hashry or the gateway failed. Check whether the outcome is unknown. |
ApiConnectionError | No answer. ApiTimeoutError is the timeout case. |
ConfigurationError | A bad setup, for example a test key with the live environment. |
WebhookError | A webhook failed its checks. Answer 400. reason says why. |
ApiError has code, status and detail. Node
also gives fieldErrors and retryAfter. Python gives field_errors and retry_after.
import { ApiConnectionError, ApiError } from "@hashrytech/pay-node";
try {
await hashry.paymentIntents.cancel(uid);
} catch (error) {
if (error instanceof ApiError && error.code === "cancel_not_allowed") {
// A card was already entered. Wait for the outcome instead.
} else if (error instanceof ApiConnectionError) {
// No answer. Try again later, or read the payment.
} else {
throw error;
}
}When the outcome is unknown, Hashry may have acted anyway. That is when outcomeUnknown (Node) or outcome_unknown (Python) is true. ApiConnectionError is always outcome unknown. Repeat the call with the same idempotency
key, or read the payment. Never create a new one.