SDK errors and test checklist
Structured errors, empty offerings, uncertain purchases, unsupported capabilities and a safe release checklist.
Keep successful operations, missing selections and network failures distinct. Do not translate a network failure into “invalid discount” or “no subscription.”
Read results
Flutter: inspect isSuccess, then data or failure; failures expose code, message, isRetryable, details. Kotlin uses InappifyResult.Success or Failure.error. Successful lookups can contain null. JavaScript rejects Promises; use isInAppifyError(error) to identify SDK failures.
Review diagnostic messages before showing them. Retryable does not mean blindly repeat a payment; retain the original attempt/context. Keep credentials, receipts, tickets and full payment URLs out of public logs/tickets.
Common signals
| Signal | Check |
|---|---|
| configuration / notConfigured | Correct public app key, package/bundle and awaited configure |
| unauthorized | Matching app/session; no invented tokens or another account fallback |
| network / timeout | Temporary state; reconcile purchases before another payment |
| purchaseCancelled | Neutral cancellation feedback; no grant |
UI_HOST_NOT_SUPPORTED | FlutterFragmentActivity or valid AndroidX Activity |
PURCHASE_IN_PROGRESS | Prevent concurrent starts and repeated taps |
PRODUCT_TYPE_REQUIRED | Fresh catalog and valid product classification; don't guess |
PURCHASE_PACKAGE_CONTEXT_REQUIRED | Complete Package from an offering |
RATE_LIMITED_NO_CACHE | Respect five-per-minute quota and retry timing |
| unsupportedOperation | Actual backend support, not facade signature alone |
| Web bridgeUnavailable | Bundle/load the JS provider before Flutter |
Kotlin/Flutter enum names differ; native codes/reasons may be in details. Unknown state is not permission to assume payment success.
Empty offerings
Check key, catalog membership, SKU, current offering, placement, targeting and Web feature permissions. Use supported sync for attribute-based targeting. An unknown explicit offering ID must not silently become default.
Charged but outcome unknown
Do not automatically start another payment. Android reconciles the saved attempt through history/events and manual syncStorePurchases() when needed. iOS uses fresh CustomerInfo and supported Direct delivery; Web queries the same checkout ID. Dashboard customers and transactions help diagnosis. Consumables may await a host grant.
Before-release checklist
- Guest/authenticated startup, restart, logout and account switch without predecessor data.
- Current, placement, exact offering and missing selection; label returned currency units without guessed rial/toman conversion.
- Attribute set/delete and quota-aware refresh, empty queue, network failure and five/60 limit.
- Success, cancellation, repeated taps, post-payment network loss and process death.
- Unknown lookup/unrelated event without cleared journals or duplicate payments.
- Durable consumable grant, failed confirm and restart without double inventory.
- Subscription expiry, inactive entitlement and failed reads without false certainty.
- Web tickets/OTP, a non-default discounted offering and a forged return without a grant.
Routine tests use fakes, not real OTP/payment requests. Perform separately authorized live store checks with appropriate test accounts/products; examples don't guarantee free transactions. Build/unit success does not establish live payment or backend deployment.