diff --git a/CHANGELOG.md b/CHANGELOG.md index f99d188a..921abb0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 5.5.0 ++ DEPRECATED: removed dataAndroid and originalJsonAndroid from PurchasedItem. Use transaction receipt instead as it is the same object. + ## 5.0.2 + Replaced obfuscatedAccountIdAndroid with obfuscatedAccountId in request purchase method [#299](https://github.com/dooboolab/flutter_inapp_purchase/pull/299) @@ -8,7 +11,7 @@ + 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 an int [#268](https://github.com/dooboolab/flutter_inapp_purchase/pull/268) ## 4.0.1 + `platform` dep version `>=2.0.0 <4.0.0` diff --git a/lib/flutter_inapp_purchase.dart b/lib/flutter_inapp_purchase.dart index 80fe0acb..af9effe5 100644 --- a/lib/flutter_inapp_purchase.dart +++ b/lib/flutter_inapp_purchase.dart @@ -50,6 +50,11 @@ class FlutterInappPurchase { return instance; } + PlatformException get _platformException => PlatformException( + code: _platform.operatingSystem, + message: "platform not supported", + ); + @visibleForTesting FlutterInappPurchase.private(Platform platform, {http.Client? client}) : _pf = platform, @@ -59,8 +64,7 @@ class FlutterInappPurchase { /// /// eg, `Android 5.1.1` Future get platformVersion async { - final String? version = await _channel.invokeMethod('getPlatformVersion'); - return version; + return _channel.invokeMethod('getPlatformVersion'); } /// Consumes all items on `Android`. @@ -73,8 +77,8 @@ class FlutterInappPurchase { } else if (_platform.isIOS) { return 'no-ops in ios'; } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// InitConnection prepare iap features for both `Android` and `iOS`. @@ -84,52 +88,47 @@ class FlutterInappPurchase { Future get initConnection async { if (_platform.isAndroid) { await _setPurchaseListener(); - final String? result = await _channel.invokeMethod('initConnection'); - return result; + return _channel.invokeMethod('initConnection'); } else if (_platform.isIOS) { await _setPurchaseListener(); - final String? result = await _channel.invokeMethod('canMakePayments'); - return result; + return _channel.invokeMethod('canMakePayments'); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Retrieves a list of products from the store on `Android` and `iOS`. /// /// `iOS` also returns subscriptions. Future> getProducts(List skus) async { - skus = skus.toList(); + if (skus.isEmpty) return []; if (_platform.isAndroid) { - dynamic result = await _channel.invokeMethod( + final result = await _channel.invokeListMethod( 'getItemsByType', { 'type': EnumUtil.getValueString(_TypeInApp.inapp), 'skus': skus, }, ); - return extractItems(result); + return extractItems(result ?? []); } else if (_platform.isIOS) { - dynamic result = await _channel.invokeMethod( + final result = await _channel.invokeListMethod( 'getItems', - { - 'skus': skus, - }, + {'skus': skus}, ); - - return extractItems(json.encode(result)); + return extractItems(result ?? []); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Retrieves subscriptions on `Android` and `iOS`. /// /// `iOS` also returns non-subscription products. Future> getSubscriptions(List skus) async { - skus = skus.toList(); + if (skus.isEmpty) return []; if (_platform.isAndroid) { - dynamic result = await _channel.invokeMethod( + final result = await _channel.invokeListMethod( 'getItemsByType', { 'type': EnumUtil.getValueString(_TypeInApp.subs), @@ -137,82 +136,73 @@ class FlutterInappPurchase { }, ); - return extractItems(result); + return extractItems(result ?? []); } else if (_platform.isIOS) { - dynamic result = await _channel.invokeMethod( + final result = await _channel.invokeListMethod( 'getItems', - { - 'skus': skus, - }, + {'skus': skus}, ); - return extractItems(json.encode(result)); + return extractItems(result ?? []); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Retrieves the user's purchase history on `Android` and `iOS` regardless of consumption status. /// /// Purchase history includes all types of products. /// Identical to [getAvailablePurchases] on `iOS`. - Future?> getPurchaseHistory() async { + Future> getPurchaseHistory() async { if (_platform.isAndroid) { - Future getInappPurchaseHistory = _channel.invokeMethod( + final getInappPurchaseHistory = _channel.invokeListMethod( 'getPurchaseHistoryByType', - { - 'type': EnumUtil.getValueString(_TypeInApp.inapp), - }, + {'type': EnumUtil.getValueString(_TypeInApp.inapp)}, ); - Future getSubsPurchaseHistory = _channel.invokeMethod( + final getSubsPurchaseHistory = _channel.invokeListMethod( 'getPurchaseHistoryByType', - { - 'type': EnumUtil.getValueString(_TypeInApp.subs), - }, + {'type': EnumUtil.getValueString(_TypeInApp.subs)}, ); - List results = + final results = await Future.wait([getInappPurchaseHistory, getSubsPurchaseHistory]); - return results.reduce((result1, result2) => - extractPurchased(result1)! + extractPurchased(result2)!); + return extractPurchased((results[0] ?? []) + (results[1] ?? [])); } else if (_platform.isIOS) { - dynamic result = await _channel.invokeMethod('getAvailableItems'); + final result = await _channel.invokeListMethod('getAvailableItems'); - return extractPurchased(json.encode(result)); + return extractPurchased(result ?? []); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Get all non-consumed purchases made on `Android` and `iOS`. /// /// This is identical to [getPurchaseHistory] on `iOS` - Future?> getAvailablePurchases() async { + Future> getAvailablePurchases() async { if (_platform.isAndroid) { - dynamic result1 = await _channel.invokeMethod( + final getInAppAvailable = _channel.invokeListMethod( 'getAvailableItemsByType', - { - 'type': EnumUtil.getValueString(_TypeInApp.inapp), - }, + {'type': EnumUtil.getValueString(_TypeInApp.inapp)}, ); - dynamic result2 = await _channel.invokeMethod( + final getSubsAvailable = _channel.invokeListMethod( 'getAvailableItemsByType', - { - 'type': EnumUtil.getValueString(_TypeInApp.subs), - }, + {'type': EnumUtil.getValueString(_TypeInApp.subs)}, ); - return extractPurchased(result1)! + extractPurchased(result2)!; + final results = await Future.wait([getInAppAvailable, getSubsAvailable]); + + return extractPurchased((results[0] ?? []) + (results[1] ?? [])); } else if (_platform.isIOS) { - dynamic result = await _channel.invokeMethod('getAvailableItems'); + final result = await _channel.invokeListMethod('getAvailableItems'); - return extractPurchased(json.encode(result)); + return extractPurchased(result ?? []); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Request a purchase on `Android` or `iOS`. @@ -227,7 +217,7 @@ class FlutterInappPurchase { String? obfuscatedProfileIdAndroid, }) async { if (_platform.isAndroid) { - return await _channel.invokeMethod('buyItemByType', { + return _channel.invokeMethod('buyItemByType', { 'type': EnumUtil.getValueString(_TypeInApp.inapp), 'sku': sku, 'oldSku': null, @@ -237,13 +227,13 @@ class FlutterInappPurchase { 'purchaseToken': purchaseTokenAndroid, }); } else if (_platform.isIOS) { - return await _channel.invokeMethod('buyProduct', { + return _channel.invokeMethod('buyProduct', { 'sku': sku, 'forUser': obfuscatedAccountId, }); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Request a subscription on `Android` or `iOS`. @@ -262,7 +252,7 @@ class FlutterInappPurchase { String? purchaseTokenAndroid, }) async { if (_platform.isAndroid) { - return await _channel.invokeMethod('buyItemByType', { + return _channel.invokeMethod('buyItemByType', { 'type': EnumUtil.getValueString(_TypeInApp.subs), 'sku': sku, 'oldSku': oldSkuAndroid, @@ -272,12 +262,12 @@ class FlutterInappPurchase { 'purchaseToken': purchaseTokenAndroid, }); } else if (_platform.isIOS) { - return await _channel.invokeMethod('buyProduct', { + return _channel.invokeMethod('buyProduct', { 'sku': sku, }); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Add Store Payment (iOS only) @@ -286,8 +276,7 @@ class FlutterInappPurchase { /// @returns {Future} Future getPromotedProductIOS() async { if (_platform.isIOS) { - String? result = await _channel.invokeMethod('getPromotedProduct'); - return result; + return _channel.invokeMethod('getPromotedProduct'); } return null; } @@ -300,8 +289,8 @@ class FlutterInappPurchase { if (_platform.isIOS) { return await _channel.invokeMethod('requestPromotedProduct'); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Buy product with offer @@ -313,15 +302,17 @@ class FlutterInappPurchase { Map withOffer, ) async { if (_platform.isIOS) { - return await _channel - .invokeMethod('requestProductWithOfferIOS', { - 'sku': sku, - 'forUser': forUser, - 'withOffer': withOffer, - }); + return _channel.invokeMethod( + 'requestProductWithOfferIOS', + { + 'sku': sku, + 'forUser': forUser, + 'withOffer': withOffer, + }, + ); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Buy product with quantity @@ -332,14 +323,13 @@ class FlutterInappPurchase { int quantity, ) async { if (_platform.isIOS) { - return await _channel - .invokeMethod('requestPurchaseWithQuantity', { - 'sku': sku, - 'quantity': quantity, - }); + return _channel.invokeMethod( + 'requestPurchaseWithQuantity', + {'sku': sku, 'quantity': quantity}, + ); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Get the pending purchases in IOS. @@ -347,11 +337,9 @@ class FlutterInappPurchase { /// @returns {Future>} Future?> getPendingTransactionsIOS() async { if (_platform.isIOS) { - dynamic result = await _channel.invokeMethod( - 'getPendingTransactions', - ); + final result = await _channel.invokeListMethod('getPendingTransactions'); - return extractPurchased(json.encode(result)); + return extractPurchased(result ?? []); } return []; } @@ -361,17 +349,15 @@ class FlutterInappPurchase { /// No effect on `iOS`, whose iap purchases are consumed at the time of purchase. Future acknowledgePurchaseAndroid(String token) async { if (_platform.isAndroid) { - String? result = - await _channel.invokeMethod('acknowledgePurchase', { - 'token': token, - }); - - return result; + return _channel.invokeMethod( + 'acknowledgePurchase', + {'token': token}, + ); } else if (_platform.isIOS) { return 'no-ops in ios'; } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Consumes a purchase on `Android`. @@ -388,33 +374,27 @@ class FlutterInappPurchase { /// break; Future consumePurchaseAndroid(String token) async { if (_platform.isAndroid) { - String? result = - await _channel.invokeMethod('consumeProduct', { + return _channel.invokeMethod('consumeProduct', { 'token': token, }); - return result; } else if (_platform.isIOS) { return 'no-ops in ios'; } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// End Play Store connection on `Android` and remove iap observer in `iOS`. /// /// Absolutely necessary to call this when done with the payment. Future get endConnection async { - if (_platform.isAndroid) { - final String? result = await _channel.invokeMethod('endConnection'); - _removePurchaseListener(); - return result; - } else if (_platform.isIOS) { - final String? result = await _channel.invokeMethod('endConnection'); + if (_platform.isAndroid || _platform.isIOS) { + final result = await _channel.invokeMethod('endConnection'); _removePurchaseListener(); return result; } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Finish a transaction on `iOS`. @@ -426,44 +406,43 @@ class FlutterInappPurchase { if (_platform.isAndroid) { return 'no ops in android'; } else if (_platform.isIOS) { - String? result = - await _channel.invokeMethod('finishTransaction', { - 'transactionIdentifier': transactionId, - }); - return result; + return _channel.invokeMethod( + 'finishTransaction', + {'transactionIdentifier': transactionId}, + ); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Finish a transaction on both `android` and `iOS`. /// /// Call this after finalizing server-side validation of the reciept. - Future finishTransaction(PurchasedItem purchasedItem, + Future finishTransaction(PurchasedItem purchasedItem, {bool isConsumable = false}) async { if (_platform.isAndroid) { if (isConsumable) { - String? result = - await _channel.invokeMethod('consumeProduct', { - 'token': purchasedItem.purchaseToken, - }); - return result; + final result = await _channel.invokeMapMethod( + 'consumeProduct', + {'token': purchasedItem.purchaseToken}, + ); + return PurchaseResult.fromJSON(Map.from(result ?? {})); } else { - String? result = await _channel - .invokeMethod('acknowledgePurchase', { - 'token': purchasedItem.purchaseToken, - }); - return result; + final result = await _channel.invokeMapMethod( + 'acknowledgePurchase', + {'token': purchasedItem.purchaseToken}, + ); + return PurchaseResult.fromJSON(Map.from(result ?? {})); } } else if (_platform.isIOS) { - String? result = - await _channel.invokeMethod('finishTransaction', { - 'transactionIdentifier': purchasedItem.transactionId, - }); - return result; + final result = await _channel.invokeMapMethod( + 'finishTransaction', + {'transactionIdentifier': purchasedItem.transactionId}, + ); + return PurchaseResult.fromJSON(Map.from(result ?? {})); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Clear all remaining transaction on `iOS`. @@ -473,11 +452,10 @@ class FlutterInappPurchase { if (_platform.isAndroid) { return 'no-ops in android.'; } else if (_platform.isIOS) { - String? result = await _channel.invokeMethod('clearTransaction'); - return result; + return _channel.invokeMethod('clearTransaction'); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Retrieves a list of products that have been attempted to purchase through the App Store `iOS` only. @@ -486,13 +464,13 @@ class FlutterInappPurchase { if (_platform.isAndroid) { return []; } else if (_platform.isIOS) { - dynamic result = - await _channel.invokeMethod('getAppStoreInitiatedProducts'); + final result = + await _channel.invokeListMethod('getAppStoreInitiatedProducts'); - return extractItems(json.encode(result)); + return extractItems(result ?? []); } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Check if a subscription is active on `Android` and `iOS`. @@ -506,10 +484,9 @@ class FlutterInappPurchase { Duration grace: const Duration(days: 3), }) async { if (_platform.isIOS) { - var history = - await (getPurchaseHistory() as FutureOr>); + final history = await getPurchaseHistory(); - for (var purchase in history) { + for (final purchase in history) { Duration difference = DateTime.now().difference(purchase.transactionDate!); if (difference.inMinutes <= (duration + grace).inMinutes && @@ -518,17 +495,16 @@ class FlutterInappPurchase { return false; } else if (_platform.isAndroid) { - var purchases = - await (getAvailablePurchases() as FutureOr>); + var purchases = await getAvailablePurchases(); - for (var purchase in purchases) { + for (final purchase in purchases) { if (purchase.productId == sku) return true; } return false; } - throw PlatformException( - code: _platform.operatingSystem, message: "platform not supported"); + + throw _platformException; } /// Validate receipt in ios @@ -613,15 +589,15 @@ class FlutterInappPurchase { _channel.setMethodCallHandler((MethodCall call) { switch (call.method) { case "purchase-updated": - Map result = jsonDecode(call.arguments); + final result = Map.from(call.arguments ?? {}); _purchaseController!.add(new PurchasedItem.fromJSON(result)); break; case "purchase-error": - Map result = jsonDecode(call.arguments); + final result = Map.from(call.arguments ?? {}); _purchaseErrorController!.add(new PurchaseResult.fromJSON(result)); break; case "connection-updated": - Map result = jsonDecode(call.arguments); + final result = Map.from(call.arguments ?? {}); _connectionController!.add(new ConnectionResult.fromJSON(result)); break; case "iap-promoted-product": diff --git a/lib/modules.dart b/lib/modules.dart index 51a490bf..d111bc49 100644 --- a/lib/modules.dart +++ b/lib/modules.dart @@ -39,7 +39,7 @@ class IAPItem { final String? iconUrl; final String? originalJson; - final String originalPrice; + final double originalPrice; /// Create [IAPItem] from a Map that was previously JSON formatted IAPItem.fromJSON(Map json) @@ -72,7 +72,7 @@ class IAPItem { signatureAndroid = json['signatureAndroid'] as String?, iconUrl = json['iconUrl'] as String?, originalJson = json['originalJson'] as String?, - originalPrice = json['originalPrice'].toString(), + originalPrice = json['originalPrice'], discountsIOS = _extractDiscountIOS(json['discounts']); /// wow, i find if i want to save a IAPItem, there is not "toJson" to cast it into String... @@ -215,12 +215,14 @@ class PurchasedItem { final String? orderId; // Android only - final String? dataAndroid; final String? signatureAndroid; final bool? autoRenewingAndroid; final bool? isAcknowledgedAndroid; final PurchaseState? purchaseStateAndroid; - final String? originalJsonAndroid; + @deprecated + String? get originalJsonAndroid => transactionReceipt; + @deprecated + String? get dataAndroid => transactionReceipt; // iOS only final DateTime? originalTransactionDateIOS; @@ -235,13 +237,11 @@ 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?, autoRenewingAndroid = json['autoRenewingAndroid'] as bool?, purchaseStateAndroid = _decodePurchaseStateAndroid(json['purchaseStateAndroid'] as int?), - originalJsonAndroid = json['originalJsonAndroid'] as String?, originalTransactionDateIOS = _extractDate(json['originalTransactionDateIOS']), originalTransactionIdentifierIOS = @@ -260,12 +260,10 @@ class PurchasedItem { 'orderId: $orderId, ' /// android specific - 'dataAndroid: $dataAndroid, ' 'signatureAndroid: $signatureAndroid, ' 'isAcknowledgedAndroid: $isAcknowledgedAndroid, ' 'autoRenewingAndroid: $autoRenewingAndroid, ' 'purchaseStateAndroid: $purchaseStateAndroid, ' - 'originalJsonAndroid: $originalJsonAndroid, ' /// ios specific 'originalTransactionDateIOS: ${originalTransactionDateIOS?.toIso8601String()}, ' @@ -275,10 +273,9 @@ class PurchasedItem { /// Coerce miliseconds since epoch in double, int, or String into DateTime format static DateTime? _extractDate(dynamic timestamp) { - if (timestamp == null) return null; + if (timestamp == null || timestamp is! int) return null; - int _toInt() => double.parse(timestamp.toString()).toInt(); - return DateTime.fromMillisecondsSinceEpoch(_toInt()); + return DateTime.fromMillisecondsSinceEpoch(timestamp); } } diff --git a/lib/utils.dart b/lib/utils.dart index 96cff361..10223d8e 100644 --- a/lib/utils.dart +++ b/lib/utils.dart @@ -1,48 +1,29 @@ import 'modules.dart'; -import 'dart:convert'; -List extractItems(dynamic result) { - List list = json.decode(result.toString()); - List products = list - .map( - (dynamic product) => IAPItem.fromJSON(product as Map), - ) +List extractItems(List result) { + List products = result + .map((map) => IAPItem.fromJSON(Map.from(map))) .toList(); return products; } -List? extractPurchased(dynamic result) { - List? decoded = json - .decode(result.toString()) +List extractPurchased(List result) { + final purhcased = result .map( - (dynamic product) => - PurchasedItem.fromJSON(product as Map), - ) - .toList(); - - return decoded; -} - - -List? extractResult(dynamic result) { - List? decoded = json - .decode(result.toString()) - .map( - (dynamic product) => - PurchaseResult.fromJSON(product as Map), + (product) => PurchasedItem.fromJSON(Map.from(product)), ) .toList(); - return decoded; + return purhcased; } - class EnumUtil { /// return enum value /// /// example: enum Type {Hoge}, /// String value = EnumUtil.getValueString(Type.Hoge); /// assert(value == "Hoge"); - static String getValueString(dynamic enumType) => enumType.toString().split('.')[1]; + static String getValueString(dynamic enumType) => + enumType.toString().split('.')[1]; } diff --git a/test/flutter_inapp_purchase_test.dart b/test/flutter_inapp_purchase_test.dart index 2a8aa0f6..2c7eda32 100644 --- a/test/flutter_inapp_purchase_test.dart +++ b/test/flutter_inapp_purchase_test.dart @@ -1,4 +1,3 @@ -import 'dart:async'; import 'dart:convert'; import 'package:flutter/services.dart'; @@ -161,13 +160,14 @@ void main() { final List log = []; List skus = []..add("testsku"); - final dynamic result = """[ + final result = [ { "productId": "com.cooni.point1000", "price": "120", "currency": "JPY", "localizedPrice": "¥120", "title": "1,000", + "originalPrice": 1.235862359273509, "description": "1000 points 1000P", "introductoryPrice": "1001", "introductoryPricePaymentModeIOS": "1002", @@ -180,7 +180,7 @@ void main() { "introductoryPricePeriodAndroid": "5", "freeTrialPeriodAndroid": "6" } - ]"""; + ]; setUp(() { FlutterInappPurchase(FlutterInappPurchase.private( @@ -213,9 +213,11 @@ void main() { test('returns correct result', () async { List products = await FlutterInappPurchase.instance.getProducts(skus); - List expected = (json.decode(result) as List) + List expected = (result) .map( - (product) => IAPItem.fromJSON(product as Map), + (product) => IAPItem.fromJSON( + Map.from(product), + ), ) .toList(); for (var i = 0; i < products.length; ++i) { @@ -260,6 +262,7 @@ void main() { "currency": "JPY", "localizedPrice": "¥120", "title": "1,000", + "originalPrice": 1.235862359273509, "description": "1000 points 1000P", "introductoryPrice": "1001", "introductoryPricePaymentModeIOS": "1002", @@ -306,7 +309,9 @@ void main() { await FlutterInappPurchase.instance.getProducts(skus); List? expected = result .map( - (product) => IAPItem.fromJSON(product as Map), + (product) => IAPItem.fromJSON( + Map.from(product), + ), ) .toList(); for (var i = 0; i < products.length; ++i) { @@ -346,13 +351,14 @@ void main() { final List log = []; List skus = []..add("testsku"); - final dynamic result = """[ + final result = [ { "productId": "com.cooni.point1000", "price": "120", "currency": "JPY", "localizedPrice": "¥120", "title": "1,000", + "originalPrice": 1.235862359273509, "description": "1000 points 1000P", "introductoryPrice": "1001", "introductoryPricePaymentModeIOS": "1002", @@ -365,7 +371,7 @@ void main() { "introductoryPricePeriodAndroid": "5", "freeTrialPeriodAndroid": "6" } - ]"""; + ]; setUp(() { FlutterInappPurchase(FlutterInappPurchase.private( @@ -397,9 +403,10 @@ void main() { test('returns correct result', () async { List products = await FlutterInappPurchase.instance.getSubscriptions(skus); - List expected = (json.decode(result) as List) + List expected = result .map( - (product) => IAPItem.fromJSON(product as Map), + (product) => + IAPItem.fromJSON(Map.from(product)), ) .toList(); for (var i = 0; i < products.length; ++i) { @@ -444,6 +451,7 @@ void main() { "currency": "JPY", "localizedPrice": "¥120", "title": "1,000", + "originalPrice": 1.235862359273509, "description": "1000 points 1000P", "introductoryPrice": "1001", "introductoryPricePaymentModeIOS": "1002", @@ -490,7 +498,9 @@ void main() { await FlutterInappPurchase.instance.getSubscriptions(skus); List? expected = result .map( - (product) => IAPItem.fromJSON(product as Map), + (product) => IAPItem.fromJSON( + Map.from(product), + ), ) .toList(); for (var i = 0; i < products.length; ++i) { @@ -529,30 +539,36 @@ void main() { group('for Android', () { final List log = []; - final String resultInapp = """[{ - "transactionDate":"1552824902000", - "transactionId":"testTransactionId", - "productId":"com.cooni.point1000", - "transactionReceipt":"testTransactionReciept", - "purchaseToken":"testPurchaseToken", - "autoRenewingAndroid":true, - "dataAndroid":"testDataAndroid", - "signatureAndroid":"testSignatureAndroid", - "originalTransactionDateIOS":"1552831136000", - "originalTransactionIdentifierIOS":"testOriginalTransactionIdentifierIOS" - }]"""; - final String resultSubs = """[{ - "transactionDate":"1552824902000", - "transactionId":"testSubsTransactionId", - "productId":"com.cooni.point1000.subs", - "transactionReceipt":"testSubsTransactionReciept", - "purchaseToken":"testSubsPurchaseToken", - "autoRenewingAndroid":true, - "dataAndroid":"testSubsDataAndroid", - "signatureAndroid":"testSubsSignatureAndroid", - "originalTransactionDateIOS":"1552831136000", - "originalTransactionIdentifierIOS":"testSubsOriginalTransactionIdentifierIOS" - }]"""; + final resultInApp = [ + { + "transactionDate": 1552824902000, + "transactionId": "testTransactionId", + "productId": "com.cooni.point1000", + "transactionReceipt": "testTransactionReciept", + "purchaseToken": "testPurchaseToken", + "autoRenewingAndroid": true, + "dataAndroid": "testDataAndroid", + "signatureAndroid": "testSignatureAndroid", + "originalTransactionDateIOS": 1552831136000, + "originalTransactionIdentifierIOS": + "testOriginalTransactionIdentifierIOS" + } + ]; + final resultSubs = [ + { + "transactionDate": 1552824902000, + "transactionId": "testSubsTransactionId", + "productId": "com.cooni.point1000.subs", + "transactionReceipt": "testSubsTransactionReciept", + "purchaseToken": "testSubsPurchaseToken", + "autoRenewingAndroid": true, + "dataAndroid": "testSubsDataAndroid", + "signatureAndroid": "testSubsSignatureAndroid", + "originalTransactionDateIOS": 1552831136000, + "originalTransactionIdentifierIOS": + "testSubsOriginalTransactionIdentifierIOS" + } + ]; setUp(() { FlutterInappPurchase(FlutterInappPurchase.private( @@ -563,7 +579,7 @@ void main() { log.add(methodCall); var m = methodCall.arguments as Map; if (m['type'] == 'inapp') { - return resultInapp; + return resultInApp; } else if (m['type'] == 'subs') { return resultSubs; } @@ -580,26 +596,21 @@ void main() { expect(log, [ isMethodCall( 'getPurchaseHistoryByType', - arguments: { - 'type': 'inapp', - }, + arguments: {'type': 'inapp'}, ), isMethodCall( 'getPurchaseHistoryByType', - arguments: { - 'type': 'subs', - }, + arguments: {'type': 'subs'}, ), ]); }); test('returns correct result', () async { - List? actualList = await (FlutterInappPurchase.instance - .getPurchaseHistory() as FutureOr?>) ?? - []; - List expectList = ((json.decode(resultInapp) as List) + - (json.decode(resultSubs) as List)) - .map((item) => PurchasedItem.fromJSON(item)) + List? actualList = + await FlutterInappPurchase.instance.getPurchaseHistory(); + List expectList = (resultInApp + resultSubs) + .map((item) => + PurchasedItem.fromJSON(Map.from(item))) .toList(); for (var i = 0; i < actualList.length; ++i) { @@ -612,7 +623,6 @@ void main() { expect(actual.transactionReceipt, expected.transactionReceipt); expect(actual.purchaseToken, expected.purchaseToken); expect(actual.autoRenewingAndroid, expected.autoRenewingAndroid); - expect(actual.dataAndroid, expected.dataAndroid); expect(actual.signatureAndroid, expected.signatureAndroid); expect(actual.originalTransactionDateIOS, expected.originalTransactionDateIOS); @@ -680,9 +690,8 @@ void main() { }); test('returns correct result', () async { - List? actualList = await (FlutterInappPurchase.instance - .getPurchaseHistory() as FutureOr?>) ?? - []; + List? actualList = + await FlutterInappPurchase.instance.getPurchaseHistory(); List? expectList = result .map((item) => PurchasedItem.fromJSON(item)) .toList(); @@ -697,7 +706,6 @@ void main() { expect(actual.transactionReceipt, expected.transactionReceipt); expect(actual.purchaseToken, expected.purchaseToken); expect(actual.autoRenewingAndroid, expected.autoRenewingAndroid); - expect(actual.dataAndroid, expected.dataAndroid); expect(actual.signatureAndroid, expected.signatureAndroid); expect(actual.originalTransactionDateIOS, expected.originalTransactionDateIOS); @@ -711,31 +719,36 @@ void main() { group('getAvailablePurchases', () { group('for Android', () { final List log = []; - - final String resultInapp = """[{ - "transactionDate":"1552824902000", - "transactionId":"testTransactionId", - "productId":"com.cooni.point1000", - "transactionReceipt":"testTransactionReciept", - "purchaseToken":"testPurchaseToken", - "autoRenewingAndroid":true, - "dataAndroid":"testDataAndroid", - "signatureAndroid":"testSignatureAndroid", - "originalTransactionDateIOS":"1552831136000", - "originalTransactionIdentifierIOS":"testOriginalTransactionIdentifierIOS" - }]"""; - final String resultSubs = """[{ - "transactionDate":"1552824902000", - "transactionId":"testSubsTransactionId", - "productId":"com.cooni.point1000.subs", - "transactionReceipt":"testSubsTransactionReciept", - "purchaseToken":"testSubsPurchaseToken", - "autoRenewingAndroid":true, - "dataAndroid":"testSubsDataAndroid", - "signatureAndroid":"testSubsSignatureAndroid", - "originalTransactionDateIOS":"1552831136000", - "originalTransactionIdentifierIOS":"testSubsOriginalTransactionIdentifierIOS" - }]"""; + final resultInApp = [ + { + "transactionDate": 1552824902000, + "transactionId": "testTransactionId", + "productId": "com.cooni.point1000", + "transactionReceipt": "testTransactionReciept", + "purchaseToken": "testPurchaseToken", + "autoRenewingAndroid": true, + "dataAndroid": "testDataAndroid", + "signatureAndroid": "testSignatureAndroid", + "originalTransactionDateIOS": 1552831136000, + "originalTransactionIdentifierIOS": + "testOriginalTransactionIdentifierIOS" + } + ]; + final resultSubs = [ + { + "transactionDate": 1552824902000, + "transactionId": "testSubsTransactionId", + "productId": "com.cooni.point1000.subs", + "transactionReceipt": "testSubsTransactionReciept", + "purchaseToken": "testSubsPurchaseToken", + "autoRenewingAndroid": true, + "dataAndroid": "testSubsDataAndroid", + "signatureAndroid": "testSubsSignatureAndroid", + "originalTransactionDateIOS": 1552831136000, + "originalTransactionIdentifierIOS": + "testSubsOriginalTransactionIdentifierIOS" + } + ]; setUp(() { FlutterInappPurchase(FlutterInappPurchase.private( @@ -746,7 +759,7 @@ void main() { log.add(methodCall); var m = methodCall.arguments as Map; if (m['type'] == 'inapp') { - return resultInapp; + return resultInApp; } else if (m['type'] == 'subs') { return resultSubs; } @@ -777,12 +790,11 @@ void main() { }); test('returns correct result', () async { - List? actualList = await (FlutterInappPurchase.instance - .getAvailablePurchases() as FutureOr?>) ?? - []; - List expectList = ((json.decode(resultInapp) as List) + - (json.decode(resultSubs) as List)) - .map((item) => PurchasedItem.fromJSON(item)) + List actualList = + await FlutterInappPurchase.instance.getAvailablePurchases(); + List expectList = (resultInApp + resultSubs) + .map((item) => + PurchasedItem.fromJSON(Map.from(item))) .toList(); for (var i = 0; i < actualList.length; ++i) { @@ -795,7 +807,6 @@ void main() { expect(actual.transactionReceipt, expected.transactionReceipt); expect(actual.purchaseToken, expected.purchaseToken); expect(actual.autoRenewingAndroid, expected.autoRenewingAndroid); - expect(actual.dataAndroid, expected.dataAndroid); expect(actual.signatureAndroid, expected.signatureAndroid); expect(actual.originalTransactionDateIOS, expected.originalTransactionDateIOS); @@ -863,9 +874,8 @@ void main() { }); test('returns correct result', () async { - List? actualList = await (FlutterInappPurchase.instance - .getAvailablePurchases() as FutureOr?>) ?? - []; + List? actualList = + await FlutterInappPurchase.instance.getAvailablePurchases(); List? expectList = result .map((item) => PurchasedItem.fromJSON(item as Map)) @@ -881,7 +891,6 @@ void main() { expect(actual.transactionReceipt, expected.transactionReceipt); expect(actual.purchaseToken, expected.purchaseToken); expect(actual.autoRenewingAndroid, expected.autoRenewingAndroid); - expect(actual.dataAndroid, expected.dataAndroid); expect(actual.signatureAndroid, expected.signatureAndroid); expect(actual.originalTransactionDateIOS, expected.originalTransactionDateIOS); @@ -1315,6 +1324,7 @@ void main() { "title": "1,000", "description": "1000 points 1000P", "introductoryPrice": "1001", + "originalPrice": 1.235862359273509, "introductoryPricePaymentModeIOS": "1002", "introductoryPriceNumberOfPeriodsIOS": "1003", "introductoryPriceSubscriptionPeriodIOS": "1004", @@ -1353,7 +1363,9 @@ void main() { .getAppStoreInitiatedProducts(); List? expected = result .map( - (product) => IAPItem.fromJSON(product as Map), + (product) => IAPItem.fromJSON( + Map.from(product), + ), ) .toList(); for (var i = 0; i < products.length; ++i) {