[{"data":1,"prerenderedAt":22},["ShallowReactive",2],{"articolo-in-app-purchase-in-flutter-vendere-prodotti-con-in-app-purchase":3,"comments-article-in-app-purchase-in-flutter-vendere-prodotti-con-in-app-purchase":21},{"id":4,"title":5,"slug":6,"excerpt":7,"body":8,"cover_image":9,"video_url":10,"status":11,"published_at":12,"meta_title":13,"meta_description":14,"category":15,"author":19},34,"In-app purchase in Flutter: vendere prodotti con in_app_purchase","in-app-purchase-in-flutter-vendere-prodotti-con-in-app-purchase","Guida pratica all'integrazione degli acquisti in-app in Flutter con il pacchetto ufficiale in_app_purchase: prodotti consumabili, non consumabili e abbonamenti su App Store e Google Play.","## Perché gli acquisti in-app\n\nMonetizzare un'app mobile passa spesso dagli acquisti in-app (IAP): sblocco di funzionalità premium, contenuti extra o abbonamenti ricorrenti. Flutter offre il pacchetto ufficiale [`in_app_purchase`](https:\u002F\u002Fpub.dev\u002Fpackages\u002Fin_app_purchase), che astrae le API di **StoreKit** (iOS) e **Google Play Billing** (Android) dietro un'interfaccia unificata.\n\nIn questa guida vediamo come configurare i prodotti, caricarli, avviare un acquisto e verificarlo lato client.\n\n## Tipi di prodotto\n\n- **Consumabili**: acquistabili più volte (es. monete di gioco).\n- **Non consumabili**: acquisto una tantum (es. rimozione pubblicità).\n- **Abbonamenti**: rinnovo automatico ricorrente.\n\n## Configurazione\n\nAggiungi la dipendenza:\n\n```yaml\ndependencies:\n  in_app_purchase: ^3.2.0\n```\n\nSu Android non servono modifiche particolari al `AndroidManifest.xml` per le versioni recenti del plugin. Su iOS assicurati di abilitare la capability **In-App Purchase** in Xcode.\n\nI prodotti vanno prima creati nelle rispettive console:\n\n- **Google Play Console** → sezione *Prodotti in-app* \u002F *Abbonamenti*.\n- **App Store Connect** → sezione *In-App Purchases*.\n\nUsa gli stessi identificatori di prodotto (`productId`) su entrambe le piattaforme per semplificare il codice.\n\n## Verificare la disponibilità dello store\n\n```dart\nimport 'package:in_app_purchase\u002Fin_app_purchase.dart';\n\nfinal InAppPurchase _iap = InAppPurchase.instance;\n\nFuture\u003Cbool> isStoreAvailable() async {\n  return _iap.isAvailable();\n}\n```\n\n## Caricare i prodotti\n\n```dart\nconst Set\u003CString> _kIds = {'remove_ads', 'premium_monthly'};\n\nFuture\u003CList\u003CProductDetails>> loadProducts() async {\n  final ProductDetailsResponse response =\n      await _iap.queryProductDetails(_kIds);\n\n  if (response.notFoundIDs.isNotEmpty) {\n    debugPrint('ID non trovati: ${response.notFoundIDs}');\n  }\n  return response.productDetails;\n}\n```\n\nOgni `ProductDetails` contiene `id`, `title`, `description` e `price` (già formattato nella valuta locale dello store).\n\n## Ascoltare gli aggiornamenti degli acquisti\n\nIl flusso di acquisto è **asincrono**: dobbiamo iscriverci allo stream `purchaseStream` prima di avviare qualsiasi transazione, tipicamente all'avvio dell'app.\n\n```dart\nlate final StreamSubscription\u003CList\u003CPurchaseDetails>> _subscription;\n\nvoid initPurchaseListener() {\n  _subscription = _iap.purchaseStream.listen(\n    _onPurchaseUpdate,\n    onDone: () => _subscription.cancel(),\n    onError: (error) => debugPrint('Errore acquisto: $error'),\n  );\n}\n\nFuture\u003Cvoid> _onPurchaseUpdate(List\u003CPurchaseDetails> purchases) async {\n  for (final purchase in purchases) {\n    switch (purchase.status) {\n      case PurchaseStatus.pending:\n        _showPending();\n        break;\n      case PurchaseStatus.purchased:\n      case PurchaseStatus.restored:\n        await _verifyAndDeliver(purchase);\n        break;\n      case PurchaseStatus.error:\n        debugPrint('Errore: ${purchase.error}');\n        break;\n      case PurchaseStatus.canceled:\n        debugPrint('Acquisto annullato');\n        break;\n    }\n\n    \u002F\u002F Fondamentale: completare la transazione\n    if (purchase.pendingCompletePurchase) {\n      await _iap.completePurchase(purchase);\n    }\n  }\n}\n```\n\n> ⚠️ Chiamare `completePurchase` è obbligatorio: se non lo fai, lo store considererà la transazione non consegnata e la ripresenterà a ogni avvio.\n\n## Avviare un acquisto\n\n```dart\nFuture\u003Cvoid> buy(ProductDetails product, {required bool isConsumable}) async {\n  final PurchaseParam param = PurchaseParam(productDetails: product);\n\n  if (isConsumable) {\n    await _iap.buyConsumable(purchaseParam: param);\n  } else {\n    await _iap.buyNonConsumable(purchaseParam: param);\n  }\n}\n```\n\nGli abbonamenti si acquistano con `buyNonConsumable`.\n\n## Verifica lato server\n\nLa validazione fatta solo sul client è vulnerabile. La verifica corretta prevede l'invio del `purchase.verificationData.serverVerificationData` a un backend che interroga le API di Google\u002FApple.\n\n```dart\nFuture\u003Cvoid> _verifyAndDeliver(PurchaseDetails purchase) async {\n  final token = purchase.verificationData.serverVerificationData;\n  final valid = await backend.verify(purchase.productID, token);\n\n  if (valid) {\n    \u002F\u002F Sblocca il contenuto e persisti lo stato\n    await _deliverProduct(purchase.productID);\n  }\n}\n```\n\n## Ripristinare gli acquisti\n\nApple richiede un pulsante \"Ripristina acquisti\" per i prodotti non consumabili e gli abbonamenti:\n\n```dart\nFuture\u003Cvoid> restorePurchases() async {\n  await _iap.restorePurchases();\n}\n```\n\nGli acquisti ripristinati arriveranno sul `purchaseStream` con stato `restored`.\n\n## Best practice\n\n- **Verifica sempre lato server** per prevenire frodi.\n- **Non fidarti del client** per lo sblocco di contenuti di valore.\n- **Testa** con account sandbox (App Store) e testers con licenza (Play Console).\n- **Ricordati di annullare** la subscription allo stream nel `dispose`.\n- Per gli abbonamenti usa librerie come **RevenueCat** se vuoi gestione multipiattaforma e webhook già pronti.\n\n## Conclusione\n\nIl pacchetto `in_app_purchase` copre bene i casi standard di monetizzazione mantenendo un'unica API cross-platform. La parte più delicata resta la verifica lato server: non trascurarla se vendi contenuti di valore. Per progetti complessi con molti abbonamenti, valuta soluzioni gestite che riducono il boilerplate e centralizzano l'analisi dei ricavi.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002F5aa8203e-16ce-4f78-bb63-ac923dee5b05.jpg",null,"published","2026-07-08T04:00:39+00:00","In-app purchase in Flutter: guida a in_app_purchase","Come integrare gli acquisti in-app in Flutter con in_app_purchase: prodotti, abbonamenti, verifica e best practice per App Store e Google Play.",{"id":16,"name":17,"slug":18},1,"Guide","guide",{"id":16,"name":20},"Flutter Bot",[],1785219602617]