Skip to main content
summer-store is a skill from the Summer skill library: a guide your AI agent reads when the task matches, so it builds the good version on the first try. Sell Summer items for Sparks in a multiplayer game: shop, checkout, owned items, authority entitlement checks.

When your agent uses it

  • Adding a shop, in-game purchases, cosmetics or skins sold for Sparks to a Summer game
  • Checking on the authority that a player really owns an item before letting them use or equip it
  • Checkout shows pending, manual_review, insufficient_funds or unavailable, or bought items don’t appear

Install

summer setup installs every skill for your agent. To add only this one:
Swap claude-code for your agent, such as codex or cursor. See Skills for every option.

The skill

Overview

Players pay with Sparks, Summer’s currency. Your game never touches money, balances or approval:
  1. The client lists your published offers with Summer.client.store.list_items().
  2. The player presses Buy. The client asks for a checkout with offer ids and quantities, never prices.
  3. The Summer app shows its own purchase sheet. The player approves or declines there.
  4. Summer charges the Sparks and issues the item into the player’s inventory.
  5. The game sees the item in Summer.client.items (client) and Summer.authority.items (authority).
Three ids, kept apart: The rule that makes it cheat-proof: the authority decides what a player may use by reading that player’s inventory itself. A client saying “I bought it” proves nothing. What runs where: Items and offers are created in your game’s Studio pages (studio.open target game). Code never creates items. This skill extends the Courtyard game from multiplayer-project and multiplayer-state.

Steps

1. The client shop

client/shop.gd, a child Shop of Main. In client/main.gd’s _ready(), call $Shop.start() right after Summer.initialize succeeds:
Bind checkouts_changed to the Buy buttons: disable them while busy is true. Update rows by id; a status can be delivered again after a crash, so rendering must be idempotent. Show each checkout state:

2. Unlock from the inventory only

Unlock cosmetics in menus with owns(definition_id), and refresh the menu on refreshed, item_replaced and item_removed. Never unlock from a purchased status: the receipt proves the debit, the inventory proves ownership, including after a reinstall or on another device. An empty inventory before is_projection_loaded() is “still loading”, not “owns nothing”.

3. The authority checks entitlement

Anything that changes the match because of an item goes through a Command, and the authority reads that Session’s inventory. Add "equip" to the match in authority/main.gd’s _on_command:
The client sends await command({"c": "equip", "definition": definition_id}) and every client draws hats from match_doc.hats. Other players see the hat only because the authority published it. Summer.authority.items.inventory_changed(session, change) hints that a player’s inventory changed during the match. To drop an item that was refunded mid-match, re-read with inventory_for_session (or refresh_inventory) on that hint.

4. Rules and why

  • Offer ids and quantities only. Never send a price, an amount or a buyer. The platform prices everything, and a client-side price is what a cheater edits.
  • One request_checkout is one purchase. Each call gets a new identity. Never call it again because a result is slow; is_outcome_unknown() means it may already be charged, and the status will still arrive.
  • Connect checkout_status_changed normally, before reading statuses. A synchronous handler is how the SDK knows the result was delivered; a deferred-only handler leaves it undelivered.
  • pending and manual_review are not failures. Don’t show success or failure until a terminal state.
  • Carts: 1–16 different offers, at most 64 units, as one atomic purchase.
  • Hide what’s unavailable. check_browse_readiness() and check_checkout_readiness() return a SummerResult; show the shop only when they’re ok.

5. Test

Local Play. The store and checkout report unavailable, so the hidden-shop path is what you test. Inventory and the authority check work from a fixture: create .summer/test-users.json with item templates.
What the fixture gives you:
  • Every persona owns one item per template.
  • Ids are local stand-ins. Bob’s hat has owned item id local-item:bob:0, definition id local-definition:courtyard:0, and item_template_version_id "hat".
  • A check against your real catalog definition id is therefore false in Local Play. To exercise the authority path locally, equip with the local definition id the client reads from items.get_owned_items().
Staging. Test real purchases there; Summer staging uses sandbox payments. Walk through every outcome:
  • decline the sheet;
  • buy with too few Sparks;
  • buy, then kill the game while the purchase is pending and relaunch: the result must arrive exactly once.

Checklist

  • Local Play: the shop button is hidden, and there are no script errors.
  • Local Play with the fixture: items.refresh() succeeds, the owned item is listed, equip is accepted for it and refused with not_owned for anything else.
  • No code path sends a price or grants an item from a checkout status.
  • Buy is disabled while has_pending_checkouts() is true.
  • manual_review shows “confirming”, not success or failure.
  • Staging: a purchase shows up in the inventory, and the equipped item appears for every player.

Common mistakes

See also

  • multiplayer-state: Commands and the match document used for equipped items
  • multiplayer-testing: Local Play fixtures and personas
  • summer-player-data: saving a player’s loadout between matches
  • summer-analytics: measuring the shop

From a shell

No Summer MCP tools in this session? Run each one as a command; when summer --version is missing or below 4.0, use npx -y summer-engine@latest in place of summer. Fields are flags in kebab-case (--scene-path res://main.tscn) or one --input '<json>', and summer ops describe <op> prints the exact input. The summer-cli skill has the rest.
  • multiplayer: Start here for Summer multiplayer: how authority, clients, Worlds and Sessions fit, and what comes next.
  • multiplayer-state: Authority-owned state in Summer multiplayer: shared and private State groups, Commands, Events.
  • multiplayer-testing: Test a Summer multiplayer game with Local Play: several players, bad network, bots, a passing run.
  • summer-player-data: Save a player’s progress so it follows them to every match: authority load and commit, autosave, safe retries.
  • summer-analytics: Send game events to Summer analytics from the authority or client: fire-and-forget, never blocking gameplay.

Source: library/skills/summer-store/SKILL.md, rendered from summer-engine@4.3.0 on npm.