$npx -y skills add rshankras/claude-code-apple-skills --skill iap-finalizerTake a one-time in-app purchase from MISSING_METADATA to READY_TO_SUBMIT in App Store Connect — set its price schedule and localized display name/description (and optional review screenshot) via the ASC REST API. Use at Phase 6 (Pre-Release), after the IAP is built in-app (Phase
| 1 | # IAP Finalizer |
| 2 | |
| 3 | Finish a **one-time** in-app purchase on the store side: **price + localization**, the two fields the `asc-metadata` MCP can't set (it exposes only reference name / review note / family-sharing). Moves an IAP from `MISSING_METADATA` → `READY_TO_SUBMIT` so it can ship with the build. |
| 4 | |
| 5 | > **This finalizes; it does not define.** The product id, tier, and price *decision* come from Phase 4 (Monetization / StoreKit). The ASC IAP record must already exist (created via the MCP `create_iap` or the ASC UI). This skill sets the metadata on that existing record. |
| 6 | |
| 7 | ## Where it fits (read the seams) |
| 8 | |
| 9 | - **Not Phase 4.** Phase 4 builds the IAP *into the app* (StoreKit 2, paywall) and *decides* the price. This is Phase 6 store-metadata finalization. |
| 10 | - **Price = one source of truth.** Do **not** re-ask the price. **Read it** from the monetization decision in `.planning/` (e.g. `MONETIZATION.md` / `PLAN.md`); only *confirm* it. Re-eliciting risks drift from the paywall/StoreKit price. |
| 11 | - **Not `promoted-iap`.** That generator displays promoted IAPs in-app; this sets ASC price/localization. Different jobs. |
| 12 | - **One-time IAPs only.** Subscriptions are a separate ASC flow (groups/offers) — out of scope here. |
| 13 | |
| 14 | ## Prerequisites |
| 15 | |
| 16 | - `_shared/asc-api/` set up (see its README — key + `~/.appstoreconnect/`). |
| 17 | - Set `ASC="python3 <path to asc.py>"`. `asc.py` lives at `_shared/asc-api/asc.py` under the same `skills/` root as this skill — resolve it **relative to this SKILL.md file's location** (`../../_shared/asc-api/asc.py` from this skill's directory), never relative to the project cwd (skills run with cwd = the user's project). Known install locations: |
| 18 | - SwiftShip symlink install: `~/.claude/swiftship-skills/_shared/asc-api/asc.py` |
| 19 | - Copied install: `.claude/skills/_shared/asc-api/asc.py` (project) or `~/.claude/skills/_shared/asc-api/asc.py` (global) |
| 20 | - Plugin install: resolve from this file's location — the `_shared/` tree ships with the plugin. |
| 21 | - The IAP already exists in ASC. Get its id with the MCP: `list_iap` → pick the product. |
| 22 | - Price decided in Phase 4. Read it from `.planning/` and confirm — don't invent it. |
| 23 | |
| 24 | ## Flow — dry-run → confirm → apply |
| 25 | |
| 26 | 1. **Confirm state.** MCP `get_iap` → current state + `productId`. Read the target price + Display Name (≤30) + Description (≤45) from `.planning/`; confirm with the user via `AskUserQuestion` if anything is missing. |
| 27 | 2. **Find the price point.** |
| 28 | ``` |
| 29 | $ASC GET "/v1/inAppPurchases/<IAP_ID>/pricePoints?filter[territory]=USA" |
| 30 | ``` |
| 31 | Pick the `inAppPurchasePricePoint` id whose `customerPrice` matches the target tier (e.g. 6.99). |
| 32 | 3. **Set the price** (one-time IAPs use *price schedules*): |
| 33 | ``` |
| 34 | $ASC POST /v1/inAppPurchasePriceSchedules @price.json # dry-run: review the body |
| 35 | $ASC POST /v1/inAppPurchasePriceSchedules @price.json --apply # after you confirm |
| 36 | ``` |
| 37 | Body: `data` relationships `inAppPurchase`→{IAP_ID}, `baseTerritory`→USA, `manualPrices`→[new `inAppPurchasePrices`]; `included` a new `inAppPurchasePrices` referencing the price point with `startDate: null` (="now"). |
| 38 | 4. **Set the localization:** |
| 39 | ``` |
| 40 | $ASC POST /v1/inAppPurchaseLocalizations '{"data":{"type":"inAppPurchaseLocalizations","attributes":{"locale":"en-US","name":"<=30","description":"<=45"},"relationships":{"inAppPurchase":{"data":{"type":"inAppPurchases","id":"<IAP_ID>"}}}}}' --apply |
| 41 | ``` |
| 42 | PATCH the existing localization id instead if one already exists. |
| 43 | 5. **Review screenshot (optional):** `POST /v1/inAppPurchaseAppStoreReviewScreenshots` — the 3-step ASC upload (reserve → upload bytes → commit). |
| 44 | 6. **Verify:** MCP `get_iap` → state no longer `MISSING_METADATA`. |
| 45 | |
| 46 | ## Done |
| 47 | |
| 48 | - IAP priced + localized in ASC; state advanced; ready to submit with the build. |
| 49 | |
| 50 | ## Caveats |
| 51 | |
| 52 | - **Verify each endpoint/field against the current [ASC API reference](https://developer.apple.com/documentation/appstoreconnectapi) before `--apply`** (captured 2026-07). |
| 53 | - Every write is **dry-run first** — show the body, confirm, then `--apply`. Never `--apply` a price the user hasn't seen. |
| 54 | - One-time IAPs only. Subscriptions → the MCP `create_subscription*` tools + a separate flow. |