diff --git a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java index faa55cb8ec5b..4502b7d43612 100644 --- a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java +++ b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java @@ -56,6 +56,7 @@ static HashMap fromPurchase(Purchase purchase) { info.put("signature", purchase.getSignature()); info.put("sku", purchase.getSku()); info.put("isAutoRenewing", purchase.isAutoRenewing()); + info.put("originalJson", purchase.getOriginalJson()); return info; } diff --git a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/TranslatorTest.java b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/TranslatorTest.java index 007ba8d5f600..639af24a9732 100644 --- a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/TranslatorTest.java +++ b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/TranslatorTest.java @@ -140,6 +140,7 @@ private void assertSerialized(Purchase expected, Map serialized) assertEquals(expected.getPurchaseTime(), serialized.get("purchaseTime")); assertEquals(expected.getPurchaseToken(), serialized.get("purchaseToken")); assertEquals(expected.getSignature(), serialized.get("signature")); + assertEquals(expected.getOriginalJson(), serialized.get("originalJson")); assertEquals(expected.getSku(), serialized.get("sku")); } } diff --git a/packages/in_app_purchase/lib/billing_client_wrappers.dart b/packages/in_app_purchase/lib/billing_client_wrappers.dart index e65225983642..127c980c15e6 100644 --- a/packages/in_app_purchase/lib/billing_client_wrappers.dart +++ b/packages/in_app_purchase/lib/billing_client_wrappers.dart @@ -3,4 +3,5 @@ // found in the LICENSE file. export 'src/billing_client_wrappers/billing_client_wrapper.dart'; +export 'src/billing_client_wrappers/purchase_wrapper.dart'; export 'src/billing_client_wrappers/sku_details_wrapper.dart'; diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/billing_client_wrapper.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/billing_client_wrapper.dart index 99cf5fd00118..da1915f8e6c6 100644 --- a/packages/in_app_purchase/lib/src/billing_client_wrappers/billing_client_wrapper.dart +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/billing_client_wrapper.dart @@ -7,12 +7,33 @@ import 'package:flutter/services.dart'; import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; import '../channel.dart'; +import 'purchase_wrapper.dart'; import 'sku_details_wrapper.dart'; import 'enum_converters.dart'; +const String _kOnPurchasesUpdated = + 'PurchasesUpdatedListener#onPurchasesUpdated(int, List)'; const String _kOnBillingServiceDisconnected = 'BillingClientStateListener#onBillingServiceDisconnected()'; +/// Callback triggered by Play in response to purchase activity. +/// +/// This callback is triggered in response to all purchase activity while an +/// instance of `BillingClient` is active. This includes purchases initiated by +/// the app ([BillingClient.launchBillingFlow]) as well as purchases made in +/// Play itself while this app is open. +/// +/// This does not provide any hooks for purchases made in the past. See +/// [BillingClient.queryPurchases] and [BillingClient.queryPurchaseHistory]. +/// +/// All purchase information should also be verified manually, with your server +/// if at all possible. See ["Verify a +/// purchase"](https://developer.android.com/google/play/billing/billing_library_overview#Verify). +/// +/// Wraps a +/// [`PurchasesUpdatedListener`](https://developer.android.com/reference/com/android/billingclient/api/PurchasesUpdatedListener.html). +typedef void PurchasesUpdatedListener(PurchasesResultWrapper purchasesResult); + /// This class can be used directly instead of [InAppPurchaseConnection] to call /// Play-specific billing APIs. /// @@ -26,8 +47,10 @@ const String _kOnBillingServiceDisconnected = /// some minor changes to account for language differences. Callbacks have been /// converted to futures where appropriate. class BillingClient { - BillingClient() { + BillingClient(PurchasesUpdatedListener onPurchasesUpdated) { + assert(onPurchasesUpdated != null); channel.setMethodCallHandler(_callHandler); + _callbacks[_kOnPurchasesUpdated] = [onPurchasesUpdated]; } // Occasionally methods in the native layer require a Dart callback to be @@ -111,8 +134,8 @@ class BillingClient { /// to complete the transaction there. /// /// This method returns a [BillingResponse] representing the initial attempt - /// to show the Google Play purchase screen. - /// TODO(mklim, flutter/flutter#26326): Expose onPurchasesUpdated() result. + /// to show the Google Play billing flow. Actual purchase updates are + /// delivered via the [PurchasesUpdatedListener]. /// /// This method calls through to /// [`BillingClient#launchBillingFlow`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient#launchbillingflow). @@ -134,8 +157,52 @@ class BillingClient { arguments)); } + /// Fetches recent purchases for the given [SkuType]. + /// + /// This only fetches whatever purchase history Play happens to have cached + /// in memory. + /// + /// All purchase information should also be verified manually, with your server + /// if at all possible. See ["Verify a + /// purchase"](https://developer.android.com/google/play/billing/billing_library_overview#Verify). + /// + /// This wraps [`BillingClient#queryPurchases(String + /// skutype)`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient#querypurchases). + Future queryPurchases(SkuType skuType) async { + assert(skuType != null); + return PurchasesResultWrapper.fromJson(await channel.invokeMapMethod( + 'BillingClient#queryPurchases(String)', + {'skuType': SkuTypeConverter().toJson(skuType)})); + } + + /// Fetches purchase history for the given [SkuType]. + /// + /// This makes a network request via Play and returns the most recent purchase + /// for each [SkuDetailsWrapper] of the given [SkuType]. + /// + /// All purchase information should also be verified manually, with your + /// server if at all possible. See ["Verify a + /// purchase"](https://developer.android.com/google/play/billing/billing_library_overview#Verify). + /// + /// This wraps [`BillingClient#queryPurchaseHistoryAsync(String skuType, + /// PurchaseHistoryResponseListener + /// listener)`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient#querypurchasehistoryasync). + Future queryPurchaseHistory(SkuType skuType) async { + assert(skuType != null); + return PurchasesResultWrapper.fromJson(await channel.invokeMapMethod( + 'BillingClient#queryPurchaseHistoryAsync(String, PurchaseHistoryResponseListener)', + {'skuType': SkuTypeConverter().toJson(skuType)})); + } + Future _callHandler(MethodCall call) async { switch (call.method) { + case _kOnPurchasesUpdated: + // The purchases updated listener is a singleton. + assert(_callbacks[_kOnPurchasesUpdated].length == 1); + final PurchasesUpdatedListener listener = + _callbacks[_kOnPurchasesUpdated].first; + listener(PurchasesResultWrapper.fromJson(call.arguments)); + break; case _kOnBillingServiceDisconnected: final int handle = call.arguments['handle']; await _callbacks[_kOnBillingServiceDisconnected][handle](); diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/purchase_wrapper.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/purchase_wrapper.dart new file mode 100644 index 000000000000..5a584e570bf5 --- /dev/null +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/purchase_wrapper.dart @@ -0,0 +1,132 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +import 'dart:ui' show hashValues; +import 'package:flutter/foundation.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'enum_converters.dart'; +import 'billing_client_wrapper.dart'; + +// WARNING: Changes to `@JsonSerializable` classes need to be reflected in the +// below generated file. Run `flutter packages pub run build_runner watch` to +// rebuild and watch for further changes. +part 'purchase_wrapper.g.dart'; + +/// Data structure reprenting a succesful purchase. +/// +/// All purchase information should also be verified manually, with your +/// server if at all possible. See ["Verify a +/// purchase"](https://developer.android.com/google/play/billing/billing_library_overview#Verify). +/// +/// This wraps [`com.android.billlingclient.api.Purchase`](https://developer.android.com/reference/com/android/billingclient/api/Purchase) +@JsonSerializable() +class PurchaseWrapper { + @visibleForTesting + PurchaseWrapper({ + @required this.orderId, + @required this.packageName, + @required this.purchaseTime, + @required this.purchaseToken, + @required this.signature, + @required this.sku, + @required this.isAutoRenewing, + @required this.originalJson, + }); + + factory PurchaseWrapper.fromJson(Map map) => _$PurchaseWrapperFromJson(map); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + if (other.runtimeType != runtimeType) return false; + final PurchaseWrapper typedOther = other; + return typedOther.orderId == orderId && + typedOther.packageName == packageName && + typedOther.purchaseTime == purchaseTime && + typedOther.purchaseToken == purchaseToken && + typedOther.signature == signature && + typedOther.sku == sku && + typedOther.isAutoRenewing == isAutoRenewing && + typedOther.originalJson == originalJson; + } + + @override + int get hashCode => hashValues(orderId, packageName, purchaseTime, + purchaseToken, signature, sku, isAutoRenewing, originalJson); + + /// The unique ID for this purchase. Corresponds to the Google Payments order + /// ID. + final String orderId; + + /// The package name the purchase was made from. + final String packageName; + + /// When the purchase was made, as an epoch timestamp. + final int purchaseTime; + + /// A unique ID for a given [SkuDetailsWrapper], user, and purchase. + final String purchaseToken; + + /// Signature of purchase data, signed with the developer's private key. Uses + /// RSASSA-PKCS1-v1_5. + final String signature; + + /// The product ID of this purchase. + final String sku; + + /// True for subscriptions that renew automatically. Does not apply to + /// [SkuType.inapp] products. + /// + /// For [SkuType.subs] this means that the subscription is canceled when it is + /// false. + final bool isAutoRenewing; + + /// Details about this purchase, in JSON. + /// + /// This can be used verify a purchase. See ["Verify a purchase on a + /// device"](https://developer.android.com/google/play/billing/billing_library_overview#Verify-purchase-device). + /// Note though that verifying a purchase locally is inherently insecure (see + /// the article for more details). + final String originalJson; +} + +/// A data struct representing the result of a transaction. +/// +/// Contains a potentially empty list of [PurchaseWrapper]s and a +/// [BillingResponse] to signify the overall state of the transaction. +/// +/// Wraps [`com.android.billingclient.api.Purchase.PurchasesResult`](https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchasesResult). +@JsonSerializable() +@BillingResponseConverter() +class PurchasesResultWrapper { + PurchasesResultWrapper( + {@required BillingResponse this.responseCode, + @required List this.purchasesList}); + + factory PurchasesResultWrapper.fromJson(Map map) => + _$PurchasesResultWrapperFromJson(map); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + if (other.runtimeType != runtimeType) return false; + final PurchasesResultWrapper typedOther = other; + return typedOther.responseCode == responseCode && + typedOther.purchasesList == purchasesList; + } + + @override + int get hashCode => hashValues(responseCode, purchasesList); + + /// The status of the operation. + /// + /// This can represent either the status of the "query purchase history" half + /// of the operation and the "user made purchases" transaction itself. + final BillingResponse responseCode; + + /// The list of succesful purchases made in this transaction. + /// + /// May be empty, especially if [responseCode] is not [BillingResponse.ok]. + final List purchasesList; +} diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/purchase_wrapper.g.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/purchase_wrapper.g.dart new file mode 100644 index 000000000000..1681a1f773bc --- /dev/null +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/purchase_wrapper.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'purchase_wrapper.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PurchaseWrapper _$PurchaseWrapperFromJson(Map json) { + return PurchaseWrapper( + orderId: json['orderId'] as String, + packageName: json['packageName'] as String, + purchaseTime: json['purchaseTime'] as int, + purchaseToken: json['purchaseToken'] as String, + signature: json['signature'] as String, + sku: json['sku'] as String, + isAutoRenewing: json['isAutoRenewing'] as bool, + originalJson: json['originalJson'] as String); +} + +PurchasesResultWrapper _$PurchasesResultWrapperFromJson(Map json) { + return PurchasesResultWrapper( + responseCode: const BillingResponseConverter() + .fromJson(json['responseCode'] as int), + purchasesList: (json['purchasesList'] as List) + .map((e) => PurchaseWrapper.fromJson(e as Map)) + .toList()); +} diff --git a/packages/in_app_purchase/lib/src/in_app_purchase_connection/google_play_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase_connection/google_play_connection.dart index c57e1cf80994..5751166c4f53 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase_connection/google_play_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase_connection/google_play_connection.dart @@ -16,7 +16,10 @@ import 'product_details.dart'; class GooglePlayConnection with WidgetsBindingObserver implements InAppPurchaseConnection { - GooglePlayConnection._() : _billingClient = BillingClient() { + GooglePlayConnection._() + : _billingClient = BillingClient((PurchasesResultWrapper _) { + // TODO(mklim): wire this in to the generic interface + }) { _readyFuture = _connect(); WidgetsBinding.instance.addObserver(this); } diff --git a/packages/in_app_purchase/test/billing_client_wrappers/billing_client_wrapper_test.dart b/packages/in_app_purchase/test/billing_client_wrappers/billing_client_wrapper_test.dart index 28b4b156738b..36d8058a3bfd 100644 --- a/packages/in_app_purchase/test/billing_client_wrappers/billing_client_wrapper_test.dart +++ b/packages/in_app_purchase/test/billing_client_wrappers/billing_client_wrapper_test.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/services.dart'; import 'package:in_app_purchase/billing_client_wrappers.dart'; @@ -10,15 +10,17 @@ import 'package:in_app_purchase/src/billing_client_wrappers/enum_converters.dart import 'package:in_app_purchase/src/channel.dart'; import '../stub_in_app_purchase_platform.dart'; import 'sku_details_wrapper_test.dart'; +import 'purchase_wrapper_test.dart'; void main() { final StubInAppPurchasePlatform stubPlatform = StubInAppPurchasePlatform(); BillingClient billingClient; + setUpAll(() => channel.setMockMethodCallHandler(stubPlatform.fakeMethodCallHandler)); setUp(() { - billingClient = BillingClient(); + billingClient = BillingClient((PurchasesResultWrapper _) {}); stubPlatform.reset(); }); @@ -88,7 +90,7 @@ void main() { final BillingResponse responseCode = BillingResponse.ok; stubPlatform.addResponse(name: queryMethodName, value: { 'responseCode': BillingResponseConverter().toJson(responseCode), - 'skuDetailsList': >[buildSkuMap(dummyWrapper)] + 'skuDetailsList': >[buildSkuMap(dummySkuDetails)] }); final SkuDetailsResponseWrapper response = await billingClient @@ -96,7 +98,7 @@ void main() { skuType: SkuType.inapp, skusList: ['invalid']); expect(response.responseCode, equals(responseCode)); - expect(response.skuDetailsList, contains(dummyWrapper)); + expect(response.skuDetailsList, contains(dummySkuDetails)); }); }); @@ -109,7 +111,7 @@ void main() { stubPlatform.addResponse( name: launchMethodName, value: BillingResponseConverter().toJson(sentCode)); - final SkuDetailsWrapper skuDetails = dummyWrapper; + final SkuDetailsWrapper skuDetails = dummySkuDetails; final String accountId = "hashedAccountId"; final BillingResponse receivedCode = await billingClient @@ -127,7 +129,7 @@ void main() { stubPlatform.addResponse( name: launchMethodName, value: BillingResponseConverter().toJson(sentCode)); - final SkuDetailsWrapper skuDetails = dummyWrapper; + final SkuDetailsWrapper skuDetails = dummySkuDetails; final BillingResponse receivedCode = await billingClient.launchBillingFlow(skuDetails: skuDetails); @@ -139,4 +141,95 @@ void main() { expect(arguments['accountId'], isNull); }); }); + + group('queryPurchases', () { + const String queryPurchasesMethodName = + 'BillingClient#queryPurchases(String)'; + + test('serializes and deserializes data', () async { + final BillingResponse expectedCode = BillingResponse.ok; + final List expectedList = [ + dummyPurchase + ]; + stubPlatform + .addResponse(name: queryPurchasesMethodName, value: { + 'responseCode': BillingResponseConverter().toJson(expectedCode), + 'purchasesList': expectedList + .map((PurchaseWrapper purchase) => buildPurchaseMap(purchase)) + .toList(), + }); + + final PurchasesResultWrapper response = + await billingClient.queryPurchases(SkuType.inapp); + + expect(response.responseCode, equals(expectedCode)); + expect(response.purchasesList, equals(expectedList)); + }); + + test('checks for null params', () async { + expect(() => billingClient.queryPurchases(null), throwsAssertionError); + }); + + test('handles empty purchases', () async { + final BillingResponse expectedCode = BillingResponse.userCanceled; + stubPlatform + .addResponse(name: queryPurchasesMethodName, value: { + 'responseCode': BillingResponseConverter().toJson(expectedCode), + 'purchasesList': [], + }); + + final PurchasesResultWrapper response = + await billingClient.queryPurchases(SkuType.inapp); + + expect(response.responseCode, equals(expectedCode)); + expect(response.purchasesList, isEmpty); + }); + }); + + group('queryPurchaseHistory', () { + const String queryPurchaseHistoryMethodName = + 'BillingClient#queryPurchaseHistoryAsync(String, PurchaseHistoryResponseListener)'; + + test('serializes and deserializes data', () async { + final BillingResponse expectedCode = BillingResponse.ok; + final List expectedList = [ + dummyPurchase + ]; + stubPlatform.addResponse( + name: queryPurchaseHistoryMethodName, + value: { + 'responseCode': BillingResponseConverter().toJson(expectedCode), + 'purchasesList': expectedList + .map((PurchaseWrapper purchase) => buildPurchaseMap(purchase)) + .toList(), + }); + + final PurchasesResultWrapper response = + await billingClient.queryPurchaseHistory(SkuType.inapp); + + expect(response.responseCode, equals(expectedCode)); + expect(response.purchasesList, equals(expectedList)); + }); + + test('checks for null params', () async { + expect( + () => billingClient.queryPurchaseHistory(null), throwsAssertionError); + }); + + test('handles empty purchases', () async { + final BillingResponse expectedCode = BillingResponse.userCanceled; + stubPlatform.addResponse( + name: queryPurchaseHistoryMethodName, + value: { + 'responseCode': BillingResponseConverter().toJson(expectedCode), + 'purchasesList': [], + }); + + final PurchasesResultWrapper response = + await billingClient.queryPurchaseHistory(SkuType.inapp); + + expect(response.responseCode, equals(expectedCode)); + expect(response.purchasesList, isEmpty); + }); + }); } diff --git a/packages/in_app_purchase/test/billing_client_wrappers/purchase_wrapper_test.dart b/packages/in_app_purchase/test/billing_client_wrappers/purchase_wrapper_test.dart new file mode 100644 index 000000000000..a8eee48ec330 --- /dev/null +++ b/packages/in_app_purchase/test/billing_client_wrappers/purchase_wrapper_test.dart @@ -0,0 +1,67 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +import 'package:test/test.dart'; +import 'package:in_app_purchase/billing_client_wrappers.dart'; +import 'package:in_app_purchase/src/billing_client_wrappers/enum_converters.dart'; + +final PurchaseWrapper dummyPurchase = PurchaseWrapper( + orderId: 'orderId', + packageName: 'packageName', + purchaseTime: 0, + signature: 'signature', + sku: 'sku', + purchaseToken: 'purchaseToken', + isAutoRenewing: false, + originalJson: '', +); + +void main() { + group('PurchaseWrapper', () { + test('converts from map', () { + final PurchaseWrapper expected = dummyPurchase; + final PurchaseWrapper parsed = + PurchaseWrapper.fromJson(buildPurchaseMap(expected)); + + expect(parsed, equals(expected)); + }); + }); + + group('PurchasesResultWrapper', () { + test('parsed from map', () { + final BillingResponse responseCode = BillingResponse.ok; + final List purchases = [ + dummyPurchase, + dummyPurchase + ]; + final PurchasesResultWrapper expected = PurchasesResultWrapper( + responseCode: responseCode, purchasesList: purchases); + + final PurchasesResultWrapper parsed = + PurchasesResultWrapper.fromJson({ + 'responseCode': BillingResponseConverter().toJson(responseCode), + 'purchasesList': >[ + buildPurchaseMap(dummyPurchase), + buildPurchaseMap(dummyPurchase) + ] + }); + + expect(parsed.responseCode, equals(expected.responseCode)); + expect(parsed.purchasesList, containsAll(expected.purchasesList)); + }); + }); +} + +Map buildPurchaseMap(PurchaseWrapper original) { + return { + 'orderId': original.orderId, + 'packageName': original.packageName, + 'purchaseTime': original.purchaseTime, + 'signature': original.signature, + 'sku': original.sku, + 'purchaseToken': original.purchaseToken, + 'isAutoRenewing': original.isAutoRenewing, + 'originalJson': original.originalJson, + }; +} diff --git a/packages/in_app_purchase/test/billing_client_wrappers/sku_details_wrapper_test.dart b/packages/in_app_purchase/test/billing_client_wrappers/sku_details_wrapper_test.dart index 186cee8895ba..61b1d53fa881 100644 --- a/packages/in_app_purchase/test/billing_client_wrappers/sku_details_wrapper_test.dart +++ b/packages/in_app_purchase/test/billing_client_wrappers/sku_details_wrapper_test.dart @@ -7,7 +7,7 @@ import 'package:in_app_purchase/billing_client_wrappers.dart'; import 'package:in_app_purchase/src/billing_client_wrappers/enum_converters.dart'; import 'package:in_app_purchase/src/in_app_purchase_connection/product_details.dart'; -final SkuDetailsWrapper dummyWrapper = SkuDetailsWrapper( +final SkuDetailsWrapper dummySkuDetails = SkuDetailsWrapper( description: 'description', freeTrialPeriod: 'freeTrialPeriod', introductoryPrice: 'introductoryPrice', @@ -27,7 +27,7 @@ final SkuDetailsWrapper dummyWrapper = SkuDetailsWrapper( void main() { group('SkuDetailsWrapper', () { test('converts from map', () { - final SkuDetailsWrapper expected = dummyWrapper; + final SkuDetailsWrapper expected = dummySkuDetails; final SkuDetailsWrapper parsed = SkuDetailsWrapper.fromJson(buildSkuMap(expected)); @@ -39,8 +39,8 @@ void main() { test('parsed from map', () { final BillingResponse responseCode = BillingResponse.ok; final List skusDetails = [ - dummyWrapper, - dummyWrapper + dummySkuDetails, + dummySkuDetails ]; final SkuDetailsResponseWrapper expected = SkuDetailsResponseWrapper( responseCode: responseCode, skuDetailsList: skusDetails); @@ -49,8 +49,8 @@ void main() { SkuDetailsResponseWrapper.fromJson({ 'responseCode': BillingResponseConverter().toJson(responseCode), 'skuDetailsList': >[ - buildSkuMap(dummyWrapper), - buildSkuMap(dummyWrapper) + buildSkuMap(dummySkuDetails), + buildSkuMap(dummySkuDetails) ] }); @@ -60,7 +60,7 @@ void main() { test('toProductDetails() should return correct Product object', () { final SkuDetailsWrapper wrapper = - SkuDetailsWrapper.fromJson(buildSkuMap(dummyWrapper)); + SkuDetailsWrapper.fromJson(buildSkuMap(dummySkuDetails)); final ProductDetails product = wrapper.toProductDetails(); expect(product.title, wrapper.title); expect(product.description, wrapper.description); diff --git a/packages/in_app_purchase/test/in_app_purchase_connection/google_play_connection_test.dart b/packages/in_app_purchase/test/in_app_purchase_connection/google_play_connection_test.dart index a9a6ec7703ad..627074df562d 100644 --- a/packages/in_app_purchase/test/in_app_purchase_connection/google_play_connection_test.dart +++ b/packages/in_app_purchase/test/in_app_purchase_connection/google_play_connection_test.dart @@ -87,23 +87,23 @@ void main() { final BillingResponse responseCode = BillingResponse.ok; stubPlatform.addResponse(name: queryMethodName, value: { 'responseCode': BillingResponseConverter().toJson(responseCode), - 'skuDetailsList': >[buildSkuMap(dummyWrapper)] + 'skuDetailsList': >[buildSkuMap(dummySkuDetails)] }); // Since queryProductDetails makes 2 platform method calls (one for each SkuType), the result will contain 2 dummyWrapper instead // of 1. final ProductDetailsResponse response = await connection.queryProductDetails(['valid'].toSet()); - expect(response.productDetails.first.title, dummyWrapper.title); - expect( - response.productDetails.first.description, dummyWrapper.description); - expect(response.productDetails.first.price, dummyWrapper.price); + expect(response.productDetails.first.title, dummySkuDetails.title); + expect(response.productDetails.first.description, + dummySkuDetails.description); + expect(response.productDetails.first.price, dummySkuDetails.price); }); test('should get the correct notFoundIDs', () async { final BillingResponse responseCode = BillingResponse.ok; stubPlatform.addResponse(name: queryMethodName, value: { 'responseCode': BillingResponseConverter().toJson(responseCode), - 'skuDetailsList': >[buildSkuMap(dummyWrapper)] + 'skuDetailsList': >[buildSkuMap(dummySkuDetails)] }); // Since queryProductDetails makes 2 platform method calls (one for each SkuType), the result will contain 2 dummyWrapper instead // of 1.