In‑app purchases (IAP)
FRVR.iap abstracts over Google Play, Facebook Instant, Apple iAP, and other channel billing backends. You describe a catalog once; each channel maps your product IDs onto its own store.
Declare a catalog
Section titled “Declare a catalog”- The key (
COINS_100) is what your code uses. storeIdis what the current channel’s store calls the product.labelis a human name you can show in UI if you don’t already have one.
Read the catalog
Section titled “Read the catalog”After FRVR.init() and the first bootstrap tick, the catalog is ready:
Price/currency come from the underlying store. Use them — don’t hard‑code prices.
Purchase
Section titled “Purchase”Error types:
| Error | Meaning |
|---|---|
IAPPurchaseErrorCancelledByUser | User closed the sheet. Not an error you show. |
IAPPurchaseErrorAlreadyOwned | Durable already owned by this account. |
IAPPurchaseErrorUnknownProduct | Wrong product ID. |
IAPPurchaseErrorInProgress | Another purchase is still running. |
IAPPurchaseErrorPopupBlocked | The browser blocked the checkout window. Nothing was charged. |
IAPPurchaseErrorPending | The player paid, but the payment isn’t confirmed yet. Don’t grant the item. |
IAPPurchaseErrorHeldByEconomy | Thrown by consumePurchase: FRVR’s economy is still settling this purchase. Don’t grant the item. |
IAPError | Generic fallback. Inspect .code. |
Every one of these extends IAPError, so a generic catch keeps working. Each also sets err.name to its class name, which is the safer check when the error may come from another bundle.
Purchases on the web
Section titled “Purchases on the web”The web channel sells through FRVR’s web payment providers: Google Pay, Apple Pay, and a card checkout. When more than one is available, the SDK shows the player a chooser. A few things differ from a store purchase:
- Call
purchase()straight from a click or tap. When the game runs inside another site’s iframe, the checkout opens in a new window, and a browser that blocks it rejects withIAPPurchaseErrorPopupBlocked. Ask the player to try again. - A payment can still be pending when the checkout closes.
purchase()then rejects withIAPPurchaseErrorPending(err.pending === true). Tell the player it’s on its way. Once confirmed, the purchase comes back fromgetUnconsumedPurchases(): grant and consume it there. - The player may need to sign in first. The SDK opens the login screen itself, then continues the purchase.
- Card checkout prices include tax.
getProductById()returns those products withtaxIncluded: true, and the player pays exactly the price shown.
To draw your own payment buttons, read the options and pass the one the player picked:
Consumables vs. durables
Section titled “Consumables vs. durables”Consumables (coins, gems) are bought over and over. After you grant the content, consume the purchase so the store frees the user to buy again:
Durables (ad removal, character skins) are never consumed. To re‑grant them on a new device:
Recovering from crashes
Section titled “Recovering from crashes”If your app crashed between purchase and content delivery, the store keeps the receipt in a pending state. On every startup:
This closes the loop even if the user force‑killed the app mid‑purchase.
If consumePurchase() rejects with IAPPurchaseErrorHeldByEconomy, skip that purchase and don’t grant it. FRVR’s economy service is settling it and will finish on a later launch.
Account changes
Section titled “Account changes”onReady can fire more than once. When the player signs in to a different account mid‑session, the SDK calls it again so the game can restore that account’s purchases:
Make the callback safe to run twice.
Try it live
Section titled “Try it live”dev channel ships a mock catalog and a fake purchase flow. Try buy → consume → restore.
IAP: catalog, purchase, consume, restore
API summary
Section titled “API summary”| Method | Purpose |
|---|---|
isReady() / onReady(cb) | Provider readiness. onReady fires again after an account change. |
getCatalog() | All products keyed by your catalog IDs. |
getProductById(id) | One product, including store price. |
purchase(id, meta?, options?) | Start checkout. Resolves with a purchase receipt. options.provider skips the payment chooser. |
getPurchaseOptions() | Payment options the player can choose from. |
consumePurchase(purchase) | Mark a consumable as delivered. |
restorePurchases() | Re‑emit durables owned by the user. |
getUnconsumedPurchases() | Unfinished consumables — call at startup to recover. |
errors.* | Error classes to instanceof‑check. |