Test store
The test store is a channel that plays the role of a store. Its purchases go through the same recording, events, signed webhooks, GET /v1/customers and GET /v1/events as real stores. Channel id: test_store, platform: test.
Everything it produces has environment: "test", platform: "test" and data.store: "test_store". Your backend should never treat it as revenue.
Turn it on
Section titled “Turn it on”The test store is off by default. While it is off, every test store call returns 403 test_store_disabled and automatic renewals pause.
npx @kasskeep/cli test-store enable --id app_xxxnpx @kasskeep/cli test-store config --id app_xxx --period-seconds 60 --renewal-success-rate 0.8 --products "demo.*"| Setting | Default | Meaning |
|---|---|---|
subscription_period_seconds |
300 | Length of one test subscription period (10 seconds to 366 days) |
renewal_success_rate |
1 | Probability that an automatic renewal succeeds (0 to 1) |
grace_period_seconds |
120 | Grace period after a failed renewal; 0 goes straight to billing retry |
billing_retry_seconds |
300 | Billing retry after the grace period; then the subscription expires |
products |
any | Allowed product ids; a trailing * matches a prefix. Others fail with 400 product_not_allowed |
prices |
0.99 USD | {"id or prefix*": {price_micros, currency, title?}}; exact match first, then longest prefix |
Turning it on for an app that already has production transactions or store credentials returns a test_store_on_live_app warning. It is allowed: one app can serve real and test traffic, separated by environment.
From the client with the public key, or through the SDK with testStore: true:
POST /v1/test-store/purchasesX-Kasskeep-Key: pk_...
{"app_user_id": "6f1c...", "product_id": "demo.gems_100", "type": "inapp", "quantity": 1, "outcome": "success"}type:inapporsubs.quantity1 to 100, subscriptions only 1.outcome:success(default),cancelled,pendingorfailed. Onlysuccessrecords a purchase.pendingkeeps a pending purchase that you can approve or decline later.- Optional
price_microsandcurrencyoverride the price. - Transaction ids look like
test_<24 hex>. A second active subscription to the same product returns409 already_subscribed.
GET /v1/test-store/products?ids=a,b&type=inapp returns allowed products with prices, which the SDK’s getProducts uses.
Simulate store events
Section titled “Simulate store events”npx @kasskeep/cli test-store simulate --id app_xxx --scenario renew_fail --user <app_user_id>npx @kasskeep/cli test-store simulate --id app_xxx --scenario upgrade --user <app_user_id> --to-product-id pro_yearly| Scenario | Applies to | Event |
|---|---|---|
renew |
Active subscription with auto-renew on | subscription.renewed |
renew_fail |
Active subscription | subscription.billing_issue |
grace_expire |
In grace period | subscription.grace_period_expired |
recover |
In grace period or billing retry | subscription.renewed with data.recovered |
cancel / uncancel |
Auto-renew on / off | subscription.canceled / subscription.uncanceled |
expire |
Not expired | subscription.expired |
upgrade |
Active, needs to_product_id |
subscription.product_changed, immediate |
downgrade |
Active, needs to_product_id |
subscription.product_changed, at next renewal |
refund |
Purchase or subscription | refund |
refund_reverse |
Refunded | refund.reversed |
revoke |
Purchase or subscription | revoked (family sharing) |
pending_approve |
Pending | purchase or subscription.started |
pending_decline |
Pending | None |
A scenario that does not fit the current state returns 409 scenario_not_applicable.
Fast-forward time
Section titled “Fast-forward time”npx @kasskeep/cli test-store advance --id app_xxx --user <app_user_id> [--outcome failure]This moves the end of the current period, grace period or retry period to now and immediately runs what is due: renew, fail, end grace, or expire. The system clock is not touched.
Without any action, a job runs every 10 seconds and renews or expires due test subscriptions according to the settings.