shop
Sell things in a Homie studio's games with the studio's OWN Stripe - a supporter pack, cosmetics, a season pass, a one-time unlock, a tip - in real money, with Stripe Checkout, Stripe Tax or Stripe Ma
Files2
View on GitHubSKILL.mdplugins/homie-rocks/homie/skills/shop/SKILL.md
GitHubCustomer AIs can buy through the studio's own /mcp. Read the toolkit's tools/TOOLS.md
and use the tools skill for studio_catalogue, cart quotes, standard agent payments,
private purchase claims, and human Stripe checkout approval. Priced studio tools sell
services through that same purchase engine; shop connect synchronizes their Payment Links.
Keep orders, grants and refunds in the existing shop. For order/refund follow-up code, use
Studio functions (functions/FUNCTIONS.md), with the event ID as the idempotency key.
The shop
Check that the toolkit exposes stripe_shop_plan and stripe_webhook; upgrade older toolkits first.
The guide is node_modules/@homie-rocks/studio/shop/SHOP.md; the owner's plain words are SELLING.md.
The model, said plainly to the person. The studio sells with its own Stripe account. The studio is the seller: its prices, its refunds, its disputes, its tax. Money goes straight from players to the studio's Stripe; homie.rocks never sees it, holds it or moves it, and Homie takes no cut. The studio is responsible for the law where it sells and for Stripe's terms.
In a rules game, keep shop UI in the view. Do not turn a client character save, an entity field or a command into proof of a purchase: grants still come from the shop's verified server records. Room state is replicated to players; keep payment and private customer data out of it. Read the game skill's RULES.md before changing game outcomes.
Default: Stripe's official plugin
Use Stripe's official plugin, authenticated with its browser OAuth approval, for Products,
Prices, Payment Links, menu links, per-slot booking links, monthly giving and the customer
portal. Install with stripe agent setup if needed; the MCP URL is
https://mcp.stripe.com. Current tools are stripe_api_search, stripe_api_details,
stripe_api_read and stripe_api_write. Discover the operation and its schema before
calling it; tool argument shapes can change. See https://docs.stripe.com/mcp.
- Create/check
shop.json. Read the app's published slot records and supply the complete slot manifest described below. Select the owner's account and sandbox in Stripe's plugin; read/v1/accountto record its account ID. Live mode only after the owner requests it. - Call Homie's
stripe_shop_planwithaccount,mode: "test",slotsandobservations: []. CLI equivalent:homie-studio shop plan --have plan-input.json. That JSON file contains the same fields.operationscontains every independent operation currently ready, with method, path, parameters and create idempotency key. Execute the batch through Stripe's official plugin, then replan for dependent work.nextremains the first operation for older clients. - Append
{ "method": "POST", "path": "…", "params": {…}, "body": <Stripe response> }for writes as well as reads. Keep this setup's observations across batches. Product creation includes its default Price; link creation expands line items. Product and Price Search use studio/origin metadata. Payment Links use paginated list reads withexpand[]=data.line_items, then filter studio/origin metadata locally; Stripe has no Payment Link metadata search. There is no per-link read loop. The planner saves public observations in.studio/stripe-mcp-observations.jsonand reuses them on the next call; send only new responses. Keep that file and.studio/stripe-mcp.jsonwhen moving computers. Changed shop/slot settings refresh mutable links while retaining product/price discovery. Account, mode and origin changes start separate reads. Passrefresh: truefor Dashboard changes or a fresh verification before webhook installation (CLI: include it in--haveinput). Follow searchnext_pageand liststarting_aftercursors. On an uncertain write, retry its exact idempotency key; never discard successful responses or invent IDs. - Call
stripe_shop_planwithverify: trueusing those observations (CLIhomie-studio shop verify --have plan-input.json). Verification checks real fields, line items, menus, slot limits and portal settings. It saves compact public Worker configuration and provenance to.studio/stripe-mcp.json, never API response history. Build writes public links tosite/dist/_site/stripe-shop-links.json; deploy ships that asset, with only its path in Wrangler vars. Large catalogues never use a text binding. Deploy after verifying, including when setup began on an already-live site. Checkout then reports onlywebhook-secretmissing; it cannot open until signed payment events can confirm orders. Changing shop.json, the slot manifest or origin requires verification again. Local test configuration goes to.dev.vars. - For a deployed HTTPS studio, run
stripe_webhook(CLIhomie-studio shop webhook). Followstudio_job. This uses Stripe CLI only for private webhook installation, checks the same account as the MCP, and installs only the signing secret through Wrangler stdin. If Stripe CLI is missing, the tool installs the official@stripe/cli@latestnpm package automatically, checks the executable, then continues. An installation failure reports npm permissions/PATH and a retry command. The CLI needs one separate browser approval if its studio profile is not already connected. MCP approval cannot be reused or exported. Cloudflare authorization and an existing deployment are prerequisites. - After installation, perform the returned
retireoperations through Stripe MCP to archive replaced catalog objects. Use fresh scoped reads for later drift checks. Test a sandbox purchase and its signed event, order grant, booking capacity or recurring invoice; a browser return is never proof. Stripe CLItriggercan send a test event, andevents resend <event> --webhook-endpoint <endpoint>retries a delivery. Check the endpoint's delivery result and the app's verified records. Do not type a card into a page for an automated proof; use Stripe's documented test PaymentMethods/fixtures.
Upgrades accept 0.51.6 verifications without a source fingerprint only when their
saved content fingerprint still matches the current shop, slots, site and mode.
Actual changes name the affected settings and require verification. An unfinished
setup still builds and deploys with checkout closed and one next step: finish
stripe_shop_plan with verify: true. Missing/unreadable assets also close checkout;
old link secrets cannot reopen it. Cached observations never renew the verification
time; use fresh reads if the private webhook tool requests them.
Never create a webhook through model-visible Stripe MCP: its response exposes the signing secret.
Why the separate approval (checked 2026-10-14). Stripe's
event destination API
also returns its signing secret only in the private create response when explicitly
included. Thin events still require signature verification and authenticated retrieval
of event details; they do not supply a secret handoff to Wrangler. The
official MCP documents no credential or secret transfer
to a studio Worker. Therefore the AI starts stripe_webhook, the owner clicks
Approve once in Stripe's CLI browser page if needed, and the tool completes endpoint
creation and Wrangler secret installation privately. MCP approval and CLI approval
are separate. No API key is requested, no secret enters chat, and Homie is never in
the money path. A connected studio CLI profile needs no further approval.
The owner never handles an API key or signing secret. No Homie backend handles payments.
The Worker needs only public link configuration and the signing secret; buyers pay
Stripe directly even when the AI and the owner's computer are offline. The old
stripe_login / shop connect flow remains an alternative that syncs everything
through Stripe CLI browser approval. --renew renews that approval; --live requires
the owner's go-live request. Never read CLI config or print login-completion output.
For local checkout, pass url: "http://localhost:8787" (or the private Wi-Fi address)
and mode: "test" to the same plan/verify flow; the official plugin makes the links.
Run homie-studio shop listen --profile <sandbox-profile> in a background terminal.
Stripe CLI is required for forwarding; one browser approval is enough if needed.
The command captures and saves the listener secret privately. Start/restart
homie-studio dev --lan once it reports ready and keep the listener running.
Never copy the listener's raw output into the conversation.
For a faster local iteration, homie-studio build --quick skips generated rules play
and the snapshot smoke-run, while retaining compilation, guards and type checks.
Run a full build before release; quick output is not evidence that play checks passed.
Do
| The person says | Run | What happens |
|---|---|---|
| "Sell a supporter pack for $5" | npx --no-install homie-studio shop init --supporter (then shop check) |
shop.json with a US$5 Supporter pack (a badge on their profile and beside their name in rooms, for a year; it changes nothing about play) and SELLING.md. Commit both. |
| "Sell a skin / a season pass / the full game" | edit shop.json items (kind: cosmetic, pass, unlock; price in Stripe currency units; gives: the keys the game reads), shop check, then the official MCP plan/verify flow again |
Kinds are studio labels, without a fixed list. "advantage": true follows the studio policy for beginner and kids servers. Homie sets no price ceiling. |
| "Let people tip" | an item { "kind": "tip", "price": "choose", "min": 200, "max": 5000 } |
Pay what you want; amounts follow Stripe currency requirements, and max is optional and chosen by the studio. |
| "Make the products in Stripe" | stripe_shop_plan |
Idempotent Product, Price, Payment Link and webhook sync from shop.json; no secret enters tool output. |
| "Connect my Stripe" / "turn the shop on" | stripe_shop_plan, then stripe_webhook |
Approve Stripe once if needed; the Worker receives links and the signing secret only. Follow the single next step returned. Live only when requested. |
| "Is tax set up?" | stripe_api_read GET /v1/tax/settings, and GET /v1/tax/registrations |
Seller "stripe": Stripe Tax needs status: active (the business address in Settings, Tax), or checkouts fail; with no registrations it collects no tax anywhere: say so, the accountant decides where to register. Seller "stripe-managed": Stripe files the tax; the connect page's test checkout said whether Managed Payments is on. Change nothing yourself. |
| "Is the shop working?" | npx --no-install homie-studio shop |
Open (test or live) or exactly what is missing, the last 30 days, the webhook address. |
| "How are sales?" / "Show me the sales" | shop and shop orders first (the studio's own books); with Stripe's MCP, read-only: stripe_analytics, or stripe_api_read on /v1/balance, /v1/payouts, /v1/checkout/sessions |
Counts, money and payouts in a few lines; never a buyer's name, email or card. Never a write to answer a question. The owner's /_studio/office/shop links every Stripe page and gives the accountant a CSV. |
| "Refund that player" | The owner’s official Stripe connection, or link their Stripe Dashboard payment | Honor Stripe’s confirmation link; signed events update the books and revoke fully refunded items. The keyed way still offers office refunds. |
| "Someone charged back" | nothing to undo: the owner answers it in Stripe (the office links it) | While open nothing changes; lost: that one item goes; won: it stays. The account is never locked or deleted over a dispute. |
| "Show the item in the game" / "the supporter badge" | in the game: createShop() from @homie-rocks/studio/shop; shop.has('skin:ember'), shop.on('change', …), shop.open('ember-skin') from a button the player pressed, shop.used(key) when equipped |
The play shell answers for the signed-in player; a badge rides on their seat as peer.badge (the Worker sets it, never a hello). |
Your shop, your choices
The default is open: guests buy repeatedly without an age question, with any wording or kind, from any page.
shop init writes no policy. A released shop file without a preset now uses the open default.
Write "policy": { "preset": "protective" } to retain earlier account and age behavior.
adults-only is another optional bundle. Neither scans content. SHOP.md lists individual overrides.
The studio can set items, quantities, prices, tip ranges, entitlements, sale dates, currency, optional
automaticTax, capPerPlayerMonth, refundDays and configurable flood protection.
No toolkit amount or duration ceiling applies. Stripe currency constraints and safe integer arithmetic apply.
A refund window includes used items unless policy.refundUsedItems is false. A player cannot refund a tip;
the owner can refund through Stripe; keyed shops also offer office refunds. Guest purchases stay owned when the buyer signs in later.
For menus or explicitly keyed shops, carts: shop.add(item, quantity, amount?), then shop.checkout(); shop.buy(item) buys directly.
Free and paid lines share one order and one Stripe Session. The studio chooses where to open the shop.
The first sale (the acceptance)
In test mode: buy the supporter pack on a phone with Stripe's test card 4242 4242 4242 4242, see "It's yours",
see the badge on the account page and beside the name in a room (from the next room the player joins), refund it
in Stripe (or through the connected AI), and see the badge go. With the protective preset, a kids server's room shows no shop and /<game>/tv shows only a code.
Keyless menus offer several fixed-price items with adjustable quantities on Stripe. Custom amounts and monthly preset gifts have their own links. Free orders stay local. A missing webhook stays pending; ask Stripe to resend it. Never grant from a return URL. Keyless has no exclusive unpaid hold or custom session expiry. Consult SHOP.md for menu limits, signed invoice fulfilment, slot sync and honest pending/full wording.
For a cause, use shop init --giving --currency <organisation currency>: one-time and monthly suggested gifts, no badge or game access. Read giving.receiptText from the organisation; leave it empty until provided. Local /shop/ previews before Stripe. Monthly gifts use the existing keyless Payment Links connection and Stripe handles renewals.
App payment references
Create the booking/ticket/gift intention first. shop.buy('full-tune', 1, undefined, { reference: booking.id }) or shop.checkout({ reference: booking.id }) sends the app's own reference through checkout. Existing boolean returns are unchanged: checkout opened is not paid. The verified durable order.paid event carries data.order, data.reference (null if omitted) and data.app. Reopened Payment Links retain the reference. A reference is buyer-supplied correlation, not authorization: verify the record, app, amount and currency, deduplicate by event/order ID, then fulfil it. Keep personal data out of references. Before Stripe is connected, local prices and unpaid requests can be demonstrated; actual payment and recurring collection remain unverified. Test events must be labelled simulations.
For referrals, use Stripe Connect to split payments or a Stripe-based affiliate service; Homie builds none.
Earning with the keyless default
Read SHOP.md for a round to table 7, shop.book and $20 monthly giving.
Menus let guests choose several quantities on Stripe; signed invoices provide actual
paid lines and the app reference. Slot Payment Links enforce completed-session
capacity. Pending guests do not block others. The owner's AI syncs published slots
with stripe_shop_plan and the complete slot manifest; the app shows Full from verified availability.
Recurring Prices provide monthly preset gifts and memberships. Connect enables the
no-code customer portal login through Stripe's API, without a Dashboard toggle.
Paid invoice periods govern membership; failed renewal never extends it.
The giving starter opens real keyless Checkout and shows a disabled preview before
connection. It never counts an intention as a gift.
For real sandbox acceptance use startKeylessStripeSandbox from the installed
lib/dev-stripe-sandbox.mjs as documented in SHOP.md. Only its explicitly authorised
sandbox CLI profile creates Stripe objects. No Worker API key. Keep returned signing
secret bindings private and temporary. Complete payments with Stripe's published
CLI fixture/API tokens; never enter card digits into a page. The local listener is
for development; production sends signed events directly to the studio's webhook.
The optional keyed fixture remains available for testing explicitly selected session
checkout. Label offline fixtures simulations.
Local Stripe test payments
Use shop connect --local --profile <sandbox-profile> --url http://<Wi-Fi-IP>:8787
and Stripe CLI listen --all-snapshot --forward-to http://localhost:8787/api/shop/hook. The full
recipe is in the installed toolkit’s apps/APPS.md, “Real Stripe test checkout on
this computer”. It uses root .dev.vars, needs no public HTTPS or Cloudflare, and
keeps the existing approved Stripe CLI session. Do not write a stand-in checkout.
Batch setup and reconciliation
Set each menu item's maxQuantity in shop.json when the studio has an order limit; the default is 99. Slot links support group quantities up to published remaining seats. Supply reviewed confirmed seat totals when resyncing: Stripe counts sessions, not people. The database confirms the entire paid group atomically. If a payment differs from the saved deposit/basket or a group no longer fits, inspect the review order and payment.review event, arrange fulfillment or refund, and show the customer the saved notice. Never discard a paid event.
Full shop connect and plugin verification both save public JSON assets; rebuild/deploy those assets before retiring replaced links. Checkout mode is a public Worker variable, not a secret. If a product search misses an existing deterministic ID, perform the planner's GET for that ID before creating it. Preserve Wrangler's diagnostic when setup fails.