-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[In_app_purchases] migrate to Play Billing Library 2.0. #2287
Changes from 6 commits
e4c9796
d47ae9c
a10b0ea
7fec09a
1d41afc
4549dc0
37df4a4
b0b69e5
4959ffb
a44756f
fb8655e
e815b17
61c5771
d63cc13
3b3c67e
e610421
a3239f7
215086f
33576c7
a3a576c
6f2c821
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -366,12 +366,14 @@ class _MyAppState extends State<MyApp> { | |||
| } else { | ||||
| if (purchaseDetails.status == PurchaseStatus.error) { | ||||
| handleError(purchaseDetails.error); | ||||
| return; | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also on iOS completePurchase() needs to be called for status == PurchaseStatus.error. With this change this will be broken. Please refer to completePurchase documentation. plugins/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart Line 197 in ed7620c
|
||||
| } else if (purchaseDetails.status == PurchaseStatus.purchased) { | ||||
| bool valid = await _verifyPurchase(purchaseDetails); | ||||
| if (valid) { | ||||
| deliverProduct(purchaseDetails); | ||||
| } else { | ||||
| _handleInvalidPurchase(purchaseDetails); | ||||
| return; | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am guessing this will have the same impact as line 374. |
||||
| } | ||||
| } | ||||
| if (Platform.isAndroid) { | ||||
|
|
||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,9 +9,8 @@ import 'package:json_annotation/json_annotation.dart'; | |
| part 'enum_converters.g.dart'; | ||
|
|
||
| /// Serializer for [BillingResponse]. | ||
| /// | ||
| /// Use these in `@JsonSerializable()` classes by annotating them with | ||
| /// `@BillingResponseConverter()`. | ||
| // Use these in `@JsonSerializable()` classes by annotating them with | ||
|
||
| // `@BillingResponseConverter()`. | ||
| class BillingResponseConverter implements JsonConverter<BillingResponse, int> { | ||
| const BillingResponseConverter(); | ||
|
|
||
|
|
@@ -24,9 +23,8 @@ class BillingResponseConverter implements JsonConverter<BillingResponse, int> { | |
| } | ||
|
|
||
| /// Serializer for [SkuType]. | ||
| /// | ||
| /// Use these in `@JsonSerializable()` classes by annotating them with | ||
| /// `@SkuTypeConverter()`. | ||
| // Use these in `@JsonSerializable()` classes by annotating them with | ||
| // `@SkuTypeConverter()`. | ||
| class SkuTypeConverter implements JsonConverter<SkuType, String> { | ||
| const SkuTypeConverter(); | ||
|
|
||
|
|
@@ -47,9 +45,8 @@ class _SerializedEnums { | |
| } | ||
|
|
||
| /// Serializer for [PurchaseStateWrapper]. | ||
| /// | ||
| /// Use these in `@JsonSerializable()` classes by annotating them with | ||
| /// `@PurchaseStateConverter()`. | ||
| // Use these in `@JsonSerializable()` classes by annotating them with | ||
| // `@PurchaseStateConverter()`. | ||
| class PurchaseStateConverter | ||
| implements JsonConverter<PurchaseStateWrapper, int> { | ||
| const PurchaseStateConverter(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,9 +9,6 @@ import 'package:flutter/widgets.dart'; | |
| import 'package:in_app_purchase/src/billing_client_wrappers/enum_converters.dart'; | ||
| import 'package:in_app_purchase/src/in_app_purchase/purchase_details.dart'; | ||
| import '../../billing_client_wrappers.dart'; | ||
| import '../../billing_client_wrappers.dart'; | ||
| import '../../billing_client_wrappers.dart'; | ||
| import '../../billing_client_wrappers.dart'; | ||
| import 'in_app_purchase_connection.dart'; | ||
| import 'product_details.dart'; | ||
|
|
||
|
|
@@ -72,20 +69,21 @@ class GooglePlayConnection | |
|
|
||
| @override | ||
| Future<BillingResultWrapper> completePurchase(PurchaseDetails purchase, | ||
| {String developerPayload}) { | ||
| AcknowledgeParams params = AcknowledgeParams( | ||
| purchaseToken: purchase.verificationData.serverVerificationData, | ||
| {String developerPayload}) async { | ||
| if (purchase.billingClientPurchase.isAcknowledged) { | ||
| return BillingResultWrapper(responseCode: BillingResponse.ok); | ||
| } | ||
| return await billingClient.acknowledgePurchase( | ||
| purchase.verificationData.serverVerificationData, | ||
| developerPayload: developerPayload); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a nice thing to have for the developer in case they accidentally call this twice, but I would rather just defer to the underlying SDK at all times here instead of trying to special case based off of what we think the state of this purchase really is. I don't want to risk introducing any bugs based on our state being somehow out of sync with the reality on the platform. |
||
| return billingClient.acknowledgePurchase(params); | ||
| } | ||
|
|
||
| @override | ||
| Future<BillingResultWrapper> consumePurchase(PurchaseDetails purchase, | ||
| {String developerPayload}) { | ||
| ConsumeParams params = ConsumeParams( | ||
| purchaseToken: purchase.verificationData.serverVerificationData, | ||
| return billingClient.consumeAsync( | ||
| purchase.verificationData.serverVerificationData, | ||
| developerPayload: developerPayload); | ||
| return billingClient.consumeAsync(params); | ||
| } | ||
|
|
||
| @override | ||
|
|
@@ -258,9 +256,8 @@ class GooglePlayConnection | |
| } | ||
| final List<Future<PurchaseDetails>> purchases = | ||
| resultWrapper.purchasesList.map((PurchaseWrapper purchase) { | ||
| return _maybeAutoConsumePurchase(PurchaseDetails.fromPurchase(purchase) | ||
| ..status = _buildPurchaseStatus(purchase.purchaseState) | ||
| ..error = error); | ||
| return _maybeAutoConsumePurchase( | ||
| PurchaseDetails.fromPurchase(purchase)..error = error); | ||
cyanglaz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }).toList(); | ||
| if (!purchases.isEmpty) { | ||
| return Future.wait(purchases); | ||
|
|
@@ -288,21 +285,16 @@ class GooglePlayConnection | |
| await instance.consumePurchase(purchaseDetails); | ||
| final BillingResponse consumedResponse = billingResult.responseCode; | ||
| if (consumedResponse != BillingResponse.ok) { | ||
| purchaseDetails.status = PurchaseStatus.error; | ||
| purchaseDetails.error = IAPError( | ||
| source: IAPSource.GooglePlay, | ||
| code: kConsumptionFailedErrorCode, | ||
| message: consumedResponse.toString(), | ||
| details: billingResult.debugMessage, | ||
| ); | ||
| } | ||
| purchaseDetails.status = _buildPurchaseStatus( | ||
| purchaseDetails.billingClientPurchase.purchaseState); | ||
| _productIdsToConsume.remove(purchaseDetails.productID); | ||
|
|
||
| return purchaseDetails; | ||
| } | ||
|
|
||
| static PurchaseStatus _buildPurchaseStatus(PurchaseStateWrapper state) { | ||
| return PurchaseStateConverter().toPurchaseStatus(state); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -94,7 +94,7 @@ abstract class InAppPurchaseConnection { | |||||
| /// purchasing process. | ||||||
| /// | ||||||
| /// This method does return whether or not the purchase request was initially | ||||||
| /// sent succesfully. | ||||||
| /// sent successfully. | ||||||
| /// | ||||||
| /// Consumable items are defined differently by the different underlying | ||||||
| /// payment platforms, and there's no way to query for whether or not the | ||||||
|
|
@@ -174,16 +174,20 @@ abstract class InAppPurchaseConnection { | |||||
| /// user. | ||||||
| /// | ||||||
| /// You are responsible for completing every [PurchaseDetails] whose | ||||||
| /// [PurchaseDetails.status] is [PurchaseStatus.purchased]. | ||||||
| /// Additionally, the purchase needs to be completed if the [PurchaseDetails.status] | ||||||
| /// [[PurchaseStatus.error]. | ||||||
| /// [PurchaseDetails.status] is [PurchaseStatus.purchased]. Additionally on iOS, | ||||||
| /// the purchase needs to be completed if the [PurchaseDetails.status] is [PurchaseStatus.error]. | ||||||
| /// Completing a [PurchaseStatus.pending] purchase will cause an exception. | ||||||
| /// For convenience, [PurchaseDetails.pendingCompletePurchase] indicates if a purchase is pending for completion. | ||||||
| /// | ||||||
| /// The method returns a [BillingResultWrapper] to indicate a detailed status of the complete process. | ||||||
mklim marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| /// If the result contains [BillingResponse.error] or [BillingResponse.serviceUnavailable], the developer should try | ||||||
| /// to complete the purchase via this method again, or retry the [completePurchase] it at a later time. | ||||||
| /// If the result indicates other errors, there might be some issue with | ||||||
| /// the app's code. The developer is responsible to fix the issue. | ||||||
| /// | ||||||
| /// Warning!Fail to call this method within 3 days of the purchase will result a refund on Android. | ||||||
| /// Warning!Failure to call this method and get a successful response within 3 days of the purchase will result a refund on Android. | ||||||
|
||||||
| /// Warning!Failure to call this method and get a successful response within 3 days of the purchase will result a refund on Android. | |
| /// Warning! Failure to call this method and get a successful response within 3 days of the purchase will result a refund on Android. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: