Defining Products

Central to the Local Currency Payments system is an Open Graph og:product object which defines either an item or virtual currency for sale within your app.
Although it is possible to price and sell virtual items within your app directly in a local currency, we recommend that instead you implement an in-app currency, and price items within your app in that currency.
To illustrate this point, let's look at an example. In the Facebook sample game Friend Smash, there are two in-game power-up items for sale, a 'Bomb' and a 'Frenzy'. Additionally, we'd like to offer a bonus 'Smashing Pack' for sale, which contains several 'Bomb' and 'Frenzy' items, to help new players get started within the game. We decide to price our 'Bomb' and 'Frenzy' items at 0.30 USD each and the 'Smashing Pack' at 2.99 USD.
As the Local Currency Payments system has no concept of a stored balance, with no capability for a user to 'over pay' and store remaining funds with Facebook, it means each transaction will directly bill the user.
To reduce the number of transactions for a user, we introduce the concept of a virtual currency to Friend Smash. In this design, rather than buying items directly the player will instead purchase a quantity of 'Friend Smash Coins'. This value store of coins, our in-game virtual currency, is managed and maintained by the developer. The player can then exchange this virtual currency for the 'Bomb' and 'Frenzy' items.
Only items and virtual currency that you wish to price directly in a local currency need to be represented as an og:product object. Therefore, in the example above, we would create an og:product object for the 'Friend Smash Coin', but not for the 'Bomb' item. Items priced inside your app in your virtual currency do not require an og:product object to be defined, as these items are never sold directly to a user in their local currency.
To create a new product, you need to construct a publicly accessible web page hosted on the internet, which contains the required meta tags of an og:product object described in the associated Product Object Reference doc.
Depending on the type of product you are describing, there are two forms this object can take:

In-game item

If you are directly selling an item in local currency, then the product:price:amount defined in theog:product object represents the fixed, final value of the item. Below is an example of an og:productfor an in-game item.
<!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" />
    <meta property="product:price:amount"      content="2.99"/>
    <meta property="product:price:currency"    content="USD"/>
    <meta property="product:price:amount"      content="1.99"/>
    <meta property="product:price:currency"    content="GBP"/>
 </head>
</html>
When selling in-game items directly, each transaction will bill the user, and result in a charge to their payment instrument. The recommended approach is to define and sell an in-game currency, allowing the user to purchase the in-game items they want from this virtual currency. See the next section for more information.

In-game currency

When using the product object to represent a virtual currency, the product:price:amount specified in the og:product object will be multiplied by the quantity provided via the JavaScript SDK method to invoke the Pay Dialog. You therefore define a single unit of your currency in the og:product object, and alter the quantity parameter during the payment process to appropriately charge the user for the amount of currency they selected in your store. Below is an example of an og:product for an in-game currency.
<!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:plural_title"           content="Friend Smash Coins" />
    <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: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>
One of the main advantages of selling in-game currency, is that it gives you the ability to be flexible with the quantity of currency you wish to sell. This can help in cases where users pay with a payment method that has fixed price points, as the quantity of currency sold can be altered during the purchase flow. See the section on handling price jumping for more information.
In order that price jumping can be handled effectively, it is important that you do not define your currency with a high unit amount. Using high amounts reduces the likelihood that the quantity can be adjusted to match available price points. In USD, an optimalproduct:price:amount to aim for is around USD 0.10. It is possible to query for mobile price points for a given user in order to get a sense of suitable in-game currency amounts.

In-game currency packages

Whilst using an in-game currency object has the benefit of supporting varying quantities, there are some scenarios under which it is not suitable:
  • Defining a single unit of in-game currency that has a product:price:amount less than 0.01. For example, selling 1,000 Coins for USD 1.00 would require a unit price of 0.001, which is not supported.
  • Selling varying quantities of in-game currency and offering incremental promotions on higher-value packages. Using a currency object may not afford the flexibility required in between pricing tiers. See creating your store in the next section for more information on this recommended approach.
In cases such as these, another approach that can be taken to selling in-app currency is to define multiple discrete currency packages, incorporating the quantity into the og:title and selling a single unit of the relevant package. Each package therefore must be defined as a distinct product object with a unique og:url. In practice it may be easier to build a dynamic endpoint using your server-side scripting language of choice to generate these packages.
Below is an example of an og:product for an in-game currency package.
<!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="10,000 Friend Smash Coins" />
    <meta property="og:plural_title"           content="10,000 Friend Smash Coins" />
    <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:price:amount"      content="14.99"/>
    <meta property="product:price:currency"    content="USD"/>
    <meta property="product:price:amount"      content="11.99"/>
    <meta property="product:price:currency"    content="GBP"/>
  </head>
</html>
Note that when using currency packages, it is not possible for the quantity to be altered in order to handle price jumping. As such it is very important that you optimize for mobile payments in order to provide a good purchase experience for users paying with mobile.
In addition to the two types of product you can specify, payment products can be priced in two different ways: static pricing and dynamic pricing, the two next sections explain what these are.

Static pricing

Payment products can be priced in two different ways: static pricing and dynamic 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 you have complete flexibility to target each region with different pricing strategies, and specify appropriately rounded prices that users are familiar seeing. If you do not define a price point for a particular local currency, users of that currency will have their price automatically calculated based upon the current exchange rate between the first currency you specify and that target currency.
As an example, see the in-game item above, which defines our sample in-game item, the Friend Smash 'Smashing Pack'. Here, there are two fixed amounts of 2.99 USD and 1.99 GBP. If a user from the US chooses to purchase this item, they will be charged the 2.99 USD defined in the object. Similarly, if a user from the UK attempts to buy the same product, they will be charged 1.99 GBP. If however, a user from Brazil attempts to purchase the same object, the price point for Brazil's currency, BRL, is not defined. In this circumstance, Facebook will automatically convert the USD price point into BRL, based on the current exchange rate at the time of rendering the Pay Dialog.
This architecture provides you the flexibility to price goods appropriate to each region in which you're selling, but gives you the assurance that if you do not provide specific pricing detail for a given region, Facebook will generate and charge an appropriate price for you.

Dynamic pricing

Payment products can be priced in two different ways: static pricing and 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 additional 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. The Local Currency Payments system supports this functionality, with a little additional work.
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 initial pay dialog for users. Whenever possible, it is recommended that you use static pricing. Dynamic pricing should only be used in scenarios where the additional pricing flexibility is required.
To enable dynamic pricing, it is necessary to omit the product:price:amount andproduct: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 Pay 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 andproduct: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, in order to obtain a price for the item. As a developer, you are responsible for responding to this request and returning an appropriate value.
Be sure to have the Payment Callback URL setup correctly and that the endpoint is available from the open internet.
Below is an example of the JSON encoded data issued to your callback URL. Note the request_id is only present if the developer originally provided this parameter when invoking the Pay Dialog, asdescribed here.
{
   "product":"http://www.friendsmash.com/og/smashingpack.html",
   "quantity":"1",
   "user_currency":"USD",
   "request_id":"abc123",
   "method":"payments_get_item_price",
   "signed_request":"lFd_bGdvcml0aG0iOiJITUFDLVNIQTI1Ni..."
}
The request will contain the following parameters, all of which are of type string:
ParameterDescription
productThe URL of your og:product object that this request pertains to.
quantityThe amount of this item the user is looking to purchase, this is initially passed in when invoking the pay dialog, but may have changed due to price jumping .
user_currencyThe user's preferred local currency.
request_idThe developer defined unique identifier for this transaction, passed in when invoking the Pay Dialog.
methodWill always be "payments_get_item_price" in this instance.
signed_requestCryptographically secured additional 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 user'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": "221159"
}
The signed request will contain the following parameters, all of which are of type string:
ParameterDescription
algorithmThe name of the algorithm used to construct the signed request.
expiresA unix timestamp detailing the expiration date of this request. Can safely be ignored.
issued_atA unix timestamp when this request was created. Can safely be ignored.
payment/productThe URL of your og:product object that this order pertains to. This ispassed in when invoking the pay dialog.
payment/quantityThe amount of this item the user is looking to purchase, this is initiallypassed in when invoking the pay dialog, but may have changed due to price jumping.
payment/user_currencyThe user's preferred local currency.
payment/request_idThe developer defined unique identifier for this transaction, passed in when invoking the Pay Dialog.
user/countryThe user's country.
user/localeThe user's language locale. See the Open Graph internationalization documentation for details on localizing your objects.
user/age/minThe minimum age of the user. Exact age retained to protect user privacy.
user_idThe user's Facebook ID.
At this point you should verify the purchase and respond to the HTTP request with a JSON array containing at a minimum: productamount 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:
ParameterDescriptionRequired
content/productThe URL of the og:product object that a dynamic price must be returned for.Yes
content/amountThe amount the user should be charged for a single unit of this product. Note: if the user 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/currencyThe currency that the amount is being returned in.Yes
content/titleOverrides the og:title field from the product object.No
content/plural_titleOverrides the og:plural_title field from the product object.No
content/descriptionOverrides the og:description field from the product object.No
content/quantity_minOverrides the quantity_min value passed into the Pay Dialog.No
content/quantity_maxOverrides the quantity_max value passed into the Pay Dialog.No
methodConfirm with Facebook you are 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/authentication/canvas
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 Facebook receives this data and successfully parses it, the user will be shown the Pay Dialog with the information provided from your og:product, combined with the payments callback.

Scraping and caching

The first time an og:product object is used in the purchase flow, Facebook will 'scrape' the page, parse the meta tags and cache the data. Subsequent invocations of the purchase flow with the same object will utilize 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 re-scrape the object when it is next used. It is also possible to trigger a re-scrape manually, either via the Object Debugger, or by issuing an HTTP GET request to the Graph API endpoint below. If you modify information in yourog:product object, remember to force a re-scrape in order 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_URLwhen using it in the Graph API call above so that any query parameters are interpreted correctly.

Next steps

Now that you have defined products available for purchase within your app, the next step is toimplement the user-facing purchase flow within your app.
Was this document helpful?