Reputation: 4210
I'm trying to implement purchases in my app with the official in_app_purchase
plugin but the purchaseStream.listen
method is not working or firing any events. The only way to get it to work is to call _inAppPurchase.restorePurchases()
which is not what I want as the products are now returned with a restored (not purchased) status.
Can anyone advise on how to get this event firing or point out any mistakes in my code? My code is almost exactly the same as the codelab and I have tested with the plugin version used in the codelab ^3.0.4
as well as the latest version ^3.0.6
class PurchasesModel with ChangeNotifier {
final InAppPurchase _inAppPurchase = InAppPurchase.instance;
late StreamSubscription<List<PurchaseDetails>> _subscription;
List _purchases = [];
List<ProductDetails> _products = [];
List get purchases => _purchases;
set purchases(List value) {
_purchases = value;
notifyListeners();
}
List<ProductDetails> get products => _products;
set products(List<ProductDetails> value) {
_products = value;
notifyListeners();
}
//Constructor
PurchasesModel() {
final purchaseUpdated = _inAppPurchase.purchaseStream;
_subscription = purchaseUpdated.listen(
_onPurchaseUpdate,
onDone: _updateStreamOnDone,
onError: _updateStreamOnError,
);
loadPurchases();
}
Future<void> _onPurchaseUpdate(List<PurchaseDetails> purchaseDetailsList) async {
for (var purchaseDetails in purchaseDetailsList) {
await _handlePurchase(purchaseDetails);
}
notifyListeners();
}
Future<void> _handlePurchase(PurchaseDetails purchaseDetails) async {
....
}
void _updateStreamOnDone() {
_subscription.cancel();
}
void _updateStreamOnError(dynamic error) {
// ignore: avoid_print
print(error);
}
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
}
And the Widget build method in main.dart
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<PurchasesModel>(
create: (_) => PurchasesModel(),
lazy: false,
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark().copyWith(
primaryColor: primaryColor,
scaffoldBackgroundColor: backgroundColor,
),
home: App(),
),
);
}
Upvotes: 1
Views: 1821
Reputation: 17732
This event is triggered only when there is a response for purchase or restore. So first you get the list of products available for sale. That's the
// Set literals require Dart 2.2. Alternatively, use
// `Set<String> _kIds = <String>['product1', 'product2'].toSet()`.
const Set<String> _kIds = <String>{'product1', 'product2'};
final ProductDetailsResponse response =
await InAppPurchase.instance.queryProductDetails(_kIds);
if (response.notFoundIDs.isNotEmpty) {
// Handle the error.
}
List<ProductDetails> products = response.productDetails;
Here you will get the list of products for sale .
Then when there is a purchase event we use
final ProductDetails productDetails = ... // Saved earlier from queryProductDetails().
final PurchaseParam purchaseParam = PurchaseParam(productDetails: productDetails);
if (_isConsumable(productDetails)) {
InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
} else {
InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
}
// From here the purchase flow will be handled by the underlying store.
// Updates will be delivered to the `InAppPurchase.instance.purchaseStream`.
Here we pass one product detail to buy consumable or buy non consumable and this is listened in the method you mentioned
Edit To restore you just call this methos and it will be listened in the listener
await InAppPurchase.instance.restorePurchases();
Upvotes: 1