This guide walks you through all the technical implementation details required to take payments in your game. Follow each step of the guide in turn: they follow a logical progression of implementation, from basic getting started steps to advanced topics like handling bank chargebacks. If you do not have your own server, you might want to look at how you can host your payment products via Payments Lite here. |
Before you can accept payments within your app, it's necessary to register your company information with Facebook. We'll begin by assuming you've set up your app to run as a Facebook Web Game, and you're ready to enable payments. Start by going to your app's Settings page in the App Dashboard. On the left rail, click on "+Add Product" and select "Web Payments" option.
Web payments are available only to Cloud Games.
You'll see a drop-down if you've already registered a company, or the register a company button if you need to register another company. If you haven't registered a company for your app yet, click the "Create a New Company" button.
You'll be given a form to fill out, where we gather information to make sure we can identify you unambiguously, transfer money to you and comply with tax regulations in your country and ours.
Then, specify the method by which Facebook will pay you, and specify verified Facebook users to act as administrators of your company. Company administrators can manage your company information, access payments reports and configure an app to pay out to a company. To do this, you need to be an administrator of both the company and the app.
Once you've completed the registration process, there will be a "Company Name" dropdown available where the "Create a New Company" button was previously. Choose the company you just set up.
After you've selected the company for your app, you'll need to set up Webhooks for Payments (formerly known as Realtime Updates). Webhooks are an essential method by which you're informed of changes to orders made through Facebook Payments within your app.
To learn how to set up Webhooks for Payments, you can read more on the Payment Fulfillment section. Once you've configured your server, the easiest way to set up your app to receive updates is to use the Webhooks section of your App Dashboard's Payments panel.
Once you've chosen the company and updated your webhooks section, click the "Save Changes" button.
To create a new product, you must construct a publicly accessible web page hosted on the Internet, which contains the required meta tags of an og:product
object.
If you do not have your own server, you might want to look at how you can host your payment products via Payments Lite here.
The Open Graph type og:product
is a structured container for descriptive metadata about a product, either a virtual item or a virtual currency. It has the propertiesog:title
, og:url
, og:image
and og:description
, similar to other Open Graph objects' use of properties with those names. There are other optional properties particular to the og:product
type, depending on which pricing model is used.
Name | Required | Description |
---|---|---|
| yes | The value must be |
| yes | Title of the product ex. 'In Your Village Money'. |
| yes | The unique URL of the product. |
| yes | The description for this product. |
| yes | The image which represents your product. The image must be at least 200px by 200px. We support PNG, JPEG and GIF formats. |
| no | Title of the product when a quantity more than 1 is purchased. |
| yes if Static Pricing / no if Dynamic Pricing | A decimal number with a '.' as the decimal separator. Values less than 0.01 aren't supported. |
| yes if Static Pricing/ no if Dynamic Pricing | Currency is a string representing the ISO-4217-3 currency code. |
Note: You can have multiple product:price:amount
and product:price:currency
pairs to specify the price in supported currencies.
Below is an example of an og:product
for payments:
<!DOCTYPE html><html> <head prefix= "og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# product: http://ogp.me/ns/product#"> <meta property="og:type" content="og:product" /> <meta property="og:title" content="Friend Smash Coin" /> <meta property="og:image" content="http://www.friendsmash.com/images/coin_600.png" /> <meta property="og:description" content="Friend Smash Coins to purchase upgrades and items!" /> <meta property="og:url" content="http://www.friendsmash.com/og/coins.html" /> <meta property="product:plural_title" content="Friend Smash Coins" /> <meta property="product:price:amount" content="0.30"/> <meta property="product:price:currency" content="USD"/> <meta property="product:price:amount" content="0.20"/> <meta property="product:price:currency" content="GBP"/> </head> </html>
In this example, exact prices are set for USD and GBP. Prices in any other currency not explicitly provided in the object, will be converted from the first currency specified (in this case USD) based on Facebook's current exchange rates.
Depending on the type of product you're describing, there are 2 forms this object can take: virtual currencies and in-game items.
The recommended model is for developers to sell their own in-app virtual currency, such as 'coins', through our payments system. This currency can then be used within the game in exchange for virtual goods. When selling an in-app currency, you provide Facebook with a quantity
parameter, dictating the amount of currency you're selling to the user at a given time. Facebook will then render the payment dialog, showing that amount, before your currency's name.
Alternatively, you may choose to sell individual, discrete products or items in your game at a set price. A simple example of this approach might be offering a 'starter pack', which contains several goods at a discounted price. By omitting a quantity
parameter during the payment flow, we assume you're selling a single unit of your item.
In addition to the two types of product you can specify, payment products can be priced in 2 different ways:
The simplest method for pricing a product is static pricing. You specify a fixed price for the item in any number of local currencies within the og:product
object, as shown above. Once you have scraped the object, its details will be cached, enabling instant display of the Pay Dialog for users purchasing this item.
The ability to provide a price for the item in multiple currencies means that you have complete flexibility to target each region with different pricing strategies, and specify appropriately rounded prices that users are familiar seeing. If you don't define a price point for a particular local currency, users of that currency will have their price automatically calculated based on the current exchange rate between the first currency you specify and that target currency.
As a best practice it's recommended that you use USD as the first currency for your statically priced items. This way we'll be able to calculate the item's price in the current person's local currency if you don't specify it in your list
While static pricing is straightforward to implement and provides users with a fast, efficient payment experience, it can sometimes be valuable to gain pricing flexibility by dynamically controlling the price of an item at the time of purchase. A common example of the utility of this feature is when implementing a flash sale, where you temporarily reduce the cost of items within your app by a small percentage, or when A/B testing different price-points to optimize conversion. Alternatively, it can be valuable to price goods specifically to individual users, allowing you to implement loyalty discounts.
Using dynamic pricing introduces a blocking call that must be made to your server during the purchase flow, which slows down the display of the pay dialog for players. Whenever possible, it is recommended that you use static pricing. Dynamic pricing must only be used in scenarios where the additional pricing flexibility is required.
To enable dynamic pricing, it's necessary to omit the product:price:amount
and product:price:currency
meta tags from the og:product
object. As with static pricing, Facebook will cache the product information provided (title, image, etc.), but since it needs a price for the item before the Pay Dialog can be rendered, an HTTP request containing order information is made to a developer defined callback when the payment flow is invoked. The developer then responds to this request with the product price, and the dialog is displayed.
As an example, here is the markup which defines the same sample in-game item, the Friend Smash 'Smashing Pack', but this time with dynamic pricing. Note the lack of product:price:amount
and product:price:currency
meta-tags.
<!DOCTYPE html><html> <head prefix= "og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# product: http://ogp.me/ns/product#"> <meta property="og:type" content="og:product" /> <meta property="og:title" content="The Smashing Pack" /> <meta property="og:image" content="http://www.friendsmash.com/images/pack_600.png" /> <meta property="og:description" content="A smashing pack full of items!" /> <meta property="og:url" content="http://www.friendsmash.com/og/smashingpack.html" /> </head> </html>
When the user invokes the Pay Dialog, Facebook will send an HTTP POST
request to the Payment Callback URL defined in the Payments section of your app's settings, to obtain a price for the item.
Payment Callback URLs must be secure HTTPS URLs.
As a developer, you're responsible for responding to this request and returning an appropriate value. Below is an example of the data issued to your callback URL. The parameters will take the form of an array of HTTP POST
fields.
[signed_request] => lFd_bGdvcml0aG0iOiJITUFDLVNIQTI1Ni... [product] => http://www.friendsmash.com/og/smashingpack.html [quantity] => 1 [user_currency] => EUR [request_id] => abc123 [method] => payments_get_item_price
The request will contain the following parameters, all of which are of type string:
Parameter | Description |
---|---|
product | The URL of your |
quantity | The amount of this item the user is looking to purchase, this is initially [passed in when invoking the pay dialog]((#paydialog), but may have changed due to price jumping. |
user_currency | The user's preferred local currency. |
request_id | The developer defined unique identifier for this transaction, passed in when invoking the Pay Dialog. Can't be longer than 255 bytes. |
method | Will always be "payments_get_item_price" in this instance. |
Cryptographically secured data about the order. See below. |
Decoding the signed_request reveals extra information about the initiated order including the product URL, the preferred currency of the user, the player's user_id and locale, etc. Once the signed request is parsed and decoded it contains data, structured as follows:
{ "algorithm": "HMAC-SHA256", "expires": 1354165200, "issued_at": 1354160453, "oauth_token": "AFJJDI81bKGbnq4ABAPoFk5b8UiGsZjBkeL9V0Ly...", "payment": { "product": "http://www.friendsmash.com/og/smashingpack.html", "quantity": 1, "user_currency": "USD", "request_id": "abc123" }, "user": { "country": "us", "locale": "en_US", "age": { "min": 21 } }, "user_id": "500535225"}
The signed request will contain the following parameters, all of which are of type string
:
Parameter | Description |
---|---|
algorithm | The name of the algorithm used to construct the signed request. |
expires | A unix timestamp detailing the expiration date of this request. Can safely be ignored. |
issued_at | A unix timestamp when this request was created. Can safely be ignored. |
payment/product | The URL of your |
payment/quantity | The amount of this item the player is looking to purchase, this is initially passed in when invoking the pay dialog, but may have changed due to price jumping. |
payment/user_currency | The player's preferred local currency. |
payment/request_id | The developer defined unique identifier for this transaction, passed in when invoking the Pay Dialog. |
user/country | The player's country. |
user/locale | The player's language locale. |
user/age/min | The minimum age of the user. Exact age retained to protect user privacy. |
user_id | The player's Facebook ID. |
At this point you must verify the purchase and respond to the HTTP request with a JSON array containing at a minimum: product
, amount
and currency
in the format defined below.
{ "content": { "product": "http://www.friendsmash.com/og/smashingpack.html", "amount": 1.99, "currency": "USD", }, "method":"payments_get_item_price" }
This table contains the complete list of supported return fields in this callback:
Parameter | Description | Required |
---|---|---|
content/product | The URL of the | Yes |
content/amount | The amount the player will be charged for a single unit of this product. Note: if the player is attempting to purchase a quantity greater than 1 of this product, then the amount returned here will be multiplied by that quantity. | Yes |
content/currency | The currency that the amount is being returned in. | Yes |
content/title | Overrides the | No |
content/plural_title | Overrides the | No |
content/description | Overrides the | No |
content/quantity_min | Overrides the | No |
content/quantity_max | Overrides the | No |
method | Confirm with Facebook that you're responding to the correct callback method by returning " | Yes |
Below is an example callback written in PHP that provides the price for an item when using dynamic pricing. Use this as a starting point for constructing your dynamic pricing infrastructure.
<?php $app_secret = 'YOUR_APP_SECRET';// Validate request is from Facebook and parse contents for use. $request = parse_signed_request($_POST['signed_request'], $app_secret);// Get request type. $request_type = $_POST['method'];// Setup response. $response = ''; if ($request == null) {// handle an unauthenticated request here} if ($request_type == 'payments_get_item_price') { // Retrieving the user's info $user_currency = $request['payment']['user_currency']; $user_country = $request['user']['country']; // Here we verify the product by passing back the URL of the OG product $item['product'] = $request['payment']['product']; // This is the quantity passed from the JS call to render the pay dialog. // This parameter is optional and defaults to 1. $quantity = $request['payment']['quantity']; // Based on the user's currency and country, we set the price. // We use fixed values here for testing. switch($user_currency) { case 'EUR': $item['amount'] = 2.99; $item['currency'] = 'EUR'; break; case 'GBP': $item['amount'] = 2.49; $item['currency'] = 'GBP'; break; case 'BRL': $item['amount'] = 6.99; $item['currency'] = 'BRL'; break; // Here we default to USD. If a user's preferred currency is different than one // that you specify, we will convert from the developer provided currency // and amount to a new amount in the user's currency. You can choose whatever // default currency works best for you. default: $item['amount'] = 3.99; $item['currency'] = 'USD'; break; } // Optionally, you may also choose to override the quantity_min / quantity_max values // which were passed in when invoking the Pay Dialog. $item['quantity_min'] = 1; $item['quantity_max'] = 100; // Optionally, it's also possible to override the OG product object's title, // plural_title and description. $item['title'] = 'Override Title'; $item['plural_title'] = 'Override Title Plural'; $item['description'] = 'Override Description'; // Finally we add the item information to the response 'content' $response['content'] = $item;}// Return the identical method $response['method'] = $request_type;// Send data back echo json_encode($response);// You can find the following functions and more details// on https://developers.facebook.com/docs/facebook-login/web function parse_signed_request($signed_request, $secret) { list($encoded_sig, $payload) = explode('.', $signed_request, 2); // Decode the data $sig = base64_url_decode($encoded_sig); $data = json_decode(base64_url_decode($payload), true); if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') { error_log('Unknown algorithm. Expected HMAC-SHA256'); return null; } // check signature $expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true); if ($sig !== $expected_sig) { error_log('Bad Signed JSON signature!'); return null; } return $data; } function base64_url_decode($input) { return base64_decode(strtr($input, '-_', '+/')); } ?>
Once we receive this data and successfully parses it, the player will be shown the Pay Dialog with the information provided from your og:product, combined with the payments callback.
The first time an og:product
object is used in the purchase flow, we will 'scrape' the page, parse the meta tags and cache the data. Subsequent invocations of the purchase flow with the same object will use this cached data, meaning the Pay Dialog renders without any blocking server-to-server call. This enables a fast, efficient user experience.
The object cache expires every 7 days, and Facebook will automatically rescrape the object when it's next used. It is also possible to trigger a rescrape manually, either via the Object Debugger, or by issuing an HTTP GET
request to the Graph API endpoint below. If you modify information in your og:product
object, remember to force a rescrape to update Facebook's cache.
GET https://graph.facebook.com/?id=OBJECT_URL&scrape=true&method=post
OBJECT_URL
is the public URL where your object can be found on the internet, the same value as used in og:url
in your object metadata. Be sure to URL encode OBJECT_URL
when using it in the Graph API call above so that any query parameters are interpreted correctly.
The user purchase flow is initiated by invoking the Pay Dialog, which is rendered as an overlay over your Facebook Web Game page. The Pay Dialog will show the item they're going to buy, its price and various methods of payment available to the user. After the player has chosen a payment method and confirmed the purchase, the dialog will return with an indication that the purchase was either completed or canceled.
To spawn the dialog, call the functionFB.ui()
from the Facebook SDK for JavaScript with a dictionary of key/value pairs. The 'method' and 'action' parameters must be set to 'pay' and purchaseitem
respectively. You're also required to pass in the url of the og:product
object that the user has selected in the 'product' parameter.
Optionally, if the player is trying to purchase a quantity (greater than 1) of a currency or item, then in addition to the og:object
url, you also need to provide a quantity
that the player wishes to buy. A sample call to invoke the pay dialog is below, along with a description of each parameter. The second parameter passed to FB.ui() is a JavaScript callback, executed after the Pay Dialog is closed and the payment flow is either completed or canceled. Full details on how to construct this callback, and the data returned is detailed here.
FB.ui({ method: 'pay', action: 'purchaseitem', product: 'http://www.friendsmash.com/og/coins.html', quantity: 10, // optional, defaults to 1 request_id: 'YOUR_REQUEST_ID' // optional, must be unique for each payment }, callback );
Parameter | Required | Description |
---|---|---|
method | yes | Must always be 'pay'. |
action | yes | Must always be 'purchaseitem'. |
product | yes | The absolute URL of your |
quantity | no | The amount of this item the player is looking to purchase - typically used when implementing a virtual currency purchase. If this parameter is omitted, it defaults to 1. |
quantity_min | no | The minimum quantity of the item the player is able to purchase. This parameter is important when handling price jumping to maximize the efficiency of the transaction. |
quantity_max | no | The maximum quantity of the item the player is able to purchase. This parameter is important when handling price jumping to maximize the efficiency of the transaction. |
request_id | no | The developer defined unique identifier for this transaction, which becomes attached to the payment within the Graph API. Learn more. |
pricepoint_id | no | Used to shortcut a mobile payer directly to the mobile purchase flow at a given price point. Full details can be found in the Advanced Payments Flows doc. |
test_currency | no | This parameter can be used during debugging and testing your implementation to force the dialog to use a specific currency rather than the current user's preferred currency. This allows you to rapidly prototype your payment experience for different currencies without having to change your personal currency. This parameter is only available for admins, developers and testers associated with the app, to minimize the security risk of a malicious JavaScript injection. Provide the 3 letter currency code of the intended forced currency. The list of supported currency codes is available here |
Certain payment methods have limitations on the price points available for a user. This results in a situation where sometimes, the price of an item that the player wishes to purchase is not available to them; in this case, Facebook will instead round up the price to the closest price point available that still covers the cost of the item. Such scenarios will result in an extra 'fee' field that reflects the difference between the price of the item and the price point at which the player must be charged.
We use the term "price jumping" to explain this scenario, where we are forced to apply this fee to players because the price point at which they wish to pay isn't supported by the selected payment instrument.
There are 2 approaches that a developer can take to solve this problem. The first, discussed below, is to support varying quantities of the item being purchased. The second, discussed in the Advanced Payments Flows, is to build a mobile store and support mobile price points explicitly.
To minimize this fee and offer the best value to the user, the optional parameters quantity_min
and quantity_max
may be provided when invoking the Pay Dialog. These parameters define for Facebook the minimum and maximum quantity of an item you'd be willing to sell to a user. We can then use this information when generating the Pay Dialog to minimize, or eliminate the amount of price jumping that must take place, thereby maximizing the amount of value offered in the transaction.
To demonstrate this, take the following example:
quantity_min
and quantity_max
aren't specified, we are forced to round the price upward to the nearest price point that covers the cost of the item. Therefore the user will be charged $10.00 USD, including a fee of $2.45.quantity_min
is specified, say at 100, you're giving Facebook permission to round to the lower price point and alter the quantity
to match. In this instance, we'll reduce the quantity to the price point at $7.50 USD. The player will be offered to purchase 150 'Friend Smash Coins' at a flat $7.50 USD, with no fee applied.FB.ui({ method: 'pay', action: 'purchaseitem', product: 'www.friendsmash.com/og/coins.html', quantity: 10, // optional, defaults to 1 quantity_min: 1, // optional, defaults to quantity quantity_max: 100, // optional, defaults to quantity request_id: 'YOUR_REQUEST_ID' // optional, must be unique for each payment }, callback );
Based on the parameters you specify, we follow some simple logic when determining the final combination of price, quantity and fee to present to the end user. This logic is detailed below:
quantity | quantity_min | quantity_max | Behavior |
---|---|---|---|
- | - | - |
|
- | specified | specified | Invalid, |
- | specified | - | Invalid, |
- | - | specified | Invalid, |
specified | - | - | When neither |
specified | specified | - | When |
specified | - | specified | When |
specified | specified | specified | Recommended - we will round the quantity as appropriate, while respecting the limits you provide, to minimize fees and maximize transaction efficiency. |
After the purchase process completes, the Pay Dialog will close and the JavaScript callback specified when invoking the dialog will be executed. A Facebook rendered confirmation will only show to the user after their first purchase.
If a payment can't be processed, the player will be redirected back to the payment method selection screen within the dialog and notified that their payment hasn't completed and that they should retry. At all other times, it's therefore the responsibility of the developer to notify the user of the outcome of the purchase (completed successfully, canceled by the player, failed due to other errors where retrying is not an option). As part of providing an intuitive user experience, you must display a confirmation immediately after the Pay Dialog closes which details the status of the purchase.
This confirmation may not be necessary in the case that the player cancels of the payment flow, but should certainly be shown when a transaction completes successfully and the user receives their virtual currency or item, or in the case of an error, to inform the user they didn't receive their item.
Once the payment has been completed, it's your obligation to give your costumer the item or currency that they purchased. There are several ways in which you can fulfill the payment, which is covered on the Fulfilment section of the games payments documentation.