@genex-ai/cli-demo 1.36.6 → 1.36.7-dev.889

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: genex-monetization
3
- description: Build an in-game shop that sells for platform coin — item catalog, purchase flow, delivery, and the per-game soft-currency economy a purchase attaches to. Use when the player asks to sell things, add a shop, monetize, or make the game earn. Carries the hard rules: no paid randomness, no gambling in coin, no donation mechanics, and a real-money price beside every coin price.
3
+ description: Add dynamic coin payments for game goods or actions — platform consent, recoverable receipts, and idempotent delivery without a required product catalog. Use when the player asks to sell things, add a shop, monetize, or make the game earn. Carries the hard rules: no paid randomness, no gambling in coin, no donation mechanics, and a real-money price beside every coin price.
4
4
  ---
5
5
 
6
6
  # Genex Monetization
@@ -10,8 +10,8 @@ buys coin with real money once; spending it inside a game is a ledger movement
10
10
  the game never touches. You design what is for sale; the platform owns the
11
11
  wallet, the confirmation, and the money.
12
12
 
13
- Load this when the game should sell something. Ask first if it should — a game
14
- with no loop worth monetizing is better without a shop (see §1).
13
+ Load this when the user asks to sell something or add payments. Build the game
14
+ loop before adding commerce; no shop screen or product catalog is required.
15
15
 
16
16
  ## The hard rules, and the test that generalizes them
17
17
 
@@ -94,15 +94,18 @@ Nine checks. Each one is answerable about your actual design.
94
94
  shop, remove the annoyance instead of selling the cure.
95
95
  5. **Everything sold is reachable free.** Spending is a shortcut or a
96
96
  decoration, never the only path.
97
- 6. **Prices land on the grid.** Item prices use 50 / 100 / 200 / 500 / 1000 coin
98
- so every coin pack divides evenly into them and nobody is left holding change
99
- they cannot spend.
100
- 7. **Every price shows real money next to it.** The server sends
101
- `priceDisplayUsdCents` with every item — render it. `250 coins ($2.49)`.
97
+ 6. **A payment has one clear price and promised result.** `amountCoins` is a
98
+ positive whole number from 1 to 100000, further bounded by the platform
99
+ surface. Dynamic payments do not use the legacy catalog price grid.
100
+ 7. **Every price shows real money next to it.** The platform confirmation
101
+ shows the server-frozen `priceDisplayUsdCents`. If rendering a receipt or
102
+ catalog price in the game, render that value too; never invent an exchange
103
+ rate. A button can simply say “Unlock” and let Genex show the price.
102
104
  8. **One currency layer between money and goods.** Coin buys items. A per-game
103
105
  earned currency buys per-game upgrades. They never convert into each other.
104
- 9. **Purchases never expire and survive a reinstall.** Entitlements live on the
105
- server; the game re-reads them on every boot.
106
+ 9. **Paid ownership survives a reinstall.** Receipts live on the server; the
107
+ game recovers them on every boot and restores the promised state. A
108
+ confirmation can expire; a paid receipt does not expire with it.
106
109
 
107
110
  For a per-game earned currency, the load-bearing number is **minutes of play per
108
111
  unit earned**. Set it, then price the cheapest meaningful item at one to three
@@ -110,257 +113,192 @@ sessions of earning. Everything else follows. Spend sinks come in three kinds
110
113
  permanent upgrades, refills, cosmetics — and cosmetics are what absorbs late-game
111
114
  currency without touching balance.
112
115
 
113
- ## Stocking the shop
116
+ ## Dynamic payment requests
114
117
 
115
- Items live on the platform, not in the game's code. You create them with the CLI,
116
- and the game names them by id — which is what stops a game inventing its own
117
- items or repricing them.
118
-
119
- ```bash
120
- npx genex shop add "Iron Key" --price 100 --type durable
121
- # → id: sku_a1b2c3 ← what the game passes to buy()
122
-
123
- npx genex shop list # what this game sells, and the valid prices
124
- npx genex shop set sku_a1b2c3 --price 200
125
- npx genex shop remove sku_a1b2c3 # retires it; players who bought it keep it
126
- ```
127
-
128
- `--type consumable` (default) is spent on use; `durable` is owned permanently.
129
-
130
- **Prices come off a fixed grid** — `genex shop list` prints it, and an off-grid
131
- price is refused. The grid exists so every coin pack divides evenly by the
132
- cheapest item, which is what stops a player being left holding change too small
133
- to spend. Pick the nearest grid price rather than working around it.
134
-
135
- Record the ids in `DESIGN.md` next to what each item does. They are the one
136
- thing the game's code cannot regenerate for itself.
137
-
138
- ## Item art
139
-
140
- **Every item ships with a picture.** `--icon` is optional to the command and not
141
- optional to the product: a SKU with no icon renders as a coloured plate with a
142
- generic box on it, in your shop and on the platform's resale market, where it
143
- sits next to other games' items that do have art.
144
-
145
- **It has to be the item.** Not "a fantasy key" — *this* key, the one the player
146
- just picked up, in this game's light and this game's palette. An icon that shows
147
- a different object than the world does is worse than the plate, because the
148
- plate at least does not claim anything.
149
-
150
- So if the item exists as an asset — a generated model, a sprite, a texture —
151
- generate the art FROM it. Point the camera at the model in the game, or use the
152
- asset image, and feed that frame in as the reference:
153
-
154
- ```bash
155
- # 1. capture the real item -> ./iron-key-ref.png (crop it SQUARE, see below)
156
- npx genex image "shop item art of the exact object in the reference image: an ornate iron key, centred, lit from above with a soft rim light, on a clean dark backdrop, no text, no watermark, no interface" \
157
- --edit ./iron-key-ref.png --quality high
158
- # -> https://assets.genex.technology/generations/<id>/image-main
159
-
160
- # 2. attach it
161
- npx genex shop add "Iron Key" --price 100 --type durable --icon <that url>
162
- npx genex shop set sku_a1b2c3 --icon <that url> # or fix one later
163
- ```
164
-
165
- `--edit` takes a **local file** (≤ 4 MB) as well as an asset URL, so a screenshot
166
- goes straight in. Two things about that lane:
167
-
168
- - **It keeps the reference's shape**, so `--aspect` does nothing here — crop the
169
- reference square before you pass it, or you will get a wide icon in a square
170
- cell.
171
- - It is a different model from the plain text lane, and a better one for this
172
- job, because the reference is what makes the picture the item and not a
173
- picture of the idea.
174
-
175
- No asset yet? Same prompt without `--edit`, anchored on the item's name and on
176
- **the game's own vibe words** — take them from `DESIGN.md` rather than inventing
177
- a style, and generate the whole shop in one sitting with the same style clause
178
- so the shelf reads as one set.
179
-
180
- Prompt shape, and every clause earns its place: one object, centred, on a clean
181
- simple backdrop, lit the way the game lights things, closing with *no text, no
182
- watermark, no interface*. Say what you want, never what you don't — an image
183
- model reads "no swords" as "swords", so a ban only works when the thing is
184
- absent from the prompt entirely.
185
-
186
- ## The API
187
-
188
- From `@genex-ai/embed-sdk`, already installed. `initEmbed()` must have run.
118
+ From `@genex-ai/embed-sdk`, after `initEmbed()` and player identity are ready:
189
119
 
190
120
  ```ts
191
- import { getShop, buy, getEntitlements, consumeEntitlement } from '@genex-ai/embed-sdk';
192
-
193
- const items = await getShop();
194
- // [{ id, type, name, iconUrl, priceCoins, priceDisplayUsdCents }]
195
- ```
196
-
197
- Render `name`, `iconUrl`, `priceCoins` **and** `priceDisplayUsdCents`. Never
198
- hardcode a price: the server charges what its own catalog says, so a hardcoded
199
- number can silently disagree with what the player is charged.
200
-
201
- `getShop()` works for a **guest** and inside a **preview** build, so the shop
202
- window renders for everyone — that is the point of showing it to a signed-out
203
- player at all. Buying is what needs an account.
204
-
205
- **Your game cannot read the player's coin balance, and no HUD should show one.**
206
- The wallet spans every game on the platform, so an untrusted game is not told how
207
- much a player can spend. Show what you *can* know — what they own, from
208
- `getEntitlements()` — and let `buy()` report `insufficient_balance` if it comes
209
- to that.
210
-
211
- ### Buying
212
-
213
- ```ts
214
- buyButton.addEventListener('click', async () => { // must be a real click
215
- const result = await buy({ skuId: item.id });
216
- if (result.status === 'canceled') return; // normal — say nothing
217
- if (result.status !== 'succeeded') {
218
- showMessage(result.message ?? 'That did not go through.');
121
+ import { requestPayment, getPayment, getPayments } from '@genex-ai/embed-sdk';
122
+
123
+ // Prepare and save this order AND its promised action before enabling the button.
124
+ // Generate its id once. Keep the same id and terms across retries and reloads.
125
+ const order = {
126
+ orderId: 'unlock-iron-gate-run-42',
127
+ title: 'Open the iron gate',
128
+ description: 'Unlock the shortcut for this run.',
129
+ amountCoins: 75,
130
+ };
131
+
132
+ buyButton.addEventListener('click', async () => {
133
+ const result = await requestPayment(order); // first async operation in the click
134
+ if (result.status === 'canceled') return;
135
+ if (result.status === 'succeeded' && result.payment) {
136
+ await applyPaidOrderOnce(result.payment); // your saved, idempotent game action
219
137
  return;
220
138
  }
221
- await deliverPending();
139
+ if (result.status === 'pending') {
140
+ showMessage('Payment is still being checked.');
141
+ return; // recover this order; do not create a replacement charge
142
+ }
143
+ showMessage('Purchase did not complete.');
222
144
  });
223
145
  ```
224
146
 
225
- **Call `buy()` synchronously from the click handler.** On the game's own origin
226
- the confirmation is a popup, and browsers only allow one while a user gesture is
227
- live — an `await` before it loses the gesture and nothing opens.
147
+ No `genex shop add`, SKU id, catalog card or item icon is needed. A payment can
148
+ buy a described game action, unlock or item. The hard rules above still apply.
149
+ The game never supplies a recipient: the platform uses the game's own split.
150
+ This API is for coin commerce; in-game LLM requests use their own funding and
151
+ consent flow (`$genex-llm-in-games`), not a payment wrapped around a model call.
228
152
 
229
- `buy()` resolves when the SERVER says what happened, not when a window closes.
230
- Statuses: `succeeded`, `canceled`, `expired`, `insufficient_balance`, `failed`.
153
+ The server freezes `title`, `description`, `amountCoins` and the real-money
154
+ value before Genex asks the player. **Creating a request neither debits nor
155
+ reserves coin.** Only the player's explicit confirmation on the platform
156
+ surface can move money. The game cannot draw, skin or confirm that sheet.
231
157
 
232
- The player confirms on a Genex-drawn surface — your game does not render the
233
- price sheet, cannot skin it, and cannot complete a purchase itself. That is
234
- deliberate: it is what lets a player trust a purchase in a game they have never
235
- played before.
158
+ An `orderId` belongs to this player in this game. The same id with the same
159
+ terms returns the same payment, including a terminal result. Changing its terms
160
+ returns `payment_conflict`. After an order is canceled or expired, a deliberate
161
+ new purchase gets a new id; a network retry never does. Do not generate ids
162
+ inside a retry loop or replace them just because the SDK stopped waiting.
236
163
 
237
- Where that surface is depends on the shell the game is in
238
- (`$genex-threejs-embed-auth`, `references/shells.md`), and two of them change
239
- what the game does:
164
+ ### Confirmation and results
240
165
 
241
- - **On the game's Genex page the sheet is drawn by the page, above the game.**
242
- To show it, the page leaves fullscreen first, and the SDK lets go of the
243
- pointer lock before it asks (`@genex-ai/embed-sdk` 0.32.0+; a build on an
244
- older SDK keeps the cursor captured until the player presses Escape). Treat
245
- a purchase like a pause: re-enter fullscreen and re-lock the pointer from
246
- the player's next Resume click, never by yourself.
247
- - **The Genex app has no purchase surface yet.** There `buy()` resolves
248
- `failed` without opening anything. That is the app, not an error: when
249
- `getShell()` from `@genex-ai/embed-sdk` is `"app"`, show the shop as
250
- browse-only or leave the buy buttons out, and never show a failure message
251
- for it.
166
+ **Call `requestPayment()` synchronously from the click handler.** On the game's
167
+ own origin it reserves a popup while the user gesture is live, then fills it
168
+ with the platform confirmation. Awaiting a save or fetch first can block it;
169
+ prepare and persist the order before enabling its purchase button.
252
170
 
253
- ### Delivering
171
+ The result has `status`, optional `payment` (the receipt), and optional `message`.
172
+ Statuses are `succeeded`, `canceled`, `expired`, `insufficient_balance`, `failed`
173
+ and `pending`. A timeout or uncertain response is `pending`: it is not proof of
174
+ failure and not permission to apply the action or charge again. `canceled` is
175
+ ordinary player choice; do not show an error for it.
176
+
177
+ **Your game cannot read the player's coin balance, and no HUD should show one.**
178
+ The wallet spans the platform; let Genex show the balance and handle consent.
179
+ Only fulfil a payment whose authoritative receipt says `succeeded`.
180
+
181
+ The surface depends on the shell (`$genex-threejs-embed-auth`,
182
+ `references/shells.md`):
183
+
184
+ - **On the game's Genex page the sheet is drawn above the game.** To show it,
185
+ the page leaves fullscreen first, and the SDK lets go of the
186
+ pointer lock before it asks. Treat payment like a pause; re-enter fullscreen
187
+ and pointer lock from the player's next Resume click.
188
+ - **The Genex app has no purchase surface yet.** `requestPayment()` fails
189
+ without opening a sheet; the legacy `buy()` resolves
190
+ `failed` without opening anything too. When `getShell()` from `@genex-ai/embed-sdk` is `"app"`,
191
+ hide payment buttons or make the shop browse-only; do not show a failure toast.
192
+ - Local test mode has no wallet. Preview builds cannot spend real coin either.
193
+ Verify layout and delivery with game-owned test fixtures; a real payment needs
194
+ a published build, a signed-in player and explicit platform consent.
195
+
196
+ ### Receipts and delivery after a restart
254
197
 
255
198
  ```ts
256
- async function deliverPending() {
257
- for (const e of await getEntitlements({ excludeConsumed: true })) {
258
- // Branch on skuType (consumable | durable), NEVER on type — `type` is how
259
- // it was acquired (purchase/gift/test/free) and can't answer this.
260
- if (e.skuType === 'durable') {
261
- wear(e.name); // ownership, re-applied on every boot. Do not consume.
262
- continue;
263
- }
264
- const { alreadyConsumed } = await consumeEntitlement(e.id);
265
- if (alreadyConsumed) continue; // someone got there first
266
- applyItem(e.skuId); // AFTER the consume
267
- await savePlayerState(currentSave());
268
- }
269
- }
199
+ const payment = await getPayment(savedOrderId); // null only when not found
200
+ if (payment?.status === 'succeeded') await applyPaidOrderOnce(payment);
201
+
202
+ let cursor: string | undefined;
203
+ do {
204
+ const page = await getPayments({ cursor, limit: 100 });
205
+ for (const receipt of page.payments) await applyPaidOrderOnce(receipt);
206
+ cursor = page.nextCursor ?? undefined;
207
+ } while (cursor);
270
208
  ```
271
209
 
272
- **Consume first, apply second, and run `deliverPending()` on every boot.**
210
+ `getPayment()` returns any status for one order; read/network errors throw and
211
+ are not absence. `getPayments()` lists successful receipts, scoped to the
212
+ current player and game. Follow `nextCursor` to completion. Use the per-order
213
+ read for a known pending request, and scan receipts on boot before purchases
214
+ can affect play.
273
215
 
274
- That order is not stylistic. If the game dies between consuming and applying,
275
- the player loses one item — a support ticket. If you apply first and die before
276
- consuming, every boot re-delivers it forever — an exploit. Re-listing on boot is
277
- what makes a purchase survive a crash, a refresh, or a closed tab.
216
+ A receipt has `id`, `orderId`, `title`, `description`, `amountCoins`,
217
+ `priceDisplayUsdCents`, `status`, `failureCode`, `createdAt`, `paidAt` and
218
+ `expiresAt`. It proves payment, **not delivery of the promised game action**.
219
+ It creates no platform entitlement and nothing the resale market can list.
278
220
 
279
- **`e.skuType` is the field that decides this, not `e.type`.** `type` says how the
280
- player got it (`purchase`/`gift`/`test`/`free`); `skuType` says what it is
281
- (`consumable`/`durable`).
221
+ `applyPaidOrderOnce` is game code, not an SDK helper. Bind the saved `orderId`
222
+ to the promised action before requesting payment. For an unlock, set the same
223
+ ownership flag every time. For a one-time quantity change, save the changed
224
+ state and the fulfilled receipt id together; replaying the receipt must be a
225
+ no-op. A game with its own backend should enforce the receipt's unique id in
226
+ that backend's transaction. Never use receipt titles as executable instructions
227
+ or apply an effect just because the confirmation window closed.
282
228
 
283
- A **consumable** is spent: consume it, then apply the effect once.
229
+ Test recovery after closing the tab, a repeated success receipt, a delayed
230
+ response and a canceled order. Also check concurrent fulfillment if the game
231
+ permits multiple tabs; two read-modify-write saves are not a transaction.
232
+ Do not make a real-money purchase automatically to test the feature.
284
233
 
285
- A **durable** is owned forever, and you have two honest ways to handle it. If the
286
- effect is ownership — a cosmetic, a skin, an unlock — do **not** consume it: the
287
- row's presence in the list IS the ownership, it survives reinstalls, and there is
288
- no local save to drift out of sync. Only consume a durable when it grants
289
- something once and non-idempotently (a permanent +100 gold), and then record it
290
- in the player's save, because re-applying that on every boot would be a bug.
234
+ ## Existing catalog items
291
235
 
292
- ### Testing the shop you just built
236
+ Catalog APIs remain supported for published games and items that need platform
237
+ entitlements or resale. They are optional, not a prerequisite for dynamic
238
+ payments:
293
239
 
294
240
  ```bash
295
- npx genex shop test <sku-id> # a free copy, for the owner only
241
+ npx genex shop add "Iron Key" --price 100 --type durable
242
+ npx genex shop list
243
+ npx genex shop test <sku-id> # owner-only free entitlement; no coin moves
296
244
  ```
297
245
 
298
- That grants the entitlement without a sale — no coin moves — so the delivery
299
- path, the inventory and the effect all run exactly as they would after a real
300
- purchase. Reload the game and it should be there.
301
-
302
- You *can* also just buy your own item, but it costs you the full price and pays
303
- you nothing: your share of a sale you funded goes to the platform, never into
304
- your own earnings. Use the test grant instead.
246
+ `getShop()` reads catalog items even for guests and preview builds;
247
+ `buy({ skuId })` requests their platform confirmation. These prices must follow
248
+ the grid printed by `genex shop list`. Render `name`, `iconUrl`, `priceCoins`
249
+ and `priceDisplayUsdCents`; use an image of the actual item for its icon.
250
+ `genex shop set` edits a listing and `genex shop remove` retires it without
251
+ removing a buyer's ownership.
252
+
253
+ Re-list `getEntitlements({ excludeConsumed: true })` on boot. Branch on
254
+ `skuType` (`consumable`/`durable`), never `type` (purchase/gift/test/free).
255
+ For a consumable, await `consumeEntitlement(id)` BEFORE applying its effect
256
+ and skip `alreadyConsumed: true`. This prevents repeated delivery but a crash
257
+ between consume and save can lose that effect; do not promise atomic game-state
258
+ delivery. A durable ownership unlock is never consumed: restore it from the
259
+ entitlement on every boot. Dynamic receipts do not use this consumption API.
305
260
 
306
261
  ## Checklist
307
262
 
308
- - [ ] Items exist (`npx genex shop list`) before the shop UI is written
309
- - [ ] Delivery was verified with `npx genex shop test`, not by trying to buy your own item
310
- - [ ] The game has a loop and a progression before it has a shop
311
- - [ ] Every item price is on the 50/100/200/500/1000 grid
312
- - [ ] Every price renders `priceDisplayUsdCents` beside the coin figure
313
- - [ ] `buy()` is called synchronously inside a click/tap handler
314
- - [ ] In the Genex app (`getShell() === "app"`) the buy buttons are hidden or
315
- the shop is browse-only — never a failure message
316
- - [ ] `canceled` is silent; only real failures show a message
317
- - [ ] `deliverPending()` runs on every boot, before the player can act
318
- - [ ] `consumeEntitlement()` is awaited BEFORE the effect is applied
319
- - [ ] `alreadyConsumed: true` skips the effect
320
- - [ ] Durable purchases are written to the player's save
321
- - [ ] No timer, "limited", "ends in", or stock counter anywhere
263
+ - [ ] The game has a worthwhile loop before commerce
264
+ - [ ] The saved order binds one stable id, exact terms and promised action
265
+ - [ ] `requestPayment()` starts directly from a click/tap
266
+ - [ ] Genex shows the frozen coin and real-money price and receives consent
267
+ - [ ] No payment buttons in the Genex app
268
+ - [ ] Cancellation is silent; uncertain outcomes remain pending
269
+ - [ ] Recovery reads receipts before applying any paid action
270
+ - [ ] Fulfillment is idempotent, including reload and concurrent-tab cases
271
+ - [ ] No paid randomness, coin wagering, donation prompt or pressure timer
322
272
  - [ ] Nothing sold is unreachable without paying
323
- - [ ] No paid randomness, no coin wagering, no donation prompt
324
273
 
325
274
  ## Troubleshooting
326
275
 
327
- **`buy()` returns `failed` with "the confirmation window was blocked"** — `buy()`
328
- was not called inside a user gesture, or an `await` ran before it. Move it to the
329
- first line of the click handler.
330
-
331
- **The purchase succeeded but the player got nothing** — the game applied the
332
- effect without consuming, or never ran `deliverPending()` on boot. The
333
- entitlement is still there; re-list it.
334
-
335
- **The player got the item twice** — the effect was applied before consuming, or
336
- `alreadyConsumed` was ignored. Both are the same bug.
276
+ **The confirmation window was blocked** — an `await` ran before
277
+ `requestPayment()`, or there was no click. Persist the order earlier and make
278
+ the payment request the click handler's first async operation.
337
279
 
338
- **`unauthorized` from `getShop()`** — no player identity yet. `initEmbed()` must
339
- have run and `waitForPlayer()` resolved. See `$genex-threejs-embed-auth`.
280
+ **`payment_conflict`** — the same order id was reused with different terms.
281
+ Recover its original request; create a new id only for a new purchase.
340
282
 
341
- **`guest_no_wallet`** — guests play but hold no wallet. Show the shop as
342
- sign-in-to-buy rather than hiding it.
283
+ **`pending` or an interrupted tab** — use `getPayment(orderId)`. Never assume
284
+ failure, issue a replacement charge, or deliver while the status is uncertain.
343
285
 
344
- **`staging_no_purchase`** — a `genex preview` build cannot spend real coin. Test
345
- the shop's layout on staging; test a purchase after `genex promote`.
286
+ **Payment succeeded but nothing happened** — find the saved order's action and
287
+ recover its receipt. The platform records payment; the game owns fulfillment.
346
288
 
347
- **`getShop()` returns nothing** — the game has no items yet. `npx genex shop add
348
- "<name>" --price <coin>` and use the id it prints.
289
+ **The player got the effect twice** — delivery did not deduplicate by receipt
290
+ id or the save update raced. Fix fulfillment; do not change the order id.
349
291
 
350
- **`price_off_grid`** — that price isn't on the platform's grid. `npx genex shop
351
- list` prints the valid ones; pick the nearest.
292
+ **`guest_no_wallet`** — ask the player to sign in before purchase.
352
293
 
353
- **Everything coin-related 404s** — in-game purchases aren't enabled on this
354
- environment. Nothing to fix in the game; say so and build the rest.
294
+ **`staging_no_purchase`** — previews cannot spend; verify layout there and use
295
+ a published build for an explicitly approved real purchase.
355
296
 
356
- **Purchases do nothing in local testing** — local test mode has no wallet and no
357
- server. `buy()` returns `failed` immediately by design. Test purchases on a
358
- preview or published build.
297
+ **`surface_cap`** — the amount exceeds the server's limit for this confirmation
298
+ surface. Do not split it into smaller hidden charges to bypass the limit.
359
299
 
360
- **`buy()` returns `failed` inside the Genex app** — the app has no purchase
361
- surface yet, so nothing opens. Not a bug to chase: hide the buy buttons there
362
- (`getShell() === "app"`).
300
+ **`price_off_grid`** — this applies to legacy catalog SKUs only. Dynamic
301
+ payments accept whole amounts subject to their own caps.
363
302
 
364
- **The game dropped out of fullscreen when the shop's confirmation opened** — by
365
- design on the game's Genex page: the confirmation is the page's, and the page
366
- has to be visible to show it. Re-enter from the player's next click.
303
+ **Everything coin-related 404s** — coin commerce is disabled on this stand.
304
+ Build the rest and report the unavailable lane.
@@ -13,11 +13,12 @@ model has to run for the player.
13
13
  ## What the platform does
14
14
 
15
15
  A game hosted on Genex can call a language model from inside the running game
16
- and get back text or JSON. **The player pays and the player approves**: coins
17
- from their Genex wallet, or their own Claude / ChatGPT subscription, chosen on
18
- an approval sheet Genex draws that the game cannot render, skin or bypass.
19
- Either one call at a time, or one standing budget the player approves once and
20
- the game then spends against without another popup. The game holds no provider
16
+ and get back text or JSON. **The player pays and the player approves**: credits
17
+ from their Genex account, billed as each call is used and never past the
18
+ per-call ceiling the game declares, or their own Claude / ChatGPT subscription,
19
+ chosen on an approval sheet Genex draws that the game cannot render, skin or
20
+ bypass. Either one call at a time, or one standing budget the player approves
21
+ once and the game then spends against without another popup. The game holds no provider
21
22
  key, sees no credential, and never talks to a model vendor.
22
23
 
23
24
  That is the whole reason this is a platform feature and not something you wire
@@ -40,9 +41,9 @@ Any of these means a model at PLAY time, however it is phrased:
40
41
  Say this and stop:
41
42
 
42
43
  > A model running while people play is built into the Genex platform — the
43
- > player pays, with Genex coins or their own Claude/ChatGPT subscription, and
44
- > approves it on a Genex sheet; your game just calls `generate()`. Want it
45
- > that way?
44
+ > player pays, with their Genex credits or their own Claude/ChatGPT
45
+ > subscription, and approves it on a Genex sheet; your game just calls
46
+ > `generate()`. Want it that way?
46
47
 
47
48
  Wait for the answer. Do not start building either version first, and do not
48
49
  expand the offer into a pitch — one line, one question.
@@ -64,7 +65,8 @@ expand the offer into a pitch — one line, one question.
64
65
  ask before you do if you have not already.
65
66
  3. Load `$genex-llm-in-games` — it arrives with the conversion and owns the
66
67
  build: the SDK surface, one-time calls versus a standing budget, measuring
67
- the price before the game declares it, and honest handling of every refusal.
68
+ the per-call ceiling before the game declares it, and honest handling of
69
+ every refusal.
68
70
 
69
71
  **The game has to be a static browser build.** A hosted game is files served
70
72
  from the edge; there is no server of yours inside it. So the familiar pattern —
@@ -38,7 +38,7 @@ update, so update immediately.)
38
38
  Run exactly the command the nudge printed, from the game project root:
39
39
 
40
40
  ```bash
41
- npm i -D @genex-ai/cli-demo@latest # the genex CLI (a dev dependency)
41
+ npm i -D @genex-ai/cli-demo@dev # the genex CLI (a dev dependency)
42
42
  npm i @genex-ai/embed-sdk@latest # identity/saves SDK (ships inside the game)
43
43
  npm i @genex-ai/multiplayer@latest # multiplayer SDK (only if the game uses it)
44
44
  ```