Product IDs configured and match store console setup
Why it matters
Placeholder product IDs like com.example.app.monthly or counts that do not match the paywall UI mean purchases fail at the store layer before revenue is ever collected. The StoreKit or Play Billing call returns an unknown-product error, the paywall renders a broken CTA, and every install that reaches the purchase screen churns out with zero attribution. This is pure reference-integrity and placeholder-hygiene failure — the code and the App Store Connect / Play Console records must agree exactly or the integration is dead on arrival.
Severity rationale
High because placeholder or mismatched IDs silently break every purchase attempt, destroying conversion before any revenue lands.
Remediation
Replace every placeholder identifier with the exact reverse-domain IDs created in App Store Connect or the Play Console, and centralize them so the paywall UI and purchase calls cannot drift apart:
// src/constants/iap.ts
export const PRODUCT_IDS = {
MONTHLY: 'com.yourcompany.yourapp.monthly',
ANNUAL: 'com.yourcompany.yourapp.annual',
} as const;
For RevenueCat, manage products in the dashboard and reference entitlement identifiers (premium, pro_monthly) in code — never hardcode store-level product IDs when an SDK is fetching offerings.
Detection
- ID:
product-ids - Severity:
high - What to look for: Count all relevant instances and enumerate each. Before evaluating, extract and quote any relevant configuration or UI text found. Find all product ID strings in the codebase — search for the app's reverse-domain prefix (from
ios.bundleIdentifierinapp.jsonorCFBundleIdentifierinInfo.plist) followed by a product name. Also search for product ID arrays or constants inconstants/iap.ts,config/products.ts,lib/iap/products.dart, or inline in paywall components. Check: (1) Product IDs follow thecom.domain.appname.productpattern on iOS (required). (2) The number of product IDs in the code matches what you'd expect given the paywall UI (e.g., if the paywall shows Monthly, Annual, and Lifetime, there should be 3 product IDs). (3) Product IDs are not left as template placeholders:YOUR_PRODUCT_ID,com.example.app.monthly,com.yourcompany.app.premium. (4) For RevenueCat/Adapty/Qonversion: check that entitlement identifiers (e.g.,"premium","pro_monthly") match the platform SDK's expected string. Look forPurchases.shared.getOfferings()(RevenueCat),Adapty.getPaywall(id:), orQonversion.shared().offerings()— these fetch remotely configured products, so hardcoded product IDs may not be needed, but entitlement identifiers must still be correct. - Pass criteria: Product IDs are defined, follow platform conventions, are not placeholder values, and the count matches the paywall's offering structure. At least 1 implementation must be verified. A partial or placeholder implementation does not count as pass. Report the count even on pass.
- Fail criteria: Placeholder product IDs (
YOUR_PRODUCT_ID,com.example.*); product ID count does not match paywall UI; product IDs use incorrect format for the target platform. - Skip (N/A) when: No IAP detected in the app.
- Detail on fail:
"Product ID 'com.example.app.monthly' is a template placeholder in src/constants/iap.ts"or"Paywall shows 3 tiers but only 1 product ID is defined in src/lib/iap/products.ts" - Remediation: Product IDs in code must exactly match what is created and approved in App Store Connect (iOS) or the Play Console (Android) before any purchase can complete.
- Create your products in App Store Connect under your app → In-App Purchases or Subscriptions
- Use the generated product IDs (e.g.,
com.yourcompany.yourapp.monthly_4_99) exactly as shown - Store product IDs in a constants file:
// src/constants/iap.ts export const PRODUCT_IDS = { MONTHLY: 'com.yourcompany.yourapp.monthly', ANNUAL: 'com.yourcompany.yourapp.annual', } as const; - For RevenueCat: configure products in the RevenueCat dashboard and reference entitlement identifiers in code — the product IDs are managed in the dashboard, not hardcoded
Taxons
History
- 2026-04-18·v1.0.0·Initial import from app-store-iap-subscriptions·automated