@indiegems/gem-web-sdk 16.3.0 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/dist/conformance.d.ts +1 -1
- package/dist/game.d.ts +17 -2
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +2 -2
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server.js +1 -1
- package/dist/index.d.ts +27 -8
- package/dist/sdk.d.ts +17 -2
- package/docs/api/api-reference-function.md +1 -1
- package/docs/api/api-reference-game.md +97 -36
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +49 -10
- package/docs/arcade-and-store.md +91 -26
- package/docs/arcade-economy.md +7 -6
- package/docs/brand-art.md +3 -1
- package/docs/changelog.md +33 -0
- package/docs/getting-started.md +1 -1
- package/docs/inventory-and-catalogs.md +6 -4
- package/docs/manifest.json +16 -16
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +75 -23
- package/package.json +1 -1
- package/schema/bridge.v1.json +137 -17
- package/schema/capabilities.public.json +7 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The browser surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v17.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/game` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
|
@@ -4590,7 +4590,7 @@ interface StoreApi
|
|
|
4590
4590
|
```
|
|
4591
4591
|
|
|
4592
4592
|
**Real money.** The Arcade Store's own Gem packs, Boosts, passes and bundles,
|
|
4593
|
-
bought with the player's money through the
|
|
4593
|
+
bought with the player's money through the Arcade Store's own checkout.
|
|
4594
4594
|
`gem.store` is one of these. To spend Gems or Coins on your game's own items,
|
|
4595
4595
|
use `gem.purchases`.
|
|
4596
4596
|
|
|
@@ -4605,6 +4605,19 @@ readonly available: boolean
|
|
|
4605
4605
|
Whether the Arcade Store answers here, synchronously. Draw your own "Get
|
|
4606
4606
|
Gems" button only when it is true.
|
|
4607
4607
|
|
|
4608
|
+
<a id="storeapi-showofferavailable"></a>
|
|
4609
|
+
|
|
4610
|
+
## StoreApi.showOfferAvailable
|
|
4611
|
+
|
|
4612
|
+
```ts
|
|
4613
|
+
readonly showOfferAvailable: boolean
|
|
4614
|
+
```
|
|
4615
|
+
|
|
4616
|
+
Whether [StoreApi.showOffer](#storeapi-showoffer) answers here, synchronously. An older
|
|
4617
|
+
Arcade can sell through `purchase` and still not draw this sheet, so read
|
|
4618
|
+
this, not [StoreApi.available](#storeapi-available), before you draw a button that calls
|
|
4619
|
+
`showOffer`. `false` until the bridge is up.
|
|
4620
|
+
|
|
4608
4621
|
<a id="storeapi-offers"></a>
|
|
4609
4622
|
|
|
4610
4623
|
## StoreApi.offers
|
|
@@ -4628,20 +4641,24 @@ is withheld. Re-read on resume: what is on sale can change.
|
|
|
4628
4641
|
purchase(skuId: string): Promise<ArcadeOutcome>
|
|
4629
4642
|
```
|
|
4630
4643
|
|
|
4631
|
-
Ask the arcade to open
|
|
4632
|
-
|
|
4644
|
+
Ask the arcade to open checkout for one SKU from [StoreApi.offers](#storeapi-offers):
|
|
4645
|
+
the payment provider's page on the web, the app store's purchase sheet in
|
|
4646
|
+
the Arcade's apps, straight away, with no confirmation screen of the
|
|
4647
|
+
arcade's own first. **Call it from the player's press handler** — the
|
|
4633
4648
|
arcade refuses an ask no person pressed for, `declined` / `busy`.
|
|
4634
4649
|
|
|
4635
4650
|
- `ok` — the player paid. Re-read balances and inventory through `gem.me`;
|
|
4636
4651
|
nothing here carries an amount. With `reason: 'pending'`, the provider
|
|
4637
4652
|
has not settled yet: [StoreApi.onSettled](#storeapi-onsettled) hears when it does.
|
|
4638
|
-
- `declined` — nothing was bought: `cancelled` (the player
|
|
4639
|
-
|
|
4653
|
+
- `declined` — nothing was bought: `cancelled` (the player closed the
|
|
4654
|
+
checkout, or the offer cannot be bought again yet) or `busy` (after a
|
|
4655
|
+
closed checkout the arcade refuses the next ask for a while). Return to
|
|
4656
|
+
your screen silently, and do not ask again unprompted.
|
|
4640
4657
|
- `unavailable` with `store_off`, `sign_in_required`, `not_found`,
|
|
4641
4658
|
`unsupported`, `invalid_argument` or a payment provider's failure code —
|
|
4642
4659
|
nothing was bought.
|
|
4643
4660
|
- `unavailable` / `error` — **the outcome is unknown, and the player may
|
|
4644
|
-
have paid.** It is what you get when the bridge closes while
|
|
4661
|
+
have paid.** It is what you get when the bridge closes while checkout
|
|
4645
4662
|
is open, or the arcade has no reliable answer. Never tell the player
|
|
4646
4663
|
nothing was bought: re-read balances and inventory through `gem.me`,
|
|
4647
4664
|
and keep listening with [StoreApi.onSettled](#storeapi-onsettled).
|
|
@@ -4655,6 +4672,43 @@ payment takes as long as it takes.
|
|
|
4655
4672
|
|
|
4656
4673
|
**Returns.** Always resolves; never rejects.
|
|
4657
4674
|
|
|
4675
|
+
<a id="storeapi-showoffer"></a>
|
|
4676
|
+
|
|
4677
|
+
## StoreApi.showOffer
|
|
4678
|
+
|
|
4679
|
+
```ts
|
|
4680
|
+
showOffer(skuId: string): Promise<ArcadeOutcome>
|
|
4681
|
+
```
|
|
4682
|
+
|
|
4683
|
+
Ask the arcade to show one SKU from [StoreApi.offers](#storeapi-offers) in its own
|
|
4684
|
+
sheet, over your paused and hidden game: its art, what the player gets, a
|
|
4685
|
+
sale if one is on, and, for a pass the player already holds, how long it
|
|
4686
|
+
has left. The sheet's Buy opens the same checkout as
|
|
4687
|
+
[StoreApi.purchase](#storeapi-purchase). **Call it from the player's press handler.**
|
|
4688
|
+
|
|
4689
|
+
Use it for an offer you would have to hardcode a pitch for: VIP and other
|
|
4690
|
+
passes, and bundles. Your button only needs to say what it opens. Draw a
|
|
4691
|
+
Gem pack yourself and use `purchase` instead. Check
|
|
4692
|
+
[StoreApi.showOfferAvailable](#storeapi-showofferavailable) first, and that the SKU is in `offers`.
|
|
4693
|
+
|
|
4694
|
+
It answers exactly as `purchase` does, with one addition: `declined` /
|
|
4695
|
+
`cancelled` also covers the player leaving the sheet without pressing Buy,
|
|
4696
|
+
and you are not told which of the two they closed. That starts the same
|
|
4697
|
+
back-off, so do not ask again unprompted. `unavailable` / `unsupported`
|
|
4698
|
+
means this Arcade does not draw the sheet, and nothing was sent.
|
|
4699
|
+
`unavailable` / `error` means **the outcome is unknown**: re-read balances
|
|
4700
|
+
and inventory through `gem.me`, and keep listening with
|
|
4701
|
+
[StoreApi.onSettled](#storeapi-onsettled).
|
|
4702
|
+
|
|
4703
|
+
NO TIMEOUT: it waits for as long as the arcade is connected, because a
|
|
4704
|
+
player may read a sheet and pay for as long as they like.
|
|
4705
|
+
|
|
4706
|
+
| Parameter | Type |
|
|
4707
|
+
| --- | --- |
|
|
4708
|
+
| `skuId` | `string` |
|
|
4709
|
+
|
|
4710
|
+
**Returns.** Always resolves; never rejects.
|
|
4711
|
+
|
|
4658
4712
|
<a id="storeapi-onsettled"></a>
|
|
4659
4713
|
|
|
4660
4714
|
## StoreApi.onSettled
|
|
@@ -10478,9 +10532,9 @@ readonly reserved: readonly GemReservedArea[]
|
|
|
10478
10532
|
```
|
|
10479
10533
|
|
|
10480
10534
|
Rectangles of your frame the arcade's own controls cover — in the Gem
|
|
10481
|
-
Arcade, its Gem button in whichever corner it sits,
|
|
10482
|
-
beside it while the player shows it
|
|
10483
|
-
|
|
10535
|
+
Arcade, its Gem button in whichever corner it sits, the frame-rate readout
|
|
10536
|
+
beside it while the player shows it, and the wallets beside it while your
|
|
10537
|
+
game asks for them (see `setWalletVisible`). Keep essential UI and touch
|
|
10484
10538
|
targets out of them. Empty when the arcade covers nothing.
|
|
10485
10539
|
|
|
10486
10540
|
Each is a keep-out area, grown out to the frame edges it sits nearest — not
|
|
@@ -10629,10 +10683,10 @@ readonly only?: readonly string[]
|
|
|
10629
10683
|
|
|
10630
10684
|
While shown, show ONLY these: platform currency slugs as
|
|
10631
10685
|
`gem.platform.currencies.get()` lists them (`GEMS`, `ARCADE_TICKETS`),
|
|
10632
|
-
matched exactly, and the literal `'score'` for the achievement score.
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10686
|
+
matched exactly, and the literal `'score'` for the achievement score. Leave
|
|
10687
|
+
it out and every wallet shows, the achievement score's included. The Gem
|
|
10688
|
+
Arcade honours it: `{ only: ['GEMS', 'ARCADE_TICKETS'] }` shows those two
|
|
10689
|
+
and no achievement score; a slug the platform does not serve names nothing.
|
|
10636
10690
|
|
|
10637
10691
|
<a id="gamemenuitem"></a>
|
|
10638
10692
|
|
|
@@ -10688,7 +10742,8 @@ interface GameMenu
|
|
|
10688
10742
|
```
|
|
10689
10743
|
|
|
10690
10744
|
What your game adds to the arcade's menu — see `gem.setMenu`. Your settings
|
|
10691
|
-
|
|
10745
|
+
screen is not part of it: it opens from your own settings button. An action
|
|
10746
|
+
on that screen (Replay the tutorial, How to play) may also be an entry here.
|
|
10692
10747
|
|
|
10693
10748
|
<a id="gamemenu-items"></a>
|
|
10694
10749
|
|
|
@@ -11211,8 +11266,10 @@ sheet. To spend Gems or Coins on your game's own items, use
|
|
|
11211
11266
|
[Gem.purchases](#gem-purchases).
|
|
11212
11267
|
|
|
11213
11268
|
List what the store sells here with `offers()`, show each `priceDisplay`
|
|
11214
|
-
exactly as given, and call `purchase(skuId)` from the player's press.
|
|
11215
|
-
|
|
11269
|
+
exactly as given, and call `purchase(skuId)` from the player's press. For a
|
|
11270
|
+
pass, a bundle or anything else whose pitch you would have to hardcode, call
|
|
11271
|
+
`showOffer(skuId)` instead, and the arcade draws the offer. After `ok`,
|
|
11272
|
+
re-read balances through [Gem.me](#gem-me). Nothing rejects.
|
|
11216
11273
|
|
|
11217
11274
|
**Guides.** [arcade-and-store](../arcade-and-store.md)
|
|
11218
11275
|
|
|
@@ -11796,9 +11853,10 @@ Tell the arcade a platform-currency reward is on its way, so it **shows it
|
|
|
11796
11853
|
landing**: in the Gem Arcade the currency flies from `origin` into the Gem
|
|
11797
11854
|
button, wherever on your frame it sits, which pops as each mark lands, and
|
|
11798
11855
|
the player's total moves once the platform confirms the grant. The Gem
|
|
11799
|
-
Arcade draws no wallet over your game
|
|
11800
|
-
Coins in the Gem menu. A payout you do not
|
|
11801
|
-
middle of your frame, so cue the ones the player
|
|
11856
|
+
Arcade draws no wallet over your game unless you ask (`setWalletVisible`):
|
|
11857
|
+
the player sees their Gems and Coins in the Gem menu. A payout you do not
|
|
11858
|
+
cue still flies, from the middle of your frame, so cue the ones the player
|
|
11859
|
+
earns on screen.
|
|
11802
11860
|
|
|
11803
11861
|
Call it the moment your game decides to award — when your server has moved
|
|
11804
11862
|
(or is about to move) the progression mapped onto a platform currency —
|
|
@@ -11843,26 +11901,25 @@ landed — which it does on every channel, dev and test included.
|
|
|
11843
11901
|
setWalletVisible(visible: boolean, options?: WalletVisibleOptions): boolean
|
|
11844
11902
|
```
|
|
11845
11903
|
|
|
11846
|
-
|
|
11847
|
-
|
|
11904
|
+
Ask the arcade to show, or stop showing, the player's wallets — Gems,
|
|
11905
|
+
Coins, the achievement score — over your game.
|
|
11848
11906
|
|
|
11849
|
-
**The Gem Arcade draws
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
while you play, show them on your menus.
|
|
11907
|
+
**The Gem Arcade draws them on request** (the owner, 2026-10-29): a game
|
|
11908
|
+
that says nothing gets no wallet over it, as since 2026-10-11, and the
|
|
11909
|
+
player sees their balances in the Gem menu; a game that asks gets them
|
|
11910
|
+
drawn beside the Gem, from the platform's own figures. An Arcade from
|
|
11911
|
+
before that ignores the call, harmlessly.
|
|
11855
11912
|
|
|
11856
11913
|
```js
|
|
11857
|
-
|
|
11858
|
-
|
|
11914
|
+
gem.setWalletVisible(true, { only: ['GEMS', 'ARCADE_TICKETS'] }); // every screen
|
|
11915
|
+
level.onStart = () => gem.setWalletVisible(false); // or put them away during play
|
|
11859
11916
|
```
|
|
11860
11917
|
|
|
11861
11918
|
**It is screen state, not a toggle.** Each call is the whole answer and
|
|
11862
|
-
replaces the last.
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11919
|
+
replaces the last. Shown wallets arrive as rectangles in
|
|
11920
|
+
`gem.viewport.reserved` (and widen the Gem's corner in the
|
|
11921
|
+
`--gem-reserved-*` variables), and hiding them gives that space back.
|
|
11922
|
+
`only` narrows what shows.
|
|
11866
11923
|
|
|
11867
11924
|
**Never draw a platform balance yourself**, and never show a pending state
|
|
11868
11925
|
for one: the arcade takes every figure from the platform. If this returns
|
|
@@ -11895,10 +11952,14 @@ gem.setMenu({
|
|
|
11895
11952
|
});
|
|
11896
11953
|
```
|
|
11897
11954
|
|
|
11898
|
-
**Your settings
|
|
11955
|
+
**Your settings screen is not in it: draw your own settings button** —
|
|
11899
11956
|
top-right, with your Back button just to its left, clear of
|
|
11900
11957
|
`gem.viewport.reserved`. The Arcade's menu never opens your settings
|
|
11901
|
-
screen.
|
|
11958
|
+
screen. Inside the Gem Arcade you may leave the button out while this
|
|
11959
|
+
returns `true`, when every setting on your screen is declared with
|
|
11960
|
+
`gem.settings` and every action on it is one of these entries; it returns
|
|
11961
|
+
`false` before the bridge is up and under an embedder with no menu, and the
|
|
11962
|
+
button stays there.
|
|
11902
11963
|
|
|
11903
11964
|
**It is screen state, not a toggle.** Each call is the whole answer and
|
|
11904
11965
|
replaces the last, so call it again when the entries change — a new
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The server surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v17.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/server` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 2,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "17.0.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -5512,7 +5512,7 @@
|
|
|
5512
5512
|
"group": "store",
|
|
5513
5513
|
"signature": "interface StoreApi",
|
|
5514
5514
|
"documented": true,
|
|
5515
|
-
"summary": "**Real money.** The Arcade Store's own Gem packs, Boosts, passes and bundles,\nbought with the player's money through the
|
|
5515
|
+
"summary": "**Real money.** The Arcade Store's own Gem packs, Boosts, passes and bundles,\nbought with the player's money through the Arcade Store's own checkout.\n`gem.store` is one of these. To spend Gems or Coins on your game's own items,\nuse `gem.purchases`.",
|
|
5516
5516
|
"deprecated": null,
|
|
5517
5517
|
"params": [],
|
|
5518
5518
|
"returns": null,
|
|
@@ -5537,6 +5537,22 @@
|
|
|
5537
5537
|
"defaultValue": null,
|
|
5538
5538
|
"guides": []
|
|
5539
5539
|
},
|
|
5540
|
+
{
|
|
5541
|
+
"id": "StoreApi.showOfferAvailable",
|
|
5542
|
+
"name": "showOfferAvailable",
|
|
5543
|
+
"anchor": "storeapi-showofferavailable",
|
|
5544
|
+
"group": "store",
|
|
5545
|
+
"signature": "readonly showOfferAvailable: boolean",
|
|
5546
|
+
"documented": true,
|
|
5547
|
+
"summary": "Whether [StoreApi.showOffer](api-reference-game.md#storeapi-showoffer) answers here, synchronously. An older\nArcade can sell through `purchase` and still not draw this sheet, so read\nthis, not [StoreApi.available](api-reference-game.md#storeapi-available), before you draw a button that calls\n`showOffer`. `false` until the bridge is up.",
|
|
5548
|
+
"deprecated": null,
|
|
5549
|
+
"params": [],
|
|
5550
|
+
"returns": null,
|
|
5551
|
+
"throws": null,
|
|
5552
|
+
"example": null,
|
|
5553
|
+
"defaultValue": null,
|
|
5554
|
+
"guides": []
|
|
5555
|
+
},
|
|
5540
5556
|
{
|
|
5541
5557
|
"id": "StoreApi.offers",
|
|
5542
5558
|
"name": "offers",
|
|
@@ -5560,7 +5576,30 @@
|
|
|
5560
5576
|
"group": "store",
|
|
5561
5577
|
"signature": "purchase(skuId: string): Promise<ArcadeOutcome>",
|
|
5562
5578
|
"documented": true,
|
|
5563
|
-
"summary": "Ask the arcade to open
|
|
5579
|
+
"summary": "Ask the arcade to open checkout for one SKU from [StoreApi.offers](api-reference-game.md#storeapi-offers):\nthe payment provider's page on the web, the app store's purchase sheet in\nthe Arcade's apps, straight away, with no confirmation screen of the\narcade's own first. **Call it from the player's press handler** — the\narcade refuses an ask no person pressed for, `declined` / `busy`.\n\n- `ok` — the player paid. Re-read balances and inventory through `gem.me`;\n nothing here carries an amount. With `reason: 'pending'`, the provider\n has not settled yet: [StoreApi.onSettled](api-reference-game.md#storeapi-onsettled) hears when it does.\n- `declined` — nothing was bought: `cancelled` (the player closed the\n checkout, or the offer cannot be bought again yet) or `busy` (after a\n closed checkout the arcade refuses the next ask for a while). Return to\n your screen silently, and do not ask again unprompted.\n- `unavailable` with `store_off`, `sign_in_required`, `not_found`,\n `unsupported`, `invalid_argument` or a payment provider's failure code —\n nothing was bought.\n- `unavailable` / `error` — **the outcome is unknown, and the player may\n have paid.** It is what you get when the bridge closes while checkout\n is open, or the arcade has no reliable answer. Never tell the player\n nothing was bought: re-read balances and inventory through `gem.me`,\n and keep listening with [StoreApi.onSettled](api-reference-game.md#storeapi-onsettled).\n\nNO TIMEOUT: it waits for as long as the arcade is connected, because a\npayment takes as long as it takes.",
|
|
5580
|
+
"deprecated": null,
|
|
5581
|
+
"params": [
|
|
5582
|
+
{
|
|
5583
|
+
"name": "skuId",
|
|
5584
|
+
"type": "string",
|
|
5585
|
+
"optional": false,
|
|
5586
|
+
"summary": null
|
|
5587
|
+
}
|
|
5588
|
+
],
|
|
5589
|
+
"returns": "Always resolves; never rejects.",
|
|
5590
|
+
"throws": null,
|
|
5591
|
+
"example": null,
|
|
5592
|
+
"defaultValue": null,
|
|
5593
|
+
"guides": []
|
|
5594
|
+
},
|
|
5595
|
+
{
|
|
5596
|
+
"id": "StoreApi.showOffer",
|
|
5597
|
+
"name": "showOffer",
|
|
5598
|
+
"anchor": "storeapi-showoffer",
|
|
5599
|
+
"group": "store",
|
|
5600
|
+
"signature": "showOffer(skuId: string): Promise<ArcadeOutcome>",
|
|
5601
|
+
"documented": true,
|
|
5602
|
+
"summary": "Ask the arcade to show one SKU from [StoreApi.offers](api-reference-game.md#storeapi-offers) in its own\nsheet, over your paused and hidden game: its art, what the player gets, a\nsale if one is on, and, for a pass the player already holds, how long it\nhas left. The sheet's Buy opens the same checkout as\n[StoreApi.purchase](api-reference-game.md#storeapi-purchase). **Call it from the player's press handler.**\n\nUse it for an offer you would have to hardcode a pitch for: VIP and other\npasses, and bundles. Your button only needs to say what it opens. Draw a\nGem pack yourself and use `purchase` instead. Check\n[StoreApi.showOfferAvailable](api-reference-game.md#storeapi-showofferavailable) first, and that the SKU is in `offers`.\n\nIt answers exactly as `purchase` does, with one addition: `declined` /\n`cancelled` also covers the player leaving the sheet without pressing Buy,\nand you are not told which of the two they closed. That starts the same\nback-off, so do not ask again unprompted. `unavailable` / `unsupported`\nmeans this Arcade does not draw the sheet, and nothing was sent.\n`unavailable` / `error` means **the outcome is unknown**: re-read balances\nand inventory through `gem.me`, and keep listening with\n[StoreApi.onSettled](api-reference-game.md#storeapi-onsettled).\n\nNO TIMEOUT: it waits for as long as the arcade is connected, because a\nplayer may read a sheet and pay for as long as they like.",
|
|
5564
5603
|
"deprecated": null,
|
|
5565
5604
|
"params": [
|
|
5566
5605
|
{
|
|
@@ -12027,7 +12066,7 @@
|
|
|
12027
12066
|
"group": "player",
|
|
12028
12067
|
"signature": "readonly reserved: readonly GemReservedArea[]",
|
|
12029
12068
|
"documented": true,
|
|
12030
|
-
"summary": "Rectangles of your frame the arcade's own controls cover — in the Gem\nArcade, its Gem button in whichever corner it sits,
|
|
12069
|
+
"summary": "Rectangles of your frame the arcade's own controls cover — in the Gem\nArcade, its Gem button in whichever corner it sits, the frame-rate readout\nbeside it while the player shows it, and the wallets beside it while your\ngame asks for them (see `setWalletVisible`). Keep essential UI and touch\ntargets out of them. Empty when the arcade covers nothing.\n\nEach is a keep-out area, grown out to the frame edges it sits nearest — not\nthe control's own box, and not something to line anything up with. Where\nthe arcade says, `control` on each is the control itself.\n\nA stylesheet reads the per-corner custom properties the SDK stamps from\nthese instead — `--gem-reserved-tl-height` and friends, and\n`--gem-reserved-tl-center-y` to line a top bar up with the Gem.",
|
|
12031
12070
|
"deprecated": null,
|
|
12032
12071
|
"params": [],
|
|
12033
12072
|
"returns": null,
|
|
@@ -12235,7 +12274,7 @@
|
|
|
12235
12274
|
"group": "platform",
|
|
12236
12275
|
"signature": "readonly only?: readonly string[]",
|
|
12237
12276
|
"documented": true,
|
|
12238
|
-
"summary": "While shown, show ONLY these: platform currency slugs as\n`gem.platform.currencies.get()` lists them (`GEMS`, `ARCADE_TICKETS`),\nmatched exactly, and the literal `'score'` for the achievement score.
|
|
12277
|
+
"summary": "While shown, show ONLY these: platform currency slugs as\n`gem.platform.currencies.get()` lists them (`GEMS`, `ARCADE_TICKETS`),\nmatched exactly, and the literal `'score'` for the achievement score. Leave\nit out and every wallet shows, the achievement score's included. The Gem\nArcade honours it: `{ only: ['GEMS', 'ARCADE_TICKETS'] }` shows those two\nand no achievement score; a slug the platform does not serve names nothing.",
|
|
12239
12278
|
"deprecated": null,
|
|
12240
12279
|
"params": [],
|
|
12241
12280
|
"returns": null,
|
|
@@ -12315,7 +12354,7 @@
|
|
|
12315
12354
|
"group": "frame",
|
|
12316
12355
|
"signature": "interface GameMenu",
|
|
12317
12356
|
"documented": true,
|
|
12318
|
-
"summary": "What your game adds to the arcade's menu — see `gem.setMenu`. Your settings\
|
|
12357
|
+
"summary": "What your game adds to the arcade's menu — see `gem.setMenu`. Your settings\nscreen is not part of it: it opens from your own settings button. An action\non that screen (Replay the tutorial, How to play) may also be an entry here.",
|
|
12319
12358
|
"deprecated": null,
|
|
12320
12359
|
"params": [],
|
|
12321
12360
|
"returns": null,
|
|
@@ -12790,7 +12829,7 @@
|
|
|
12790
12829
|
"group": "start",
|
|
12791
12830
|
"signature": "readonly store: StoreApi",
|
|
12792
12831
|
"documented": true,
|
|
12793
|
-
"summary": "**Real money.** The Arcade Store's own Gem packs, Boosts, passes and\nbundles, sold for the player's money through the arcade's own purchase\nsheet. To spend Gems or Coins on your game's own items, use\n[Gem.purchases](api-reference-game.md#gem-purchases).\n\nList what the store sells here with `offers()`, show each `priceDisplay`\nexactly as given, and call `purchase(skuId)` from the player's press.
|
|
12832
|
+
"summary": "**Real money.** The Arcade Store's own Gem packs, Boosts, passes and\nbundles, sold for the player's money through the arcade's own purchase\nsheet. To spend Gems or Coins on your game's own items, use\n[Gem.purchases](api-reference-game.md#gem-purchases).\n\nList what the store sells here with `offers()`, show each `priceDisplay`\nexactly as given, and call `purchase(skuId)` from the player's press. For a\npass, a bundle or anything else whose pitch you would have to hardcode, call\n`showOffer(skuId)` instead, and the arcade draws the offer. After `ok`,\nre-read balances through [Gem.me](api-reference-game.md#gem-me). Nothing rejects.",
|
|
12794
12833
|
"deprecated": null,
|
|
12795
12834
|
"params": [],
|
|
12796
12835
|
"returns": null,
|
|
@@ -13234,7 +13273,7 @@
|
|
|
13234
13273
|
"group": "start",
|
|
13235
13274
|
"signature": "cueReward(cue: RewardCue): boolean",
|
|
13236
13275
|
"documented": true,
|
|
13237
|
-
"summary": "Tell the arcade a platform-currency reward is on its way, so it **shows it\nlanding**: in the Gem Arcade the currency flies from `origin` into the Gem\nbutton, wherever on your frame it sits, which pops as each mark lands, and\nthe player's total moves once the platform confirms the grant. The Gem\nArcade draws no wallet over your game
|
|
13276
|
+
"summary": "Tell the arcade a platform-currency reward is on its way, so it **shows it\nlanding**: in the Gem Arcade the currency flies from `origin` into the Gem\nbutton, wherever on your frame it sits, which pops as each mark lands, and\nthe player's total moves once the platform confirms the grant. The Gem\nArcade draws no wallet over your game unless you ask (`setWalletVisible`):\nthe player sees their Gems and Coins in the Gem menu. A payout you do not\ncue still flies, from the middle of your frame, so cue the ones the player\nearns on screen.\n\nCall it the moment your game decides to award — when your server has moved\n(or is about to move) the progression mapped onto a platform currency —\nand pass the centre of that currency's icon (or hero) on your screen, so\nthe flight starts from the mark the player is looking at:\n\n```js\nconst { x, y, width, height } = rewardGem.getBoundingClientRect(); // the gem's own box\ngem.cueReward({\n currencySlug: 'GEMS', // or 'ARCADE_TICKETS', from the coin\n expected: 15,\n origin: { x: x + width / 2, y: y + height / 2 },\n});\n```\n\n**This is the platform-currency animation, and the only one.** The display\nof every platform currency — Gems and Coins alike — belongs to the\narcade: a game draws no flight or celebration of its own for one and keeps\nno counter of one. **If this returns `false`, do nothing** — there is no\nfallback celebration.\n\n**The arcade never takes your word for the amount.** What lands, and the\nplayer's new total, come from the platform itself; `expected` is drawn as\nprovisional, and a cue the platform never confirms fades on its own — a\ngrant clamped to zero, say, or one the receipt reports `suppressed`. So\nthe cue can only ever make the wallet move for a reward that really\nlanded — which it does on every channel, dev and test included.",
|
|
13238
13277
|
"deprecated": null,
|
|
13239
13278
|
"params": [
|
|
13240
13279
|
{
|
|
@@ -13257,7 +13296,7 @@
|
|
|
13257
13296
|
"group": "start",
|
|
13258
13297
|
"signature": "setWalletVisible(visible: boolean, options?: WalletVisibleOptions): boolean",
|
|
13259
13298
|
"documented": true,
|
|
13260
|
-
"summary": "
|
|
13299
|
+
"summary": "Ask the arcade to show, or stop showing, the player's wallets — Gems,\nCoins, the achievement score — over your game.\n\n**The Gem Arcade draws them on request** (the owner, 2026-10-29): a game\nthat says nothing gets no wallet over it, as since 2026-10-11, and the\nplayer sees their balances in the Gem menu; a game that asks gets them\ndrawn beside the Gem, from the platform's own figures. An Arcade from\nbefore that ignores the call, harmlessly.\n\n```js\ngem.setWalletVisible(true, { only: ['GEMS', 'ARCADE_TICKETS'] }); // every screen\nlevel.onStart = () => gem.setWalletVisible(false); // or put them away during play\n```\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last. Shown wallets arrive as rectangles in\n`gem.viewport.reserved` (and widen the Gem's corner in the\n`--gem-reserved-*` variables), and hiding them gives that space back.\n`only` narrows what shows.\n\n**Never draw a platform balance yourself**, and never show a pending state\nfor one: the arcade takes every figure from the platform. If this returns\n`false`, do nothing in its place.",
|
|
13261
13300
|
"deprecated": null,
|
|
13262
13301
|
"params": [
|
|
13263
13302
|
{
|
|
@@ -13286,7 +13325,7 @@
|
|
|
13286
13325
|
"group": "start",
|
|
13287
13326
|
"signature": "setMenu(menu: GameMenu): boolean",
|
|
13288
13327
|
"documented": true,
|
|
13289
|
-
"summary": "Put up to four entries of your own in the arcade's menu. The arcade keeps\none menu button over your game: it is how a player leaves, mutes, goes\nfullscreen, reports a problem and changes their Arcade-wide settings.\n\n```js\ngem.setMenu({\n items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],\n});\n```\n\n**Your settings
|
|
13328
|
+
"summary": "Put up to four entries of your own in the arcade's menu. The arcade keeps\none menu button over your game: it is how a player leaves, mutes, goes\nfullscreen, reports a problem and changes their Arcade-wide settings.\n\n```js\ngem.setMenu({\n items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],\n});\n```\n\n**Your settings screen is not in it: draw your own settings button** —\ntop-right, with your Back button just to its left, clear of\n`gem.viewport.reserved`. The Arcade's menu never opens your settings\nscreen. Inside the Gem Arcade you may leave the button out while this\nreturns `true`, when every setting on your screen is declared with\n`gem.settings` and every action on it is one of these entries; it returns\n`false` before the bridge is up and under an embedder with no menu, and the\nbutton stays there.\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last, so call it again when the entries change — a new\nlanguage, a screen with different options. `gem.setMenu({})` takes\neverything of yours back out. The arcade's own entries are not yours to\nchange, and Return to the Arcade is always among them.\n\n**The arcade pauses your game while its menu is open** (`onPause`), and\nresumes it before a handler of yours runs. A handler opens a screen; it\nshould not assume the game was paused by you.",
|
|
13290
13329
|
"deprecated": null,
|
|
13291
13330
|
"params": [
|
|
13292
13331
|
{
|
package/docs/arcade-and-store.md
CHANGED
|
@@ -18,11 +18,17 @@ async function onBuyPressed(offer) {
|
|
|
18
18
|
// Read through gem.me; nothing here carries an amount. `error` is "outcome unknown" (§9).
|
|
19
19
|
if (outcome === 'ok' || reason === 'error') await refreshBalances();
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
// From the press on your own "Get VIP" button: the Arcade draws the offer.
|
|
23
|
+
async function onVipPressed() {
|
|
24
|
+
const { outcome, reason } = await gem.store.showOffer(VIP_SKU);
|
|
25
|
+
if (outcome === 'ok' || reason === 'error') await refreshBalances();
|
|
26
|
+
}
|
|
21
27
|
```
|
|
22
28
|
|
|
23
29
|
**Your game asks; the Arcade decides.** Nothing your game sends carries a URL, a price or a card
|
|
24
|
-
field. The Arcade builds every link, reads every price,
|
|
25
|
-
|
|
30
|
+
field. The Arcade builds every link, reads every price, opens every checkout and takes the payment
|
|
31
|
+
itself. You get an outcome.
|
|
26
32
|
|
|
27
33
|
- [1. Two surfaces, and which one is real money](#1-two-surfaces-and-which-one-is-real-money)
|
|
28
34
|
- [2. Nothing rejects](#2-nothing-rejects)
|
|
@@ -33,15 +39,16 @@ payment itself. You get an outcome.
|
|
|
33
39
|
- [7. Similar games, as cards](#7-similar-games-as-cards)
|
|
34
40
|
- [8. The Arcade Store's offers](#8-the-arcade-stores-offers)
|
|
35
41
|
- [9. Buying from the Arcade Store](#9-buying-from-the-arcade-store)
|
|
36
|
-
- [10.
|
|
42
|
+
- [10. Letting the Arcade show one offer](#10-letting-the-arcade-show-one-offer)
|
|
43
|
+
- [11. Outcomes and reasons](#11-outcomes-and-reasons)
|
|
37
44
|
|
|
38
45
|
## 1. Two surfaces, and which one is real money
|
|
39
46
|
|
|
40
|
-
| | **`gem.arcade`** | **`gem.store`**
|
|
41
|
-
| ------------ | ------------------------------------------------ |
|
|
42
|
-
| What it does | app-store listings, another game, similar games | the Arcade Store's own SKUs
|
|
43
|
-
| Money | none | **real money**, through the Arcade's
|
|
44
|
-
| Who draws it | the Arcade (a new tab, a QR code, the next game) | the Arcade
|
|
47
|
+
| | **`gem.arcade`** | **`gem.store`** |
|
|
48
|
+
| ------------ | ------------------------------------------------ | --------------------------------------------------- |
|
|
49
|
+
| What it does | app-store listings, another game, similar games | the Arcade Store's own SKUs |
|
|
50
|
+
| Money | none | **real money**, through the Arcade Store's checkout |
|
|
51
|
+
| Who draws it | the Arcade (a new tab, a QR code, the next game) | your card or the Arcade's sheet, then the checkout |
|
|
45
52
|
|
|
46
53
|
`gem.store` is not `gem.purchases`. **`gem.purchases` spends Gems or Coins on your game's own
|
|
47
54
|
items** ([`inventory-and-catalogs.md`](inventory-and-catalogs.md)). **`gem.store` sells the Arcade
|
|
@@ -60,7 +67,7 @@ three and never grows:
|
|
|
60
67
|
|
|
61
68
|
`reason` is a short code that says why. The set is open: a new Arcade build may send a reason your
|
|
62
69
|
game has never seen, so branch on `outcome` and treat `reason` as a hint. The codes in use today are
|
|
63
|
-
in [§
|
|
70
|
+
in [§11](#11-outcomes-and-reasons).
|
|
64
71
|
|
|
65
72
|
## 3. What is available here
|
|
66
73
|
|
|
@@ -71,17 +78,19 @@ An Arcade that does not offer a feature answers every call for it at once with `
|
|
|
71
78
|
if (gem.arcade.available.openGame) showMoreGamesSection();
|
|
72
79
|
if (gem.arcade.available.appLinks) showGetTheAppButton();
|
|
73
80
|
if (gem.store.available) showGetGemsButton();
|
|
81
|
+
if (gem.store.showOfferAvailable) showGetVipButton();
|
|
74
82
|
```
|
|
75
83
|
|
|
76
|
-
`gem.arcade.available` is `{ appLinks, openGame }`, and `gem.store.available`
|
|
77
|
-
|
|
84
|
+
`gem.arcade.available` is `{ appLinks, openGame }`, and `gem.store.available` and
|
|
85
|
+
`gem.store.showOfferAvailable` are one boolean each. All of them are synchronous and `false` until
|
|
86
|
+
the bridge is up. A feature can be off for reasons you
|
|
78
87
|
cannot see from inside the frame: a TV, an in-app browser that cannot take payment, an app build
|
|
79
88
|
where it is withheld. Do not guess; read it.
|
|
80
89
|
|
|
81
90
|
## 4. Call the acting ones from a press
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
`gem.store.purchase`. **Call each one from the player's own press handler**, in the same turn as the
|
|
92
|
+
Four calls put something in front of the player: `gem.arcade.openApp`, `gem.arcade.openGame`,
|
|
93
|
+
`gem.store.purchase` and `gem.store.showOffer`. **Call each one from the player's own press handler**, in the same turn as the
|
|
85
94
|
press. The Arcade checks that a person pressed, and refuses an ask nobody pressed for with
|
|
86
95
|
`declined` / `busy`, showing nothing.
|
|
87
96
|
|
|
@@ -221,7 +230,7 @@ Gem pack, a `grant` naming the currency and amount.
|
|
|
221
230
|
currency, already formatted. Never parse it, compare two of them, or compute a discount from it.
|
|
222
231
|
There is no numeric price.
|
|
223
232
|
- **Re-read offers when your game resumes.** What is on sale can change while the player is away.
|
|
224
|
-
- **Do not imitate the Arcade's chrome.** Your offer list is yours to style; the
|
|
233
|
+
- **Do not imitate the Arcade's chrome.** Your offer list is yours to style; the checkout that takes
|
|
225
234
|
the money is the Arcade's, and a game that draws something that looks like it confuses players
|
|
226
235
|
about which one is real.
|
|
227
236
|
- `unavailable` / `store_off` means the store is not offered here (a TV, an in-app browser that
|
|
@@ -234,12 +243,15 @@ Gem pack, a `grant` naming the currency and amount.
|
|
|
234
243
|
const { outcome, reason } = await gem.store.purchase(offer.skuId);
|
|
235
244
|
```
|
|
236
245
|
|
|
237
|
-
`purchase(skuId)` asks the Arcade to open
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
246
|
+
`purchase(skuId)` asks the Arcade to open checkout for one SKU from `offers()`. The Arcade pauses
|
|
247
|
+
and hides your game and opens checkout **straight away**, with no confirmation screen of its own
|
|
248
|
+
first: the payment provider's page on the web, the app store's own purchase sheet in the Arcade's
|
|
249
|
+
apps. That checkout is where the player confirms and pays. Your game is resumed before the answer
|
|
250
|
+
arrives, so your own celebration is visible.
|
|
241
251
|
|
|
242
|
-
- **Call it from the press** ([§4](#4-call-the-acting-ones-from-a-press)).
|
|
252
|
+
- **Call it from the press** ([§4](#4-call-the-acting-ones-from-a-press)). The player's press on
|
|
253
|
+
your Buy button is what opens checkout, so make that button say plainly what it buys and for how
|
|
254
|
+
much (`priceDisplay`).
|
|
243
255
|
- **It has no timeout.** A payment takes as long as it takes, so the call waits for as long as the
|
|
244
256
|
Arcade is connected.
|
|
245
257
|
- `ok`: the player paid. **Read balances and inventory through `gem.me`**; the answer carries no
|
|
@@ -252,14 +264,16 @@ own celebration is visible.
|
|
|
252
264
|
const stop = gem.store.onSettled(({ skuId }) => refreshBalances());
|
|
253
265
|
```
|
|
254
266
|
|
|
255
|
-
- `declined` / `cancelled`: the player closed the
|
|
256
|
-
ask again unprompted**: the Arcade backs off after
|
|
257
|
-
`declined` / `busy` for a while.
|
|
267
|
+
- `declined` / `cancelled`: the player closed the checkout without paying. Return to your screen
|
|
268
|
+
quietly and **do not ask again unprompted**: the Arcade backs off after the player closes one, and
|
|
269
|
+
the next ask is `declined` / `busy` for a while, longer each time. An offer the player cannot buy
|
|
270
|
+
again yet also answers `declined` / `cancelled`: the Arcade tells the player when it can be
|
|
271
|
+
bought, opens nothing, and does not back off.
|
|
258
272
|
- `unavailable` with `store_off`, `sign_in_required` or `not_found` (a SKU that is not in
|
|
259
273
|
`offers()` right now), or with a payment provider's own failure code: nothing was bought. The
|
|
260
274
|
first three show nothing.
|
|
261
275
|
- `unavailable` / `error`: **the outcome is unknown, and the player may have paid.** You get it
|
|
262
|
-
when the bridge to the Arcade closes while
|
|
276
|
+
when the bridge to the Arcade closes while checkout is open, which can happen after the payment
|
|
263
277
|
went through, and when the Arcade has no reliable answer. Never tell the player nothing was
|
|
264
278
|
bought. Re-read balances and inventory through `gem.me`, keep your `gem.store.onSettled`
|
|
265
279
|
listener, and re-read again when your game resumes:
|
|
@@ -268,7 +282,57 @@ own celebration is visible.
|
|
|
268
282
|
if (outcome === 'unavailable' && reason === 'error') await refreshBalances(); // not "nothing bought"
|
|
269
283
|
```
|
|
270
284
|
|
|
271
|
-
## 10.
|
|
285
|
+
## 10. Letting the Arcade show one offer
|
|
286
|
+
|
|
287
|
+
```js
|
|
288
|
+
// Decide on the screen you draw the button on.
|
|
289
|
+
const { offers } = await gem.store.offers();
|
|
290
|
+
const vip = offers.find((offer) => offer.skuId === VIP_SKU);
|
|
291
|
+
if (gem.store.showOfferAvailable && vip) showGetVipButton();
|
|
292
|
+
else hideGetVipButton();
|
|
293
|
+
|
|
294
|
+
// From the press on that button.
|
|
295
|
+
const { outcome, reason } = await gem.store.showOffer(VIP_SKU);
|
|
296
|
+
if (outcome === 'ok' || reason === 'error') await refreshBalances();
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`showOffer(skuId)` asks the Arcade to draw one SKU from `offers()` in its own sheet, over your paused
|
|
300
|
+
and hidden game. The Arcade draws the art, what the player gets, a sale if one is running, and, for a
|
|
301
|
+
pass the player already holds, how long it has left. The sheet's Buy opens the same checkout
|
|
302
|
+
`purchase` opens.
|
|
303
|
+
|
|
304
|
+
Pick by who can say what the offer is:
|
|
305
|
+
|
|
306
|
+
| | **You draw it: `offers()` + `purchase()`** | **The Arcade draws it: `showOffer()`** |
|
|
307
|
+
| --------------- | --------------------------------------------------------- | ------------------------------------------------------------ |
|
|
308
|
+
| Use it for | Gem packs: a name, an amount and a price say it all | VIP and other passes, and bundles |
|
|
309
|
+
| Your button | says what it buys and shows `priceDisplay` | says what it opens, such as "Get VIP" |
|
|
310
|
+
| The press opens | the checkout, straight away | the Arcade's sheet, then the checkout if the player presses Buy |
|
|
311
|
+
| Perks and sales | not available to you | always current, in the player's language |
|
|
312
|
+
| A holder sees | the same Buy button | what they already have |
|
|
313
|
+
|
|
314
|
+
- **Never write an offer's perks into your game.** They change without a new build of your game, and
|
|
315
|
+
yours would go stale. Your button only needs the offer's name.
|
|
316
|
+
- **Do not show a price on the button.** The sheet shows the right one for this player, which a
|
|
317
|
+
sale or a pass can change, and `priceDisplay` does not say either of those. If you want a price on
|
|
318
|
+
screen, draw the offer yourself and use `purchase`.
|
|
319
|
+
- **You cannot tell whether the player holds a pass.** The Arcade keeps that to itself, and the
|
|
320
|
+
sheet tells the player. So the button stays up for a holder, and that is fine: the sheet is what
|
|
321
|
+
they see, and they choose from there.
|
|
322
|
+
- **Hide the button when the SKU is not in `offers()`.** A SKU that is not on sale here answers
|
|
323
|
+
`unavailable` / `not_found`, and the player would have pressed a button that does nothing.
|
|
324
|
+
- **Read `showOfferAvailable`, not `available`.** An older Arcade can sell through `purchase` and
|
|
325
|
+
still not draw this sheet. It answers `unavailable` / `unsupported` and sends nothing. Hide the
|
|
326
|
+
button there.
|
|
327
|
+
|
|
328
|
+
Every rule of `purchase` holds ([§9](#9-buying-from-the-arcade-store)), and the answers are the
|
|
329
|
+
same, with one addition. `declined` / `cancelled` also covers the player leaving the sheet without
|
|
330
|
+
pressing Buy, and you are not told which of the two they closed. That starts the same back-off as a
|
|
331
|
+
closed checkout, shared with `purchase`: the next ask of either kind is `declined` / `busy` for a
|
|
332
|
+
while. The press shows nothing during a back-off, so give the player something else to do on that
|
|
333
|
+
screen.
|
|
334
|
+
|
|
335
|
+
## 11. Outcomes and reasons
|
|
272
336
|
|
|
273
337
|
| Call | `ok` | `declined` | `unavailable` |
|
|
274
338
|
| ---------------- | ------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
@@ -278,6 +342,7 @@ own celebration is visible.
|
|
|
278
342
|
| `similarGames` | cards, possibly none | | `unsupported`, `error` |
|
|
279
343
|
| `store.offers` | offers, possibly none | | `sign_in_required`, `store_off`, `unsupported`, `error` |
|
|
280
344
|
| `store.purchase` | paid, or `pending` | `cancelled`, `busy` | `sign_in_required`, `store_off`, `not_found`, the provider's reason, `unsupported`, `error` (outcome unknown) |
|
|
345
|
+
| `store.showOffer` | paid, or `pending` | `cancelled`, `busy` | `sign_in_required`, `store_off`, `not_found`, the provider's reason, `unsupported`, `error` (outcome unknown) |
|
|
281
346
|
|
|
282
347
|
Every reason is a short code of lower-case letters and underscores. The Arcade turns a payment
|
|
283
348
|
provider's own code into that shape before it reaches you.
|
|
@@ -291,5 +356,5 @@ Four reasons come from the SDK itself rather than the Arcade:
|
|
|
291
356
|
- `error`: no reliable answer arrived. The ask could not be sent, a read or an open waited out its
|
|
292
357
|
timeout (30 seconds for a read, two minutes for `openApp` and `openGame`), the bridge closed
|
|
293
358
|
before the answer came, or the Arcade said it had no reliable answer. For a read, try again
|
|
294
|
-
later. For `store.purchase` it means **the outcome is unknown**, never
|
|
295
|
-
([§9](#9-buying-from-the-arcade-store)).
|
|
359
|
+
later. For `store.purchase` and `store.showOffer` it means **the outcome is unknown**, never
|
|
360
|
+
"nothing was bought" ([§9](#9-buying-from-the-arcade-store)).
|