Instant Games In-App Purchases (IAP) provide a way for developers to leverage micro-transactions in their Instant Games on Facebook.com. Developers will be able to immediately test IAP in their games. Once a game has passed through In App Purchase review, players will be able to make purchases on Android and Facebook.com.
In-App Purchases are supported on web, iOS and Android.
Note: From Sep 16, 2024, In-App Purchase functionality is now supported in the native Facebook iOS app.
The first step of enabling In App Purchases is to configure the products that you would like to make available to players and configure payout information. This can be done on the In App Purchase tab under the Instant Games product.

Select or create a company that will the designated recipient of payouts from Instant Games in-app purchases. You can find more information here.
The following is the expected flow per game session:
getPurchasesAsync() to obtain a list of unconsumed purchases. Then call consumePurchase(<purchase_token>) on any consumables, awarding the user the corresponding in-game item or virtual currency.getCatalogAsync() to obtain the list of products to display to the user. Use this data to dynamically populate the store UI.purchaseAsync(<purchase_config>).consumePurchase(<purchase_token>), passing in the purchaseToken, and award the user the corresponding in-game item or virtual currency.Payments in Instant Games are only supported on Facebook.com (not Messenger.com) and on Android once you have passed review. Your code should ensure that payments are available on the player's device before showing any payments functionality.
As with other Instant Games APIs, use getSupportedAPIs and look for payments.purchaseAsync.
If available, subscribe to payments.onReady with a callback as follows.
FBInstant.payments.onReady(function () {
console.log('Payments Ready!');
});If getSupportedAPIs does not contain payments.purchaseAsync, then payments is not supported on the device.
If the callback set in payments.onReady is never called, payments is also not supported for this session and no payment related functionality should be used.
Use the getCatalogAsync method to get the list of available products. Make sure to use the proper local currency (priceCurrencyCode) and price (price) for each product when displaying your store.
FBInstant.payments.getCatalogAsync().then(function (catalog) {
console.log(catalog); // [{productID: '12345', ...}, ...]
});When a player shows intent to purchase an item, you can trigger the purchase confirmation dialog using purchaseAsync.
FBInstant.payments.purchaseAsync({
productID: '12345',
developerPayload: 'foobar',
}).then(function (purchase) {
console.log(purchase);
// {productID: '12345', purchaseToken: '54321', developerPayload: 'foobar', ...}
});As within the signed request, the purchasePrice and paymentActionType fields will appear on web and Facebook for Android v323 or later.
It is possible to verify that a payment has occurred on a backend system by parsing the signedRequest value that is returned with the payment object.
The signedRequest consists of two parts separated by the . character. The first part is a Base64 encoded SHA256 hash of the payment information, while the second part is the same payment information encoded directly in Base64.
Never embed your app secret into your game. The signedRequest should only be validated in server-side code.
You can validate that the payment information is authentic by using the following code samples:
Javascript sample:
import hmacSHA256 from "crypto-js/hmac-sha256";
import Base64 from "crypto-js/enc-base64";
import utf8 from "crypto-js/enc-utf8";
import JSONbig from "json-bigint";
const signedRequest = "<SIGNED_REQUEST>";
const firstPart = signedRequest.split(".")[0].replace(/-/g, "+").replace(/_/g, "/");
const secondPart = signedRequest.split(".")[1];
const signature = Base64.parse(firstPart).toString();
const dataHash = hmacSHA256(secondPart, "<APP_SECRET>").toString();
const isValid = signature === dataHash;
const json = Base64.parse(secondPart).toString(utf8);
/*
NOTE: The purchase_token field will exceed the bounds of Number.MAX_SAFE_INTEGER,
so JSON.parse(...) is unsafe. This will be changed soon, but until then,
please avoid using. Alternatively, pass the string directly from the
purchase API response instead.
*/
const data = JSONbig({ storeAsString: true }).parse(json);
console.log(isValid); // this will be true if the payment is verified as coming from the game
console.log(data); // a JSON object as follows:
/*
{
algorithm: "HMAC-SHA256"
app_id: 772899436149321
is_consumed: false
issued_at: 1628530124
payment_action_type: "charge"
payment_id: 2373285299469015
product_id: "sample_product"
purchase_price: Object // {"amount":"0.01", "currency":"USD"},
purchase_time: 1628171348
purchase_token: 10102867843382867
}
*/
/*
Starting 8/25/21:
{
algorithm: "HMAC-SHA256"
app_id: "772899436149321"
is_consumed: false
issued_at: 1628530124
payment_action_type: "charge"
payment_id: "2373285299469015"
product_id: "sample_product"
purchase_price: Object // {"amount":"0.01", "currency":"USD"},
purchase_time: 1628171348
purchase_token: "10102867843382867"
}
*/Java sample:
import java.util.Base64;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
...
String signedRequest = "<SIGNED_REQUEST>";
String[] parts = signedRequest.split("\\.",2);
String firstPart = parts[0].trim().replaceAll("-","+").replaceAll("_","/");
String secondPart = parts[1];
Base64.Decoder decoder = Base64.getDecoder();
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
// Handle first part of signed request
byte[] signature = decoder.decode(firstPart);
// Handle second part of signed request
SecretKeySpec secret_key = new SecretKeySpec("<APP_SECRET>".getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] datahash = sha256_HMAC.doFinal(secondPart.getBytes());
// SignedRequest is valid if both matches
boolean isValid = Arrays.equals(signature, datahash);Use getPurchasesAsync to get the player's unconsumed purchases.
FBInstant.payments.getPurchasesAsync().then(function (purchases) {
console.log(purchases); // [{productID: '12345', ...}, ...]
});When a player decides to use a purchased item, call consumePurchaseAsync and award the effect of the item. Once called, the item will no longer be returned by getPurchasesAsync. Until this is done, the player will be unable to purchase the same item in the future.
If the purchase of consumable products are not handled properly via this consumption mechanism, subsequent purchaseAsync(<purchase_config>) calls for the same item will reject with a INVALID_PARAM error with the message "Product not purchaseable".
If the item is a non-consumable product, don't call consumePurchaseAsync. This purchase will always be listed in getPurchasesAsync.
FBInstant.payments.consumePurchaseAsync('54321').then(function () {
// Purchase successfully consumed!
// Game should now provision the product to the player
}).catch(function(error) {
// Handle error
});Bulk product creation is available only for consumable products; bulk creation of subscribable products is not supported.
To create multiple consumable products in a single operation, go to the In-App Purchase Products pane, select Add multiple products and then choose one of the following creation methods:

To manually create multiple consumable products, select Add multiple products and then select Add manually. Each row in the Add products form represents a single consumable product.

For each consumable product, provide the following information (hover over the form header for additional guidance):
To create multiple consumable products from a CSV file, select Add multiple products and then select Upload file. This method supports the creation of 750 products per operation; to add a greater number of products, split the CSV file into multiple batches.
For each consumable product, provide the following information:
The following table provides an example of a CSV file for the creation of three consumable products.
| Product ID | Locale; Title; Description | Price |
|---|---|---|
com.facebook.sample_game.199 | en_US; A Handful of Gems; This gives you 5 gems! | 1.99 |
com.facebook.sample_game.499 | en_US; A Bag of Gems; This gives you 15 gems! | 4.99 |
com.facebook.sample_games.1099 | en_US; A Chest of Gems; This gives you 40 gems! | 10.99 |
When upload of the CSV file is complete you will see the Product preview form. Confirm that the imported information is correct; you can also edit fields and fix any errors in this form before completing the product creation.
Because our store provides only discrete price points, we will find the closest price point from the updated price point if an exact match cannot be found.

The In-App Test Payment is a new, lightweight service designed to simulate in-app purchases that allows users with assigned roles (admin, developer, tester) on your app to test payment implementations. Roles can be assigned by admins and developers in the App Roles section of your App Dashboard.
When a payment tester invokes the payment dialog within your app, a test flow will appear instead of the standard payment flow. This enables testing of purchase functionality without performing actual transactions, ensuring no charges to the payment instrument and no appearance in the payments reporting API (though it will show up in events tracker).
Choose an outcome from the dropdown and click “Buy”. The system will return a mock IAP response of success or failure without performing an actual transaction to reliably test your game's payment logic. For example, selecting "Succeed" will return a Purchase object with a mock purchase token, while "Invalid Operation" will result in an Invalid Operation error being thrown.
Sample test purchase response:
// Success
{
developerPayload: "{customPayload: 'sample_custom_payload'}"
isConsumed: false
paymentActionType: "charge"
paymentID: "11111111111111111"
productID: "sample_product_id"
purchasePlatform: "FB"
purchasePrice: Object // {"amount":"0.01", "currency":"USD"},
purchaseTime: 1755529556226
purchaseToken: "11111111111111111"
signedRequest: "rtFTxipyVf0KHecdSpqqFVuCXi8DV67IlihJVrNkdW4.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImFwcF9pZCI6IjczNjI0MzQ0NTA0NzE4MjUiLCJkZXZlbG9wZXJfcGF5bG9hZCI6IntjdXN0b21QYXlsb2FkOiAxMjM0fSIsImlzX2NvbnN1bWVkIjoiIiwiaXNzdWVkX2F0IjoxNzU1NTI5NTU2LCJwYXltZW50X2FjdGlvbl90eXBlIjoiY2hhcmdlIiwicGF5bWVudF9pZCI6IjExMTExMTExMTExMTExMTExIiwicHJvZHVjdF9pZCI6ImhleSIsInB1cmNoYXNlX3BsYXRmb3JtIjoiRkIiLCJwdXJjaGFzZV9wcmljZSI6eyJhbW91bnQiOiIwLjg5IiwiY3VycmVuY3kiOiJVU0QifSwicHVyY2hhc2VfdGltZSI6MTc1NTUyOTU1NjIyNiwicHVyY2hhc2VfdG9rZW4iOiIxMTExMTExMTExMTExMTExMSJ9"
}
// Failure
{
code: "INVALID_OPERATION",
message: "[Mock IAP] Operation not valid."
}All successful mock purchases will have the same paymentID and purchaseToken structure and length (17 digits): "111111111randomNumber". All failed mock purchases will include "[Mock IAP]" in the error message. Please filter for this when looking at purchase tracking events.
A payment tester can also temporarily switch to the production flow on the current purchase by clicking the “Switch to production payment flow” button at the bottom of the dialog. To permanently enable or disable the In-App Test Payment flow, admins and developers of a game can go to the Game Payments or In-app purchases section of the App Dashboard and scroll down to In-App Test Payment Settings.
Currently only supported on the 'latest', 'restricted.latest', '8.0', '7.1', '7.0', and '6.3' SDK.
We are deprecating the use of test credit cards soon. Please use the In-App Test Payment System above.
When users are added as an IAP tester, they CANNOT use a real credit card and MUST use one of the above.
| Number | Brand | Date | CVC | Zipcode | Billing Country |
|---|---|---|---|---|---|
5454543112674402 | Master Card | Any future date | Any 3 Digits | Any valid | United States |
Before In App Purchases will work across all platforms, there are several checks that must be made.
The app must pass a functional In App Purchase review. Navigate to the Test Users Section (App roles -> Test users), and create a few Test User Accounts. Then provide the credentials for these test users in your Review Submission. Instant Games can be submitted for In App Purchases on the Review tab.
On the In App Purchase tab, statuses are shown to help you determine which of these activities need to be completed.

In the In App Purchase tab, please follow the link to directly see the purchase metrics in FBS Insights.
Purchases will also be automatically logged in Events Manager.
Our primary goal is to build IAP in a way so that our developer partners can sustain and grow: for purchases made on web (Facebook.com), the revenue share model is 70% for developers - as it has been historically for games on the Facebook platform.
On mobile, Instant Games follow in-app billing terms from each platform. For purchases made in games on Google Play, developers will retain the full amount after the standard mobile platform revenue share. This means developers take home $0.70 of every dollar earned with $0.30 fee to Google.
Payout should be received in 30 day windows.
If you have received a message from Meta asking you for additional information about your developer account, it’s because the info we have is under review and has been found to be out of date or inaccurate. Meta's regulatory obligations and/or internal policies (or safeguards or controls) require us to make sure the information we have about any developer account with access to Meta's Developer tools is current and accurate. We’ll only use this info to verify your organization.
Financial Admins are the individuals your organization has authorized to access and modify payout account information such as the designated Bank Account to receive payouts. The full list of Financial Admins can be accessed at facebook.com/payout on your browser, while logged into an existing Financial Admin’s personal Facebook account.
Messages from Meta’s legal team can be accessed through the personal support inbox of your payout account’s Financial Admins. The Support Inbox is accessible by Financial Admins logged into their personal Facebook account, and can be found at facebook.com/support. Once an initial request for information has been sent, you will have 60 days to reply with the requested information. If you fail to provide the information, Meta will suspend your ability to monetize through in-app purchases on Meta’s Developer platform. Following the initial 60-day window, the Support Inbox correspondence will remain open for an additional 30 days to allow for an appeal at which point your Financial Admin will be notified that the conversation has been closed.
In order to submit an appeal during this 30-day window, Financial Admins should respond with the originally requested information and continue to monitor their Support Inbox for additional messages. Following the closure of the Support Inbox conversation, appeals can be submitted by filling out this form.