Auf dieser Seite
Fehlerbehandlung
Fehler der Händler-API einheitlich behandeln: Antwortumschlag, HTTP-Statusgruppen, maschinenlesbare Codes, Anfragegrenzen und Hinweise zum Wiederholen.
Die API nutzt die üblichen HTTP-Statuscodes. Erfolgreiche Antworten geben den Ressourcenkörper direkt zurück. Fehler folgen RFC 9457 Problem Details for HTTP APIs (application/problem+json) mit zwei Erweiterungsfeldern: einem code auf oberster Ebene, auf den Sie programmatisch verzweigen, und einem field auf oberster Ebene (snake_case) bei Validierungsfehlern eines einzelnen Feldes. Schlagen mehrere Felder gleichzeitig fehl, tritt an die Stelle von field ein Array errors[] (ein Paymos-Erweiterungsfeld, §3.2) mit der Aufschlüsselung je Feld.
Verzweigen Sie für die programmatische Behandlung und die Lokalisierung auf code — niemals auf den Text von title oder detail. Die Textfelder können zwischen Versionen umformuliert werden; code ist der stabile Vertrag.
{
"type": "https://paymos.io/docs/errors/codes#insufficient_balance",
"title": "Conflict",
"status": 409,
"detail": "Merchant balance is insufficient.",
"code": "insufficient_balance"
}
Jede Fehlerantwort folgt RFC 9457 Problem Details for HTTP APIs und wird als Content-Type: application/problem+json ausgeliefert.
Einzelner Fehler (die meisten Antworten)
{
"type": "https://paymos.io/docs/errors/codes#insufficient_balance",
"title": "Conflict",
"status": 409,
"detail": "Merchant balance is insufficient.",
"code": "insufficient_balance"
}
Validierung mehrerer Felder
Wird nur gesendet, wenn mehrere Felder gleichzeitig die Prüfung nicht bestehen. Der code auf oberster Ebene ist dann immer validation_failed; die Details je Feld stehen in errors[]:
{
"type": "https://paymos.io/docs/errors/codes#validation_failed",
"title": "Bad Request",
"status": 400,
"detail": "Validation failed.",
"code": "validation_failed",
"errors": [
{ "code": "field_required", "field": "currency", "message": "Currency is required." },
{ "code": "field_must_be_positive", "field": "amount", "message": "Amount must be > 0." }
]
}
Referenz der Felder
| Feld | Typ | RFC | Beschreibung |
|---|---|---|---|
type |
Zeichenkette (URI) | RFC 9457 §3.1.1 | Direktverweis auf die Dokumentationszeile zum jeweiligen code. Je Code stabil — ändert sich nach der Veröffentlichung nie. |
title |
Zeichenkette | RFC 9457 §3.1.3 | Kurzer Titel des HTTP-Status ("Bad Request", "Conflict", …). |
status |
Ganzzahl | RFC 9457 §3.1.2 | HTTP-Statuscode (spiegelt den Antwortstatus). |
detail |
Zeichenkette | RFC 9457 §3.1.4 | Menschenlesbare englische Erläuterung dieses konkreten Vorfalls. |
code |
Zeichenkette | Erweiterung (§3.2) | Stabile maschinenlesbare Kennung — siehe Fehlercodes. Verzweigen Sie darauf in Ihrer Anbindung. |
field |
Zeichenkette | Erweiterung (§3.2) | Feldname im Übertragungsformat (snake_case) bei Validierungsfehlern eines einzelnen Feldes. Bei Fehlern ohne Feldbezug entfällt der Schlüssel vollständig — er fehlt im JSON und ist nie null. Nur vorhanden, wenn errors[] fehlt. |
errors |
Array | Erweiterung (§3.2) | Nur vorhanden, wenn mehrere Felder gleichzeitig fehlschlagen. Jeder Eintrag: { code, field, message }. |
Warum strukturierte Codes und nicht nur message
Der Text von message und detail kann sich zwischen Versionen weiterentwickeln, lokalisiert oder um Kontext ergänzt werden. code ist der stabile Vertrag — ist ein Code einmal veröffentlicht, ändert sich seine Bedeutung nie. Verzweigen Sie für die programmatische Behandlung und die Lokalisierung auf code.
import { RateLimitError, ValidationError } from '@paymos/sdk';
try {
await paymos.withdrawals.create(request);
} catch (error) {
if (error instanceof ValidationError) {
for (const item of error.errors) highlight(item.field, item.message);
} else if (error instanceof RateLimitError) {
scheduleRetry(error.retryAfterSeconds);
}
throw error;
}
use paymos::{ApiErrorKind, Error};
match paymos.withdrawals().create(&request).await {
Ok(withdrawal) => process(withdrawal),
Err(Error::Api(error)) if error.kind == ApiErrorKind::Validation => {
for item in &error.errors {
highlight(item.field.as_deref(), &item.message);
}
}
Err(Error::Api(error)) if error.kind == ApiErrorKind::RateLimit => {
schedule_retry(error.retry_after);
}
Err(error) => return Err(error),
}
Fehlerarten
Jeder Status kommt im Fehlerumschlag mit einem title und einem maschinenlesbaren code. Ein Status kann mehrere Fälle abdecken — verzweigen Sie auf den code, nicht auf den Status.
| HTTP-Code | title |
Beschreibung |
|---|---|---|
400 |
Bad Request |
Die Eingabeprüfung ist fehlgeschlagen oder der Anfragekörper ist ungültig. Fehler eines einzelnen Feldes tragen code und field auf oberster Ebene; Fehler mehrerer Felder füllen errors[]. |
401 |
Unauthorized |
Die HMAC-Zugangsdaten fehlen, sind fehlerhaft, abgelaufen oder ungültig. Der genaue Grund steht in code (invalid_credentials, timestamp_expired, authorization_malformed, …). |
403 |
Forbidden |
Authentifiziert, aber Schlüsselart, Umgebung oder Geltungsbereich erlauben die Operation nicht. Der genaue Grund steht in code (merchant_suspended, whitelist_required und weitere). |
404 |
Not Found |
Die Ressource existiert nicht, ist für den Aufrufer nicht sichtbar (eine Ressource eines anderen Händlers liefert not_found, nie forbidden), oder kein Endpunkt passt zum Pfad der Anfrage (route_not_found). |
405 |
Method Not Allowed |
Der Pfad existiert, aber nicht für diese HTTP-Methode. Trägt den Code method_not_allowed. |
409 |
Conflict |
Die Anfrage steht im Widerspruch zum aktuellen Zustand — ein ungültiger Übergang, eine bereits vorhandene Ressource oder eine Sicherung, die die Aktion blockiert. Der genaue Grund steht in code (insufficient_balance, withdrawal_quota_exceeded, invoice_cannot_be_cancelled, …). |
410 |
Gone |
Die Ressource lässt sich nicht mehr verwenden (etwa eine abgelaufene oder stornierte Rechnung). |
413 |
Payload Too Large |
Der Anfragekörper überschreitet die Größengrenze (1 MiB). Trägt den Code payload_too_large. |
415 |
Unsupported Media Type |
Content-Type ist nicht application/json. Trägt den Code unsupported_media_type. |
429 |
Too Many Requests |
Die Anfragegrenze je Händler ist überschritten. Lesen Sie den Header Retry-After (in Sekunden), bevor Sie erneut senden. |
500 |
Internal Server Error |
Auf unserer Seite ist etwas fehlgeschlagen. Wiederholen Sie mit exponentiellem Backoff. |
503 |
Service Unavailable |
Vorübergehendes Problem einer Abhängigkeit oder der Verarbeitung (exchange_rate_unavailable, acceptance_disabled, outbound_frozen, …). Wiederholen Sie mit Backoff. |
Steuern Sie die Logik Ihres Clients immer über code, nicht über den HTTP-Status — mehrere fachlich verschiedene Fälle teilen sich einen HTTP-Status, und erst der Code auf der Leitung unterscheidet sie.
Anfragegrenzen
Die API erlaubt standardmäßig 30 Anfragen pro Sekunde je Händler, mit einer strengeren Grenze für POST /v1/invoices (5 Anfragen/Sekunde). Gezählt wird je Händler und je Ein-Sekunden-Fenster — alle Ihre API-Schlüssel schöpfen aus demselben Budget —, und beide Grenzen sind je Händler konfigurierbar. Wird die Grenze überschritten, erhalten Sie eine Antwort 429 Too Many Requests mit dem Header Retry-After: 1.
{
"type": "https://paymos.io/docs/errors/codes#rate_limited",
"title": "Too Many Requests",
"status": 429,
"detail": "Rate limit exceeded (30 req/sec). Retry after 1 second.",
"code": "rate_limited"
}