Skip to content

In-app purchases (Unity)

FRVRSDK.IAP lets a Unity WebGL build transact through whichever store the host channel uses (Facebook IAP, Apple IAP, Google Play Billing, etc.). You don’t pick the provider — the channel injected for that release does.

The wrapper methods return/receive JSON strings (not typed objects). You parse them yourself.

if (FRVRSDK.IAP.IsReady())
{
    // Safe to query the catalog and call Purchase.
}

Call this before you try to populate a shop UI or show buy buttons — the catalog takes a moment to resolve on session start.

string catalogJson = FRVRSDK.IAP.GetCatalog();
// JSON array of product objects:
// [
//   {
//     "label": "Small Bundle",
//     "price": "$2.99",
//     "priceValue": "2.99",
//     "currencyCode": "USD",
//     "storeId": "bundlesmall",
//     "trackingName": "Small Bundle"
//   },
//   ...
// ]

Specific product details + price lookup (handy for buttons):

string productJson    = FRVRSDK.IAP.GetProduct("bundlesmall");
string localisedPrice = FRVRSDK.IAP.GetProductPrice("bundlesmall"); // e.g. "Din 110" or "$2.99"

Use GetProductPrice directly in button labels — it’s already in the player’s local currency from the store.

FRVRSDK.IAP.Purchase("bundlesmall", resultJson =>
{
    // resultJson is "{}" on failure/cancel, or a purchase object on success:
    // {
    //   "purchaseId":          "11111111116739804",
    //   "productId":           "bundlesmall",
    //   "channelId":           "facebook-instant",
    //   "purchaseTime":        1761220217239,
    //   "transactionId":       "11111111116739804",
    //   "transactionReceipt":  "9OlnbR6KcvqiDZox....AFVn0"
    // }

    if (resultJson == "{}") return;    // user cancelled or purchase failed

    GrantProduct(resultJson);
    FRVRSDK.IAP.ConsumePurchase(resultJson, purchaseId =>
    {
        // `purchaseId` is the consumed purchase's id (a plain string, not JSON).
    });
});

Only consume consumable products (coins, lives, single‑use boosters). Permanent unlocks (remove‑ads, skins) must not be consumed — they’d become re‑buyable.

In Editor simulation the Purchase callback returns {"productId":"<id>","purchaseId":"testPurchaseId"} — a stripped object with only those two fields — so don’t rely on every production field being present when running in playmode.

If the game crashed between a successful purchase and granting the content, the receipt stays pending. Check at boot — GetUnconsumedPurchases is async and delivers the array via callback:

FRVRSDK.IAP.GetUnconsumedPurchases(unconsumedJson =>
{
    // unconsumedJson is a JSON array (or "[]" if none):
    // [
    //   { "purchaseId": "...", "productId": "bundlesmall", "purchaseTime": ..., ... },
    //   ...
    // ]
    // For each entry: grant the content, then ConsumePurchase to close the loop.
});

For non‑consumable entitlements (remove‑ads, permanent skins) the host channel can be asked to replay the player’s history. Use this from a “Restore purchases” button or — on iOS WebView — at first launch:

FRVRSDK.IAP.RestorePurchases(purchasesJson =>
{
    // purchasesJson is a JSON array of restored purchase objects ("[]" if none).
});

RestorePurchases is a no‑op on channels that don’t expose a restore flow; the callback fires with "[]".