Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

In-app purchases

Some games are free to play and sell extras: a bag of coins, a new costume, no more ads. I talk to the store for you, whichever store it is, so your game asks the same way everywhere.

The store

The things for sale are listed in a file called iap.json, in the game’s folder. Each has an id your game buys it by, a name, a price, and whether it’s consumable, bought again and again like coins, or non-consumable, bought once and kept, like “no more ads”.

{
  "version": 1,
  "products": [
    { "id": "coins_100", "type": "consumable", "name": "100 Coins",
      "price": { "amount": 99, "currency": "usd" },
      "apple_product_id": "com.example.coins100",
      "google_product_id": "coins_100" },
    { "id": "no_ads", "type": "non_consumable", "name": "No More Ads",
      "price": { "amount": 299, "currency": "usd" },
      "apple_product_id": "com.example.noads",
      "google_product_id": "no_ads" }
  ],
  "providers": { "ios": "apple", "android": "google",
                 "web": "iap_server", "desktop": "iap_server" },
  "iap_server": { "endpoint": "https://example.com/iap" }
}

providers says which store each kind of machine uses:

  • On a phone or a tablet, it’s the App Store on iPhone and iPad, and Google Play on Android. A product is only on sale there if it has the id you gave it in that store, and the store shows its own window to pay in.
  • In a web browser, and on a PC, it’s an IAP server: a web server of your own (or one you use) that knows how to sell things. The price in iap.json is the one it shows, and paying happens in the browser.

Is it open?

purchase.available is 1 when the game is talking to a store, and 0 when there’s no store or it can’t be reached. purchases is the list of what’s for sale, each with its id, name, description, price and whether it’s purchased.

SCREEN 320, 240
IF purchase.available
    FOR item = purchases
        PRINT item.name, item.price, item.purchased
    NEXT
ELSE
    PRINT "no store here"
END IF
PRINT "the store has", len(purchases), "things"
no store here
the store has 0 things

Here in the book there’s no store at all, so it’s closed, with nothing for sale. A game should always be fine with that: just don’t show the shop.

Buying

PURCHASE NEW and the id of what to buy opens the store’s window, and your game carries on. When it’s over, I fire the "purchase" event, with the id, whether it worked (1 or 0), and why not when it didn’t. Have a CALL EVENT ready for it before anyone can buy.

SCREEN 320, 240
ATOMIC coins = 0
ATOMIC news = "visiting the shop..."

FUNCTION bought(product, ok, why)
    IF ok
        IF product = "coins_100" THEN coins = coins + 100
        news = "thank you!"
    ELSE
        news = "no sale: " + why
    END IF
END FUNCTION
CALL EVENT bought, "purchase"

PURCHASE NEW "coins_100"
// A spinner while the store has its turn.
turn = 0
DO WHILE news = "visiting the shop..."
    IF COOLDOWN(1)
        MEMORYBUFFER "spinner"
            CIRCLE (160 + cos(turn) * 20, 120 + sin(turn) * 20), 5, 14, 14
        END MEMORYBUFFER
        FRAME
            CLS
            PUTIMAGE (0, 0), "spinner"
        END FRAME
        turn = turn + 0.1
    END IF
LOOP
PRINT news
PRINT "coins:", coins
no sale: declined
coins: 0

With no store, I can’t sell anything, so the event says no, and why. On a phone, the player would see the store’s window, and the event would come when they’d paid, or changed their mind.

Keeping count

purchased only means something for a non-consumable: it’s 1 once it’s been bought, and the store remembers. A consumable is always 0, because it can be bought again, so the store doesn’t keep count of your coins. Your game does: add them up when the event says they were bought, and save them, the way Saving your game does.

FUNCTION bought(product, ok, why)
    IF ok AND product = "coins_100"
        coins = coins + 100
        SAVETEXT "coins.txt", str(coins)
    END IF
END FUNCTION

Bought it before?

Someone who bought “no more ads” on their old phone wants it on their new one too. PURCHASE RESTORE asks the store for everything they’ve bought before, and the "purchase" event comes once for each one. Put it behind a “Restore purchases” button.

SCREEN 320, 240
ATOMIC ads = TRUE
FUNCTION bought(product, ok, why)
    IF ok AND product = "no_ads" THEN ads = FALSE
END FUNCTION
CALL EVENT bought, "purchase"
PURCHASE RESTORE
PRINT "asked the store for what you bought before"
asked the store for what you bought before

Only non-consumables come back: the coins were spent long ago.

Crash-tan’s Tip: Never decide a purchase worked yourself. Wait for the "purchase" event to say so: the window can be closed, the card can be turned down, and the player can be offline.