Taking Payments in Games

Game monetization

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.

Company Registration

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.

Setting Up

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.

Defining your Products

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.

OG:Product Properties

Name Required Description

og:type

yes

The value must be og:product

og:title

yes

Title of the product ex. 'In Your Village Money'.

og:url

yes

The unique URL of the product.

og:description

yes

The description for this product.

og:image

yes

The image which represents your product. The image must be at least 200px by 200px. We support PNG, JPEG and GIF formats.

product:plural_title

no

Title of the product when a quantity more than 1 is purchased.

product:price:amount

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.

product:price:currency

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.

Virtual Currencies VS In-Game Items

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.

Virtual currency (gold bars)

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 quantityparameter during the payment flow, we assume you're selling a single unit of your item.

Products

In addition to the two types of product you can specify, payment products can be priced in 2 different ways:

Static Pricing

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

Dynamic Pricing

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 og:product object that this request pertains to.

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.

signed_request

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 og:product object that this order pertains to. This parameter is passed in when invoking the pay dialog.

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 og:product object that a dynamic price must be returned for.

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 og:title field from the product object.

No

content/plural_title

Overrides the og:plural_title field from the product object.

No

content/description

Overrides the og:description field from the product object.

No

content/quantity_min

Overrides the quantity_min value passed into the Pay Dialog.

No

content/quantity_max

Overrides the quantity_max value passed into the Pay Dialog.

No

method

Confirm with Facebook that you're responding to the correct callback method by returning "payments_get_item_price".

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.

Scraping the og:product object

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&amp;scrape=true&amp;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.

Pay Dialog

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.

Payment Dialog

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
);

Parameters

Parameter Required Description

method

yes

Must always be 'pay'.

action

yes

Must always be 'purchaseitem'.

product

yes

The absolute URL of your og:product object that the player is looking to purchase.

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

Price Jumping

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:

  • We define a virtual currency, the 'Friend Smash Coin' to be worth $0.05 USD each.
  • The user is prompted to purchase 151 of them, coming to a total cost of $7.55 USD.
  • There's no price point available to the user at $7.55 USD. The closest price points are $7.50 USD ($0.05 below the target price), and $10.00 USD ($2.45 above the target price).
  • If 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.
  • However, if a 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

-

-

-

quantity defaults to 1. No quantity rounding will occur.

-

specified

specified

Invalid, quantity must be specified when specifying either quantity_min or quantity_max.

-

specified

-

Invalid, quantity must be specified when specifyingquantity_min.

-

-

specified

Invalid, quantity must be specified when specifyingquantity_max.

specified

-

-

When neither quantity_min or quantity_max are specified, they both default to quantity and no quantity rounding will occur.

specified

specified

-

When quantity_min is provided butquantity_max is omitted, quantity_max is considered to be at a maximum limit, typically 1,000,000.

specified

-

specified

When quantity_max is provided butquantity_min is omitted, quantity_min is considered to be at a minimum limit of 1.

specified

specified

specified

Recommended - we will round the quantity as appropriate, while respecting the limits you provide, to minimize fees and maximize transaction efficiency.

Confirmation

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.

Next Steps

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.