From 7f0036a7bea297b4fa7de1060e89b2b7067f20a6 Mon Sep 17 00:00:00 2001 From: Attilio Patania Date: Sun, 14 Nov 2021 14:28:29 +0100 Subject: [PATCH] Ios promocodes (#325) * updated dependecies: http: ^0.13.4 meta: ^1.7.0 for ios: added presentCodeRedemptionSheet * upgraded dependecies http: ^0.13.4 meta: ^1.7.0 ios: added presentCodeRedemptionSheet * Update FlutterInappPurchasePlugin.m * Rename showPromoCodes to iOS specific Co-authored-by: hyochan --- CHANGELOG.md | 331 +++++++++++------- README.md | 178 ++++++---- android/.project | 28 ++ .../org.eclipse.buildship.core.prefs | 13 + example/android/.project | 28 ++ .../org.eclipse.buildship.core.prefs | 13 + ios/Classes/FlutterInappPurchasePlugin.m | 10 + lib/flutter_inapp_purchase.dart | 8 + pubspec.yaml | 16 +- 9 files changed, 422 insertions(+), 203 deletions(-) create mode 100644 android/.project create mode 100644 android/.settings/org.eclipse.buildship.core.prefs create mode 100644 example/android/.project create mode 100644 example/android/.settings/org.eclipse.buildship.core.prefs diff --git a/CHANGELOG.md b/CHANGELOG.md index 957c4474..9be99b15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,251 +1,342 @@ +## 5.0.4 + +- Add iOS promo codes (#325) +- Use http client in validateReceiptIos (#322) +- Amazon `getPrice` directly withoiut formatting (#316) + ## 5.0.3 -+ Fix plugin exception for `requestProductWithQuantityIOS` [#306](https://github.com/dooboolab/flutter_inapp_purchase/pull/306) + +- Fix plugin exception for `requestProductWithQuantityIOS` [#306](https://github.com/dooboolab/flutter_inapp_purchase/pull/306) ## 5.0.2 -+ Replaced obfuscatedAccountIdAndroid with obfuscatedAccountId in request purchase method [#299](https://github.com/dooboolab/flutter_inapp_purchase/pull/299) + +- Replaced obfuscatedAccountIdAndroid with obfuscatedAccountId in request purchase method [#299](https://github.com/dooboolab/flutter_inapp_purchase/pull/299) ## 5.0.1 -+ Add AndroidProrationMode values [#273](https://github.com/dooboolab/flutter_inapp_purchase/pull/273) + +- Add AndroidProrationMode values [#273](https://github.com/dooboolab/flutter_inapp_purchase/pull/273) ## 5.0.0 -+ Support null safety [#275](https://github.com/dooboolab/flutter_inapp_purchase/pull/275) + +- Support null safety [#275](https://github.com/dooboolab/flutter_inapp_purchase/pull/275) ## 4.0.2 -+ The dart side requires "introductoryPriceCyclesAndroid" to be a int [#268](https://github.com/dooboolab/flutter_inapp_purchase/pull/268) + +- The dart side requires "introductoryPriceCyclesAndroid" to be a int [#268](https://github.com/dooboolab/flutter_inapp_purchase/pull/268) ## 4.0.1 -+ `platform` dep version `>=2.0.0 <4.0.0` + +- `platform` dep version `>=2.0.0 <4.0.0` ## 4.0.0 -+ Support flutter v2 [#265](https://github.com/dooboolab/flutter_inapp_purchase/pull/265) + +- Support flutter v2 [#265](https://github.com/dooboolab/flutter_inapp_purchase/pull/265) ## 3.0.1 -+ Migrate to flutter embedding v2 [#240](https://github.com/dooboolab/flutter_inapp_purchase/pull/240) -+ Expose android purchase state as enum [#243](https://github.com/dooboolab/flutter_inapp_purchase/pull/243) + +- Migrate to flutter embedding v2 [#240](https://github.com/dooboolab/flutter_inapp_purchase/pull/240) +- Expose android purchase state as enum [#243](https://github.com/dooboolab/flutter_inapp_purchase/pull/243) ## 3.0.0 -+ Upgrade android billing client to `2.1.0` from `3.0.0`. -+ Removed `deveoperId` and `accountId` when requesting `purchase` or `subscription` in `android`. -+ Added `obfuscatedAccountIdAndroid` and `obfuscatedProfileIdAndroid` when requesting `purchase` or `subscription` in `android`. -+ Removed `developerPayload` in `android`. -+ Added `purchaseTokenAndroid` as an optional parameter to `requestPurchase` and `requestSubscription`. + +- Upgrade android billing client to `2.1.0` from `3.0.0`. +- Removed `deveoperId` and `accountId` when requesting `purchase` or `subscription` in `android`. +- Added `obfuscatedAccountIdAndroid` and `obfuscatedProfileIdAndroid` when requesting `purchase` or `subscription` in `android`. +- Removed `developerPayload` in `android`. +- Added `purchaseTokenAndroid` as an optional parameter to `requestPurchase` and `requestSubscription`. ## 2.3.1 + Republishing since sourcode seems not merged correctly. ## 2.3.0 -+ Bugfix IAPItem deserialization [#212](https://github.com/dooboolab/flutter_inapp_purchase/pull/212) -+ Add introductoryPriceNumberIOS [#214](https://github.com/dooboolab/flutter_inapp_purchase/pull/214) -+ Fix iOS promotional offers [#220](https://github.com/dooboolab/flutter_inapp_purchase/pull/220) + +- Bugfix IAPItem deserialization [#212](https://github.com/dooboolab/flutter_inapp_purchase/pull/212) +- Add introductoryPriceNumberIOS [#214](https://github.com/dooboolab/flutter_inapp_purchase/pull/214) +- Fix iOS promotional offers [#220](https://github.com/dooboolab/flutter_inapp_purchase/pull/220) ## 2.2.0 -+ Implement `endConnection` method to declaratively finish observer in iOS. -+ Remove `addTransactionObserver` in IAPPromotionObserver.m for dup observer problems. -+ Automatically startPromotionObserver in `initConnection` for iOS. + +- Implement `endConnection` method to declaratively finish observer in iOS. +- Remove `addTransactionObserver` in IAPPromotionObserver.m for dup observer problems. +- Automatically startPromotionObserver in `initConnection` for iOS. ## 2.1.5 -+ Fix ios failed purchase handling problem in 11.4+ [#176](https://github.com/dooboolab/flutter_inapp_purchase/pull/176) + +- Fix ios failed purchase handling problem in 11.4+ [#176](https://github.com/dooboolab/flutter_inapp_purchase/pull/176) ## 2.1.4 -+ Fix dart side expression warning [#169](https://github.com/dooboolab/flutter_inapp_purchase/pull/169). + +- Fix dart side expression warning [#169](https://github.com/dooboolab/flutter_inapp_purchase/pull/169). ## 2.1.3 -+ Fix wrong introductory price number of periods [#164](https://github.com/dooboolab/flutter_inapp_purchase/pull/164). + +- Fix wrong introductory price number of periods [#164](https://github.com/dooboolab/flutter_inapp_purchase/pull/164). ## 2.1.2 -+ Trigger purchaseUpdated callback when iap purchased [#165](https://github.com/dooboolab/flutter_inapp_purchase/pull/165). + +- Trigger purchaseUpdated callback when iap purchased [#165](https://github.com/dooboolab/flutter_inapp_purchase/pull/165). ## 2.1.1 -+ Renamed `finishTransactionIOS` argument `purchaseToken` to `transactionId`. + +- Renamed `finishTransactionIOS` argument `purchaseToken` to `transactionId`. ## 2.1.0 -+ `finishTransaction` parameter changes to `purchasedItem` from `purchaseToken`. -+ Update android billing client to `2.1.0` from `2.0.3`. + +- `finishTransaction` parameter changes to `purchasedItem` from `purchaseToken`. +- Update android billing client to `2.1.0` from `2.0.3`. ## 2.0.5 -+ [bugfix] Fix double call of result reply on connection init [#126](https://github.com/dooboolab/flutter_inapp_purchase/pull/126) + +- [bugfix] Fix double call of result reply on connection init [#126](https://github.com/dooboolab/flutter_inapp_purchase/pull/126) + ## 2.0.4 -+ [bugfix] Fix plugin throws exceptions with flutter v1.10.7 beta [#117](https://github.com/dooboolab/flutter_inapp_purchase/pull/117) + +- [bugfix] Fix plugin throws exceptions with flutter v1.10.7 beta [#117](https://github.com/dooboolab/flutter_inapp_purchase/pull/117) + ## 2.0.3 -+ [bugfix] Decode response code for connection updates stream [#114](https://github.com/dooboolab/flutter_inapp_purchase/pull/114) -+ [bugfix] Fix typo in `consumePurchase` [#115](https://github.com/dooboolab/flutter_inapp_purchase/pull/115) + +- [bugfix] Decode response code for connection updates stream [#114](https://github.com/dooboolab/flutter_inapp_purchase/pull/114) +- [bugfix] Fix typo in `consumePurchase` [#115](https://github.com/dooboolab/flutter_inapp_purchase/pull/115) + ## 2.0.2 -* use ConnectionResult as type for connection stream, fix controller creation [#112](https://github.com/dooboolab/flutter_inapp_purchase/pull/112) + +- use ConnectionResult as type for connection stream, fix controller creation [#112](https://github.com/dooboolab/flutter_inapp_purchase/pull/112) + ## 2.0.0+16 -* Resolve [#106](https://github.com/dooboolab/flutter_inapp_purchase/issues/106) by not sending `result.error` to the listener. Created use `_conectionSubscription`. + +- Resolve [#106](https://github.com/dooboolab/flutter_inapp_purchase/issues/106) by not sending `result.error` to the listener. Created use `_conectionSubscription`. + ## 2.0.0+15 -* Fixed minor typo when generating string with `toString`. Resolve [#110](https://github.com/dooboolab/flutter_inapp_purchase/issues/110). + +- Fixed minor typo when generating string with `toString`. Resolve [#110](https://github.com/dooboolab/flutter_inapp_purchase/issues/110). + ## 2.0.0+14 -* Pass android exception to flutter side. + +- Pass android exception to flutter side. + ## 2.0.0+13 -* Android receipt validation api upgrade to `v3`. + +- Android receipt validation api upgrade to `v3`. + ## 2.0.0+12 -* Resolve [#102](https://github.com/dooboolab/flutter_inapp_purchase/issues/102). Fluter seems to only sends strings between platforms. + +- Resolve [#102](https://github.com/dooboolab/flutter_inapp_purchase/issues/102). Fluter seems to only sends strings between platforms. + ## 2.0.0+9 -* Resolve [#101](https://github.com/dooboolab/flutter_inapp_purchase/issues/101). + +- Resolve [#101](https://github.com/dooboolab/flutter_inapp_purchase/issues/101). + ## 2.0.0+8 -* Resolve [#100](https://github.com/dooboolab/flutter_inapp_purchase/issues/100). + +- Resolve [#100](https://github.com/dooboolab/flutter_inapp_purchase/issues/100). + ## 2.0.0+7 -* Resolve [#99](https://github.com/dooboolab/flutter_inapp_purchase/issues/99). + +- Resolve [#99](https://github.com/dooboolab/flutter_inapp_purchase/issues/99). + ## 2.0.0+6 -* Send `purchase-error` with purchases returns null. + +- Send `purchase-error` with purchases returns null. + ## 2.0.0+5 -* Renamed invoked parameters non-platform specific. + +- Renamed invoked parameters non-platform specific. + ## 2.0.0+4 -* Add `deveoperId` and `accountId` when requesting `purchase` or `subscription` in `android`. Find out more in `requestPurchase` and `requestSubscription`. + +- Add `deveoperId` and `accountId` when requesting `purchase` or `subscription` in `android`. Find out more in `requestPurchase` and `requestSubscription`. ## 2.0.0+3 -* Correctly mock invoke method and return results [#94](https://github.com/dooboolab/flutter_inapp_purchase/pull/96) + +- Correctly mock invoke method and return results [#94](https://github.com/dooboolab/flutter_inapp_purchase/pull/96) ## 2.0.0+2 -* Seperate long `example` code to `example` readme. + +- Seperate long `example` code to `example` readme. ## 2.0.0+1 -* Properly set return type `PurchaseResult` of when finishing transaction. + +- Properly set return type `PurchaseResult` of when finishing transaction. ## 2.0.0 :tada: -* Removed deprecated note in the `readme`. -* Make the previous tests work in `travis`. -* Documentation on `readme` for breaking features. -* Abstracts `finishTransaction`. + +- Removed deprecated note in the `readme`. +- Make the previous tests work in `travis`. +- Documentation on `readme` for breaking features. +- Abstracts `finishTransaction`. - `acknowledgePurchaseAndroid`, `consumePurchaseAndroid`, `finishTransactionIOS`. [Android] -* Completely remove prepare. -* Upgrade billingclient to 2.0.3 which is currently recent in Sep 15 2019. -* Remove [IInAppBillingService] binding since billingClient has its own functionalities. -* Add [DoobooUtils] and add `getBillingResponseData` that visualizes erorr codes better. -* `buyProduct` no more return asyn result. It rather relies on the `purchaseUpdatedListener`. -* Add feature method `acknowledgePurchaseAndroid` - - Implement `acknowledgePurchaseAndroid`. - - Renamed `consumePurchase` to `consumePurchaseAndroid` in dart side. - - Update test codes. -* Renamed methods - - `buyProduct` to `requestPurchase`. - - `buySubscription` to `requestSubscription`. + +- Completely remove prepare. +- Upgrade billingclient to 2.0.3 which is currently recent in Sep 15 2019. +- Remove [IInAppBillingService] binding since billingClient has its own functionalities. +- Add [DoobooUtils] and add `getBillingResponseData` that visualizes erorr codes better. +- `buyProduct` no more return asyn result. It rather relies on the `purchaseUpdatedListener`. +- Add feature method `acknowledgePurchaseAndroid` + - Implement `acknowledgePurchaseAndroid`. + - Renamed `consumePurchase` to `consumePurchaseAndroid` in dart side. + - Update test codes. +- Renamed methods + - `buyProduct` to `requestPurchase`. + - `buySubscription` to `requestSubscription`. [iOS] -* Implment features in new releases. - - enforce to `finishTransaction` after purchases. - - Work with `purchaseUpdated` and `purchaseError` listener as in android. - - Feature set from `react-native-iap v3`. - - Should call finish transaction in every purchase request. - - Add `IAPPromotionObserver` cocoa touch file - - Convert dic to json string before invoking purchase-updated - - Add `getPromotedProductIOS` and `requestPromotedProductIOS` methods - - Implement clearTransaction for ios - - Include `purchasePromoted` stream that listens to `iap-promoted-product`. + +- Implment features in new releases. + - enforce to `finishTransaction` after purchases. + - Work with `purchaseUpdated` and `purchaseError` listener as in android. + - Feature set from `react-native-iap v3`. + - Should call finish transaction in every purchase request. + - Add `IAPPromotionObserver` cocoa touch file + - Convert dic to json string before invoking purchase-updated + - Add `getPromotedProductIOS` and `requestPromotedProductIOS` methods + - Implement clearTransaction for ios + - Include `purchasePromoted` stream that listens to `iap-promoted-product`. ## 1.0.0 -+ Add `DEPRECATION` note. Please use [in_app_purchase](https://pub.dev/packages/in_app_purchase). + +- Add `DEPRECATION` note. Please use [in_app_purchase](https://pub.dev/packages/in_app_purchase). ## 0.9.+ -* Breaking change. Migrate from the deprecated original Android Support Library to AndroidX. This shouldn't result in any functional changes, but it requires any Android apps using this plugin to also migrate to Android X if they're using the original support library. [Android's Migrating to Android X Guide](https://developer.android.com/jetpack/androidx/migrate). -+ Improved getPurchaseHistory's speed 44% faster [#68](https://github.com/dooboolab/flutter_inapp_purchase/pull/68). + +- Breaking change. Migrate from the deprecated original Android Support Library to AndroidX. This shouldn't result in any functional changes, but it requires any Android apps using this plugin to also migrate to Android X if they're using the original support library. [Android's Migrating to Android X Guide](https://developer.android.com/jetpack/androidx/migrate). + +* Improved getPurchaseHistory's speed 44% faster [#68](https://github.com/dooboolab/flutter_inapp_purchase/pull/68). ## 0.8.+ -* Fixed receipt validation param for `android`. -* Updated `http` package. -* Implemented new method `getAppStoreInitiatedProducts`. + +- Fixed receipt validation param for `android`. +- Updated `http` package. +- Implemented new method `getAppStoreInitiatedProducts`. - Handling of iOS method `paymentQueue:shouldAddStorePayment:forProduct:` - Has no effect on Android. -* Fixed issue with method `buyProductWithoutFinishTransaction` for iOS, was not getting the productId. -* Fixed issue with `toString` method of class `IAPItem`, was printing incorrect values. -* Fixes for #44. Unsafe getting `originalJson` when restoring item and `Android`. -* Use dictionaryWithObjectsAndKeys in NSDictionary to fetch product values. This will prevent from NSInvalidArgumentException in ios which rarely occurs. -* Fixed wrong npe in `android` when `getAvailablePurchases`. -+ Only parse `orderId` when exists in `Android` to prevent crashing. -+ Add additional success purchase listener in `iOS`. Related [#54](https://github.com/dooboolab/flutter_inapp_purchase/issues/54) +- Fixed issue with method `buyProductWithoutFinishTransaction` for iOS, was not getting the productId. +- Fixed issue with `toString` method of class `IAPItem`, was printing incorrect values. +- Fixes for #44. Unsafe getting `originalJson` when restoring item and `Android`. +- Use dictionaryWithObjectsAndKeys in NSDictionary to fetch product values. This will prevent from NSInvalidArgumentException in ios which rarely occurs. +- Fixed wrong npe in `android` when `getAvailablePurchases`. + +* Only parse `orderId` when exists in `Android` to prevent crashing. +* Add additional success purchase listener in `iOS`. Related [#54](https://github.com/dooboolab/flutter_inapp_purchase/issues/54) ## 0.7.1 -* Implemented receiptValidation for both android and ios. + +- Implemented receiptValidation for both android and ios. - In Android, you need own backend to get your `accessToken`. ## 0.7.0 -* Addition of Amazon In-App Purchases. + +- Addition of Amazon In-App Purchases. ## 0.6.9 -* Prevent nil element exception when getting products. + +- Prevent nil element exception when getting products. ## 0.6.8 -* Prevent nil exception in ios when fetching products. + +- Prevent nil exception in ios when fetching products. ## 0.6.7 -* Fix broken images on pub. + +- Fix broken images on pub. ## 0.6.6 -* Added missing introductory fields in ios. + +- Added missing introductory fields in ios. ## 0.6.5 -* convert dynamic objects to PurchasedItems. -* Fix return type for getAvailablePurchases(). -* Fix ios null value if optional operator. + +- convert dynamic objects to PurchasedItems. +- Fix return type for getAvailablePurchases(). +- Fix ios null value if optional operator. ## 0.6.3 -* Update readme. + +- Update readme. ## 0.6.2 -* Fixed failing when there is no introductory price in ios. + +- Fixed failing when there is no introductory price in ios. ## 0.6.1 -* Fixed `checkSubscribed` that can interrupt billing lifecycle. + +- Fixed `checkSubscribed` that can interrupt billing lifecycle. ## 0.6.0 -* Major code refactoring by lukepighetti. Unify PlatformException, cleanup new, DateTime instead of string. + +- Major code refactoring by lukepighetti. Unify PlatformException, cleanup new, DateTime instead of string. ## 0.5.9 -* Fix getSubscription json encoding problem in `ios`. + +- Fix getSubscription json encoding problem in `ios`. ## 0.5.8 -* Avoid crashing on android caused by IllegalStateException. + +- Avoid crashing on android caused by IllegalStateException. ## 0.5.7 -* Avoid possible memory leak in android by deleting static declaration of activity and context. + +- Avoid possible memory leak in android by deleting static declaration of activity and context. ## 0.5.6 -* Few types fixed. + +- Few types fixed. ## 0.5.4 -* Fixed error parsing IAPItem. + +- Fixed error parsing IAPItem. ## 0.5.3 -* Fixed error parsing purchaseHistory. + +- Fixed error parsing purchaseHistory. ## 0.5.2 -* Fix crashing on error. + +- Fix crashing on error. ## 0.5.1 -* Give better error message on ios. + +- Give better error message on ios. ## 0.5.0 -* Code migration. -* Support subscription period. -* There was parameter renaming in `0.5.0` to identify different parameters sent from the device. Please check the readme. + +- Code migration. +- Support subscription period. +- There was parameter renaming in `0.5.0` to identify different parameters sent from the device. Please check the readme. ## 0.4.3 -* Fixed subscription return types. + +- Fixed subscription return types. ## 0.4.0 -* Well formatted code. + +- Well formatted code. ## 0.3.3 -* Code formatted -* Updated missing data types + +- Code formatted +- Updated missing data types ## 0.3.1 -* Upgraded readme for ease of usage. -* Enabled strong mode. + +- Upgraded readme for ease of usage. +- Enabled strong mode. ## 0.3.0 -* Moved dynamic return type away and instead give `PurchasedItem`. + +- Moved dynamic return type away and instead give `PurchasedItem`. ## 0.2.3 -* Quickly fixed purchase bug out there in [issue](https://github.com/dooboolab/flutter_inapp_purchase/issues/2). Need much more improvement currently. + +- Quickly fixed purchase bug out there in [issue](https://github.com/dooboolab/flutter_inapp_purchase/issues/2). Need much more improvement currently. ## 0.2.2 -* Migrated packages from FlutterInApp to FlutterInAppPurchase because pub won't get it. + +- Migrated packages from FlutterInApp to FlutterInAppPurchase because pub won't get it. ## 0.1.0 -* Initial release of beta -* Moved code from [react-native-iap](https://github.com/dooboolab/react-native-iap) +- Initial release of beta +- Moved code from [react-native-iap](https://github.com/dooboolab/react-native-iap) diff --git a/README.md b/README.md index 7f1588ed..ebf40d2e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # flutter_inapp_purchase + [![Pub Version](https://img.shields.io/pub/v/flutter_inapp_purchase.svg?style=flat-square)](https://pub.dartlang.org/packages/flutter_inapp_purchase) [![Build Status](https://travis-ci.com/dooboolab/flutter_inapp_purchase.svg?branch=master)](https://travis-ci.com/dooboolab/flutter_inapp_purchase) ![License](https://img.shields.io/badge/license-MIT-blue.svg) [![Coverage Status](https://coveralls.io/repos/github/dooboolab/flutter_inapp_purchase/badge.svg?branch=master)](https://coveralls.io/github/dooboolab/flutter_inapp_purchase?branch=master) - ## Flutter V2 + This packages is compatible with flutter v2 from `4.0.0`. For those who use older version please use `< 4.0.0`. ## Sun Rise :sunrise: + Since many one of you wanted me to keep working on this plugin in [#93](https://github.com/dooboolab/flutter_inapp_purchase/issues/93), I've decided to keep working on current project. I hope many one of you can help me maintain this. Thank you for all your supports in advance :tada:. ~~## Deprecated @@ -17,20 +19,24 @@ Also, thanks for all your supports that made me stubborn to work hard on this pl I'll leave this project as live for those who need time. I'll also try to merge the new `PR`'s and publish to `pub` if there's any further work given to this repo.~~ ## What this plugin do + This is an `In App Purchase` plugin for flutter. This project has been `forked` from [react-native-iap](https://github.com/dooboolab/react-native-iap). We are trying to share same experience of `in-app-purchase` in `flutter` as in `react-native`. We will keep working on it as time goes by just like we did in `react-native-iap`. `PR` is always welcomed. ## Breaking Changes -* Sunrise in `2.0.0` for highly requests from customers on discomfort in what's called an `official` plugin [in_app_purchase](https://pub.dev/packages/in_app_purchase). -* Migrated to Android X in `0.9.0`. Please check the [Migration Guide](#migration-guide). -* There was parameter renaming in `0.5.0` to identify different parameters sent from the device. Please check the readme. + +- Sunrise in `2.0.0` for highly requests from customers on discomfort in what's called an `official` plugin [in_app_purchase](https://pub.dev/packages/in_app_purchase). +- Migrated to Android X in `0.9.0`. Please check the [Migration Guide](#migration-guide). +- There was parameter renaming in `0.5.0` to identify different parameters sent from the device. Please check the readme. ## Migration Guide + To migrate to `0.9.0` you must migrate your Android app to Android X by following the [Migrating to AndroidX Guide](https://developer.android.com/jetpack/androidx/migrate). ## Getting Started + Follow the [Medium Blog](https://medium.com/@dooboolab/flutter-in-app-purchase-7a3fb9345e2a) for the configuration. Follow the [Medium Blog](https://medium.com/bosc-tech-labs-private-limited/how-to-implement-subscriptions-in-app-purchase-in-flutter-7ce8906e608a) to add **subscriptions** in app purchase. @@ -40,34 +46,38 @@ For help getting started with Flutter, view our online For help on editing plugin code, view the [documentation](https://flutter.io/developing-packages/#edit-plugin-package). - ## Methods -| Func | Param | Return | Description | -| :------------ |:---------------:| :---------------:| :-----| -| initConnection | | `String` | Prepare IAP module. Must be called on Android before any other purchase flow methods. In ios, it will simply call `canMakePayments` method and return value.| -| getProducts | `List` Product IDs/skus | `List` | Get a list of products (consumable and non-consumable items, but not subscriptions). Note: On iOS versions earlier than 11.2 this method _will_ return subscriptions if they are included in your list of SKUs. This is because we cannot differentiate between IAP products and subscriptions prior to 11.2. | -| getSubscriptions | `List` Subscription IDs/skus | `List` | Get a list of subscriptions. Note: On iOS this method has the same output as `getProducts`. Because iOS does not differentiate between IAP products and subscriptions. | -| getPurchaseHistory | | `List` | Gets an invetory of purchases made by the user regardless of consumption status (where possible) | -| getAvailablePurchases | | `List` | (aka restore purchase) Get all purchases made by the user (either non-consumable, or haven't been consumed yet) -| getAppStoreInitiatedProducts | | `List` | If the user has initiated a purchase directly on the App Store, the products that the user is attempting to purchase will be returned here. (iOS only) Note: On iOS versions earlier than 11.0 this method will always return an empty list, as the functionality was introduced in v11.0. [See Apple Docs for more info](https://developer.apple.com/documentation/storekit/skpaymenttransactionobserver/2877502-paymentqueue) Always returns an empty list on Android. -| requestSubscription | `String` sku, `String` oldSkuAndroid?, `int` prorationModeAndroid?, `String` obfuscatedAccountIdAndroid?, `String` obfuscatedProfileIdAndroid?, `String` purchaseTokenAndroid? | Null | Create (request) a subscription to a sku. For upgrading/downgrading subscription on Android pass second parameter with current subscription ID, on iOS this is handled automatically by store. `purchaseUpdatedListener` will receive the result. | -| requestPurchase | `String` sku, `String` obfuscatedAccountIdAndroid?, `String` obfuscatedProfileIdAndroid?, `String` purchaseToken? | Null | Request a purchase. `purchaseUpdatedListener` will receive the result. | -| finishTransactionIOS | `String` purchaseTokenAndroid | `PurchaseResult` | Send finishTransaction call to Apple IAP server. Call this function after receipt validation process | -| acknowledgePurchaseAndroid | `String` purchaseToken | `PurchaseResult` | Acknowledge a product (on Android) for `non-consumable` and `subscription` purchase. No-op on iOS. | -| consumePurchaseAndroid | `String` purchaseToken | `PurchaseResult` | Consume a product (on Android) for `consumable` purchase. No-op on iOS. | -| finishTransaction | `String` purchaseToken, `bool` isConsumable? } | `PurchaseResult` | Send finishTransaction call that abstracts all `acknowledgePurchaseAndroid`, `finishTransactionIOS`, `consumePurchaseAndroid` methods. | -| endConnection | | `String` | End billing connection. | -| consumeAllItems | | `String` | Manually consume all items in android. Do NOT call if you have any non-consumables (one time purchase items). No-op on iOS. | -| validateReceiptIos | `Map` receiptBody, `bool` isTest | `http.Response` | Validate receipt for ios. | -| validateReceiptAndroid | `String` packageName, `String` productId, `String` productToken, `String` accessToken, `bool` isSubscription | `http.Response` | Validate receipt for android. | - -Purchase flow in `flutter_inapp_purchase@2.0.0+ ------------------ + +| Func | Param | Return | Description | +| :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| initConnection | | `String` | Prepare IAP module. Must be called on Android before any other purchase flow methods. In ios, it will simply call `canMakePayments` method and return value. | +| getProducts | `List` Product IDs/skus | `List` | Get a list of products (consumable and non-consumable items, but not subscriptions). Note: On iOS versions earlier than 11.2 this method _will_ return subscriptions if they are included in your list of SKUs. This is because we cannot differentiate between IAP products and subscriptions prior to 11.2. | +| getSubscriptions | `List` Subscription IDs/skus | `List` | Get a list of subscriptions. Note: On iOS this method has the same output as `getProducts`. Because iOS does not differentiate between IAP products and subscriptions. | +| getPurchaseHistory | | `List` | Gets an invetory of purchases made by the user regardless of consumption status (where possible) | +| getAvailablePurchases | | `List` | (aka restore purchase) Get all purchases made by the user (either non-consumable, or haven't been consumed yet) | +| getAppStoreInitiatedProducts | | `List` | If the user has initiated a purchase directly on the App Store, the products that the user is attempting to purchase will be returned here. (iOS only) Note: On iOS versions earlier than 11.0 this method will always return an empty list, as the functionality was introduced in v11.0. [See Apple Docs for more info](https://developer.apple.com/documentation/storekit/skpaymenttransactionobserver/2877502-paymentqueue) Always returns an empty list on Android. | +| requestSubscription | `String` sku, `String` oldSkuAndroid?, `int` prorationModeAndroid?, `String` obfuscatedAccountIdAndroid?, `String` obfuscatedProfileIdAndroid?, `String` purchaseTokenAndroid? | Null | Create (request) a subscription to a sku. For upgrading/downgrading subscription on Android pass second parameter with current subscription ID, on iOS this is handled automatically by store. `purchaseUpdatedListener` will receive the result. | +| requestPurchase | `String` sku, `String` obfuscatedAccountIdAndroid?, `String` obfuscatedProfileIdAndroid?, `String` purchaseToken? | Null | Request a purchase. `purchaseUpdatedListener` will receive the result. | +| finishTransactionIOS | `String` purchaseTokenAndroid | `PurchaseResult` | Send finishTransaction call to Apple IAP server. Call this function after receipt validation process | +| acknowledgePurchaseAndroid | `String` purchaseToken | `PurchaseResult` | Acknowledge a product (on Android) for `non-consumable` and `subscription` purchase. No-op on iOS. | +| consumePurchaseAndroid | `String` purchaseToken | `PurchaseResult` | Consume a product (on Android) for `consumable` purchase. No-op on iOS. | +| finishTransaction | `String` purchaseToken, `bool` isConsumable? } | `PurchaseResult` | Send finishTransaction call that abstracts all `acknowledgePurchaseAndroid`, `finishTransactionIOS`, `consumePurchaseAndroid` methods. | +| endConnection | | `String` | End billing connection. | +| consumeAllItems | | `String` | Manually consume all items in android. Do NOT call if you have any non-consumables (one time purchase items). No-op on iOS. | +| validateReceiptIos | `Map` receiptBody, `bool` isTest | `http.Response` | Validate receipt for ios. | +| validateReceiptAndroid | `String` packageName, `String` productId, `String` productToken, `String` accessToken, `bool` isSubscription | `http.Response` | Validate receipt for android. | +| showPromoCodesIOS | | | Show redeem codes in iOS. | + +## Purchase flow in `flutter_inapp_purchase@2.0.0+ + ![purchase-flow-sequence](https://github.com/dooboolab/react-native-iap/blob/master/docs/react-native-iapv3.svg) + > When you've successfully received result from `purchaseUpdated` listener, you'll have to `verify` the purchase either by `acknowledgePurchaseAndroid`, `consumePurchaseAndroid`, `finishTransactionIOS` depending on the purchase types or platforms. You'll have to use `consumePurchaseAndroid` for `consumable` products and `android` and `acknowledgePurchaseAndroid` for `non-consumable` products either `subscription`. For `ios`, there is no differences in `verifying` purchases. You can just call `finishTransaction`. If you do not verify the purchase, it will be refunded within 3 days to users. We recommend you to `verifyReceipt` first before actually finishing transaction. Lastly, if you want to abstract three different methods into one, consider using `finishTransaction` method. ## Data Types -* IAPItem + +- IAPItem + ```dart final String productId; final String price; @@ -96,7 +106,8 @@ Purchase flow in `flutter_inapp_purchase@2.0.0+ final String originalPrice; ``` -* PurchasedItem +- PurchasedItem + ```dart final String productId; final String transactionId; @@ -118,21 +129,22 @@ Purchase flow in `flutter_inapp_purchase@2.0.0+ final String originalTransactionIdentifierIOS; ``` - ## Install -Add ```flutter_inapp_purchase``` as a dependency in pubspec.yaml -For help on adding as a dependency, view the [documentation](https://flutter.io/using-packages/). +Add `flutter_inapp_purchase` as a dependency in pubspec.yaml +For help on adding as a dependency, view the [documentation](https://flutter.io/using-packages/). ## Configuring in app purchase + - Please refer to [Blog](https://medium.com/@dooboolab/react-native-in-app-purchase-121622d26b67). - [Amazon Kindle Fire](KINDLE.md) - ## Usage Guide + #### Android `connect` and `endConnection` -* You should start the billing service in android to use its funtionalities. We recommend you to use `initConnection` getter method in `initState()`. Note that this step is necessary in `ios` also from `flutter_inapp_purchase@2.0.0+` which will also register the `purchaseUpdated` and `purchaseError` `Stream`. + +- You should start the billing service in android to use its funtionalities. We recommend you to use `initConnection` getter method in `initState()`. Note that this step is necessary in `ios` also from `flutter_inapp_purchase@2.0.0+` which will also register the `purchaseUpdated` and `purchaseError` `Stream`. ```dart /// start connection for android @@ -146,14 +158,17 @@ For help on adding as a dependency, view the [documentation](https://flutter.io/ await FlutterInappPurchase.instance.initConnection; } ``` -* You should end the billing service in android when you are done with it. Otherwise it will be keep running in background. We recommend to use this feature in `dispose()`. -* Additionally, we've added `connectionUpdated` stream just in case if you'd like to monitor the connection more thoroughly form `2.0.1`. +- You should end the billing service in android when you are done with it. Otherwise it will be keep running in background. We recommend to use this feature in `dispose()`. + +- Additionally, we've added `connectionUpdated` stream just in case if you'd like to monitor the connection more thoroughly form `2.0.1`. + ``` _conectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) { print('connected: $connected'); }); ``` + > You can see how you can use this in detail in `example` project. ```dart @@ -164,61 +179,68 @@ For help on adding as a dependency, view the [documentation](https://flutter.io/ await FlutterInappPurchase.instance.endConnection; } ``` + #### Get IAP items - ```dart - void getItems () async { - List items = await FlutterInappPurchase.instance.getProducts(_productLists); - for (var item in items) { - print('${item.toString()}'); - this._items.add(item); - } + +```dart +void getItems () async { + List items = await FlutterInappPurchase.instance.getProducts(_productLists); + for (var item in items) { + print('${item.toString()}'); + this._items.add(item); } - ``` +} +``` #### Purchase Item - ```dart - void purchase() { - FlutterInappPurchase.instance.requestPurchase(item.productId); - } - ``` + +```dart +void purchase() { + FlutterInappPurchase.instance.requestPurchase(item.productId); +} +``` #### Register listeners to receive purchase - ```dart - StreamSubscription _purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) { - print('purchase-updated: $productItem'); - }); - StreamSubscription _purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) { - print('purchase-error: $purchaseError'); - }); - ``` +```dart +StreamSubscription _purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen((productItem) { + print('purchase-updated: $productItem'); +}); + +StreamSubscription _purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen((purchaseError) { + print('purchase-error: $purchaseError'); +}); +``` #### Remove listeners when ending connection - ```dart - _purchaseUpdatedSubscription.cancel(); - _purchaseUpdatedSubscription = null; - _purchaseErrorSubscription.cancel(); - _purchaseErrorSubscription = null; - ``` + +```dart +_purchaseUpdatedSubscription.cancel(); +_purchaseUpdatedSubscription = null; +_purchaseErrorSubscription.cancel(); +_purchaseErrorSubscription = null; +``` #### Receipt validation + From `0.7.1`, we support receipt validation. For Android, you need separate json file from the service account to get the `access_token` from `google-apis`, therefore it is impossible to implement serverless. You should have your own backend and get `access_token`. With `access_token` you can simply call `validateReceiptAndroid` method we implemented. Further reading is [here](https://stackoverflow.com/questions/35127086/android-inapp-purchase-receipt-validation-google-play?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa). Currently, serverless receipt validation is possible using `validateReceiptIos` method. The first parameter, you should pass `transactionReceipt` which returns after `requestPurchase`. The second parameter, you should pass whether this is `test` environment. If `true`, it will request to `sandbox` and `false` it will request to `production`. - ```dart - validateReceipt() async { - var receiptBody = { - 'receipt-data': purchased.transactionReceipt, - 'password': '******' - }; - const result = await validateReceiptIos(receiptBody, false); - console.log(result); - } - ``` -For further information, please refer to [guide](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html). +```dart +validateReceipt() async { + var receiptBody = { + 'receipt-data': purchased.transactionReceipt, + 'password': '******' + }; + const result = await validateReceiptIos(receiptBody, false); + console.log(result); +} +``` +For further information, please refer to [guide](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html). #### App Store initiated purchases + When the user starts an in-app purchase in the App Store, the transaction continues in your app, the product will then be added to a list that you can access through the method `getAppStoreInitiatedProducts`. This means you can decide how and when to continue the transaction. To continue the transaction simple use the standard purchase flow from this plugin. @@ -231,11 +253,12 @@ void checkForAppStoreInitiatedProducts() async { } ``` - ## Example + Direct to [example readme](example/README.md) which is just a `cp` from example project. You can test this in real example project. ## ProGuard + If you have enabled proguard you will need to add the following rules to your `proguard-rules.pro` ``` @@ -250,17 +273,21 @@ If you have enabled proguard you will need to add the following rules to your `p ## Q & A #### Can I buy product right away skipping fetching products if I already know productId? + - You can in `Android` but not in `ios`. In `ios` you should always `fetchProducts` first. You can see more info [here](https://medium.com/ios-development-tips-and-tricks/working-with-ios-in-app-purchases-e4b55491479b). #### How do I validate receipt in ios? + - Official doc is [here](https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html). #### How do I validate receipt in android? + - Offical doc is [here](https://developer.android.com/google/play/billing/billing_library_overview). - I've developed this feature for other developers to contribute easily who are aware of these things. The doc says you can also get the `accessToken` via play console without any of your backend server. You can get this by following process. - * Select your app > Services & APIs > "YOUR LICENSE KEY FOR THIS APPLICATION Base64-encoded RSA public key to include in your binary". [reference](https://stackoverflow.com/questions/27132443/how-to-find-my-google-play-services-android-base64-public-key). + - Select your app > Services & APIs > "YOUR LICENSE KEY FOR THIS APPLICATION Base64-encoded RSA public key to include in your binary". [reference](https://stackoverflow.com/questions/27132443/how-to-find-my-google-play-services-android-base64-public-key). #### Invalid productId in ios. + - Please try below and make sure you've done belows. - Steps 1. Completed an effective "Agreements, Tax, and Banking." @@ -273,6 +300,7 @@ If you have enabled proguard you will need to add the following rules to your `p 5. Enable "In-App Purchase" in Xcode "Capabilities" and in Apple Developer -> "App ID" setting. Delete app / Restart device / Quit "store" related processes in Activity Monitor / Xcode Development Provisioning Profile -> Clean -> Build. ## Help Maintenance + I've been maintaining quite many repos these days and burning out slowly. If you could help me cheer up, buying me a cup of coffee will make my life really happy and get much energy out of it. [![Paypal](https://www.paypalobjects.com/webstatic/mktg/Logo/pp-logo-100px.png)](https://paypal.me/dooboolab) diff --git a/android/.project b/android/.project new file mode 100644 index 00000000..eb1ee9ab --- /dev/null +++ b/android/.project @@ -0,0 +1,28 @@ + + + android_ + Project android_ created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + + + 1636786187292 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..4a2e916b --- /dev/null +++ b/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.0-rc-1)) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/example/android/.project b/example/android/.project new file mode 100644 index 00000000..b85a0c28 --- /dev/null +++ b/example/android/.project @@ -0,0 +1,28 @@ + + + android + Project android created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + + + 1636786187287 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/example/android/.settings/org.eclipse.buildship.core.prefs b/example/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..61544433 --- /dev/null +++ b/example/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/ios/Classes/FlutterInappPurchasePlugin.m b/ios/Classes/FlutterInappPurchasePlugin.m index b7832ee5..3c871840 100644 --- a/ios/Classes/FlutterInappPurchasePlugin.m +++ b/ios/Classes/FlutterInappPurchasePlugin.m @@ -235,6 +235,16 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self getAvailableItems:result]; } else if ([@"getAppStoreInitiatedProducts" isEqualToString:call.method]) { [self getAppStoreInitiatedProducts:result]; + } else if ([@"showRedeemCodesIOS" isEqualToString:call.method]) { +#if __IPHONE_12_2 + if (@available(iOS 14.0, *)) { + [[SKPaymentQueue defaultQueue] presentCodeRedemptionSheet]; + result(@"present PromoCodes"); + return; + } +#endif + result(@"the functionality is available starting from ios 14.0"); + } else { result(FlutterMethodNotImplemented); } diff --git a/lib/flutter_inapp_purchase.dart b/lib/flutter_inapp_purchase.dart index f8860fe4..b5ce95d5 100644 --- a/lib/flutter_inapp_purchase.dart +++ b/lib/flutter_inapp_purchase.dart @@ -649,6 +649,14 @@ class FlutterInappPurchase { _purchaseErrorController = null; } } + + Future showPromoCodesIOS() async { + if (_platform.isIOS) { + return await _channel.invokeMethod('showRedeemCodesIOS'); + } + throw PlatformException( + code: _platform.operatingSystem, message: "platform not supported"); + } } /// A list of valid values for ProrationMode parameter diff --git a/pubspec.yaml b/pubspec.yaml index 176d283a..3578f446 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,28 +1,28 @@ name: flutter_inapp_purchase -description: In App Purchase plugin for flutter. This project has been forked by react-native-iap and we are willing to share same experience with that on react-native. -version: 5.0.3 +description: In App Purchase plugin for flutter. This project has been forked by + react-native-iap and we are willing to share same experience with that on + react-native. +version: 5.0.4 homepage: https://github.com/dooboolab/flutter_inapp_purchase/blob/master/pubspec.yaml environment: sdk: ">=2.12.0 <3.0.0" flutter: ">=2.0.0" dependencies: - http: ^0.13.0 - meta: ^1.3.0 flutter: sdk: flutter - + http: ^0.13.4 + meta: ^1.7.0 platform: ^3.0.0 dev_dependencies: - test: ^1.16.5 - mockito: ^5.0.1 flutter_test: sdk: flutter + mockito: ^5.0.1 + test: ^1.16.5 # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec - # The following section is specific to Flutter. flutter: plugin: