From d73dcc5b626129e5d2d1129c1f9a9342d8e0770f Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 25 Feb 2021 16:10:11 -0800 Subject: [PATCH 01/12] initial merge --- .cirrus.yml | 5 --- packages/in_app_purchase/CHANGELOG.md | 8 +++++ packages/in_app_purchase/android/build.gradle | 2 +- .../inapppurchase/MethodCallHandlerImpl.java | 21 ++++++------ .../plugins/inapppurchase/Translator.java | 1 - .../inapppurchase/MethodCallHandlerTest.java | 8 +---- .../in_app_purchase/example/lib/main.dart | 4 +-- .../billing_client_wrapper.dart | 33 ++++++++++-------- .../purchase_wrapper.dart | 2 ++ .../sku_details_wrapper.dart | 7 ---- .../in_app_purchase/app_store_connection.dart | 6 ++-- .../google_play_connection.dart | 12 +++---- .../in_app_purchase_connection.dart | 10 ++---- .../billing_client_wrapper_test.dart | 34 +++++++++++++------ .../sku_details_wrapper_test.dart | 2 -- .../google_play_connection_test.dart | 3 +- 16 files changed, 75 insertions(+), 83 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 6b3614178b11..71f5cd9ab384 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -22,7 +22,6 @@ task: matrix: - name: publishable script: - - flutter channel master - ./script/check_publish.sh - name: format install_script: @@ -141,7 +140,6 @@ task: - flutter config --enable-linux-desktop build_script: # TODO(stuartmorgan): Include stable once Linux is supported on stable. - - flutter channel master - ./script/incremental_build.sh build-examples --linux - xvfb-run ./script/incremental_build.sh drive-examples --linux @@ -251,7 +249,6 @@ task: - flutter config --enable-macos-desktop upgrade_script: - sudo gem install cocoapods - - flutter channel master - flutter upgrade - git fetch origin master submodules_script: @@ -260,12 +257,10 @@ task: matrix: - name: build_all_plugins_app script: - - flutter channel master - ./script/build_all_plugins_app.sh macos - name: build-apps+drive-examples env: PATH: $PATH:/usr/local/bin build_script: - - flutter channel master - ./script/incremental_build.sh build-examples --macos --no-ipa - ./script/incremental_build.sh drive-examples --macos diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md index 0dbc2427ccd6..fb111c6efcca 100644 --- a/packages/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.5.0 + +* Migrate to Google Billing Library 3.0 + * Add `obfuscatedProfileId`, `purchaseToken` in [BillingClientWrapper.launchBillingFlow]. + * Removed `developerPayload` in [BillingClientWrapper.acknowledgePurchase], [BillingClientWrapper.consumeAsync], [InAppPurchaseConnection.completePurchase], [InAppPurchaseConnection.consumePurchase]. + * **Breaking Change** + * Removed `isRewarded` from [SkuDetailsWrapper] + ## 0.4.1 * Support InApp subscription upgrade/downgrade. diff --git a/packages/in_app_purchase/android/build.gradle b/packages/in_app_purchase/android/build.gradle index 2539f507ed26..ccc12e1c1a34 100644 --- a/packages/in_app_purchase/android/build.gradle +++ b/packages/in_app_purchase/android/build.gradle @@ -35,7 +35,7 @@ android { dependencies { implementation 'androidx.annotation:annotation:1.0.0' - implementation 'com.android.billingclient:billing:2.0.3' + implementation 'com.android.billingclient:billing:3.0.2' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.17.0' androidTestImplementation 'androidx.test:runner:1.1.1' diff --git a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java index 58d077673a03..9cd042caf6a3 100644 --- a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java +++ b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java @@ -125,7 +125,9 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { launchBillingFlow( (String) call.argument("sku"), (String) call.argument("accountId"), + (String) call.argument("obfuscatedProfileId"), (String) call.argument("oldSku"), + (String) call.argument("purchaseToken"), call.hasArgument("prorationMode") ? (int) call.argument("prorationMode") : ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, @@ -140,13 +142,11 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { case InAppPurchasePlugin.MethodNames.CONSUME_PURCHASE_ASYNC: consumeAsync( (String) call.argument("purchaseToken"), - (String) call.argument("developerPayload"), result); break; case InAppPurchasePlugin.MethodNames.ACKNOWLEDGE_PURCHASE: acknowledgePurchase( (String) call.argument("purchaseToken"), - (String) call.argument("developerPayload"), result); break; default: @@ -200,7 +200,9 @@ public void onSkuDetailsResponse( private void launchBillingFlow( String sku, @Nullable String accountId, + @Nullable String obfuscatedProfileId, @Nullable String oldSku, + @Nullable String purchaseToken, int prorationMode, MethodChannel.Result result) { if (billingClientError(result)) { @@ -248,10 +250,13 @@ private void launchBillingFlow( BillingFlowParams.Builder paramsBuilder = BillingFlowParams.newBuilder().setSkuDetails(skuDetails); if (accountId != null && !accountId.isEmpty()) { - paramsBuilder.setAccountId(accountId); + paramsBuilder.setObfuscatedAccountId(accountId); + } + if (obfuscatedProfileId != null && !obfuscatedProfileId.isEmpty()){ + paramsBuilder.setObfuscatedProfileId(obfuscatedProfileId); } if (oldSku != null && !oldSku.isEmpty()) { - paramsBuilder.setOldSku(oldSku); + paramsBuilder.setOldSku(oldSku, purchaseToken); } // The proration mode value has to match one of the following declared in // https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode @@ -262,7 +267,7 @@ private void launchBillingFlow( } private void consumeAsync( - String purchaseToken, String developerPayload, final MethodChannel.Result result) { + String purchaseToken, final MethodChannel.Result result) { if (billingClientError(result)) { return; } @@ -277,9 +282,6 @@ public void onConsumeResponse(BillingResult billingResult, String outToken) { ConsumeParams.Builder paramsBuilder = ConsumeParams.newBuilder().setPurchaseToken(purchaseToken); - if (developerPayload != null) { - paramsBuilder.setDeveloperPayload(developerPayload); - } ConsumeParams params = paramsBuilder.build(); billingClient.consumeAsync(params, listener); @@ -349,13 +351,12 @@ public void onBillingServiceDisconnected() { } private void acknowledgePurchase( - String purchaseToken, @Nullable String developerPayload, final MethodChannel.Result result) { + String purchaseToken, final MethodChannel.Result result) { if (billingClientError(result)) { return; } AcknowledgePurchaseParams params = AcknowledgePurchaseParams.newBuilder() - .setDeveloperPayload(developerPayload) .setPurchaseToken(purchaseToken) .build(); billingClient.acknowledgePurchase( 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 80b6f1362255..73180ec5ec05 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 @@ -31,7 +31,6 @@ static HashMap fromSkuDetail(SkuDetails detail) { info.put("priceCurrencyCode", detail.getPriceCurrencyCode()); info.put("sku", detail.getSku()); info.put("type", detail.getType()); - info.put("isRewarded", detail.isRewarded()); info.put("subscriptionPeriod", detail.getSubscriptionPeriod()); info.put("originalPrice", detail.getOriginalPrice()); info.put("originalPriceAmountMicros", detail.getOriginalPriceAmountMicros()); diff --git a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java index cc7bc4a9b9b1..6b44390141d2 100644 --- a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java +++ b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java @@ -269,6 +269,7 @@ public void launchBillingFlow_ok_null_AccountId() { HashMap arguments = new HashMap<>(); arguments.put("sku", skuId); arguments.put("accountId", null); + arguments.put("obfuscatedProfileId", null); MethodCall launchCall = new MethodCall(LAUNCH_BILLING_FLOW, arguments); // Launch the billing flow @@ -286,7 +287,6 @@ public void launchBillingFlow_ok_null_AccountId() { verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); assertEquals(params.getSku(), skuId); - assertNull(params.getAccountId()); // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); @@ -320,7 +320,6 @@ public void launchBillingFlow_ok_null_OldSku() { verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); assertEquals(params.getSku(), skuId); - assertEquals(params.getAccountId(), accountId); assertNull(params.getOldSku()); // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); @@ -374,7 +373,6 @@ public void launchBillingFlow_ok_oldSku() { verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); assertEquals(params.getSku(), skuId); - assertEquals(params.getAccountId(), accountId); assertEquals(params.getOldSku(), oldSkuId); // Verify we pass the response code to result @@ -408,7 +406,6 @@ public void launchBillingFlow_ok_AccountId() { verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); assertEquals(params.getSku(), skuId); - assertEquals(params.getAccountId(), accountId); // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); @@ -445,7 +442,6 @@ public void launchBillingFlow_ok_Proration() { verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); assertEquals(params.getSku(), skuId); - assertEquals(params.getAccountId(), accountId); assertEquals(params.getOldSku(), oldSkuId); assertEquals(params.getReplaceSkusProrationMode(), prorationMode); @@ -670,7 +666,6 @@ public void consumeAsync() { ConsumeParams params = ConsumeParams.newBuilder() - .setDeveloperPayload("mockPayload") .setPurchaseToken("mockToken") .build(); @@ -704,7 +699,6 @@ public void acknowledgePurchase() { AcknowledgePurchaseParams params = AcknowledgePurchaseParams.newBuilder() - .setDeveloperPayload("mockPayload") .setPurchaseToken("mockToken") .build(); diff --git a/packages/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/example/lib/main.dart index c9f0bb6ece25..422392699c2c 100644 --- a/packages/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/example/lib/main.dart @@ -25,8 +25,8 @@ const String _kGoldSubscriptionId = 'subscription_gold'; const List _kProductIds = [ _kConsumableId, _kUpgradeId, - _kSilverSubscriptionId, - _kGoldSubscriptionId, + // _kSilverSubscriptionId, + // _kGoldSubscriptionId, ]; class _MyApp extends StatefulWidget { 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 a0ba91556094..9ae3400f5f29 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 @@ -155,8 +155,13 @@ class BillingClient { /// The [skuDetails] needs to have already been fetched in a [querySkuDetails] /// call. The [accountId] is an optional hashed string associated with the user /// that's unique to your app. It's used by Google to detect unusual behavior. - /// Do not pass in a cleartext [accountId], use your developer ID, or use the - /// user's Google ID for this field. + /// Do not pass in a cleartext [accountId], Do not use this field to store any Personally Identifiable Information (PII) + /// such as emails in cleartext. Attempting to store PII in this field will result in purchases being blocked. + /// Google Play recommends that you use either encryption or a one-way hash to generate an obfuscated identifier to send to Google Play. + /// + /// Specifies an optional [obfuscatedProfileId] that is uniquely associated with the user's profile in your app. + /// Some applications allow users to have multiple profiles within a single account. Use this method to send the user's profile identifier to Google. + /// Setting this field requests the user's obfuscated account id. /// /// Calling this attemps to show the Google Play purchase UI. The user is free /// to complete the transaction there. @@ -171,25 +176,31 @@ class BillingClient { /// [`BillingFlowParams`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams) /// instance by [setting the given /// skuDetails](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder.html#setskudetails) - /// and [the given - /// accountId](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder.html#setAccountId(java.lang.String)). + /// , [the given + /// accountId](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setObfuscatedAccountId(java.lang.String)) + /// and the [obfuscatedProfileId] (https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setobfuscatedprofileid). /// /// When this method is called to purchase a subscription, an optional `oldSku` /// can be passed in. This will tell Google Play that rather than purchasing a new subscription, /// the user needs to upgrade/downgrade the existing subscription. - /// The [oldSku](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setoldsku) is the SKU id that the user is upgrading or downgrading from. + /// The [oldSku](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setoldsku) and [purchaseToken] are the SKU id and purchase token that the user is upgrading or downgrading from. + /// [purchaseToken] must not be `null` if [oldSku] is not `null`. /// The [prorationMode](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setreplaceskusprorationmode) is the mode of proration during subscription upgrade/downgrade. /// This value will only be effective if the `oldSku` is also set. Future launchBillingFlow( {required String sku, String? accountId, + String? obfuscatedProfileId, String? oldSku, + String? purchaseToken, ProrationMode? prorationMode}) async { assert(sku != null); final Map arguments = { 'sku': sku, 'accountId': accountId, + 'obfuscatedProfileId': obfuscatedProfileId, 'oldSku': oldSku, + 'purchaseToken': purchaseToken, 'prorationMode': ProrationModeConverter().toJson(prorationMode ?? ProrationMode.unknownSubscriptionUpgradeDowngradePolicy) }; @@ -250,18 +261,14 @@ class BillingClient { /// Consuming can only be done on an item that's owned, and as a result of consumption, the user will no longer own it. /// Consumption is done asynchronously. The method returns a Future containing a [BillingResultWrapper]. /// - /// The `developerPayload` is the developer data associated with the purchase to be consumed, it defaults to null. - /// /// This wraps [`BillingClient#consumeAsync(String, ConsumeResponseListener)`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#consumeAsync(java.lang.String,%20com.android.billingclient.api.ConsumeResponseListener)) - Future consumeAsync(String purchaseToken, - {String? developerPayload}) async { + Future consumeAsync(String purchaseToken) async { assert(purchaseToken != null); return BillingResultWrapper.fromJson((await channel .invokeMapMethod( 'BillingClient#consumeAsync(String, ConsumeResponseListener)', { 'purchaseToken': purchaseToken, - 'developerPayload': developerPayload, })) ?? {}); } @@ -282,18 +289,14 @@ class BillingClient { /// Please refer to [acknowledge](https://developer.android.com/google/play/billing/billing_library_overview#acknowledge) for more /// details. /// - /// The `developerPayload` is the developer data associated with the purchase to be consumed, it defaults to null. - /// /// This wraps [`BillingClient#acknowledgePurchase(String, AcknowledgePurchaseResponseListener)`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#acknowledgePurchase(com.android.billingclient.api.AcknowledgePurchaseParams,%20com.android.billingclient.api.AcknowledgePurchaseResponseListener)) - Future acknowledgePurchase(String purchaseToken, - {String? developerPayload}) async { + Future acknowledgePurchase(String purchaseToken) async { assert(purchaseToken != null); return BillingResultWrapper.fromJson((await channel.invokeMapMethod( 'BillingClient#(AcknowledgePurchaseParams params, (AcknowledgePurchaseParams, AcknowledgePurchaseResponseListener)', { 'purchaseToken': purchaseToken, - 'developerPayload': developerPayload, })) ?? {}); } 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 index 05472278968a..1b0133920bd7 100644 --- 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 @@ -120,6 +120,8 @@ class PurchaseWrapper { /// The payload specified by the developer when the purchase was acknowledged or consumed. /// /// The value is `null` if it wasn't specified when the purchase was acknowledged or consumed. + /// The `developerPayload` is removed from [BillingClientWrapper.acknowledgePurchase], [BillingClientWrapper.consumeAsync], [InAppPurchaseConnection.completePurchase], [InAppPurchaseConnection.consumePurchase] + /// after plugin version `0.5.0`. As a result, this will be `null` for new purchases happened after `0.5.0`. final String? developerPayload; /// Whether the purchase has been acknowledged. diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart index b3872958e5b9..c1cc0790b22a 100644 --- a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart @@ -42,7 +42,6 @@ class SkuDetailsWrapper { required this.subscriptionPeriod, required this.title, required this.type, - required this.isRewarded, required this.originalPrice, required this.originalPriceAmountMicros, }); @@ -106,10 +105,6 @@ class SkuDetailsWrapper { /// The [SkuType] of the product. final SkuType type; - /// False if the product is paid. - @JsonKey(defaultValue: false) - final bool isRewarded; - /// The original price that the user purchased this product for. @JsonKey(defaultValue: '') final String originalPrice; @@ -138,7 +133,6 @@ class SkuDetailsWrapper { typedOther.subscriptionPeriod == subscriptionPeriod && typedOther.title == title && typedOther.type == type && - typedOther.isRewarded == isRewarded && typedOther.originalPrice == originalPrice && typedOther.originalPriceAmountMicros == originalPriceAmountMicros; } @@ -158,7 +152,6 @@ class SkuDetailsWrapper { subscriptionPeriod.hashCode, title.hashCode, type.hashCode, - isRewarded.hashCode, originalPrice, originalPriceAmountMicros); } diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart index d4601fd809db..934b91abfdef 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart @@ -84,8 +84,7 @@ class AppStoreConnection implements InAppPurchaseConnection { } @override - Future completePurchase(PurchaseDetails purchase, - {String? developerPayload}) async { + Future completePurchase(PurchaseDetails purchase) async { if (purchase.skPaymentTransaction == null) { throw ArgumentError( 'completePurchase unsuccessful. The `purchase.skPaymentTransaction` is not valid'); @@ -96,8 +95,7 @@ class AppStoreConnection implements InAppPurchaseConnection { } @override - Future consumePurchase(PurchaseDetails purchase, - {String? developerPayload}) { + Future consumePurchase(PurchaseDetails purchase) { throw UnsupportedError('consume purchase is not available on Android'); } diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart index 1a47f3ebd095..8ff9c30aade7 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart @@ -81,8 +81,7 @@ class GooglePlayConnection } @override - Future completePurchase(PurchaseDetails purchase, - {String? developerPayload}) async { + Future completePurchase(PurchaseDetails purchase) async { if (purchase.billingClientPurchase!.isAcknowledged) { return BillingResultWrapper(responseCode: BillingResponse.ok); } @@ -91,20 +90,17 @@ class GooglePlayConnection 'completePurchase unsuccessful. The `purchase.verificationData` is not valid'); } return await billingClient.acknowledgePurchase( - purchase.verificationData.serverVerificationData, - developerPayload: developerPayload); + purchase.verificationData.serverVerificationData); } @override - Future consumePurchase(PurchaseDetails purchase, - {String? developerPayload}) { + Future consumePurchase(PurchaseDetails purchase) { if (purchase.verificationData == null) { throw ArgumentError( 'consumePurchase unsuccessful. The `purchase.verificationData` is not valid'); } return billingClient.consumeAsync( - purchase.verificationData.serverVerificationData, - developerPayload: developerPayload); + purchase.verificationData.serverVerificationData); } @override diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart index 81a0e92cc591..aac5eae93e55 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart @@ -204,10 +204,7 @@ abstract class InAppPurchaseConnection { /// /// Warning! Failure to call this method and get a successful response within 3 days of the purchase will result a refund on Android. /// The [consumePurchase] acts as an implicit [completePurchase] on Android. - /// - /// The optional parameter `developerPayload` (defaults to `null`) only works on Android. - Future completePurchase(PurchaseDetails purchase, - {String? developerPayload}); + Future completePurchase(PurchaseDetails purchase); /// (Play only) Mark that the user has consumed a product. /// @@ -215,11 +212,8 @@ abstract class InAppPurchaseConnection { /// delivered. The user won't be able to buy the same product again until the /// purchase of the product is consumed. /// - /// The `developerPayload` (defaults to `null`) can be specified to be associated with this consumption. - /// /// This throws an [UnsupportedError] on iOS. - Future consumePurchase(PurchaseDetails purchase, - {String? developerPayload}); + Future consumePurchase(PurchaseDetails purchase); /// Query all previous purchases. /// 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 3aa62ddd96a1..46690b6e2939 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 @@ -196,15 +196,17 @@ void main() { ); final SkuDetailsWrapper skuDetails = dummySkuDetails; final String accountId = "hashedAccountId"; + final String profileId = "hashedProfileId"; expect( await billingClient.launchBillingFlow( - sku: skuDetails.sku, accountId: accountId), + sku: skuDetails.sku, accountId: accountId, obfuscatedProfileId: profileId), equals(expectedBillingResult)); Map arguments = stubPlatform.previousCallMatching(launchMethodName).arguments; expect(arguments['sku'], equals(skuDetails.sku)); expect(arguments['accountId'], equals(accountId)); + expect(arguments['obfuscatedProfileId'], equals(profileId)); }); test( @@ -219,19 +221,25 @@ void main() { value: buildBillingResultMap(expectedBillingResult), ); final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + final String accountId = 'hashedAccountId'; + final String profileId = 'hashedProfileId'; + final String purchaseToken = 'aPurchaseToken'; expect( await billingClient.launchBillingFlow( sku: skuDetails.sku, accountId: accountId, - oldSku: dummyOldPurchase.sku), + obfuscatedProfileId: profileId, + oldSku: dummyOldPurchase.sku, + purchaseToken: purchaseToken), equals(expectedBillingResult)); Map arguments = stubPlatform.previousCallMatching(launchMethodName).arguments; expect(arguments['sku'], equals(skuDetails.sku)); expect(arguments['accountId'], equals(accountId)); expect(arguments['oldSku'], equals(dummyOldPurchase.sku)); + expect(arguments['purchaseToken'], equals(purchaseToken)); + expect(arguments['obfuscatedProfileId'], equals(profileId)); }); test( @@ -246,21 +254,27 @@ void main() { value: buildBillingResultMap(expectedBillingResult), ); final SkuDetailsWrapper skuDetails = dummySkuDetails; - final String accountId = "hashedAccountId"; + final String accountId = 'hashedAccountId'; + final String profileId = 'hashedProfileId'; + final String purchaseToken = 'aPurchaseToken'; final prorationMode = ProrationMode.immediateAndChargeProratedPrice; expect( await billingClient.launchBillingFlow( sku: skuDetails.sku, accountId: accountId, + obfuscatedProfileId: profileId, oldSku: dummyOldPurchase.sku, - prorationMode: prorationMode), + prorationMode: prorationMode, + purchaseToken: purchaseToken), equals(expectedBillingResult)); Map arguments = stubPlatform.previousCallMatching(launchMethodName).arguments; expect(arguments['sku'], equals(skuDetails.sku)); expect(arguments['accountId'], equals(accountId)); expect(arguments['oldSku'], equals(dummyOldPurchase.sku)); + expect(arguments['obfuscatedProfileId'], equals(profileId)); + expect(arguments['purchaseToken'], equals(purchaseToken)); expect(arguments['prorationMode'], ProrationModeConverter().toJson(prorationMode)); }); @@ -441,7 +455,7 @@ void main() { value: buildBillingResultMap(expectedBillingResult)); final BillingResultWrapper billingResult = await billingClient - .consumeAsync('dummy token', developerPayload: 'dummy payload'); + .consumeAsync('dummy token'); expect(billingResult, equals(expectedBillingResult)); }); @@ -452,7 +466,7 @@ void main() { value: null, ); final BillingResultWrapper billingResult = await billingClient - .consumeAsync('dummy token', developerPayload: 'dummy payload'); + .consumeAsync('dummy token'); expect( billingResult, @@ -475,8 +489,7 @@ void main() { value: buildBillingResultMap(expectedBillingResult)); final BillingResultWrapper billingResult = - await billingClient.acknowledgePurchase('dummy token', - developerPayload: 'dummy payload'); + await billingClient.acknowledgePurchase('dummy token'); expect(billingResult, equals(expectedBillingResult)); }); @@ -486,8 +499,7 @@ void main() { value: null, ); final BillingResultWrapper billingResult = - await billingClient.acknowledgePurchase('dummy token', - developerPayload: 'dummy payload'); + await billingClient.acknowledgePurchase('dummy token'); expect( billingResult, 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 13715eeb9fc0..9d937803f428 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 @@ -21,7 +21,6 @@ final SkuDetailsWrapper dummySkuDetails = SkuDetailsWrapper( subscriptionPeriod: 'subscriptionPeriod', title: 'title', type: SkuType.inapp, - isRewarded: true, originalPrice: 'originalPrice', originalPriceAmountMicros: 1000, ); @@ -144,7 +143,6 @@ Map buildSkuMap(SkuDetailsWrapper original) { 'subscriptionPeriod': original.subscriptionPeriod, 'title': original.title, 'type': original.type.toString().substring(8), - 'isRewarded': original.isRewarded, 'originalPrice': original.originalPrice, 'originalPriceAmountMicros': original.originalPriceAmountMicros, }; 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 79c2ee436c5c..687f55ae3f24 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 @@ -633,8 +633,7 @@ void main() { if (purchaseDetails.pendingCompletePurchase) { final BillingResultWrapper billingResultWrapper = await GooglePlayConnection.instance.completePurchase( - purchaseDetails, - developerPayload: 'dummy payload'); + purchaseDetails); expect(billingResultWrapper, equals(expectedBillingResult)); completer.complete(billingResultWrapper); } From ea34ac47991ea53b1a64341899a71ac032f911d7 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 25 Feb 2021 16:19:05 -0800 Subject: [PATCH 02/12] auto generate --- .../lib/src/billing_client_wrappers/sku_details_wrapper.g.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart index 247dbd54b666..d085031614aa 100644 --- a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart @@ -21,7 +21,6 @@ SkuDetailsWrapper _$SkuDetailsWrapperFromJson(Map json) { subscriptionPeriod: json['subscriptionPeriod'] as String? ?? '', title: json['title'] as String? ?? '', type: const SkuTypeConverter().fromJson(json['type'] as String?), - isRewarded: json['isRewarded'] as bool? ?? false, originalPrice: json['originalPrice'] as String? ?? '', originalPriceAmountMicros: json['originalPriceAmountMicros'] as int? ?? 0, ); @@ -42,7 +41,6 @@ Map _$SkuDetailsWrapperToJson(SkuDetailsWrapper instance) => 'subscriptionPeriod': instance.subscriptionPeriod, 'title': instance.title, 'type': const SkuTypeConverter().toJson(instance.type), - 'isRewarded': instance.isRewarded, 'originalPrice': instance.originalPrice, 'originalPriceAmountMicros': instance.originalPriceAmountMicros, }; From d6e49d3be072311132038e967e05576a74bf09af Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 25 Feb 2021 16:34:32 -0800 Subject: [PATCH 03/12] remove getIntroductoryPriceCycles --- packages/in_app_purchase/CHANGELOG.md | 3 ++- packages/in_app_purchase/example/lib/main.dart | 4 ++-- .../src/billing_client_wrappers/sku_details_wrapper.dart | 8 +++++--- .../billing_client_wrappers/sku_details_wrapper.g.dart | 2 +- .../billing_client_wrappers/sku_details_wrapper_test.dart | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md index fb111c6efcca..34412792d221 100644 --- a/packages/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/CHANGELOG.md @@ -4,7 +4,8 @@ * Add `obfuscatedProfileId`, `purchaseToken` in [BillingClientWrapper.launchBillingFlow]. * Removed `developerPayload` in [BillingClientWrapper.acknowledgePurchase], [BillingClientWrapper.consumeAsync], [InAppPurchaseConnection.completePurchase], [InAppPurchaseConnection.consumePurchase]. * **Breaking Change** - * Removed `isRewarded` from [SkuDetailsWrapper] + * Removed `isRewarded` from [SkuDetailsWrapper]. + * [SkuDetailsWrapper.introductoryPriceCycles] now returns `int` instead of `String`. ## 0.4.1 diff --git a/packages/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/example/lib/main.dart index 422392699c2c..c9f0bb6ece25 100644 --- a/packages/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/example/lib/main.dart @@ -25,8 +25,8 @@ const String _kGoldSubscriptionId = 'subscription_gold'; const List _kProductIds = [ _kConsumableId, _kUpgradeId, - // _kSilverSubscriptionId, - // _kGoldSubscriptionId, + _kSilverSubscriptionId, + _kGoldSubscriptionId, ]; class _MyApp extends StatefulWidget { diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart index c1cc0790b22a..d212ebd06dbb 100644 --- a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart @@ -70,9 +70,11 @@ class SkuDetailsWrapper { @JsonKey(defaultValue: '') final String introductoryPriceMicros; - /// The number of billing perios that [introductoryPrice] is valid for ("2"). - @JsonKey(defaultValue: '') - final String introductoryPriceCycles; + /// The number of subscription billing periods for which the user will be given the introductory price, such as 3. + /// Returns 0 if the SKU is not a subscription or doesn't have an introductory period. + /// Returns -1 if the value is not available. + @JsonKey(defaultValue: -1) + final int introductoryPriceCycles; /// The billing period of [introductoryPrice], in ISO 8601 format. @JsonKey(defaultValue: '') diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart index d085031614aa..4f385f6aa6bd 100644 --- a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart @@ -12,7 +12,7 @@ SkuDetailsWrapper _$SkuDetailsWrapperFromJson(Map json) { freeTrialPeriod: json['freeTrialPeriod'] as String? ?? '', introductoryPrice: json['introductoryPrice'] as String? ?? '', introductoryPriceMicros: json['introductoryPriceMicros'] as String? ?? '', - introductoryPriceCycles: json['introductoryPriceCycles'] as String? ?? '', + introductoryPriceCycles: json['introductoryPriceCycles'] as int? ?? -1, introductoryPricePeriod: json['introductoryPricePeriod'] as String? ?? '', price: json['price'] as String? ?? '', priceAmountMicros: json['priceAmountMicros'] as int? ?? 0, 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 9d937803f428..7a7b7fb86364 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 @@ -12,7 +12,7 @@ final SkuDetailsWrapper dummySkuDetails = SkuDetailsWrapper( freeTrialPeriod: 'freeTrialPeriod', introductoryPrice: 'introductoryPrice', introductoryPriceMicros: 'introductoryPriceMicros', - introductoryPriceCycles: 'introductoryPriceCycles', + introductoryPriceCycles: 1, introductoryPricePeriod: 'introductoryPricePeriod', price: 'price', priceAmountMicros: 1000, From 34ecb8b33f5b8daa16258c0e6cb64d5e85e3e69f Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 25 Feb 2021 16:35:04 -0800 Subject: [PATCH 04/12] format --- .../inapppurchase/MethodCallHandlerImpl.java | 24 +++++++------------ .../inapppurchase/MethodCallHandlerTest.java | 9 ++----- .../in_app_purchase/app_store_connection.dart | 3 ++- .../google_play_connection.dart | 11 +++++---- .../billing_client_wrapper_test.dart | 12 ++++++---- .../google_play_connection_test.dart | 4 ++-- 6 files changed, 27 insertions(+), 36 deletions(-) diff --git a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java index 9cd042caf6a3..d90fc6040454 100644 --- a/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java +++ b/packages/in_app_purchase/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java @@ -125,9 +125,9 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { launchBillingFlow( (String) call.argument("sku"), (String) call.argument("accountId"), - (String) call.argument("obfuscatedProfileId"), + (String) call.argument("obfuscatedProfileId"), (String) call.argument("oldSku"), - (String) call.argument("purchaseToken"), + (String) call.argument("purchaseToken"), call.hasArgument("prorationMode") ? (int) call.argument("prorationMode") : ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, @@ -140,14 +140,10 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { queryPurchaseHistoryAsync((String) call.argument("skuType"), result); break; case InAppPurchasePlugin.MethodNames.CONSUME_PURCHASE_ASYNC: - consumeAsync( - (String) call.argument("purchaseToken"), - result); + consumeAsync((String) call.argument("purchaseToken"), result); break; case InAppPurchasePlugin.MethodNames.ACKNOWLEDGE_PURCHASE: - acknowledgePurchase( - (String) call.argument("purchaseToken"), - result); + acknowledgePurchase((String) call.argument("purchaseToken"), result); break; default: result.notImplemented(); @@ -252,7 +248,7 @@ private void launchBillingFlow( if (accountId != null && !accountId.isEmpty()) { paramsBuilder.setObfuscatedAccountId(accountId); } - if (obfuscatedProfileId != null && !obfuscatedProfileId.isEmpty()){ + if (obfuscatedProfileId != null && !obfuscatedProfileId.isEmpty()) { paramsBuilder.setObfuscatedProfileId(obfuscatedProfileId); } if (oldSku != null && !oldSku.isEmpty()) { @@ -266,8 +262,7 @@ private void launchBillingFlow( billingClient.launchBillingFlow(activity, paramsBuilder.build()))); } - private void consumeAsync( - String purchaseToken, final MethodChannel.Result result) { + private void consumeAsync(String purchaseToken, final MethodChannel.Result result) { if (billingClientError(result)) { return; } @@ -350,15 +345,12 @@ public void onBillingServiceDisconnected() { }); } - private void acknowledgePurchase( - String purchaseToken, final MethodChannel.Result result) { + private void acknowledgePurchase(String purchaseToken, final MethodChannel.Result result) { if (billingClientError(result)) { return; } AcknowledgePurchaseParams params = - AcknowledgePurchaseParams.newBuilder() - .setPurchaseToken(purchaseToken) - .build(); + AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchaseToken).build(); billingClient.acknowledgePurchase( params, new AcknowledgePurchaseResponseListener() { diff --git a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java index 6b44390141d2..6ae9bee62461 100644 --- a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java +++ b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java @@ -664,10 +664,7 @@ public void consumeAsync() { methodChannelHandler.onMethodCall(new MethodCall(CONSUME_PURCHASE_ASYNC, arguments), result); - ConsumeParams params = - ConsumeParams.newBuilder() - .setPurchaseToken("mockToken") - .build(); + ConsumeParams params = ConsumeParams.newBuilder().setPurchaseToken("mockToken").build(); // Verify we pass the data to result verify(mockBillingClient).consumeAsync(refEq(params), listenerCaptor.capture()); @@ -698,9 +695,7 @@ public void acknowledgePurchase() { methodChannelHandler.onMethodCall(new MethodCall(ACKNOWLEDGE_PURCHASE, arguments), result); AcknowledgePurchaseParams params = - AcknowledgePurchaseParams.newBuilder() - .setPurchaseToken("mockToken") - .build(); + AcknowledgePurchaseParams.newBuilder().setPurchaseToken("mockToken").build(); // Verify we pass the data to result verify(mockBillingClient).acknowledgePurchase(refEq(params), listenerCaptor.capture()); diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart index 934b91abfdef..79a4a61fb328 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart @@ -84,7 +84,8 @@ class AppStoreConnection implements InAppPurchaseConnection { } @override - Future completePurchase(PurchaseDetails purchase) async { + Future completePurchase( + PurchaseDetails purchase) async { if (purchase.skPaymentTransaction == null) { throw ArgumentError( 'completePurchase unsuccessful. The `purchase.skPaymentTransaction` is not valid'); diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart index 8ff9c30aade7..847ad86a708a 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart @@ -81,7 +81,8 @@ class GooglePlayConnection } @override - Future completePurchase(PurchaseDetails purchase) async { + Future completePurchase( + PurchaseDetails purchase) async { if (purchase.billingClientPurchase!.isAcknowledged) { return BillingResultWrapper(responseCode: BillingResponse.ok); } @@ -89,8 +90,8 @@ class GooglePlayConnection throw ArgumentError( 'completePurchase unsuccessful. The `purchase.verificationData` is not valid'); } - return await billingClient.acknowledgePurchase( - purchase.verificationData.serverVerificationData); + return await billingClient + .acknowledgePurchase(purchase.verificationData.serverVerificationData); } @override @@ -99,8 +100,8 @@ class GooglePlayConnection throw ArgumentError( 'consumePurchase unsuccessful. The `purchase.verificationData` is not valid'); } - return billingClient.consumeAsync( - purchase.verificationData.serverVerificationData); + return billingClient + .consumeAsync(purchase.verificationData.serverVerificationData); } @override 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 46690b6e2939..7330bb2941a0 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 @@ -200,7 +200,9 @@ void main() { expect( await billingClient.launchBillingFlow( - sku: skuDetails.sku, accountId: accountId, obfuscatedProfileId: profileId), + sku: skuDetails.sku, + accountId: accountId, + obfuscatedProfileId: profileId), equals(expectedBillingResult)); Map arguments = stubPlatform.previousCallMatching(launchMethodName).arguments; @@ -454,8 +456,8 @@ void main() { name: consumeMethodName, value: buildBillingResultMap(expectedBillingResult)); - final BillingResultWrapper billingResult = await billingClient - .consumeAsync('dummy token'); + final BillingResultWrapper billingResult = + await billingClient.consumeAsync('dummy token'); expect(billingResult, equals(expectedBillingResult)); }); @@ -465,8 +467,8 @@ void main() { name: consumeMethodName, value: null, ); - final BillingResultWrapper billingResult = await billingClient - .consumeAsync('dummy token'); + final BillingResultWrapper billingResult = + await billingClient.consumeAsync('dummy token'); expect( billingResult, 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 687f55ae3f24..5a265b8de907 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 @@ -632,8 +632,8 @@ void main() { purchaseDetails.status = PurchaseStatus.purchased; if (purchaseDetails.pendingCompletePurchase) { final BillingResultWrapper billingResultWrapper = - await GooglePlayConnection.instance.completePurchase( - purchaseDetails); + await GooglePlayConnection.instance + .completePurchase(purchaseDetails); expect(billingResultWrapper, equals(expectedBillingResult)); completer.complete(billingResultWrapper); } From 65a3cbdf0be3b39c65cef2bbb79437c976ef0d8e Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 25 Feb 2021 16:38:17 -0800 Subject: [PATCH 05/12] revert cirrus change --- .cirrus.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index 71f5cd9ab384..6b3614178b11 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -22,6 +22,7 @@ task: matrix: - name: publishable script: + - flutter channel master - ./script/check_publish.sh - name: format install_script: @@ -140,6 +141,7 @@ task: - flutter config --enable-linux-desktop build_script: # TODO(stuartmorgan): Include stable once Linux is supported on stable. + - flutter channel master - ./script/incremental_build.sh build-examples --linux - xvfb-run ./script/incremental_build.sh drive-examples --linux @@ -249,6 +251,7 @@ task: - flutter config --enable-macos-desktop upgrade_script: - sudo gem install cocoapods + - flutter channel master - flutter upgrade - git fetch origin master submodules_script: @@ -257,10 +260,12 @@ task: matrix: - name: build_all_plugins_app script: + - flutter channel master - ./script/build_all_plugins_app.sh macos - name: build-apps+drive-examples env: PATH: $PATH:/usr/local/bin build_script: + - flutter channel master - ./script/incremental_build.sh build-examples --macos --no-ipa - ./script/incremental_build.sh drive-examples --macos From 102d7cd64d7295115c1b5a32a857ae0085353537 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 25 Feb 2021 19:35:44 -0800 Subject: [PATCH 06/12] fix tests --- packages/in_app_purchase/android/build.gradle | 2 +- .../example/android/app/build.gradle | 4 ++-- .../inapppurchase/MethodCallHandlerTest.java | 23 +++++++++++++++---- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/in_app_purchase/android/build.gradle b/packages/in_app_purchase/android/build.gradle index ccc12e1c1a34..8d5840b4daff 100644 --- a/packages/in_app_purchase/android/build.gradle +++ b/packages/in_app_purchase/android/build.gradle @@ -37,7 +37,7 @@ dependencies { implementation 'androidx.annotation:annotation:1.0.0' implementation 'com.android.billingclient:billing:3.0.2' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.17.0' + testImplementation 'org.mockito:mockito-core:3.6.0' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/packages/in_app_purchase/example/android/app/build.gradle b/packages/in_app_purchase/example/android/app/build.gradle index 261c7f0fe58e..c95804685219 100644 --- a/packages/in_app_purchase/example/android/app/build.gradle +++ b/packages/in_app_purchase/example/android/app/build.gradle @@ -106,9 +106,9 @@ flutter { } dependencies { - implementation 'com.android.billingclient:billing:1.2' + implementation 'com.android.billingclient:billing:3.0.2' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.17.0' + testImplementation 'org.mockito:mockito-core:3.6.0' testImplementation 'org.json:json:20180813' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' diff --git a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java index 6ae9bee62461..c120cbb5cb36 100644 --- a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java +++ b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java @@ -22,6 +22,7 @@ import static java.util.stream.Collectors.toList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; @@ -60,6 +61,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; + +import org.json.JSONException; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -79,7 +82,7 @@ public class MethodCallHandlerTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); factory = (@NonNull Context context, @NonNull MethodChannel channel, @@ -409,7 +412,7 @@ public void launchBillingFlow_ok_AccountId() { // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); - verify(result, times(1)).success(fromBillingResult(billingResult)); +// verify(result, times(1)).success(fromBillingResult(billingResult)); } @Test @@ -417,6 +420,7 @@ public void launchBillingFlow_ok_Proration() { // Fetch the sku details first and query the method call String skuId = "foo"; String oldSkuId = "oldFoo"; + String purchaseToken = "purchaseTokenFoo"; String accountId = "account"; int prorationMode = BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_PRORATED_PRICE; queryForSkus(unmodifiableList(asList(skuId, oldSkuId))); @@ -424,6 +428,7 @@ public void launchBillingFlow_ok_Proration() { arguments.put("sku", skuId); arguments.put("accountId", accountId); arguments.put("oldSku", oldSkuId); + arguments.put("purchaseToken", purchaseToken); arguments.put("prorationMode", prorationMode); MethodCall launchCall = new MethodCall(LAUNCH_BILLING_FLOW, arguments); @@ -443,6 +448,7 @@ public void launchBillingFlow_ok_Proration() { BillingFlowParams params = billingFlowParamsCaptor.getValue(); assertEquals(params.getSku(), skuId); assertEquals(params.getOldSku(), oldSkuId); + assertEquals(params.getOldSkuPurchaseToken(), purchaseToken); assertEquals(params.getReplaceSkusProrationMode(), prorationMode); // Verify we pass the response code to result @@ -745,7 +751,7 @@ private void establishConnectedBillingClient( methodChannelHandler.onMethodCall(connectCall, result); } - private void queryForSkus(List skusList) { + private void queryForSkus(List skusList){ // Set up the query method call establishConnectedBillingClient(/* arguments= */ null, /* result= */ null); HashMap arguments = new HashMap<>(); @@ -763,6 +769,7 @@ private void queryForSkus(List skusList) { verify(mockBillingClient).querySkuDetailsAsync(any(), listenerCaptor.capture()); List skuDetailsResponse = skusList.stream().map(this::buildSkuDetails).collect(toList()); + BillingResult billingResult = BillingResult.newBuilder() .setResponseCode(100) @@ -772,8 +779,14 @@ private void queryForSkus(List skusList) { } private SkuDetails buildSkuDetails(String id) { - SkuDetails details = mock(SkuDetails.class); - when(details.getSku()).thenReturn(id); + String json = String.format( + "{\"packageName\": \"dummyPackageName\",\"productId\":\"%s\",\"type\":\"inapp\",\"price\":\"$0.99\",\"price_amount_micros\":990000,\"price_currency_code\":\"USD\",\"title\":\"Example title\",\"description\":\"Example description.\",\"original_price\":\"$0.99\",\"original_price_micros\":990000}", id); + SkuDetails details = null; + try { + details = new SkuDetails(json); + } catch (JSONException e) { + fail("buildSkuDetails failed with JSONException " + e.toString()); + } return details; } From d6b65db48642b595f6be1e1efd9fefc394a6d2c0 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 25 Feb 2021 19:36:12 -0800 Subject: [PATCH 07/12] format --- .../inapppurchase/MethodCallHandlerTest.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java index c120cbb5cb36..23866f5aaa0a 100644 --- a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java +++ b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java @@ -61,7 +61,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - import org.json.JSONException; import org.junit.Before; import org.junit.Test; @@ -412,7 +411,7 @@ public void launchBillingFlow_ok_AccountId() { // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); -// verify(result, times(1)).success(fromBillingResult(billingResult)); + // verify(result, times(1)).success(fromBillingResult(billingResult)); } @Test @@ -751,7 +750,7 @@ private void establishConnectedBillingClient( methodChannelHandler.onMethodCall(connectCall, result); } - private void queryForSkus(List skusList){ + private void queryForSkus(List skusList) { // Set up the query method call establishConnectedBillingClient(/* arguments= */ null, /* result= */ null); HashMap arguments = new HashMap<>(); @@ -779,11 +778,13 @@ private void queryForSkus(List skusList){ } private SkuDetails buildSkuDetails(String id) { - String json = String.format( - "{\"packageName\": \"dummyPackageName\",\"productId\":\"%s\",\"type\":\"inapp\",\"price\":\"$0.99\",\"price_amount_micros\":990000,\"price_currency_code\":\"USD\",\"title\":\"Example title\",\"description\":\"Example description.\",\"original_price\":\"$0.99\",\"original_price_micros\":990000}", id); - SkuDetails details = null; + String json = + String.format( + "{\"packageName\": \"dummyPackageName\",\"productId\":\"%s\",\"type\":\"inapp\",\"price\":\"$0.99\",\"price_amount_micros\":990000,\"price_currency_code\":\"USD\",\"title\":\"Example title\",\"description\":\"Example description.\",\"original_price\":\"$0.99\",\"original_price_micros\":990000}", + id); + SkuDetails details = null; try { - details = new SkuDetails(json); + details = new SkuDetails(json); } catch (JSONException e) { fail("buildSkuDetails failed with JSONException " + e.toString()); } From df3a45bc4cfcc64db7fca8d22e40fde3a47df576 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 25 Feb 2021 19:39:17 -0800 Subject: [PATCH 08/12] put back commented out code --- .../io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java index 23866f5aaa0a..9ecc99153e42 100644 --- a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java +++ b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java @@ -411,7 +411,7 @@ public void launchBillingFlow_ok_AccountId() { // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); - // verify(result, times(1)).success(fromBillingResult(billingResult)); + verify(result, times(1)).success(fromBillingResult(billingResult)); } @Test From 822e631b0da29a2dbccd6235ebe06019e88d4b0e Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 26 Feb 2021 09:09:36 -0800 Subject: [PATCH 09/12] fix sub changing --- .../lib/src/in_app_purchase/google_play_connection.dart | 1 + .../lib/src/in_app_purchase/purchase_details.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart index 847ad86a708a..1c53d0ac5213 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart @@ -66,6 +66,7 @@ class GooglePlayConnection accountId: purchaseParam.applicationUserName, oldSku: purchaseParam .changeSubscriptionParam?.oldPurchaseDetails.productID, + purchaseToken: purchaseParam.changeSubscriptionParam?.oldPurchaseDetails.verificationData.serverVerificationData, prorationMode: purchaseParam.changeSubscriptionParam?.prorationMode); return billingResultWrapper.responseCode == BillingResponse.ok; diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/purchase_details.dart b/packages/in_app_purchase/lib/src/in_app_purchase/purchase_details.dart index b4a509055f14..9feca76e63d4 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/purchase_details.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/purchase_details.dart @@ -150,6 +150,7 @@ class ChangeSubscriptionParam { /// This is an optional parameter that indicates how to handle the existing /// subscription when the new subscription comes into effect. final ProrationMode? prorationMode; + } /// Represents the transaction details of a purchase. From ac4348c68f91c30516f7f6b408c40cc1270bc150 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 26 Feb 2021 09:10:05 -0800 Subject: [PATCH 10/12] formatting --- .../lib/src/in_app_purchase/google_play_connection.dart | 3 ++- .../lib/src/in_app_purchase/purchase_details.dart | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart index 1c53d0ac5213..c45512ed353f 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/google_play_connection.dart @@ -66,7 +66,8 @@ class GooglePlayConnection accountId: purchaseParam.applicationUserName, oldSku: purchaseParam .changeSubscriptionParam?.oldPurchaseDetails.productID, - purchaseToken: purchaseParam.changeSubscriptionParam?.oldPurchaseDetails.verificationData.serverVerificationData, + purchaseToken: purchaseParam.changeSubscriptionParam + ?.oldPurchaseDetails.verificationData.serverVerificationData, prorationMode: purchaseParam.changeSubscriptionParam?.prorationMode); return billingResultWrapper.responseCode == BillingResponse.ok; diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/purchase_details.dart b/packages/in_app_purchase/lib/src/in_app_purchase/purchase_details.dart index 9feca76e63d4..b4a509055f14 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/purchase_details.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/purchase_details.dart @@ -150,7 +150,6 @@ class ChangeSubscriptionParam { /// This is an optional parameter that indicates how to handle the existing /// subscription when the new subscription comes into effect. final ProrationMode? prorationMode; - } /// Represents the transaction details of a purchase. From 99b4eef2473fa089bd701ab86ac1684d715c5ac2 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 26 Feb 2021 09:56:03 -0800 Subject: [PATCH 11/12] review --- packages/in_app_purchase/CHANGELOG.md | 6 ++- .../inapppurchase/MethodCallHandlerTest.java | 5 +- .../billing_client_wrapper.dart | 12 ++--- .../purchase_wrapper.dart | 2 +- .../sku_details_wrapper.dart | 3 +- .../sku_details_wrapper.g.dart | 2 +- .../billing_client_wrapper_test.dart | 46 ++++++++++++++++--- 7 files changed, 58 insertions(+), 18 deletions(-) diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md index 34412792d221..535295a2f8af 100644 --- a/packages/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/CHANGELOG.md @@ -2,10 +2,14 @@ * Migrate to Google Billing Library 3.0 * Add `obfuscatedProfileId`, `purchaseToken` in [BillingClientWrapper.launchBillingFlow]. - * Removed `developerPayload` in [BillingClientWrapper.acknowledgePurchase], [BillingClientWrapper.consumeAsync], [InAppPurchaseConnection.completePurchase], [InAppPurchaseConnection.consumePurchase]. * **Breaking Change** + * Removed `developerPayload` in [BillingClientWrapper.acknowledgePurchase], [BillingClientWrapper.consumeAsync], [InAppPurchaseConnection.completePurchase], [InAppPurchaseConnection.consumePurchase]. * Removed `isRewarded` from [SkuDetailsWrapper]. * [SkuDetailsWrapper.introductoryPriceCycles] now returns `int` instead of `String`. + * Above breaking changes are inline with the breaking changes introduced in [Google Play Billing 3.0 release](https://developer.android.com/google/play/billing/release-notes#3-0). + * Additional information on some the changes: + * [Dropping reward SKU support](https://support.google.com/googleplay/android-developer/answer/9155268?hl=en) + * [Developer payload](https://developer.android.com/google/play/billing/developer-payload) ## 0.4.1 diff --git a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java index 9ecc99153e42..eef43346f655 100644 --- a/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java +++ b/packages/in_app_purchase/example/android/app/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java @@ -263,8 +263,11 @@ public void querySkuDetailsAsync_clientDisconnected() { verify(result, never()).success(any()); } + // Test launchBillingFlow not crash if `accountId` is `null` + // Ideally, we should check if the `accountId` is null in the parameter; however, + // since PBL 3.0, the `accountId` variable is not public. @Test - public void launchBillingFlow_ok_null_AccountId() { + public void launchBillingFlow_null_AccountId_do_not_crash() { // Fetch the sku details first and then prepare the launch billing flow call String skuId = "foo"; queryForSkus(singletonList(skuId)); 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 9ae3400f5f29..36f128c7f702 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 @@ -155,11 +155,11 @@ class BillingClient { /// The [skuDetails] needs to have already been fetched in a [querySkuDetails] /// call. The [accountId] is an optional hashed string associated with the user /// that's unique to your app. It's used by Google to detect unusual behavior. - /// Do not pass in a cleartext [accountId], Do not use this field to store any Personally Identifiable Information (PII) + /// Do not pass in a cleartext [accountId], and do not use this field to store any Personally Identifiable Information (PII) /// such as emails in cleartext. Attempting to store PII in this field will result in purchases being blocked. /// Google Play recommends that you use either encryption or a one-way hash to generate an obfuscated identifier to send to Google Play. /// - /// Specifies an optional [obfuscatedProfileId] that is uniquely associated with the user's profile in your app. + /// Specifies an optional [obfuscatedProfileId] that is uniquely associated with the user's profile in your app. /// Some applications allow users to have multiple profiles within a single account. Use this method to send the user's profile identifier to Google. /// Setting this field requests the user's obfuscated account id. /// @@ -174,10 +174,8 @@ class BillingClient { /// [`BillingClient#launchBillingFlow`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient#launchbillingflow). /// It constructs a /// [`BillingFlowParams`](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams) - /// instance by [setting the given - /// skuDetails](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder.html#setskudetails) - /// , [the given - /// accountId](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setObfuscatedAccountId(java.lang.String)) + /// instance by [setting the given skuDetails](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder.html#setskudetails), + /// [the given accountId](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setObfuscatedAccountId(java.lang.String)) /// and the [obfuscatedProfileId] (https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setobfuscatedprofileid). /// /// When this method is called to purchase a subscription, an optional `oldSku` @@ -195,6 +193,8 @@ class BillingClient { String? purchaseToken, ProrationMode? prorationMode}) async { assert(sku != null); + assert((oldSku == null) == (purchaseToken == null), + 'oldSku and purchaseToken should have the same nullability'); final Map arguments = { 'sku': sku, 'accountId': accountId, 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 index 1b0133920bd7..929b58292a2f 100644 --- 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 @@ -121,7 +121,7 @@ class PurchaseWrapper { /// /// The value is `null` if it wasn't specified when the purchase was acknowledged or consumed. /// The `developerPayload` is removed from [BillingClientWrapper.acknowledgePurchase], [BillingClientWrapper.consumeAsync], [InAppPurchaseConnection.completePurchase], [InAppPurchaseConnection.consumePurchase] - /// after plugin version `0.5.0`. As a result, this will be `null` for new purchases happened after `0.5.0`. + /// after plugin version `0.5.0`. As a result, this will be `null` for new purchases that happen after updating to `0.5.0`. final String? developerPayload; /// Whether the purchase has been acknowledged. diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart index d212ebd06dbb..f93dd60284f8 100644 --- a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.dart @@ -72,8 +72,7 @@ class SkuDetailsWrapper { /// The number of subscription billing periods for which the user will be given the introductory price, such as 3. /// Returns 0 if the SKU is not a subscription or doesn't have an introductory period. - /// Returns -1 if the value is not available. - @JsonKey(defaultValue: -1) + @JsonKey(defaultValue: 0) final int introductoryPriceCycles; /// The billing period of [introductoryPrice], in ISO 8601 format. diff --git a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart index 4f385f6aa6bd..a14affdf9ed3 100644 --- a/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart +++ b/packages/in_app_purchase/lib/src/billing_client_wrappers/sku_details_wrapper.g.dart @@ -12,7 +12,7 @@ SkuDetailsWrapper _$SkuDetailsWrapperFromJson(Map json) { freeTrialPeriod: json['freeTrialPeriod'] as String? ?? '', introductoryPrice: json['introductoryPrice'] as String? ?? '', introductoryPriceMicros: json['introductoryPriceMicros'] as String? ?? '', - introductoryPriceCycles: json['introductoryPriceCycles'] as int? ?? -1, + introductoryPriceCycles: json['introductoryPriceCycles'] as int? ?? 0, introductoryPricePeriod: json['introductoryPricePeriod'] as String? ?? '', price: json['price'] as String? ?? '', priceAmountMicros: json['priceAmountMicros'] as int? ?? 0, 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 7330bb2941a0..7ba560257b39 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 @@ -211,6 +211,40 @@ void main() { expect(arguments['obfuscatedProfileId'], equals(profileId)); }); + test( + 'Change subscription throws assertion error `oldSku` and `purchaseToken` has different nullability', + () async { + const String debugMessage = 'dummy message'; + final BillingResponse responseCode = BillingResponse.ok; + final BillingResultWrapper expectedBillingResult = BillingResultWrapper( + responseCode: responseCode, debugMessage: debugMessage); + stubPlatform.addResponse( + name: launchMethodName, + value: buildBillingResultMap(expectedBillingResult), + ); + final SkuDetailsWrapper skuDetails = dummySkuDetails; + final String accountId = 'hashedAccountId'; + final String profileId = 'hashedProfileId'; + + expect( + billingClient.launchBillingFlow( + sku: skuDetails.sku, + accountId: accountId, + obfuscatedProfileId: profileId, + oldSku: dummyOldPurchase.sku, + purchaseToken: null), + throwsAssertionError); + + expect( + billingClient.launchBillingFlow( + sku: skuDetails.sku, + accountId: accountId, + obfuscatedProfileId: profileId, + oldSku: null, + purchaseToken: dummyOldPurchase.purchaseToken), + throwsAssertionError); + }); + test( 'serializes and deserializes data on change subscription without proration', () async { @@ -225,7 +259,6 @@ void main() { final SkuDetailsWrapper skuDetails = dummySkuDetails; final String accountId = 'hashedAccountId'; final String profileId = 'hashedProfileId'; - final String purchaseToken = 'aPurchaseToken'; expect( await billingClient.launchBillingFlow( @@ -233,14 +266,15 @@ void main() { accountId: accountId, obfuscatedProfileId: profileId, oldSku: dummyOldPurchase.sku, - purchaseToken: purchaseToken), + purchaseToken: dummyOldPurchase.purchaseToken), equals(expectedBillingResult)); Map arguments = stubPlatform.previousCallMatching(launchMethodName).arguments; expect(arguments['sku'], equals(skuDetails.sku)); expect(arguments['accountId'], equals(accountId)); expect(arguments['oldSku'], equals(dummyOldPurchase.sku)); - expect(arguments['purchaseToken'], equals(purchaseToken)); + expect( + arguments['purchaseToken'], equals(dummyOldPurchase.purchaseToken)); expect(arguments['obfuscatedProfileId'], equals(profileId)); }); @@ -258,7 +292,6 @@ void main() { final SkuDetailsWrapper skuDetails = dummySkuDetails; final String accountId = 'hashedAccountId'; final String profileId = 'hashedProfileId'; - final String purchaseToken = 'aPurchaseToken'; final prorationMode = ProrationMode.immediateAndChargeProratedPrice; expect( @@ -268,7 +301,7 @@ void main() { obfuscatedProfileId: profileId, oldSku: dummyOldPurchase.sku, prorationMode: prorationMode, - purchaseToken: purchaseToken), + purchaseToken: dummyOldPurchase.purchaseToken), equals(expectedBillingResult)); Map arguments = stubPlatform.previousCallMatching(launchMethodName).arguments; @@ -276,7 +309,8 @@ void main() { expect(arguments['accountId'], equals(accountId)); expect(arguments['oldSku'], equals(dummyOldPurchase.sku)); expect(arguments['obfuscatedProfileId'], equals(profileId)); - expect(arguments['purchaseToken'], equals(purchaseToken)); + expect( + arguments['purchaseToken'], equals(dummyOldPurchase.purchaseToken)); expect(arguments['prorationMode'], ProrationModeConverter().toJson(prorationMode)); }); From 763360dd2cfc100c98a6a4e50177c7bd40ca8aed Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 1 Mar 2021 11:13:08 -0800 Subject: [PATCH 12/12] nits --- .../lib/src/billing_client_wrappers/billing_client_wrapper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 36f128c7f702..f14e8dbec6bd 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 @@ -194,7 +194,7 @@ class BillingClient { ProrationMode? prorationMode}) async { assert(sku != null); assert((oldSku == null) == (purchaseToken == null), - 'oldSku and purchaseToken should have the same nullability'); + 'oldSku and purchaseToken must both be set, or both be null.'); final Map arguments = { 'sku': sku, 'accountId': accountId,