ARTICLE AD BOX
We're using in_app_purchase: ^3.2.3 (in_app_purchase_android: 0.4.0+10) on Flutter. On Android, when a user completes a purchase:
Google Play processes it correctly (test receipt email received, order ID generated, "Test card, always approves" shown in sandbox)
The app briefly shows "Purchase didn't complete. If you cancelled, just try again."
The tier remains unchanged
Our backend Cloud Function (verifyIAP) is never invoked — confirmed via GCP logs
On the second attempt: "You already own this item" — confirming Google Play considers the purchase complete but unacknowledged.
Our purchase stream handler:
void _onPurchaseUpdate(List<PurchaseDetails> purchases) { for (final purchase in purchases) { if (purchase.productID != _pendingProductId) { if (purchase.status != PurchaseStatus.pending) { _iap.completePurchase(purchase); } continue; } switch (purchase.status) { case PurchaseStatus.purchased: case PurchaseStatus.restored: _handlePurchased(purchase); case PurchaseStatus.canceled: _iap.completePurchase(purchase); _resolvePending(IAPResult.cancelled); case PurchaseStatus.error: _iap.completePurchase(purchase); _resolvePending(IAPResult.storeError); case PurchaseStatus.pending: break; } } }The error message "Purchase didn't complete. If you cancelled, just try again." maps to IAPResult.storeError.
Question: On Android, can a completed Google Play purchase arrive on the purchaseStream as PurchaseStatus.error rather than PurchaseStatus.purchased? If so, under what conditions? Or is there another mechanism by which the stream event would be dropped before reaching our handler?
What we've already ruled out:
The didChangeAppLifecycleState cancellation path is guarded with if (Platform.isAndroid) return;
The 30-second timeout did not fire (error appeared quickly)
_pendingProductId mismatch is possible but unclear why it would be null at time of stream delivery
iOS works correctly with the same code
Any and all help highly appreciated! Thanks
