Skip to content

Test Your Integration ​

Your store has a test store beside it: the same token page, the same token and payout, and a copy of your catalog. Orders on it are paid with one API call instead of a wallet. Nothing is sent on chain, no money moves and no fee is taken. Everything else is the store API you already use: the same orders, items, receipts, events and webhooks, marked as test.

Build your server against the test store, run a purchase end to end as often as you like, then go live by switching the key.

Test mode is available where GET https://api.chaindaddy.io/api/v2/iap/config answers "testMode": true.

1. Create the test store ​

You need your account credentials and manager role on the token page, as for any store change (Authentication). Pass your live store's id:

bash
chaindaddy iap store test 3f0c… --json
#   → {"ok":true,"store":{"id":"7a21…","mode":"test","name":"MYTOKEN (test)","feeBps":0,…}}
bash
curl -X POST https://api.chaindaddy.io/api/v2/iap/stores/3f0c…/test-store \
  -H "Authorization: Bearer $CHAINDADDY_API_KEY"

MCP: iap_open_test_store. The first call creates the test store (201) and every call after that returns it (200). GET on the same path returns it, or 404 before it exists.

The test store starts with a copy of every item in your live catalog as it is at that moment, drafts included, with nothing sold. From then on the two catalogs are separate: change a test item and the live one is untouched, and a new live item does not appear in the test store until you add it there (chaindaddy iap sku create 7a21… --file …).

2. Create a test key ​

Keys are made on the test store exactly as on your live store (Store keys). A test store's keys start with cd_iap_test_:

bash
chaindaddy iap key create 7a21… --name app-server-test \
  --scope orders:read orders:write entitlements:read entitlements:write events:read
#   prints the cd_iap_test_… secret ONCE
export CHAINDADDY_IAP_KEY=cd_iap_test_…

A test key works only on its test store, and a live key only on its live store.

3. Create an order for a wallet ​

The same call as a live purchase, with the test key and the test store's id:

bash
curl -X POST https://api.chaindaddy.io/api/v2/iap/orders \
  -H "Authorization: Bearer $CHAINDADDY_IAP_KEY" \
  -H "Idempotency-Key: test-cart-1" \
  -H "Content-Type: application/json" \
  -d '{"storeId":"7a21…","sku":"credits.100","quantity":1,"wallet":"0xabc…","appAccountToken":"user-42"}'

The order locks its price and deadline as a live one does, with no fee, and its payment asks for nothing to be signed:

json
{
  "id": "0x51c0…",
  "storeId": "7a21…",
  "sku": "credits.100",
  "wallet": "0xabc…",
  "amount": "50000000000000000000",
  "feeAmount": "0",
  "feeBps": 0,
  "status": "pending",
  "payment": { "kind": "test" }
}

4. Pay it ​

bash
curl -X POST https://api.chaindaddy.io/api/v2/iap/orders/0x51c0…/test-pay \
  -H "Authorization: Bearer $CHAINDADDY_IAP_KEY"
#   → {"status":"confirmed","txHash":"test:4f0e…","receipt":"eyJhbGciOiJFZERTQSIs…",…}

CLI: chaindaddy iap orders test-pay 0x51c0…. MCP: iap_test_pay.

The order is confirmed in the response. It went through the same confirmation a real payment does: the items are granted, the receipt is signed, supply and per-wallet limits count it, and the order.confirmed event is written. Where a transaction hash would be, it carries test: and an id. Calling it again returns the same confirmed order and changes nothing.

It needs a key with orders:write, or your own session as a manager of the token page. It answers 409 LIVE_ORDER for an order on a live store, which is only ever paid on chain, and 409 ORDER_EXPIRED once the order's deadline has passed; create a new order then. POST …/submit answers 409 TEST_ORDER for a test order.

5. Check the receipt ​

The receipt is signed with the same key as live receipts and verifies the same way (Verifying receipts). Its payload names the test store and says it is a test:

json
{
  "orderId": "0x51c0…",
  "storeId": "7a21…",
  "feeAmount": "0",
  "payment": { "txHash": "test:4f0e…", "logIndex": 0, "blockNumber": 0, "payer": "0xabc…" },
  "livemode": false
}

Live receipts carry "livemode": true. In production, check both storeId (your live store) and livemode before you hand anything over.

6. Read and spend the items ​

bash
chaindaddy iap entitlements 7a21… --wallet 0xabc… --json
chaindaddy iap consume 7a21… --wallet 0xabc… --sku credits --qty 1 --idempotency-key job-1

These are the same calls as live (Read and spend items). Grants, revokes, refunds, drops you award from your server, the event feed and the store's stats all work on the test store and stay in it.

7. Receive the webhooks ​

Give the test store its own webhook, pointed at your test endpoint:

bash
chaindaddy iap webhook set 7a21… --url https://staging.example.com/hooks/chaindaddy

Every delivery carries livemode:

json
{
  "id": "iap_evt_2291",
  "type": "iap.order.confirmed",
  "data": {
    "eventId": 2291,
    "type": "order.confirmed",
    "storeId": "7a21…",
    "chainId": "eip155:8453",
    "crownId": 8,
    "livemode": false,
    "wallet": "0xabc…",
    "orderId": "0x51c0…",
    "payload": { "sku": "credits.100", "status": "confirmed", "txHash": "test:4f0e…", "receipt": "eyJ…", "appAccountToken": "user-42" }
  }
}

A test store's events go to the test store's webhook and nowhere else: not to your live store's webhook, and not to the iap.* webhooks on your account. Your live store's events never reach the test store's webhook. iap.entitlement.consumed and the rest arrive the same way.

8. Go live ​

Switch your server to the live store's key. Nothing else in your code changes if you read the store id from the key instead of configuring it: a store key's GET /api/v2/iap/stores returns its own store.

bash
curl https://api.chaindaddy.io/api/v2/iap/stores -H "Authorization: Bearer $CHAINDADDY_IAP_KEY"
#   → {"entries":[{"id":"3f0c…","mode":"live",…}]}

The test store stays where it is for your next change.

What is different in test mode ​

Test storeLive store
Paying an orderPOST /api/v2/iap/orders/{id}/test-payThe buyer's wallet, on chain
payment on a pending order{"kind":"test"}The EVM or Solana payment
FeeNone (feeBps: 0)The fee
Receipts and webhookslivemode: falselivemode: true
Notifications to you and the buyerNoneAs listed
Visible on your token page, in the Item showcase and to buyersNo. Only people who manage the token page can read itYes
Claim links of its dropsDo not openOpen
Token prizesRefused (409 LIVE_STORE_ONLY): they pay real tokensYes
Messages to customersNone: a test store has no customersYes

An order that is never paid expires at its deadline, as a live one does. An item behind a hold rule still checks the wallet's real balance, and a dollar-priced item converts at the live rate.