--- name: ppp-pricing description: Propose purchasing power parity (PPP) prices for a mobile app's or game's in-app products in every App Store territory and Google Play region with `storebound prices ppp` - World Bank ICP PPP factors (bundled, CC BY 4.0), The Economist Big Mac index (downloaded on the owner's machine) or the owner's own factor CSV, applied to the store's own US-equivalent price (VAT included) only in lower-income economies and never upward by default, snapped to real App Store price points and clean local Play prices, written as a price sheet that `storebound prices import` applies after its own diff and confirmation. Use when the user asks for regional pricing, PPP or purchasing power pricing, cheaper prices in emerging markets, localized price tiers, or "price by country". --- # PPP pricing (proposal sheet, both stores) CLI: `storebound` (or `bunx storebound`, or `bun /cli/index.ts`). This command only proposes. It writes a CSV and prints a diff; nothing reaches a store until the owner runs `storebound prices import` and confirms there. ## What it computes Like with like: store prices include VAT and follow today's exchange rate, the US price excludes sales tax, and the PPP price level compares net prices. So the proposal starts from the store's own conversion of the US price and scales it by the price level. Per product, store and region: - `level = PPP factor / exchange rate` (same year): the region's price level against the US. - `par` = the store's US-equivalent price there: Play `convertRegionPrices`, Apple the equalizations of the USA price point (both VAT included, today's rate). Offline: `base x rate x (1 + VAT)` rounded up to a clean price (standard VAT rates in `cli/lib/ppp.ts`; the dataset's rate stands in for today's). - `share`: - 1 (par) for the US, World Bank high-income economies (unless `--include-high-income`), mainland China, and every economy whose price level is above `--max-level` (default 0.5: only economies at or below half the US price level get a PPP price); - else `clamp(level, --min-ratio 0.3, --max-ratio 1)`: never below 30 % and never above the US-equivalent price. - `price = snap(par x share)`; with `--only-decreases` (the default; `--no-only-decreases` turns it off) never above the store's current price. - Snapping: App Store online, the nearest real price point of that product in that territory (Apple takes nothing else); Google Play and the App Store offline, a clean local ending (x.99, 0.x9, whole numbers ending in 9; round numbers for JPY, KRW, VND, IDR, HUF, CLP, TWD and every value over 1000). - China: Google Play is not available in mainland China (no Play rows); on the App Store CN keeps the store's price. - Top markets: a top-10 revenue market (from the sales reports in `.storebound/reports/`, `storebound reports apple-sales` / `play-sales`, else US JP CN KR GB DE FR CA AU TW) that would drop more than 20 % is warned about. - Only regions whose price changes go into the sheet (`rows`); regions that keep their price are listed as `kept`, so `prices import` leaves the store's own price there. Example (bundled World Bank data, USD 4.99, defaults): Germany EUR 5.99 (high-income, par; the old method gave EUR 3.59), UK, France, Japan at par; India and Turkey drop (price levels 0.23 and 0.41); Mexico stays (level above 0.5). ## Data sources (the owner picks one) - `worldbank` (default, bundled `cli/data/ppp-worldbank.json`): World Bank PA.NUS.PPP (PPP conversion factor, GDP) and PA.NUS.FCRF (official exchange rate), latest year per country, CC BY 4.0. Euro regions use the euro area rate. Countries without recent data (Taiwan, Venezuela, a few small territories) get no row. - `bigmac`: The Economist Big Mac index (implied PPP = local Big Mac price / US price). Not bundled (its licence leaves third-party data out). Download it on the owner's machine first: `storebound prices ppp --update-bigmac` (writes `.storebound/ppp-bigmac.json`, gitignored). - `file`: the owner's own CSV, `region,factor,currency,fx` (`fx` optional: then the bundled rate when the currency matches). `--file ` or `prices.ppp.file` in the config. Config (optional): `prices: { ppp: { source, file, minRatio, maxRatio, maxLevel, onlyDecreases, includeHighIncome, out } }` (`floor` / `ceiling` are the old names of `minRatio` / `maxRatio`). ## Steps 1. `storebound check`. Products must be in `products[]` with a `usd` base. For real App Store price points the products must exist on App Store Connect (`storebound apple iap`); otherwise those rows are offline proposals. 2. Dry run and show it to the owner: `storebound prices ppp --dry` (all products; `--only id1,id2`, `--store apple|play`, `--base 4.99` to try another US price, `--min-ratio 0.4`, `--max-level 0.6`, `--include-high-income`, `--source bigmac|file`, `--offline` to skip every store read). Change a default only when the owner asks for it. It prints the sheet, the diff against the stores' current prices (offline: every row "new") and the regions without a proposal. 3. The owner decides: base price, the rules above if not the defaults, and which regions to keep. Name every top-market warning to the owner. Then write the sheet: `storebound prices ppp` (default `store/prices-ppp.csv`, `--out` to change it). 4. The owner reviews and edits the CSV (delete rows to keep a store's own conversion, round to a house price ladder). 5. Apply through the normal price sheet flow: `storebound prices import --csv store/prices-ppp.csv --dry`, then `--yes --allow-price-changes` after the owner says yes to that diff (see store-iap-setup; `--yes` alone never changes an existing price). Import checks Apple price points (the error lists the neighbours) and Play's region currencies. ## Read before applying - PPP is a proposal, not a rule. Cheaper prices can cannibalize, invite VPN arbitrage on web checkouts and change revenue; the owner decides region by region. - Taxes: App Store and Play customer prices are tax-inclusive in most regions (EU VAT, UK VAT, many others), while the US base is tax-exclusive. That is why par is the store's own (tax-inclusive) conversion and the PPP level only scales it; never compare a PPP price in dollars with a local store price directly. - Offline values: Apple only accepts its own price points and its own storefront currency (USD or EUR in many smaller storefronts). An offline sheet is a starting point; `prices import --dry` shows what does not fit. - An App Store price increase cannot be undone once it starts. - Data age: `--json` and the log show the dataset's year and retrieval date. Maintainers refresh the bundled file with `bun tools/update-ppp.ts` (`--check` prints its age). ## Output `--json` prints only JSON on stdout (logs on stderr): `{ source: {name, year, retrieved, url, license}, base: {product: usd}, floor, ceiling, options, online: {apple, play}, rows: [{product, store, region, currency, price, factor, fx, level, par, parFrom, share, reason, current, usdEquivalent, snappedFrom, snap}], kept: [same shape], skipped: [{product, store, region, why}], diff, topMarkets: {markets, from, drops}, out, written }`. Use `--json --dry` for a read-only run. ## Out of scope - Writing prices to a store (that is `storebound prices import`, after the owner's yes). - Subscriptions (their prices go through `storebound apple subscriptions` / `play subscriptions` with a prices CSV). - Choosing the base price or deciding which markets to discount: the owner's call.