Przejdź do treści
Przewodniki
Przewodniki

Wywołania zwrotne

Bezpiecznie odbieraj i weryfikuj dostarczenia wywołań zwrotnych zadania.

Używaj wywołań zwrotnych, aby odbierać zdarzenia cyklu życia Zadania (Task) pod publicznym punktem końcowym HTTPS. Weryfikuj każde wywołanie zwrotne przed przetworzeniem jego treści JSON, aby tylko dostarczenia podpisane dla Twojego konta mogły zmieniać stan aplikacji.

Konfiguracja adresu URL wywołania zwrotnego

Dodaj publiczny adres HTTPS callback_url podczas tworzenia zadania (Task). Treść zdarzenia i stany cyklu życia zależą od punktu końcowego; zapoznaj się z dokumentacją interfejsu API danego punktu końcowego, aby poznać ładunki wywołań zwrotnych.

JSON
{
  "model": "flux-2-pro-text-to-image",
  "prompt": "A product photograph on a clean studio background",
  "callback_url": "https://your-domain.com/webhooks/runapi"
}

Tworzenie tajnego klucza wywołania zwrotnego

Postępuj zgodnie z Przewodnikiem po uwierzytelnianiu, aby się zalogować, a następnie otwórz sekcję Klucze API i utwórz Callback Secret dla konta, które tworzy zadanie (Task). Przechowuj wartość w menedżerze sekretów i udostępniaj ją wyłącznie odbiorcy wywołań zwrotnych. Nie jest to klucz API i nigdy nie wolno go wysyłać w żądaniu Task.

Sekret podpisuje wywołania zwrotne dla danego konta. Jego rotacja zmienia sygnaturę dla późniejszych dostaw, dlatego natychmiast zaktualizuj każdy odbiornik wywołań zwrotnych i przechowuj obie wartości tylko przez czas niezbędny do obsługi dostarczeń w toku.

Weryfikuj podpis wywołania zwrotnego

Każde wywołanie zwrotne to HTTP POST z nagłówkiem Content-Type: application/json. RunAPI nie dodaje nagłówka Authorization do tego żądania. Zweryfikuj następujące nagłówki przed deserializacją treści:

Nagłówek Znaczenie X-Callback-Id Unikalny identyfikator tej próby dostarczenia. X-Callback-Timestamp Znacznik czasu Unix w sekundach określający moment podpisania dostarczenia. X-Callback-Signature Podpis HMAC-SHA-256 zakodowany w Base64.

Zbuduj podpisaną wartość dokładnie w następujący sposób, używając niezmodyfikowanych bajtów treści żądania:

TEXT
X-Callback-Id + "." + X-Callback-Timestamp + "." + raw request body

Zdekoduj Callback Secret z Base64, oblicz HMAC-SHA-256 na tej wartości, zakoduj wynik w Base64 i porównaj go z X-Callback-Signature przy użyciu porównania odpornego na ataki czasowe. Nie parsuj i nie serializuj ponownie JSON przed weryfikacją.

JavaScript

JAVASCRIPT
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyCallback({headers, rawBody, callbackSecret}) {
  const callbackId = headers["x-callback-id"];
  const timestamp = Number(headers["x-callback-timestamp"]);
  const signature = headers["x-callback-signature"];

  if (!callbackId || !signature || !Number.isSafeInteger(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const signedContent = `${callbackId}.${timestamp}.${rawBody}`;
  const expected = createHmac("sha256", Buffer.from(callbackSecret, "base64"))
    .update(signedContent, "utf8")
    .digest();
  const received = Buffer.from(signature, "base64");

  return expected.length === received.length && timingSafeEqual(expected, received);
}

Przekaż surowy ciąg treści żądania ze środowiska do rawBody; nie wywołuj JSON.stringify na przetworzonych danych.

Python

PYTHON
import base64
import hashlib
import hmac
import time


def verify_callback(headers, raw_body, callback_secret):
    callback_id = headers.get("X-Callback-Id")
    timestamp = headers.get("X-Callback-Timestamp")
    signature = headers.get("X-Callback-Signature")

    if not callback_id or not timestamp or not signature:
        return False

    try:
        timestamp = int(timestamp)
        secret = base64.b64decode(callback_secret, validate=True)
        received = base64.b64decode(signature, validate=True)
    except (ValueError, TypeError):
        return False

    if abs(time.time() - timestamp) > 300:
        return False

    signed_content = f"{callback_id}.{timestamp}.{raw_body}".encode("utf-8")
    expected = hmac.new(secret, signed_content, hashlib.sha256).digest()
    return hmac.compare_digest(expected, received)

Przekaż dokładny surowy ciąg treści żądania odebrany przez Twoje środowisko HTTP jako raw_body.

Bezpieczne przetwarzanie dostaw

  • Zwróć odpowiedź 2xx dopiero po zaakceptowaniu wywołania zwrotnego do przetworzenia. Po odpowiedzi innej niż 2xx lub awarii transportu dostarczenie jest ponawiane do 10 razy.
  • Odpowiedz w ciągu 15 sekund. Wolniejsze zadania umieszczaj w kolejce po weryfikacji, zamiast blokować odpowiedź HTTP.
  • Odrzucaj wywołania zwrotne z brakującymi nagłówkami podpisu, nieprawidłowym podpisem lub znacznikiem czasu poza oknem tolerancji zdefiniowanym przez odbiornik.

Rozwiązywanie problemów z weryfikacją

  • Niezgodność sygnatury: upewnij się, że Callback Secret należy do tego samego konta co Zadanie, zdekoduj go za pomocą Base64 i podpisz oryginalną treść, a nie przeanalizowany JSON.
  • Brakujące nagłówki sygnatury: utwórz Callback Secret przed poleganiem na wywołaniach zwrotnych w przypadku zmian stanu.
  • Znacznik czasu odrzucony: zsynchronizuj zegar odbiornika i użyj tolerancji odpowiedniej dla swojego wdrożenia.

W celu uzyskania wskazówek dotyczących konfiguracji i cyklu życia wróć do Szybkiego startu Task API.