Zum Inhalt springen

API

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"
}