DEV Community

孫昊
孫昊

Posted on

Auto-Pricing 3 iOS IAPs via Browser Automation: The 7-Step CDP Flow

TL;DR: ASC has no public API for setting IAP price tiers. CDP + Playwright + JS clicks (not Playwright clicks) gets it done in ~30s per IAP. 7-step flow with exact selectors below.


The problem

Apple's App Store Connect API lets you create IAPs, manage availability, and submit for review — but does not let you set the price tier. The tier is gatekept behind their web UI.

For a single IAP this is fine. For 3+ apps with similar tiers, manual setup means clicking through ~15 modal steps × 3 apps × 7 steps each = 100+ clicks.

I'd rather automate it once.

What blocks Playwright

page.locator('button:has-text("添加定价")').click()
# TimeoutError: subtree intercepts pointer events
Enter fullscreen mode Exit fullscreen mode

ASC's React SPA wraps every interactive element in a <dialog> overlay during transitions. Playwright's intelligent click waits for "stable" — which never happens because animations keep firing. After 30s, timeout.

The fix: JS click via page.evaluate

Bypass Playwright entirely and use the DOM API directly:

page.evaluate("""
    () => {
        const btn = Array.from(document.querySelectorAll('button'))
            .find(b => (b.innerText||'').trim() === '添加定价' && b.offsetParent);
        if (!btn) return 'NOT FOUND';
        btn.scrollIntoView({block: 'center'});
        btn.click();
        return 'OK';
    }
""")
Enter fullscreen mode Exit fullscreen mode

Native Element.click() doesn't go through Playwright's stability checks. The React event handler fires immediately.

The 7-step flow for ASC IAP pricing

For each IAP, after navigating to /apps/<app_id>/distribution/iaps/<iap_id>:

  1. 添加定价 (Add Pricing) — opens region selection modal
  2. 选取 (in dialog) — opens tier dropdown
  3. $1.99 (in dialog, exact text match) — selects tier
  4. 下一步 (in dialog) — review per-region prices auto-converted from USD base
  5. 下一步 (in dialog) — go to final confirm
  6. 确认 (in dialog) — commit
  7. 保存 (page header button) — save the IAP record

After step 7, verify:

body = page.evaluate("document.body.innerText")
section = body[body.index("价格时间表"):body.index("价格时间表") + 800]
saved = "添加定价" not in section
Enter fullscreen mode Exit fullscreen mode

If 添加定价 is gone from the section, pricing is locked in.

Critical timing

ASC SPA hydration is slow. After page.goto(url, wait_until="domcontentloaded"), you need:

  • 22-25s before the page is interactive
  • 4-6s after each click for next step to render

Skip these and you'll race the SPA. The :has-text("$1.99") selector matches both $1.99 and $19.99 — use exact innerText === '$1.99' instead.

Production result

3 IAPs (AutoChoice / AltitudeNow / DaysUntil) priced at $1.99 USD in 4 minutes wall-clock. Apple-side state confirmed via the same API I bootstrap with for app/IAP IDs.

Source

Full script (~140 LOC, Python 3.10+, Playwright 1.40+): I'll share via the AutoApp newsletter — 60-day indie iOS dev real-time log: https://autoappnotes.substack.com

Caveats

  • This works for price tier selection only. Localizations / images / Submit-for-Review are separate flows (and the API can do those).
  • Apple may change CSS selectors or button text any week. The brittle parts: 添加定价, 选取, 下一步, 确认, 保存. If your locale is English, swap to "Add Pricing" / "Select" / "Next" / "Confirm" / "Save".
  • Don't run this in headless mode without first verifying the flow visually. CDP + visible Chrome catches selector drift in 5 seconds vs 5 hours in CI.

Want the full pricing automation playbook? iOS Indie Launch Playbook on Gumroad — 50pp PDF, real numbers from shipping 4 apps in 60 days, including the ASC bureaucracy chapter.

Top comments (0)