Catalog ManagementReal-time Catalog

Catalog Items and Prices

The catalog item types, how Stash prices each item, purchase packs, and web store bonuses in a Real-time Catalog response.

The item types and prices your GetCatalog response carries.

Catalog Items

Each catalog item is exactly one of five types (a protobuf oneof; the JSON key is in parentheses):

TypeUse Case
PurchasableItem (product)Standard products and bundle offers with pricing
PurchasePack (pack)One offer sold at several prices; see Purchase packs
OfferChainItem (chain)Progressive offers that unlock sequentially with CLAIMED / UNLOCKED / LOCKED link statuses
NonPurchasableItem (info)Banners, promotions, and informational displays
FreeItem (free)Free-to-claim gifts with a claim status, an optional refreshAt window, and claim tracking

For the full field-level structure of each type, including pricing, contents, expiration, attributes, and offer chain link details, see the GetCatalog API Reference.

Stash skips an item whose type it does not recognize and renders the rest of the catalog.

Prices

Each PurchasableItem carries one price, set in one of two ways:

priceWhat Stash does
amount (currency, cents)Shows and charges this price as sent. No Stash Studio price is looked up for the item.
priceIdLooks up the price imported in Stash Studio for that ID and the player's platform and region. See Studio price lookup.
OmittedThe same Studio lookup, using the item's productId.

When you send amount:

  • cents is an integer in the currency's minor units: 199 in USD is $1.99, and 300 in JPY is ¥300. The number of decimal places per currency follows your shop's minor-unit standard.
  • cents must be greater than 0. To give an item away, use a FreeItem.
  • Use one currency for all priced items in a response. Stash shows one currency per shop page and leaves out priced items in any other currency.

Minor-unit standard

Each shop has one minor-unit standard. It sets the number of decimal places Stash uses for a currency when it reads cents in your catalog prices and when it writes cents, tax, total and decimalPlaces in RegisterPayment, ConfirmPayment and CancelPayment requests.

StandardDecimal places per currency
Unicode CLDR release 32 (default)The digits values under fractions in the CLDR release 32 supplemental data. A currency not listed there has 2.
Unicode CLDR release 47The digits values under fractions in the CLDR release 47 supplemental data. A currency not listed there has 2.
ISO 4217The minor unit in the ISO 4217 currency list.

The standards disagree for some currencies, for example:

CurrencyCLDR release 32CLDR release 47ISO 4217
COP, IDR, PKR, TZS022
MMK, RSD002
IQD003

A price of 5,000 COP is cents: 5000 under CLDR release 32 and cents: 500000 under the other two.

To use a standard other than the default, contact your Stash engineering partner: they can configure it for your shop.

Studio price lookup

Stash looks up an imported price in the currency that is primary for the player's region, and takes the first match in this order:

  1. The player's region and platform.
  2. The player's region, from a file imported for all platforms (Universal in Studio).
  3. US in USD, the player's platform.
  4. US in USD, Universal.

If none of these exists, the item is left out of the catalog. A missing country therefore shows the US price rather than hiding the item, and a row imported for a country in a currency other than its primary one is never matched. When no platform is resolved, only Universal rows match.

To return a different price per platform or country, choose the price from the request's platform and region:

  • platform comes from the Unity SDK session, then from the platform stored on the player's account (the user.platform your backend sends on Approve custom login), then from the browser's user agent (iPhone, iPad and iPod are iOS; Android is Android). A desktop browser has no platform unless the account has one stored, so send user.platform when you link the player if prices differ by platform.
  • region is the most recent region your backend sent for the player, as user.regionCode on Approve custom login or Generate authenticated URL when the player signs in. When you send none, it is the country of the player's IP address, or US when that is unknown. It is not the country of the player's app store account.

Purchase packs

Use a pack when one offer is sold at several prices and the player buys one of them, for example the same reward at three price points. Each price point is one entry in the pack's items (a PurchasePackItem).

Declare the values the whole offer shares once, on the pack:

FieldMeaning
idRequired. Your stable identifier for the offer, unique within the response. A pack without an id is left out of the catalog.
itemsRequired. The offer's price points, in display order. Each one has its own productId and price, and optional contents.
maxPurchasableOptional. The player's remaining purchases of this offer. Stash applies it to every item in the pack, together with each item's own maxPurchasable.
expirationOptional. After this time no item in the pack is available for purchase, except a purchase already in checkout within a checkout grace period agreed for your shop.
refreshAtOptional. When the offer's allowance next resets, for display such as a countdown. Purchase checks never use it.

A PurchasePackItem has no expiration or refreshAt of its own, so the pack's values govern every item in it. It can have its own maxPurchasable: the player's remaining purchases of that price point. The lower of the item's and the pack's maxPurchasable applies to the item.

  • Buy the whole offer. To sell one item that buys everything in the offer in a single transaction, give it type: PURCHASE_PACK_ITEM_TYPE_WHOLE_PURCHASE_PACK and list the combined rewards in its contents. Use this type on at most one item per pack.
  • Remaining count. Compute maxPurchasable per player and send it fresh in every response. Stash keeps no purchase count of its own: it checks each purchase against the value from your catalog.
  • Purchase calls. For an item bought from a pack, RegisterPayment, ConfirmPayment and CancelPayment carry the pack's id as purchasePackId on that item. productId still identifies the price point.
  • Unavailable offers. To withdraw an offer or a price point, leave it out of the response. To show a price point as sold out instead, keep it and send maxPurchasable: 0 on it: the player sees it but cannot buy it, and Stash refuses checkout for it.

See PurchasePack in the API reference for every field.

Web Store Bonuses

Give players extra content for buying on the web:

FieldWhat it does
bonusItems on a product, or on an item in a packThe bonuses that come with the item, each a type, an id and an amount. On purchase, Stash sends them to your ConfirmPayment.
contents[].bonusQuantityA bonus amount shown next to a content item's quantity, for example +25. It is an amount, not a percentage. Display only: Stash does not send it to ConfirmPayment, and when the item also sets bonusItems, those decide what is granted.
contents[].type: CONTENT_ITEM_TYPE_WEB_STORE_BONUSShows a content item as a web store bonus.

Grant bonuses from ConfirmPayment. The bonusItems amounts on the ConfirmPayment request are the amounts to grant for the bonus IDs they name. Do not also grant your offer's own bonus for an ID listed there, or the player receives it twice.

How is this guide?

On this page