Release 6.6.0 - Android OpenIAP module
· 3 min read
Short and sweet — this release brings the Android migration to openiap-google, tighter null-safety, and example stability fixes. We also rolled in the follow up maintenance work that shipped as 6.6.1 so you have a single snapshot of the 6.6 line.
Highlights
- Android: Switched to openiap-google with coroutine-based connection gating and improved error logging.
- Dart: More robust product parsing and safer generics to avoid UI stalls.
- iOS: Added small helper methods (subscription group, App Store country, typed app transaction, histories).
- Example: Guarded mounted across async paths to prevent setState after dispose.
- Tooling: Pre-commit formatting matches CI; avoids staging untracked files.
Breaking Changes & Migration
We’re standardizing identifiers and tokens across platforms. Please review and migrate accordingly.
Transaction Identifier
- Canonical field:
purchase.id
- Deprecated:
transactionId
(kept for compatibility, will be removed in a future release) - Impacted APIs:
- Finish transaction on iOS now uses
purchase.id
internally. If you previously passed or storedtransactionId
, switch topurchase.id
.
- Finish transaction on iOS now uses
Before
await FlutterInappPurchase.instance.finishTransaction(
purchase, // relies on purchase.transactionId
);
After
await FlutterInappPurchase.instance.finishTransaction(
purchase, // uses purchase.id (canonical)
);
final txnId = purchase.id; // use this instead of transactionId
Purchase Token
- Canonical field:
purchaseToken
- Deprecated:
purchaseTokenAndroid
(usepurchaseToken
everywhere) - Android subscription upgrade/downgrade:
- When specifying the existing subscription, pass the token under
purchaseToken
.
- When specifying the existing subscription, pass the token under
Before
await requestPurchase(
'premium_monthly',
replacementModeAndroid: AndroidReplacementMode.withTimeProration.value,
purchaseTokenAndroid: existingSub.purchaseToken,
);
After
await FlutterInappPurchase.instance.requestPurchase(
options: RequestPurchase(
android: AndroidRequestSubscriptionProps(
skus: ['premium_monthly'],
subscriptionOffers: myOffers,
// unified token field
// when calling platform channel beneath, the key is `purchaseToken`
),
),
);
// For server validation, always send `purchase.purchaseToken` (Android)
Receipt Data (iOS)
- Canonical verification input:
purchaseToken
(JWS representation from StoreKit 2) - Deprecated:
transactionReceipt
for server-side verification flows - App-side verification:
- Use
validateReceiptIOS(sku: ...)
for local testing only. It returns a JWS aspurchaseToken
. - For production, send the returned
purchaseToken
to your server and verify with Apple public keys.
- Use
Before
// Legacy (server hits Apple verifyReceipt endpoint with raw receipt)
await http.post(url, body: {'receipt-data': purchase.transactionReceipt});
After
final res = await FlutterInappPurchase.instance.validateReceiptIOS(sku: productId);
if (res.isValid) {
await yourServer.verifyIOS(purchaseToken: res.purchaseToken); // JWS
}
Clarification
- Use
purchase.id
as the canonical transaction identifier for logging, idempotency, andfinishTransaction
on iOS. - Receipt verification itself uses
purchaseToken
(JWS), notpurchase.id
ortransactionId
.
What to change in your code
- Replace
purchase.transactionId
withpurchase.id
. - Replace
purchaseTokenAndroid
withpurchaseToken
. - For iOS server verification, stop sending
transactionReceipt
; switch to the JWS returned aspurchaseToken
.
Notes on Backward Compatibility
- This release keeps
transactionId
andtransactionReceipt
readable to ease migration, but they are deprecated and will be removed in a future version. - Internally, finishing transactions on iOS already uses
purchase.id
.
6.6.1 follow-up
- Completed docs/examples migration from
PurchasedItem
→Purchase
so the new types are consistent everywhere. - Added unified helpers that callers had to bolt on manually:
Purchase.quantity
(iOS quantity, defaults to 1 on Android)Purchase.isAutoRenewing
- Hardened product parsing so
Product.id
is always populated regardless of store quirks. - Example UX tweaks: no alert for user-cancelled purchases; avoids returning
inside
finally
blocks. - Analyzer clean-up: targeted ignores for legacy symbols, opacity lints, and duplicate library directives.