付款專用 Webhooks

關於交易的即時更新。

付款專用 Webhooks(前稱「即時更新」)是一種很重要的方法,我們會透過這種方式通知您應用程式內 Facebook 付款發生的訂單變更。

概覽

Webhooks 是 Facebook 與您伺服器之間的訂閱式系統。訂閱後,您的應用程式會透過指定 HTTPS 端點接收來自 Facebook 的更新項目。如果您應用程式內的訂單有所更新,我們就會向該端點發出 HTTPS POST 要求,以通知您的伺服器相關變更項目。

在以下 3 種主要情況下,我們會將更新項目傳送到您的開發人員伺服器:

訂閱 Webhooks

如要訂閱付款 Webhooks,請先建立公開端點網址以接收 HTTPS GETPOST,分別用於訂閱驗證和變更資料要求。有關這兩種要求類型的結構,請見下文說明。然後,設定訂閱應用程式的 payment 物件。如要執行此操作,可以採用以下 2 種方式:

無論採用哪種方式,您的端點都會以相同方式收到相同的資料。請查看回呼伺服器部分,進一步了解您伺服器會收到的內容。

透過應用程式管理中心訂閱

如要設定應用程式以接收 Webhooks 更新資訊,最簡單的方法就是使用應用程式管理中心的「付款」面板。在管理中心找出應用程式,然後點擊 Payments 分頁。「Webhooks」區塊位於公司「設定」區塊的正下方。

付款專用 Webhooks

然後,這個畫面會列出應用程式的訂閱狀態,供您了解此為透過此面板加入還是透過 API 加入。您可以在此變更訂閱回呼網址並對其進行測試。

在「回呼」欄位中,您必須提供有效且可公開存取的伺服器端點。這是我們用來驗證訂閱與傳送更新的網址,而此網址需要以回呼伺服器部分中所述的方式作出回應。

最後,請提供「驗證憑證」。這個憑證只會在註冊階段時傳送,用來驗證訂閱是否來源於安全的地點。此憑證不會透過一般 Webhook 更新項目傳送。

測試設定

儲存訂閱前,請先測試回呼設定。此操作會向您的端點傳送驗證 GET 要求,其中包含 hub.modehub.challengehub.verify_token 參數,並確保您以正確方式完成處理。舉例來說,您必須確保端點會向 Facebook 回應 hub.challenge

測試設定

輸入訂閱詳情後,請記得點擊頁面下方的「儲存變更」按鈕。編輯訂閱相當簡單,只需修改欄位內容、重新測試,然後再次儲存表格即可。

透過 Graph API 訂閱

您也可以透過 Graph API 設定訂閱,並以程式輔助方式列出訂閱清單。您需要應用程式的 access token,此憑證可透過存取憑證工具或使用 Graph API 的 /oauth 端點取得

訂閱 API 可從 https://graph.facebook.com/[APP_ID]/subscriptions 端點取得

您可以透過此 API 執行 3 種任務:

  • 新增或修改訂閱(透過傳送 HTTPS POST 要求)
  • 列出各項現有訂閱(透過傳送 HTTPS GET 要求)

新增或修改訂閱

如要設定訂閱,請傳送包含以下參數的 POST。請注意,這些參數對應上述表格中的欄位:

  • object:如上所述,此為您想接收相關更新的物件類型。指定 payments
  • fields:逗號分隔清單,其中列出您想接收相關變更更新通知的物件類型屬性。指定「actions」及「disputes」。
  • callback_url:有效且可公開存取的伺服器端點。
  • verify_token:在訂閱經驗證後向您端點傳送的任何字串。

我們接收到這個要求及上述表格配置後,便會向您的回呼執行 GET,以確保回呼有效且已準備好接收更新項目。請特別留意,您必須確保端點會向 Facebook 回應 hub.challenge

請注意,應用程式每一種物件類型只能有一個訂閱,因此如果這種物件類型已有訂閱存在,新發佈的資料便將取代現有的所有資料。

列出訂閱

如果向訂閱 API 傳送 HTTP GET,系統將會傳回 JSON 編碼內容,當中列有您的訂閱。例如:

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

您可以使用圖形測試工具直接測試此 API,但請記得使用應用程式的存取憑證

回呼伺服器

您的回呼伺服器必須處理 2 類要求。請確保回呼伺服器位於公開網址,這樣我們才能成功發出這些要求。

訂閱驗證

首先,當您嘗試新增或修改訂閱時,Facebook 伺服器會向您的回呼網址發出單獨的 HTTPS GET。查詢字串會連同以下參數一同附加至回呼網址:

參數 說明

hub.mode

subscribe」字串會在此參數中傳遞

hub.challenge

隨機字串

hub.verify_token

您在建立訂閱時指定的 verify_token

端點應先驗證 hub.verify_token,以確保您的伺服器知道此要求是由 Facebook 發出,且與您剛才配置的訂閱有關。

然後,伺服器應只回應 hub.challenge 值,以向 Facebook 確認此伺服器已配置為可接收回呼,並預防出現阻斷服務攻擊 (DDoS) 漏洞。

PHP 開發人員注意事項:在 PHP 中,查詢參數名稱中的圓點和空格會自動轉換為底線。因此,如果您在 PHP 中撰寫回呼端點,應該使用 $_GET['hub_mode']$_GET['hub_challenge']$_GET['hub_verify_token'] 來存取這些參數。如要了解詳情,請參閱 PHP 語言手冊中的注意事項

接收更新

訂閱成功後,當您所選欄位或連結有所變更,我們就會向您的伺服器端點傳送 HTTPS POST。您必須以 HTTP 代碼 200 回應此要求。

注意:我們會將 200 以外的所有 HTTP 回應視為錯誤。在這些情況下,我們會繼續重新嘗試傳送 Webhooks 更新。如果您仍未以正確方式作出回應,就可能會多次收到相同的更新。

要求會是 application/json 內容類型,其正文由 JSON 編碼字串組成,當中包含一個或多個變更項目。

PHP 開發人員注意事項:在 PHP 中,如要取得已編碼的資料,需要使用以下代碼:

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

請注意,一旦確認訂閱,系統不會再次傳送 hub.modehub.challengehub.verify_token 參數。

以下為向 payments 物件訂閱執行回呼的常見範例:

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

請特別注意,Webhooks 更新只會通知您某個特定付款(id 欄位識別)有所變更。收到更新後,您接下來需要查詢 Graph API 以了解交易詳情,以妥善處理變更。

注意:其他物件類型的 Webhooks 可作批次處理,但付款更新項目絕對無法作批次處理

我們可確保每當交易有所更新時,您都一定會收到新的更新項目,無論更新是由用戶還是開發人員操作也是如此。

如果更新無法送至您的伺服器,我們將立即重試,然後在接下來的 24 小時內再重試幾次,但重試頻率會下降。

在每個要求中,我們都會以應用程式密鑰作為密鑰來傳送 X-Hub-Signature-256 HTTP 頁首,其中包含要求裝載的 SHA256 簽章,首碼為 sha256=。回呼端點可以驗證此簽章,從而驗證裝載的完整性及來源。

回應更新

伺服器收到更新後,您應該使用 id 欄位查詢 Graph API,以了解交易新狀態的詳情。然後,您應根據狀態採取相應行動。

以下部分列出了所有會觸發傳送更新項目的狀態變更項目,大致可分為兩類:

  • 動作陣列的變更項目:發生原因包括沒有同步完成付款、發起退款(發起方可以是您或 Facebook),或是發生要求退款事件。
  • 異議陣列的變更項目:發生原因包括消費者提出訂單異議。

動作

每個 payment 物件都包含一個名為 actions 的陣列,其中包含一系列交易已處理完的狀態變更項目。actions 陣列中的每個條目都有一個名為 type 的屬性,用於說明已發生的動作之類型。type 可以是以下的值:chargerefundchargebackchargeback_reversaldecline詳見此處)。

以下為 Graph API 連同相關動作對付款物件發出的回應範例:

{
   "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,}`

由於您在註冊 Webhook 時已訂閱動作欄位,因此當陣列發生以下變更時,我們會發出更新:

收費

一開始,所有訂單都包含一個帶有 "status": "initiated" 的收費條目。「已發起的付款」表示付款只是有人發起,尚未完成。我們不會為處於「已發起」狀態的付款傳送更新。

成功完成付款後,"status": "initiated" 會變更為 "status": "completed",我們會為此傳送更新。看見此變更項目後,您應該查看付款記錄,以確認此為新交易還是現有的交易,並按以下方式作出回應:

  • 如果是已知訂單,且訂單已由 JavaScript 回呼(首選建議)完成,則您可以放心略過此更新,或將其視為額外的確認資訊。
  • 如果是已知訂單,但其處於 initiated 狀態,則您可以繼續完成訂單,並向消費者傳送相關虛擬商品或貨幣。之後,此次付款可順利被標記為完成。
  • 如果是未知訂單,即表示用戶端流程未完成,這很可能是連線問題所致,或是因為消費者在結帳過程中關閉了瀏覽器。您仍然可以放心完成此訂單,因為 Facebook 在保障用戶帳單安全方面一直相當可靠。

此外,您亦會收到付款為 "status": "failed" 的更新。請勿完成此類訂單。

退款

每當您透過 Graph API 發起退款,就會收到更新。與 "type": "charge" 一樣,退款也有不同狀態,需要多加注意。其中最重要的一點就是,退款可能會失敗,這通常是處理或連線出錯所致。如果發生這種情況,您應重新發起退款。

買家要求退款、取消退款與拒絕退款

與退款一樣,如有買家要求退款、取消退款與拒絕退款,您也會收到通知。買家要求退款、取消退款與拒絕退款物件會新增至 Graph API 付款傳回資料中的動作陣列。

異議

如有發起異議,我們會傳送更新項目通知您。在這種情況下,您會看到 payment 物件中出現新的 "disputes" 陣列。此陣列會包含發起異議的時間、消費者提出回應的原因,以及消費者的電郵地址,以便您透過此電郵地址直接聯絡消費者解決異議。

以下為 Graph API 對有異議交易發出的完整回應範例:

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

如需進一步了解如何回應異議及發起退款,請參閱付款操作說明:處理異議和退款