Title
Android: fetchProducts({ type: 'all' }) returns [] — FetchProductsResultAll dropped in ExpoIapModule.kt
Description
Summary
On Android, fetchProducts({ skus, type: 'all' }) resolves to an empty array [], even when the exact same SKUs return correctly via fetchProducts({ skus, type: 'subs' }). So 'subs' (and 'in-app') work, but 'all' silently returns nothing.
Environment
- expo-iap: 4.3.1
- openiap-google: 2.2.1 (pinned in
openiap-versions.json)
- React Native: 0.83.4, Expo SDK 55
- Platform: Android (Google Play Billing)
Reproduction
With one or more active subscriptions configured in Play Console:
// returns [] ❌
await fetchProducts({ skus: ['com.example.monthly'], type: 'all' });
// returns [{ id: 'com.example.monthly', type: 'subs', ... }] ✅
await fetchProducts({ skus: ['com.example.monthly'], type: 'subs' });
Expected behavior
type: 'all' returns the union of in-app + subscription products (the docs state: "a mixed array for 'all'").
Actual behavior
type: 'all' → []. 'subs' and 'in-app' each work individually.
Root cause
In android/src/main/java/expo/modules/iap/ExpoIapModule.kt, the fetchProducts function maps only two of the three sealed FetchProductsResult subtypes returned by openiap-google:
val result = openIap.fetchProducts(request)
val payload = when (result) {
is FetchProductsResultProducts -> result.value.orEmpty().map { it.toJson() }
is FetchProductsResultSubscriptions -> result.value.orEmpty().map { it.toJson() }
else -> emptyList<Map<String, Any?>>() // ← FetchProductsResultAll lands here
}
The flow is correct right up to this point:
parseProductQueryType("all") → ProductQueryType.All (correct).
- openiap-google handles
All correctly: it queries Google Play for both "inapp" and "subs" (each wrapped in its own runCatching, so an empty in-app half does not drop the subs half) and returns a populated FetchProductsResultAll (List<ProductOrSubscription>).
- expo-iap then discards it: there is no
is FetchProductsResultAll branch (the type isn't even imported), so the populated result hits else -> emptyList().
'subs' works because openiap returns FetchProductsResultSubscriptions, which is matched.
This branch is still missing on main.
Proposed fix
Add the missing branch (and import dev.hyo.openiap.FetchProductsResultAll):
when (result) {
is FetchProductsResultProducts -> result.value.orEmpty().map { it.toJson() }
is FetchProductsResultSubscriptions -> result.value.orEmpty().map { it.toJson() }
is FetchProductsResultAll -> result.value.orEmpty().map { it.toJson() } // add
else -> emptyList<Map<String, Any?>>()
}
Workaround
Query 'subs' and 'in-app' separately and merge the results, avoiding 'all' entirely.
Title
Android:
fetchProducts({ type: 'all' })returns[]—FetchProductsResultAlldropped inExpoIapModule.ktDescription
Summary
On Android,
fetchProducts({ skus, type: 'all' })resolves to an empty array[], even when the exact same SKUs return correctly viafetchProducts({ skus, type: 'subs' }). So'subs'(and'in-app') work, but'all'silently returns nothing.Environment
openiap-versions.json)Reproduction
With one or more active subscriptions configured in Play Console:
Expected behavior
type: 'all'returns the union of in-app + subscription products (the docs state: "a mixed array for'all'").Actual behavior
type: 'all'→[].'subs'and'in-app'each work individually.Root cause
In
android/src/main/java/expo/modules/iap/ExpoIapModule.kt, thefetchProductsfunction maps only two of the three sealedFetchProductsResultsubtypes returned by openiap-google:The flow is correct right up to this point:
parseProductQueryType("all")→ProductQueryType.All(correct).Allcorrectly: it queries Google Play for both"inapp"and"subs"(each wrapped in its ownrunCatching, so an empty in-app half does not drop the subs half) and returns a populatedFetchProductsResultAll(List<ProductOrSubscription>).is FetchProductsResultAllbranch (the type isn't even imported), so the populated result hitselse -> emptyList().'subs'works because openiap returnsFetchProductsResultSubscriptions, which is matched.This branch is still missing on
main.Proposed fix
Add the missing branch (and import
dev.hyo.openiap.FetchProductsResultAll):Workaround
Query
'subs'and'in-app'separately and merge the results, avoiding'all'entirely.