---
name: sell-items-in-your-app
description: Run a Chain Daddy creator store - open it, add items priced in a token or in dollars, create orders from your server, have the buyer's wallet pay, wait for confirmed, deliver, read and spend what a wallet owns, drops and rewards, webhooks, refunds - and test the whole loop on a no-money test store first. Use when someone wants to sell credits, skins, passes or badges for their token, give items away, or unlock perks for holders.
license: MIT
---

# Sell items in your app

A store sells **items** for one token. The buyer's wallet pays the token's payout wallet directly, in one
on-chain transaction; the checkout contract has no owner and holds no balance. Once the payment confirms,
Chain Daddy grants the item and signs a receipt. Items live off chain: they can't be transferred or cashed out.

## Tools

| Step | MCP (local server) | CLI (`npm i -g @chaindaddy/cli@beta`) | HTTP (`https://api.chaindaddy.io`) |
|---|---|---|---|
| Open the store | `iap_open_store` | `chaindaddy iap store open --chain base --crown 8` | `POST /api/v2/iap/stores` |
| Test store | `iap_open_test_store` | `chaindaddy iap store test <storeId>` | `POST /api/v2/iap/stores/{id}/test-store` |
| Items | `iap_create_sku`, `iap_update_sku`, `iap_list_skus` | `chaindaddy iap sku create/update/archive` | `/api/v2/iap/stores/{id}/skus` |
| Server key | `iap_create_store_key` | `chaindaddy iap key create` | `POST /api/v2/iap/stores/{id}/keys` |
| Orders | `iap_create_order`, `iap_get_order`, `iap_test_pay` | `chaindaddy iap order create/show/test-pay` | `POST /api/v2/iap/orders` |
| What a wallet owns | `iap_get_entitlements`, `iap_consume`, `iap_grant`, `iap_revoke` | `chaindaddy iap entitlements/consume/grant/revoke` | `/api/v2/iap/stores/{id}/entitlements` |
| Rewards | `iap_create_drop`, `iap_set_drop_status`, `iap_award` | `chaindaddy iap drop …` | `/api/v2/iap/stores/{id}/drops` |
| Events | `iap_list_events`, `iap_set_webhook`, `iap_test_webhook` | `chaindaddy iap events/webhook …` | `/api/v2/iap/stores/{id}/events` |

Credentials: account calls (open a store, manage items and keys) use the human's `chaindaddy login` on this
computer (or a `cd_live_…` key in `CHAINDADDY_API_KEY` on a server). The game server uses a **store key** `cd_iap_…` from `CHAINDADDY_IAP_KEY`: one store,
only its scopes, sent alone as `Authorization: Bearer`. The plan gate: Developer, Partner or Enterprise, or
Developer Beta Program membership; otherwise every call answers `403 IAP_PLAN_REQUIRED` - stop and tell the human.

## Steps

1. **Open the store** with the token page's chain key and crown id. Opening again returns the same store.
   Report `id`, `payoutAddress` (starts as the owner's wallet), and `feeBps` (0 on a token launched in a Chain
   Daddy launchpad pool, 250 otherwise).
2. **Add items.** New items are `draft`; set `status: "active"` to list one. Create reward-only items (no
   price, like `credits`) before the bundles that `grants` them. Example credit pack:
   ```json
   { "sku": "credits.100", "type": "consumable", "name": "100 credits",
     "priceUsdMicros": 5000000, "minPrice": "1000000000000000000000",
     "grants": [{ "sku": "credits", "quantity": 100 }], "status": "active" }
   ```
   `sku`: lowercase letters, digits, `.`, `_`, `-`, up to 64. A fixed `price` is a **base-unit string**
   (5,000 tokens at 18 decimals = `"5000000000000000000000"`). An item has `price` or `priceUsdMicros`, not
   both. `currency` is `token` (default), `native` or `usdc`. Confirm human-readable amounts with the human.
3. **Make a store key** with only the scopes the server needs, typically `orders:read orders:write
   entitlements:read entitlements:write`. The secret is shown **once**: tell the human to store it as
   `CHAINDADDY_IAP_KEY` in their secrets manager. Never print it back.
4. **Test it** (next section) before any live sale.
5. **Server creates the order** for the user's wallet, with `appAccountToken` = your own user id and an
   `Idempotency-Key` per purchase attempt (your cart id). It locks the price for about 20 minutes (5 for a
   dollar price paid in the token or coin).
6. **Client pays** from the order's `payment` block, as given. EVM: send `calls.approve` (when present), wait
   for it, then `calls.pay`; or sign `permitTypedData` and call `payWithPermit`. Solana: the wallet signs and
   sends `payment.transaction`. The client never computes an amount.
7. **Wait for `confirmed`.** Poll the order, or listen for `iap.order.confirmed`. Optionally
   `POST /api/v2/iap/orders/{id}/submit` with `{"txHash": …}` to check at once.
8. **Deliver.** Verify the receipt (`verify-receipts-and-sessions`), then grant; or read the item ledger and
   spend with `consume` (with an idempotency key).

On a token page, a Crown App skips steps 5-7: it declares `iap:purchase` and sends
`onAction({ type: 'iap-purchase', sku, quantity })`; the page runs the sale (see `build-a-crown-app`).

A Crown App can also spend the viewer's own consumable with no server: it declares `iap:consume` and sends
`onAction({ type: 'iap-consume', sku, quantity, key })` from the viewer's tap. It works only for an item whose
`maxAppSpend` the store's owner set (the most an app may spend per request; null means no app can), and it is
never proof: a holder can make that spend without the app. Keep `maxAppSpend` null and spend with `consume`
on your server for anything competitive or redeemed for a prize.

## Test store first

Available where `GET /api/v2/iap/config` answers `"testMode": true`.

1. `iap_open_test_store` with the **live** store id. It copies the live catalog once; after that they are separate.
2. `iap_create_store_key` on the **test** store id. Its secret starts `cd_iap_test_`.
3. Create an order with the test key; its `payment` is `{"kind":"test"}`.
4. `iap_test_pay` (`POST /api/v2/iap/orders/{id}/test-pay`): the order confirms through the real path. No
   chain, no money, no fee.
5. The receipt and webhooks carry `"livemode": false`. Give the test store its own webhook.
6. Go live by switching to the live key. Token prizes and claim links don't run on a test store.

## Rewards and webhooks

- One-off gift: `iap_grant`. A drop: `iap_create_drop` with `mode` `claim` (a link), `push` (a wallet list),
  or `award` (your server awards a wallet with `iap_award`, scope `rewards:award`). Drops start as drafts.
- Webhook: `iap_set_webhook` with a public `https://` URL; the `whsec_…` secret is shown once. Check
  `X-Webhook-Signature` (hex HMAC-SHA256 of the raw body) before parsing. Answer 2xx within 5 seconds.
- Reconcile from the event feed (`iap_list_events` with `after` = your last event id). The webhook makes you
  fast; the feed makes you complete.
- Refunds: the creator sends the money back from their wallet, then records it with `iap_refund_order`
  (`refund_tx_hash`; CLI `--tx`), which revokes the items and receipt. Chain Daddy cannot move a buyer's funds.

## Mistakes to avoid

- Granting on `paid`, or before verifying the receipt's signature, `storeId` and `livemode`.
- A store key in the game client. Only the server holds it.
- Retrying a write with a new idempotency key: it can sell or grant twice. Reuse the same key.
- Editing the `payment` block, or paying in another currency: the payment is recorded as `order.mismatch`
  and grants nothing.
- Treating `503 PRICE_UNAVAILABLE` as a failure: the dollar price can't be converted right now; retry in a minute.
- Items with a random outcome (loot boxes): not allowed. Never call items money or a balance.
- Sending a payment for the human. They pay from their own wallet.

## Docs

- https://docs.chaindaddy.io/developer/store-and-rewards (every field, rule, event and limit)
- https://docs.chaindaddy.io/developer/test-your-integration
- OpenAPI: https://docs.chaindaddy.io/openapi.yaml, tag **Creator Store**
