支払いのWebhooks

取引に関するリアルタイムアップデート。

支払いのWebhooks (旧「リアルタイムアップデート」)は、アプリ内のFacebook決済で行われた注文に関する変更の知らせを受け取るのに不可欠な手段です。

概要

Webhooksとは、Facebookと開発者のサーバーとの間のサブスクリプションベースのシステムです。アプリはサブスクリプション登録することで、指定されたHTTPSエンドポイントを経由して、Facebookからアップデートを受け取ります。アプリ内で行われた注文が変更されると、FacebookからそのエンドポイントにHTTPS POSTリクエストが発行されて、サーバーにその変更が通知されます。

開発者サーバーにアップデートが送信されるシナリオとして、主に以下の3つがあります。

Webhooksにサブスクリプション登録する

支払いWebhooksにサブスクリプション登録するには、まず、サブスクリプション認証用のHTTPS GETと変更データリクエスト用のPOSTの両方を受け取る公開エンドポイントのURLを作成します。これら両方のリクエストタイプの構造については、後述します。次に、アプリのpaymentオブジェクトをサブスクリプション登録します。次の2とおりの設定方法があります。

いずれの方法でも、エンドポイントは同じデータを同じ方法で受け取ります。サーバーが受け取る内容について詳しくは、コールバックサーバーをご覧ください。

アプリダッシュボードからサブスクリプション登録する

Webhooksのアップデートを受信するようにアプリを設定する場合、アプリダッシュボードの[支払い]パネルを使う方法が最も簡単です。ダッシュボードで当該アプリを見つけたら、Paymentsタブをクリックします。[Webhooks]セクションは、自社の[設定]セクションのすぐ下にあります。

支払いのWebhooks

アプリのサブスクリプションがこのパネルから追加された場合であっても、APIから追加された場合であっても、この画面にはアプリのサブスクリプションステータスが表示されます。ここから、サブスクリプションコールバックURLを変更してテストすることができます。

[コールバック]フィールドには、公開アクセスできる有効なサーバーエンドポイントを指定する必要があります。コールバックサーバーで説明したように、これはサブスクリプションの認証とアップデートの送信の両方に使用されるアドレスであり、応答する必要があります。

最後に、[認証トークン]を指定します。このトークンは、サブスクリプションが安全な場所から行われていることを検証するために、登録フェーズの間だけ送信されます。通常のWebhooksのアップデートでは送信されません。

設定をテストする

サブスクリプションを保存する前に、コールバック設定をテストします。テストでは、hub.modehub.challenge、およびhub.verify_tokenパラメーターを指定した認証GETリクエストがエンドポイントに発行され、これらが正しく処理されていることが確認されます。例えば次のように、エンドポイントからFacebookにhub.challengeがエコーバックされることを確認します。

設定をテストする

サブスクリプションの詳細を入力したら、必ずページ下部にある[変更を保存]ボタンをクリックしてください。サブスクリプションの編集は、フィールドの内容を変更し、再度テストし、もう一度フォームを保存するというシンプルな操作です。

グラフAPIからサブスクリプション登録する

グラフAPIを使って、サブスクリプションをプログラムで設定し、リストすることも可能です。その際には、アプリのaccess tokenが必要になります。これは、アクセストークンツールから入手することも、グラフAPIの/oauthエンドポイントを使って入手することもできます。

サブスクリプションAPIは、https://graph.facebook.com/[APP_ID]/subscriptionsエンドポイントで利用できます。

これを使って実行できるタスクは3つあります。

  • (HTTPS POSTリクエストを送信して)サブスクリプションを追加または変更する
  • (HTTPS GETリクエストを送信して)既存の各サブスクリプションのリストを取得する

サブスクリプションを追加する/変更する

サブスクリプションを設定するには、次のパラメーターを指定してPOSTを送信します。これらのパラメーターは、上述のフォーム内のフィールドと対応しています。

  • object - 上述のとおり、アップデートを受信するオブジェクトのタイプ。paymentsを指定します。
  • fields - 変更に関する最新情報を希望するオブジェクトタイプのプロパティのコンマ区切りリスト。「actions」と「disputes」を指定します。
  • callback_url - 公開アクセスできる有効なサーバーのエンドポイント。
  • verify_token - サブスクリプションが認証されるときにエンドポイントに送信される、任意の文字列。

Facebookは、上記のフォーム構成でこのリクエストを受け取ると、コールバックに対してGETを実行し、有効であることとアップデートの受信準備が整っていることを確認します。特に、エンドポイントからFacebookに確実にhub.challengeがエコーバックされるようにしなければなりません。

1つのアプリでサブスクリプション登録できるのは、1オブジェクトタイプにつき1つだけです。そのため、このオブジェクトタイプに既存のサブスクリプションがある場合、新たにPOSTされたデータで既存のデータが置き換えられるので注意してください。

サブスクリプションをリストする

サブスクリプションAPIにHTTP GETを発行すると、サブスクリプションをリストした、JSONエンコードのコンテンツが返されます。以下はその例です。

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

グラフエクスプローラを利用して、直接このAPIを試すこともできます。その際には、必ず自分のアプリのアクセストークンを使ってください。

コールバックサーバー

コールバックサーバーは、2種類のリクエストを処理する必要があります。これらのリクエストを成功させるため、必ず公開URLのサーバーにしてください。

サブスクリプションを認証する

サブスクリプションの追加や変更が試みられると、まず、FacebookサーバーからコールバックURLに単独のHTTPS GETが実行されます。コールバックURLには、次のパラメーターを指定したクエリ文字列が追加されます。

パラメーター 説明

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言語マニュアルのこちらの注記をご覧ください。

アップデートを受け取る

サブスクリプション登録が完了したら、(対象のフィールドや接続に)変更があるたびに、FacebookからサーバーエンドポイントにHTTPS POSTが発行されます。このリクエストには、HTTPコード200で応答する必要があります。

注 - 200以外のHTTP応答はエラーと見なされます。その場合も、Webhooksアップデートの送信は引き続き再試行されます。そのため、正しく応答しないと、同じアップデートを複数回受信することがあります。

リクエストのコンテンツタイプはapplication/jsonであり、本文はJSONエンコードの文字列で構成されており、そこに1件以上の変更が含まれています。

PHP開発者向けの注記: JSONエンコードのデータを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"
      ]
    }
  ]
}

Webhookアップデートは、idフィールドで特定される特定の支払いが変更されたことだけを通知します。アップデートの受信後に、グラフAPIをクエリして取引の詳細を確認し、変更に適宜対応する必要があります。

注 - 他のオブジェクトタイプのWebhooksはバッチ処理ができますが、paymentのアップデートについてはバッチ処理できません

ユーザー側と開発者側のいずれのアクションによるものであっても、取引が更新されるたびに新しいアップデートを受け取ることが保証されます。

サーバーへのアップデート送信が失敗すると、直ちに再試行されます。その後24時間、頻度を落としながら数回にわたって試行が繰り返されます。

毎回のリクエストで、X-Hub-Signature-256 HTTPヘッダーが送信されます。これには、リクエストペイロードのSHA256署名があり、app secretがキーとして使用され、sha256=のプレフィックスが付いています。コールバックエンドポイントは、この署名を認証して、ペイロードが改ざんされていないことと、ペイロードの発生元を確認できます。

アップデートに対応する

サーバーがアップデートを受信したら、idフィールドを使用してグラフAPIをクエリし、取引の新しいステータスに関して詳細を確認する必要があります。その後、そのステータスに応じて対応します。

以降のセクションでは、アップデート送信をトリガーする可能性があるすべての状態変更を列挙しています。これらは大きく次のように分類されます。

  • actions配列の変更。これは、支払いが非同期に完了した場合、返金が(あなたもしくはFacebookによって)発行された場合、またはチャージバックが行われた場合に発生します。
  • disputes配列の変更。これは、消費者から注文に関して申し立てがあった場合に発生します。

アクション

paymentオブジェクトにはactionsというタイトルの配列があります。これに、取引の進行に伴う状態変更のコレクションが含まれています。actions配列内の各項目には、実行されたアクションのタイプを記述するtypeという名前のプロパティがあります。typeの可能な値には、chargerefundchargebackchargeback_reversaldeclineがあります。これらの値については、こちらで詳しく説明しています

paymentオブジェクトと関連するactionsが含まれる、グラフ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,}`

Webhooksへの登録時にactionsフィールドをサブスクリプション登録すると、配列が次のように変更された場合にアップデートが発行されます。

チャージ

最初は、すべての注文のチャージエントリは"status": "initiated"になっています。initiatedステータスの支払いは、開始されただけで、まだ完了していません。initiated状態の支払いではアップデートは送信されません。

支払いが正常に完了すると、"status": "initiated""status": "completed"に変わり、アップデートが発行されます。この変更を受け取ったら、決済記録を確認して、新規取引であるか既存の取引であるかを確かめ、以下のように対応します。

  • 既知の注文であり、JavaScriptコールバックによって注文が履行されている(推奨される最初の選択肢)場合は、アップデートを無視することも、予備の確認として利用することもできます。
  • 既知の注文であるが、initiated状態である場合は、注文履行に進み、関連付けられている仮想アイテムまたは通貨を消費者に発行することができます。その後、この支払いを安全に完了とすることができます。
  • 注文が新規である場合は、クライアント側のフローが完了していないことを意味します。たいていは、接続の問題か、消費者が会計の途中でブラウザーを閉じてしまったことが原因です。Facebookはユーザーの請求に関して絶対的な信頼を置ける情報源であるため、その注文を安全に履行し完了することができます。

"status": "failed"の支払いのアップデートも受け取ります。これらの注文は履行しないでください。

返金

グラフAPIを使って返金を発行すると、必ずアップデートを受け取ります。"type": "charge"同様に、返金にも留意すべき各種のステータスがあります。主に処理エラーや接続エラーによって返金が失敗すると、返金の発行を再試行しなければならないので注意が必要です。

チャージバック、チャージバックの取り消し、却下

返金と同様に、チャージバック、チャージバックの取り消し、却下が発行されたときにも通知が届きます。チャージバック、チャージバックの取り消し、却下のオブジェクトが、当該支払いのグラフAPIの戻りデータのactions配列に追加されます。

申し立て

申し立てが開始されると、Facebookからアップデートを発行して通知します。すると、paymentオブジェクトの一部として新たな"disputes"配列が表示されます。この配列には、申し立ての開始時刻、当該消費者がその申し立てを開始した理由と、申し立て解決のためにその消費者と直接連絡を取れるメールアドレスが含まれます。

取引で申し立てが行われた場合の、グラフ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"
      }
   ]
}

申し立てへの対応方法と返金の発行方法については、支払いの方法: 申し立てと返金に対応するをご覧ください。