diff --git a/CHANGELOG.md b/CHANGELOG.md index d971052d..f99d188a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 5.0.2 ++ 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) diff --git a/ios/Classes/FlutterInappPurchasePlugin.m b/ios/Classes/FlutterInappPurchasePlugin.m index 881e1768..ce168a38 100644 --- a/ios/Classes/FlutterInappPurchasePlugin.m +++ b/ios/Classes/FlutterInappPurchasePlugin.m @@ -70,6 +70,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } } else if ([@"buyProduct" isEqualToString:call.method]) { NSString* identifier = (NSString*)call.arguments[@"sku"]; + NSString* usernameHash = (NSString*)call.arguments[@"forUser"]; SKProduct *product; for (SKProduct *p in validProducts) { @@ -80,6 +81,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } if (product) { SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; + payment.applicationUsername = usernameHash; [[SKPaymentQueue defaultQueue] addPayment:payment]; } else { NSDictionary *err = [NSDictionary dictionaryWithObjectsAndKeys: diff --git a/lib/flutter_inapp_purchase.dart b/lib/flutter_inapp_purchase.dart index db62630b..80fe0acb 100644 --- a/lib/flutter_inapp_purchase.dart +++ b/lib/flutter_inapp_purchase.dart @@ -99,7 +99,6 @@ class FlutterInappPurchase { /// /// `iOS` also returns subscriptions. Future> getProducts(List skus) async { - if (skus == null || skus.contains(null)) return []; skus = skus.toList(); if (_platform.isAndroid) { dynamic result = await _channel.invokeMethod( @@ -128,7 +127,6 @@ class FlutterInappPurchase { /// /// `iOS` also returns non-subscription products. Future> getSubscriptions(List skus) async { - if (skus == null || skus.contains(null)) return []; skus = skus.toList(); if (_platform.isAndroid) { dynamic result = await _channel.invokeMethod( @@ -219,14 +217,14 @@ class FlutterInappPurchase { /// Request a purchase on `Android` or `iOS`. /// Result will be received in `purchaseUpdated` listener or `purchaseError` listener. - /// + /// /// Check [AndroidProrationMode] for valid proration values /// Identical to [requestSubscription] on `iOS`. Future requestPurchase( String sku, { - String? obfuscatedAccountIdAndroid, - String? obfuscatedProfileIdAndroid, + String? obfuscatedAccountId, String? purchaseTokenAndroid, + String? obfuscatedProfileIdAndroid, }) async { if (_platform.isAndroid) { return await _channel.invokeMethod('buyItemByType', { @@ -234,13 +232,14 @@ class FlutterInappPurchase { 'sku': sku, 'oldSku': null, 'prorationMode': -1, - 'obfuscatedAccountId': obfuscatedAccountIdAndroid, + 'obfuscatedAccountId': obfuscatedAccountId, 'obfuscatedProfileId': obfuscatedProfileIdAndroid, 'purchaseToken': purchaseTokenAndroid, }); } else if (_platform.isIOS) { return await _channel.invokeMethod('buyProduct', { 'sku': sku, + 'forUser': obfuscatedAccountId, }); } throw PlatformException( @@ -251,7 +250,7 @@ class FlutterInappPurchase { /// Result will be received in `purchaseUpdated` listener or `purchaseError` listener. /// /// **NOTICE** second parameter is required on `Android`. - /// + /// /// Check [AndroidProrationMode] for valid proration values /// Identical to [requestPurchase] on `iOS`. Future requestSubscription( @@ -506,7 +505,6 @@ class FlutterInappPurchase { Duration duration: const Duration(days: 30), Duration grace: const Duration(days: 3), }) async { - assert(sku != null); if (_platform.isIOS) { var history = await (getPurchaseHistory() as FutureOr>); @@ -548,9 +546,6 @@ class FlutterInappPurchase { required Map receiptBody, bool isTest = true, }) async { - assert(receiptBody != null); - assert(isTest != null); - final String url = isTest ? 'https://sandbox.itunes.apple.com/verifyReceipt' : 'https://buy.itunes.apple.com/verifyReceipt'; @@ -587,11 +582,6 @@ class FlutterInappPurchase { required String accessToken, bool isSubscription = false, }) async { - assert(packageName != null); - assert(productId != null); - assert(productToken != null); - assert(accessToken != null); - final String type = isSubscription ? 'subscriptions' : 'products'; final String url = 'https://www.googleapis.com/androidpublisher/v3/applications/$packageName/purchases/$type/$productId/tokens/$productToken?access_token=$accessToken'; @@ -642,7 +632,7 @@ class FlutterInappPurchase { throw new ArgumentError('Unknown method ${call.method}'); } return Future.value(null); - } as Future Function(MethodCall)?); + }); } Future _removePurchaseListener() async { @@ -663,7 +653,7 @@ class FlutterInappPurchase { /// A list of valid values for ProrationMode parameter /// https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode -class AndroidProrationMode{ +class AndroidProrationMode { /// Replacement takes effect when the old plan expires, and the new price will be charged at the same time. /// https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode#DEFERRED static const int DEFERRED = 4; @@ -682,4 +672,4 @@ class AndroidProrationMode{ /// https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode#unknown_subscription_upgrade_downgrade_policy static const int UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY = 0; -} \ No newline at end of file +} diff --git a/lib/modules.dart b/lib/modules.dart index 15ee3f2f..51a490bf 100644 --- a/lib/modules.dart +++ b/lib/modules.dart @@ -56,11 +56,14 @@ class IAPItem { json['introductoryPriceNumberOfPeriodsIOS'] as String?, introductoryPriceSubscriptionPeriodIOS = json['introductoryPriceSubscriptionPeriodIOS'] as String?, - introductoryPriceNumberIOS = json['introductoryPriceNumberIOS'] as String?, + introductoryPriceNumberIOS = + json['introductoryPriceNumberIOS'] as String?, subscriptionPeriodNumberIOS = json['subscriptionPeriodNumberIOS'] as String?, - subscriptionPeriodUnitIOS = json['subscriptionPeriodUnitIOS'] as String?, - subscriptionPeriodAndroid = json['subscriptionPeriodAndroid'] as String?, + subscriptionPeriodUnitIOS = + json['subscriptionPeriodUnitIOS'] as String?, + subscriptionPeriodAndroid = + json['subscriptionPeriodAndroid'] as String?, introductoryPriceCyclesAndroid = json['introductoryPriceCyclesAndroid'] as int?, introductoryPricePeriodAndroid = @@ -93,13 +96,18 @@ class IAPItem { data['subscriptionPeriodNumberIOS'] = this.subscriptionPeriodNumberIOS; data['subscriptionPeriodUnitIOS'] = this.subscriptionPeriodUnitIOS; - data['introductoryPricePaymentModeIOS'] = this.introductoryPricePaymentModeIOS; - data['introductoryPriceNumberOfPeriodsIOS'] = this.introductoryPriceNumberOfPeriodsIOS; - data['introductoryPriceSubscriptionPeriodIOS'] = this.introductoryPriceSubscriptionPeriodIOS; + data['introductoryPricePaymentModeIOS'] = + this.introductoryPricePaymentModeIOS; + data['introductoryPriceNumberOfPeriodsIOS'] = + this.introductoryPriceNumberOfPeriodsIOS; + data['introductoryPriceSubscriptionPeriodIOS'] = + this.introductoryPriceSubscriptionPeriodIOS; data['subscriptionPeriodAndroid'] = this.subscriptionPeriodAndroid; - data['introductoryPriceCyclesAndroid'] = this.introductoryPriceCyclesAndroid; - data['introductoryPricePeriodAndroid'] = this.introductoryPricePeriodAndroid; + data['introductoryPriceCyclesAndroid'] = + this.introductoryPriceCyclesAndroid; + data['introductoryPricePeriodAndroid'] = + this.introductoryPricePeriodAndroid; data['freeTrialPeriodAndroid'] = this.freeTrialPeriodAndroid; data['signatureAndroid'] = this.signatureAndroid; @@ -133,8 +141,7 @@ class IAPItem { 'iconUrl: $iconUrl, ' 'originalJson: $originalJson, ' 'originalPrice: $originalPrice, ' - 'discounts: $discountsIOS, ' - ; + 'discounts: $discountsIOS, '; } static List? _extractDiscountIOS(dynamic json) { @@ -144,12 +151,12 @@ class IAPItem { if (list != null) { discounts = list .map( - (dynamic discount) => DiscountIOS.fromJSON(discount as Map), - ) + (dynamic discount) => + DiscountIOS.fromJSON(discount as Map), + ) .toList(); } - return discounts; } } @@ -194,8 +201,7 @@ class DiscountIOS { 'price: $price, ' 'localizedPrice: $localizedPrice, ' 'paymentMode: $paymentMode, ' - 'subscriptionPeriod: $subscriptionPeriod, ' - ; + 'subscriptionPeriod: $subscriptionPeriod, '; } } @@ -229,7 +235,6 @@ class PurchasedItem { transactionReceipt = json['transactionReceipt'] as String?, purchaseToken = json['purchaseToken'] as String?, orderId = json['orderId'] as String?, - dataAndroid = json['dataAndroid'] as String?, signatureAndroid = json['signatureAndroid'] as String?, isAcknowledgedAndroid = json['isAcknowledgedAndroid'] as bool?, @@ -237,7 +242,6 @@ class PurchasedItem { purchaseStateAndroid = _decodePurchaseStateAndroid(json['purchaseStateAndroid'] as int?), originalJsonAndroid = json['originalJsonAndroid'] as String?, - originalTransactionDateIOS = _extractDate(json['originalTransactionDateIOS']), originalTransactionIdentifierIOS = @@ -254,6 +258,7 @@ class PurchasedItem { 'transactionReceipt: $transactionReceipt, ' 'purchaseToken: $purchaseToken, ' 'orderId: $orderId, ' + /// android specific 'dataAndroid: $dataAndroid, ' 'signatureAndroid: $signatureAndroid, ' @@ -261,6 +266,7 @@ class PurchasedItem { 'autoRenewingAndroid: $autoRenewingAndroid, ' 'purchaseStateAndroid: $purchaseStateAndroid, ' 'originalJsonAndroid: $originalJsonAndroid, ' + /// ios specific 'originalTransactionDateIOS: ${originalTransactionDateIOS?.toIso8601String()}, ' 'originalTransactionIdentifierIOS: $originalTransactionIdentifierIOS, ' @@ -286,7 +292,7 @@ class PurchaseResult { this.responseCode, this.debugMessage, this.code, - this.message + this.message, }); PurchaseResult.fromJSON(Map json) @@ -296,23 +302,21 @@ class PurchaseResult { message = json['message'] as String?; Map toJson() => { - "responseCode": responseCode ?? 0, - "debugMessage": debugMessage ?? '', - "code": code ?? '', - "message": message ?? '', - }; + "responseCode": responseCode ?? 0, + "debugMessage": debugMessage ?? '', + "code": code ?? '', + "message": message ?? '', + }; @override String toString() { return 'responseCode: $responseCode, ' 'debugMessage: $debugMessage, ' 'code: $code, ' - 'message: $message' - ; + 'message: $message'; } } - class ConnectionResult { final bool? connected; @@ -324,13 +328,12 @@ class ConnectionResult { : connected = json['connected'] as bool?; Map toJson() => { - "connected": connected ?? false, - }; + "connected": connected ?? false, + }; @override String toString() { - return 'connected: $connected' - ; + return 'connected: $connected'; } } diff --git a/pubspec.yaml b/pubspec.yaml index efb5d77f..2a6309c5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,9 +1,9 @@ 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.1 +version: 5.0.2 homepage: https://github.com/dooboolab/flutter_inapp_purchase/blob/master/pubspec.yaml environment: - sdk: '>=2.12.0 <3.0.0' + sdk: ">=2.12.0 <3.0.0" flutter: ">=2.0.0 <3.0.0" dependencies: @@ -20,7 +20,6 @@ dev_dependencies: flutter_test: sdk: flutter - # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec @@ -33,7 +32,6 @@ flutter: pluginClass: FlutterInappPurchasePlugin ios: pluginClass: FlutterInappPurchasePlugin - # To add assets to your plugin package, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg @@ -44,7 +42,6 @@ flutter: # # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.io/assets-and-images/#resolution-aware. - # To add custom fonts to your plugin package, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a diff --git a/test/flutter_inapp_purchase_test.dart b/test/flutter_inapp_purchase_test.dart index 79be4848..2a8aa0f6 100644 --- a/test/flutter_inapp_purchase_test.dart +++ b/test/flutter_inapp_purchase_test.dart @@ -910,6 +910,7 @@ void main() { }; final String sku = "testsku"; + final String forUser = "testObfuscatedUser"; setUp(() { FlutterInappPurchase(FlutterInappPurchase.private( @@ -927,12 +928,16 @@ void main() { }); test('invokes correct method', () async { - await FlutterInappPurchase.instance.requestPurchase(sku); + await FlutterInappPurchase.instance.requestPurchase( + sku, + obfuscatedAccountId: forUser, + ); expect(log, [ isMethodCall( 'buyProduct', arguments: { 'sku': sku, + 'forUser': forUser, }, ), ]); @@ -1063,6 +1068,7 @@ void main() { group('for iOS', () { final List log = []; final String sku = "testsku"; + final String forUser = "testObfuscatedUser"; final dynamic result = { "transactionDate": "1552824902000", "transactionId": "testTransactionId", @@ -1093,12 +1099,16 @@ void main() { }); test('invokes correct method', () async { - await FlutterInappPurchase.instance.requestPurchase(sku); + await FlutterInappPurchase.instance.requestPurchase( + sku, + obfuscatedAccountId: forUser, + ); expect(log, [ isMethodCall( 'buyProduct', arguments: { 'sku': sku, + 'forUser': forUser, }, ), ]);