Coin packages
A coin package is one SKU in the coin store: “Get 500 coins + 100 bonus for $4.99”. Each package can be enabled on a subset of the 5 payment rails. Store rails (Apple/Google) need matching SKUs configured in App Store Connect / Play Console.
Monetization → Coin packages (/monetization/coin-packages).
Fields
Section titled “Fields”| Field | Type | Notes |
|---|---|---|
| SKU | string, unique | Immutable. Match this on the store side. Convention: tc_coins_500_bonus100. |
| Coins | int | Base coins. |
| Bonus coins | int, ≥0 | Marketing lever; shown as “+bonus” in the store UI. |
| Price (cents) | int | USD cents. Provider conversions handle FX at checkout. |
| Currency | string, ISO-3 | Usually USD; store providers do local conversion. |
| Rails | string[] | Any subset of apple, google, card_web, dlocal_*, pawapay_*, vas_*. |
| Status | active / paused | Paused = hidden from mobile but still resolvable for existing pending payments. |
Enabling a package on a rail
Section titled “Enabling a package on a rail”Store rails (Apple/Google) require an out-of-band SKU to exist on the store side before you enable the rail here. If the SKU doesn’t exist:
- Apple: RevenueCat will refuse to complete the purchase.
- Google: Play returns
SKU_NOT_FOUND.
The admin does not validate the store side: it’s your responsibility to make sure both sides line up.
Common package shapes
Section titled “Common package shapes”| Coins | Bonus | Price | Notes |
|---|---|---|---|
| 100 | 0 | $0.99 | Entry-level impulse buy. |
| 500 | 50 | $4.99 | The workhorse. |
| 1200 | 200 | $9.99 | Best value on the shelf. |
| 3000 | 700 | $24.99 | High-spender tier. |
| 6500 | 2000 | $49.99 | Whale bait. |
Bonus coins are minted into wallet_bonus; base coins into wallet_paid.
Ledger accounting on purchase
Section titled “Ledger accounting on purchase”Store IAP → RevenueCat webhook → API:
event_id = <UUID>- source_iap_apple : -($4.99 in coins) (metadata: purchase_id, transaction_id)- wallet_paid : +500 coins- wallet_bonus : +50 coins (offset by source_bonus)- source_bonus : -50 coinsZero-sum across the event_id (except FX/fees, which we track separately).
Ordering in the store UI
Section titled “Ordering in the store UI”Packages are displayed sorted by coins ASC on the mobile app. There’s no manual sort field in v2: if you want a specific order, adjust the coin/price to fit.
Pausing a SKU
Section titled “Pausing a SKU”Set status='paused'. New store fetches will not include it. In-flight purchases (webhook already fired) still complete.
Gotchas
Section titled “Gotchas”- Deleting a package: the API refuses if any Purchase row points at that package. Pause instead.
- Editing coins or price on a live SKU: allowed but audited. The mobile app caches store info for a few minutes; changes take that long to propagate.
- Rail conflicts: a package enabled on
applebut notgooglewill only appear in the iOS store screen. Nothing enforces symmetry; that’s an editorial choice.
API calls
Section titled “API calls”| Action | Endpoint |
|---|---|
| List | GET /v1/admin/coin-packages |
| Detail | GET /v1/admin/coin-packages/:id |
| Create | POST /v1/admin/coin-packages |
| Update | PUT /v1/admin/coin-packages/:id |
| Delete | DELETE /v1/admin/coin-packages/:id |
Full details in API: Monetization.