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. |
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:
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.
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.
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.
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:
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.
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 /oauth
endpoint
The Subscription API is available on the https://graph.facebook.com/[APP_ID]/subscriptions
endpoint
There are 3 tasks you can perform with it:
POST
request)GET
request)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.
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 must handle 2 types of requests. Ensure that it is on a public URL so that we can make these requests successfully.
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 |
---|---|
| The string " |
| A random string |
| The |
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.
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.
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:
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:
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:
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.You'll also receive updates for payments with "status": "failed"
. These should not be fulfilled.
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.
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.
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.