Webhooks for Payments

Realtime updates about your transactions.

Webhooks for Payments (formerly Realtime Updates) are an essential method by which you're informed of changes to orders made through Facebook Payments within your app.

Overview

Webhooks are a subscription based system between Facebook and your server. Your app subscribes to receive updates from Facebook via a specified HTTPS endpoint. When an order made within your app is updated, we will issue an HTTPS POST request to that endpoint, notifying your server of the change.

There are 3 primary scenarios in which updates are sent to your developer server:

Subscribing to Webhooks

To subscribe to Payments Webhooks, first create a public endpoint URL that receives both HTTPS GET for subscription verification and POST for change data requests. The structure of both of these types of requests is described below. Next, set up subscriptions to the payment object of your app. There are 2 ways to do this:

In either case, your endpoint will receive the same data in the same manner. See Your Callback Server for more information on what your server wiil receive.

Subscribing via the App Dashboard

The easiest way to set up your app to receive Webhooks updates is to use the App Dashboard's Payments panel. Find your app in the dashboard and then click on the Payments tab. The Webhooks section will be just below your company's Settings section.

Webhooks for Payments

This screen will then list your app's subscription status, whether it's been added through this panel or the API. From here, it's possible to change the subscription callback URL and test it.

In the 'Callback' field, you must provide a valid publicly accessible server endpoint. This is the address that we will use to both verify the subscription and send the updates, and needs to respond as described in Your Callback Server.

Finally, provide a 'Verification Token'. This token will be sent only during the enrolling phase to verify that the subscription is being originated from a secure location. This token won't be sent on regular Webhook updates.

Testing your settings

You should test the callback settings before saving the subscription. This will issue a verification GET request to your endpoint, containing the hub.mode, hub.challenge and hub.verify_token parameters, and will ensure that you handle them correctly. For example, you must be sure your endpoint echoes hub.challenge back to Facebook:

Testing your settinfs

Once you’ve entered your subscription details, be sure to click the ‘Save Changes’ button at the bottom of the page. Editing a subscription is a simple matter of altering the contents of the fields, re-testing and then saving the form again.

Subscribing via the Graph API

It's also possible to set up and list subscriptions programmatically, through the Graph API. You'll need your app's access token, which is available from the access token Tool or by using the Graph API's /oauthendpoint

The Subscription API is available on the https://graph.facebook.com/[APP_ID]/subscriptions endpoint

There are 3 tasks you can perform with it:

  • Add or modify a subscription (by sending an HTTPS POST request)
  • List each of your existing subscriptions (by sending an HTTPS GET request)

Adding and modifying subscriptions

To set up a subscription, send a POST with the following parameters. Note that these parameters correspond to the fields in the form described above:

  • object - As above, the type of the object you want to receive updates about. Specify payments.
  • fields - A comma-separated list the properties of the object type that you'd like to be updated about changes to. Specify 'actions' and 'disputes'.
  • callback_url - A valid, and publicly accessible, server endpoint.
  • verify_token - An arbitrary string, sent to your endpoint when the subscription is verified.

When we receive this request, as with the form configuration above, we will perform a GET to your callback to ensure that it's valid and ready to receive updates. In particular, you must be sure your endpoint echoes hub.challenge back to Facebook.

Note that, because an app can only have one subscription for each object type, if a subscription exists for this object type, then the newly posted data replaces any existing data.

Listing your subscriptions

Issuing an HTTP GET to the Subscription API returns JSON-encoded content that lists your subscriptions. For example:

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

You can use the Graph Explorer to experiment with this API directly, remembering to use your app's access token.

Your Callback Server

Your callback server must handle 2 types of requests. Ensure that it is on a public URL so that we can make these requests successfully.

Subscription Verification

First, Facebook servers will make a single HTTPS GET to your callback URL when you try to add or modify a subscription. A query string will be appended to your callback URL with the following parameters:

Parameter Description

hub.mode

The string "subscribe" is passed in this parameter

hub.challenge

A random string

hub.verify_token

The verify_token value you specified when you created the subscription

The endpoint should first verify the hub.verify_token. This ensures that your server knows that the request is being made by Facebook and relates to the subscription you just configured.

It should then echo just the hub.challenge value back, which confirms to Facebook that this server is configured to accept callbacks, and prevents denial-of-service (DDoS) vulnerabilities.

Note for PHP developers: In PHP, dots and spaces in query parameter names are converted to underscores automatically. Therefore, you should access these parameters using $_GET['hub_mode'],$_GET['hub_challenge'] and $_GET['hub_verify_token'] if you are writing your callback endpoint in PHP. See this note in the PHP language manual for more details.

Receiving Updates

Following a successful subscription, we will proceed to issue an HTTPS POST to your server endpoint every time that there are changes (to the chosen fields or connections). You must respond to this request with HTTP code 200.

Note - we consider any HTTP response other than 200 to be an error. In these circumstances we'll continue to retry sending the webhooks update. If you don't respond correctly therefore, you may receive the same update multiple times.

The request will have content type of application/json and the body will comprise a JSON-encoded string containing one or more changes.

Note for PHP developers: In PHP, to get the encoded data you'd use the following code:

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

Note that the hub.mode, hub.challenge and hub.verify_token parameters aren't sent again once the subscription has been confirmed.

Here is a typical example of a callback made for a payments object subscription:

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

It's important to note that Webhook updates only inform you that a particular payment, identified by the id field has been changed. After receiving the update, you're then required to query the Graph API for details of the transaction, to handle the change appropriately.

Note - While Webhooks for other object types can be batched, payment updates are never batched.

You're guaranteed to receive a new update every time a transaction is updated, either by user action or developer action.

If an update to your server fails, we will retry again immediately and then a few times more, with decreasing frequency, over the subsequent 24 hours.

With every request, we send a X-Hub-Signature-256 HTTP header which contains the SHA256 signature of the request payload, using the app secret as the key, and prefixed with sha256=. Your callback endpoint can verify this signature to validate the integrity and origin of the payload.

Responding to Updates

After your server receives an update, you should query the Graph API using the id field for details on the new status of the transaction. You should then take action depending on the status.

The following sections enumerate all the potential state changes that trigger an update to be sent. These are broadly divided into:

  • Changes to the actions array, which occurs when a payment completes asynchronously, a refund is issued (by either you or by Facebook) or when a chargeback occurs.
  • Changes to the disputes array, which occurs when an order dispute is initiated by the consumer.

Actions

Each payment object contains an array titled actions, containing the collection of state changes the transaction has progressed through. Each entry in the actions array has a property named type which describes the type of action that has taken place. type can have the following values: charge, refund,chargeback, chargeback_reversal and decline, which are fully explained here.

A sample response from the Graph API for a payment object with associated actions is below:

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

As you subscribed to the actions field when registering for Webhooks, we will issue an update when the array changes as follows:

Charge

Initially, all orders contain a charge entry with "status": "initiated". An initiated payment designates the payment was only initiated and hasn't yet fully completed. we won't send updates for payments in an initiated state.

When a payment completes successfully, "status": "initiated" will be changed to "status": "completed" and we will issue an update. Upon seeing this change you should check your payment records to verify if this is a new or existing transaction and respond as follows:

  • If the order is already known to you, and has been fulfilled by the JavaScript callback (preferable as a first-choice), then you can either safely ignore the update, or use it as an extra confirmation.
  • If the order is known to you, but exists in an initiated state, then you can proceed to fulfill the order, issuing the associated virtual item or currency to the consumer. This payment can then safely be marked as complete.
  • If the order is unknown, it indicates that the client-side flow didn't complete, most likely due to a connectivity issue or the consumer closing their browser mid-checkout. You can still safely fulfill and complete this order, because Facebook remains the ultimate source of truth regarding user billing.

You'll also receive updates for payments with "status": "failed". These should not be fulfilled.

Refund

Whenever you issue a refund via the Graph API, you'll receive an update. As with "type": "charge", a refund can also have a varying status of which you must be aware. Most notably, it's possible for a refund to fail, typically due to a processing or connectivity error - in which case you should retry to issue the refund.

Chargeback, Chargeback Reversal and Declines

As with refunds, you'll also be notified when a chargeback, chargeback reversal or decline has been issued. A chargeback, chargeback reversal or decline object will be added to the actions array of the Graph API return data for the payment.

Disputes

We will notify you by issuing an update when a dispute is initiated. In this case, you'll see a new"disputes" array appear as part of the payment object. The array will contain the time the dispute was initiated, the consumer's reason for initiated the response and the consumer's email address, which you can use to contact them directly to resolve the dispute.

A full sample response from the Graph API for a disputed transaction is below:

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

For more information on how to respond to disputes and issue refunds, please see Payments How-to: Handling Disputes and Refunds.