Webhooks für Zahlungen

Echtzeit-Updates zu deinen Transaktionen.

Webhooks für Zahlungen wurden früher als Echtzeit-Updates bezeichnet. Sie sind eine wichtige Methode, mit der du über Änderungen an Bestellungen über Facebook Payments in deiner App informiert wirst.

Übersicht

Webhooks sind ein abonnementbasiertes System zwischen Facebook und deinem Server. Deine App abonniert Updates von Facebook über einen angegebenen HTTPS-Endpunkt. Wenn eine mit deiner App aufgegebene Bestellung aktualisiert wird, erstellen wir eine HTTPS-POST-Anfrage an diesen Endpunkt und benachrichtigen deinen Server über die Änderung.

Es gibt drei Hauptszenarien, in denen Updates an deinen Entwicklungsserver gesendet werden:

Abonnieren von Webhooks

Erstelle zum Abonnieren von Zahlungen-Webhooks zunächst eine öffentliche Endpunkt-URL, die sowohl HTTPS-GET für die Abonnementprüfung als auch POST für Änderungsdatenanfragen empfängt. Die Struktur dieser beiden Anfragetypen wird nachfolgend beschrieben. Richte als Nächstes Abonnements des payment-Objekts deiner App ein. Hierfür gibt es zwei Möglichkeiten:

In beiden Fällen empfängt dein Endpunkt dieselben Daten auf dieselbe Weise. Weitere Informationen über das, was dein Server empfängt, findest du unter Dein Callback-Server.

Abonnieren über das App-Dashboard

Am einfachsten richtest du deine App für den Empfang von Webhook-Updates über das Panel „Zahlungen“ deines App-Dashboards ein. Suche deine App im Dashboard und klicke dann auf den Tab Payments. Der Abschnitt „Webhooks“ befindet sich direkt unterhalb des Abschnitts mit den Einstellungen deines Unternehmens.

Webhooks für Zahlungen

Auf diesem Bildschirm wird dann der Abonnementstatus deiner App aufgelistet, unabhängig davon, ob er über dieses Panel hinzugefügt wurde oder über die API. Hier kannst du die Abonnement-Callback-URL ändern und testen.

Im Feld „Callback“ musst du einen gültigen öffentlich zugänglichen Serverendpunkt angeben. Diese Adresse verwenden wir zur Prüfung des Abonnements und auch zum Senden von Updates. Sie muss wie unter Dein Callback-Server beschrieben antworten.

Gib zum Schluss einen „Verifizierungs-Token“ an. Dieser Token wird nur während der Registrierungsphase gesendet, um zu prüfen, ob das Abonnement von einem sicheren Ort stammt. Dieser Token wird nicht bei normalen Webhook-Updates gesendet.

Testen deiner Einstellungen

Du solltest die Callback-Einstellungen testen, bevor du das Abonnement speicherst. Dadurch wird eine GET-Anfrage zur Prüfung an deinen Endpunkt gesendet. Sie enthält die Parameter hub.mode, hub.challenge und hub.verify_token und stellt sicher, dass du sie korrekt verarbeitest. Du musst beispielsweise sicherstellen, dass dein Endpunkt hub.challenge an Facebook zurücksendet:

Testen deiner Einstellungen

Nach Eingabe der Abonnementdetails musst du unten auf der Seite auf den Button „Änderungen speichern“ klicken. Zum Bearbeiten eines Abonnements änderst du einfach den Inhalt der Felder, testest sie erneut und speicherst das Formular wieder.

Abonnieren über die Graph API

Es ist auch möglich, Abonnements programmatisch durch die Graph API einzurichten und aufzulisten. Du brauchst dazu den access token deiner App. Du findest ihn über das Zugriffsschlüssel-Tool oder den /oauth-Endpunkt der Graph API.

Die Subscription API ist am Endpunkt unter https://graph.facebook.com/[APP_ID]/subscriptions verfügbar.

Du kannst damit drei Aufgaben ausführen:

  • Hinzufügen oder Ändern eines Abonnements (durch Senden einer HTTPS-POST-Anfrage)
  • Auflisten der bestehenden Abonnements (durch Senden einer HTTPS-GET-Anfrage)

Hinzufügen und Ändern von Abonnements

Sende zum Einrichten eines Abonnements einen POST mit den nachfolgenden Parametern. Beachte, dass diese Parameter den Feldern im oben beschriebenen Formular entsprechen:

  • object – Wie oben, der Typ des Objekts, zu dem du Updates erhalten möchtest. Gib payments an.
  • fields – Eine durch Kommas getrennte Liste der Eigenschaften des Objekttyps, zu dem du Updates bei Änderungen erhalten möchtest. Gib „actions“ und „disputes“ an.
  • callback_url – Ein gültiger und öffentlich zugänglicher Serverendpunkt.
  • verify_token – Ein beliebiger String, der bei Verifizierung des Abonnements an deinen Endpunkt gesendet wird.

Wenn wir diese Anfrage erhalten, führen wir wie bei der oben genannten Konfiguration des Formulars einen GET an deinen Callback durch, um sicherzustellen, dass er gültig ist und Updates erhalten kann. Du musst insbesondere sicher sein, dass dein Endpunkt hub.challenge an Facebook zurücksendet.

Beachte, dass neu gepostete Daten bestehende Daten ersetzen, wenn ein Abonnement für einen bestimmten Objekttyp vorhanden ist, weil eine App nur ein Abonnement pro Objekttyp haben kann.

Auflisten deiner Abonnements

Durch das Senden eines HTTP-GET an die Subscription API wird JSON-verschlüsselter Inhalt zurückgegeben, der deine Abonnements auflistet. Beispiel:

[
  {
    "object": "payments",
    "callback_url": "https://www.friendsmash.com/rtu.php",
    "fields": ["actions", "disputes"],
    "active": true
  }
]

Du kannst mit dem Graph Explorer direkt mit dieser API experimentieren. Denke daran, den Zugriffsschlüssel deiner App zu verwenden.

Dein Callback-Server

Dein Callback-Server muss zwei Arten von Anfragen verarbeiten. Vergewissere dich, dass er unter einer öffentlichen URL zu finden ist. Nur so sind die Anfragen erfolgreich.

Verifizierung des Abonnements

Zunächst senden Facebook-Server einen einzelnen HTTPS-GET an deine Callback-URL, wenn du versuchst, ein Abonnement hinzuzufügen oder zu ändern. Ein Abfrage-String mit den folgenden Parametern wird an die Callback-URL angehängt:

Parameter Beschreibung

hub.mode

Der String „subscribe“ wird in diesem Parameter übergeben.

hub.challenge

Ein zufälliger String

hub.verify_token

Der verify_token-Wert, den du beim Erstellen des Abonnements angegeben hast.

Der Endpunkt sollte zunächst den hub.verify_token verifizieren. Dadurch weiß dein Server, dass die Anfrage von Facebook gestellt wurde und sich auf das soeben konfigurierte Abonnement bezieht.

Er sollte dann nur den hub.challenge zurücksenden. Für Facebook ist dies die Bestätigung, dass dieser Server für die Annahme von Callbacks konfiguriert wurde. Außerdem werden so Denial-of-Service(DoS)-Schwachstellen verhindert.

Hinweis für PHP-Entwickler*innen: In PHP werden Punkte und Leerzeichen in Abfrageparameternamen automatisch zu Unterstrichen konvertiert. Daher solltest du auf diese Parameter mithilfe von $_GET['hub_mode'], $_GET['hub_challenge'] und $_GET['hub_verify_token'] zugreifen, wenn du deinen Callback-Endpunkt in PHP schreibst. Weitere Details findest du in diesem Hinweis im PHP-Handbuch.

Empfangen von Updates

Nach erfolgreicher Einrichtung des Abonnements senden wir bei jeder Änderung an den ausgewählten Feldern oder Verbindungen einen HTTP-POST an deinen Serverendpunkt. Du musst auf diese Anfrage mit HTTP-Code 200 antworten.

Hinweis: Andere HTTP-Antworten als 200 betrachten wir als Fehler. Unter diesen Umständen versuchen wir weiterhin, das Webhooks-Update zu senden. Wenn du also nicht korrekt antwortest, erhältst du möglicherweise dasselbe Update mehrmals.

Die Anfrage weist den Inhaltstyp application/json auf. Der Text enthält einen JSON-verschlüsselten String mit einer oder mehreren Änderungen.

Hinweis für PHP-Entwickler*innen: In PHP rufst du verschlüsselte Daten mit folgendem Code ab:

$data = file_get_contents("php://input");
$json = json_decode($data);`

Beachte, dass die Parameter hub.mode, hub.challenge und hub.verify_token nicht erneut gesendet werden, sobald das Abonnement bestätigt ist.

Hier siehst du ein typisches Beispiel eines Callbacks zu einem payments-Objekt-Abonnement:

{
  "object": "payments",
  "entry": [
    {
      "id": "296989303750203",
      "time": 1347996346,
      "changed_fields": [
        "actions"
      ]
    }
  ]
}

Es ist wichtig zu wissen, dass Webhook-Updates dich nur darüber informieren, dass eine bestimmte Zahlung geändert wurde. Diese ist durch das id-Feld gekennzeichnet. Nach Erhalt des Updates musst du die Graph API abfragen, um Details der Transaktion zu erhalten und die Änderung entsprechend zu verarbeiten.

Hinweis: Webhooks für andere Objekttypen können gestapelt werden. Zahlungs-Updates werden niemals gestapelt.

Du erhältst garantiert ein neues Update bei jeder Aktualisierung einer Transaktion. Dies erfolgt entweder durch eine Nutzer*innen- oder eine Entwickler*innen-Handlung.

Wenn ein Update nicht an deinen Server gesendet werden kann, versuchen wir es sofort erneut. Danach nehmen wir in immer größeren Abständen in den nächsten 24 Stunden weitere Wiederholungsversuche vor.

Mit jeder Anfrage senden wir einen X-Hub-Signature-256-HTTP-Header mit der SHA256-Signatur der Anfrage-Payload. Wir verwenden dazu den App-Geheimcode als Schlüssel und das Präfix sha256=. Dein Callback-Endpunkt kann diese Signatur überprüfen, um die Integrität und den Ursprung der Payload zu validieren.

Reagieren auf Updates

Nach Erhalt eines Updates am Server solltest du die Graph API abfragen. Nutze dazu das id-Feld, um Details zum neuen Status der Transaktion zu erhalten. Du solltest dann abhängig vom Status handeln.

In den folgenden Abschnitten sind alle potenziellen Statusänderungen aufgeführt, die das Senden eines Updates auslösen. Sie sind grob unterteilt in:

  • Änderungen am actions-Array. Diese treten auf, wenn eine Zahlung asynchron abgeschlossen, eine Rückerstattung durch dich oder Facebook ausgestellt wird oder wenn eine Rückbuchung erfolgt.
  • Änderungen am disputes-Array. Diese treten auf, wenn der Verbraucher eine Bestellungs-Anfechtung initiiert.

Handlungen

Jedes payment-Objekt enthält ein Array mit dem Titel actions. Es enthält die Sammlung von Statusänderungen, die die Transaktion durchlaufen hat. Jeder Eintrag im actions-Array umfasst eine Eigenschaft namens type. Sie beschreibt die Handlung, die vorgenommen wurde. type kann die folgenden Werte haben: charge, refund, chargeback, chargeback_reversal und decline, die hier vollständig erläutert sind.

Nachfolgend findest du eine Beispielantwort der Graph API auf ein Zahlungsobjekt mit verknüpften Handlungen:

{
   "id": "3603105474213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      },
      {
         "type": "refund",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-23T21:18:54+0000",
         "time_updated": "2013-03-23T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.00"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,}`

Da du beim Registrieren für Webhooks das Handlungsfeld abonniert hast, stellen wir ein Update aus, wenn sich das Array wie folgt ändert:

Belastung

Zu Beginn enthalten alle Bestellungen einen Belastungseintrag mit "status": "initiated". Eine initiierte Zahlung bezeichnet, dass die Zahlung nur initiiert, jedoch nicht vollständig abgeschlossen wurde. Wir senden keine Updates für Zahlungen mit dem Status „initiated“.

Wenn eine Zahlung erfolgreich abgeschlossen wurde, wird "status": "initiated" zu "status": "completed" geändert und wir stellen ein Update aus. Wenn du diese Änderung siehst, solltest du deine Zahlungsdatensätze prüfen, um zu sehen, ob es sich um eine neue oder bestehende Transaktion handelt und folgendermaßen vorgehen:

  • Wenn du die Bestellung bereits kennst und sie vom JavaScript-Callback (vorzugsweise als erste Wahl) ausgeführt wurde, kannst du das Update entweder ignorieren oder als zusätzliche Bestätigung verwenden.
  • Wenn du die Bestellung kennst, sie jedoch den Status initiated aufweist, kannst du mit der Ausführung der Bestellung fortfahren und das verknüpfte virtuelle Element oder den Währungsbetrag an den Kunden ausstellen. Diese Zahlung kann dann als abgeschlossen markiert werden.
  • Wenn die Bestellung unbekannt ist, bedeutet dies, dass der Vorgang am Client nicht abgeschlossen wurde. Der Grund ist wahrscheinlich ein Verbindungsproblem oder ein Abbruch des Bestellvorgangs durch den Verbraucher. Du kannst diese Bestellung immer noch ausführen und abschließen, weil Facebook letztendlich hinsichtlich der Nutzer*innen-Abrechnung die zuverlässigste Datenquelle ist.

Du erhältst auch Updates für Zahlungen mit "status": "failed". Diese sollten nicht ausgeführt werden.

Rückerstattung

Du erhältst ein Update, sobald du eine Rückerstattung über die Graph API ausstellst. Wie bei "type": "charge" kann eine Rückerstattung unterschiedliche Statusangaben aufweisen, die du beachten musst. Möglicherweise schlägt eine Rückerstattung aufgrund eines Verarbeitungs- oder Verbindungsproblems fehl. In diesem Fall solltest du die Rückerstattung einfach erneut ausstellen.

Rückbuchungen, Rückbuchungs-Stornierungen und -Absagen

Wie auch bei Rückerstattungen wirst du benachrichtigt, wenn eine Rückbuchung oder eine Rückbuchungs-Stornierung oder -Absage ausgestellt wurde. Ein Objekt für eine Rückbuchung oder für eine Rückbuchungs-Stornierung oder -Absage wird dem „actions“-Array der Graph API-Rückgabedaten für die Zahlung hinzugefügt.

Anfechtungen

Wir benachrichtigen dich mit einem Update, wenn eine Anfechtung initiiert wird. In diesem Fall siehst du ein neues "disputes"-Array als Teil des payment-Objekts. Das Array enthält die Uhrzeit der Initiierung der Anfechtung, den Grund des*der Verbraucher*in für die Initiierung der Reaktion sowie die E-Mail-Adresse des*der Verbraucher*in. Letztere kannst du verwenden, um den*die Verbraucher*in zur Beilegung der Anfechtung direkt zu kontaktieren.

Nachfolgend findest du ein vollständiges Beispiel einer Antwort der Graph API auf eine angefochtene Transaktion:

{
   "id": "990361254213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.99"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,
   "disputes": [
      {
         "user_comment": "I didn't receive my item! I want a refund, please!",
         "time_created": "2013-03-24T18:21:02+0000",
         "user_email": "email\u0040domain.com",
         "status": "resolved", 
         "reason": "refunded_in_cash"
      }
   ]
}

Weitere Informationen zu Antworten auf Anfechtungen und die Ausstellung von Rückerstattungen findest du unter Informationen zur Vorgehensweise bei Anfechtungen und Rückerstattungen.