Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.4.0+2

* Adds support for setting the `offerToken` on `GooglePlayPurchaseParam`.

## 0.4.0+1

* Updates compileSdk 34 to flutter.compileSdkVersion.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,15 @@ class InAppPurchaseAndroidPlatform extends InAppPurchasePlatform {
@override
Future<bool> buyNonConsumable({required PurchaseParam purchaseParam}) async {
ChangeSubscriptionParam? changeSubscriptionParam;
String? offerToken;

if (purchaseParam is GooglePlayPurchaseParam) {
changeSubscriptionParam = purchaseParam.changeSubscriptionParam;
offerToken = purchaseParam.offerToken;
}

String? offerToken;
if (purchaseParam.productDetails is GooglePlayProductDetails) {
if (offerToken == null &&
purchaseParam.productDetails is GooglePlayProductDetails) {
offerToken =
(purchaseParam.productDetails as GooglePlayProductDetails).offerToken;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ class GooglePlayPurchaseParam extends PurchaseParam {
required super.productDetails,
super.applicationUserName,
this.changeSubscriptionParam,
this.offerToken,
});

/// The 'changeSubscriptionParam' containing information for upgrading or
/// downgrading an existing subscription.
final ChangeSubscriptionParam? changeSubscriptionParam;

/// For One-time product, "offerToken" shouldn't be filled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Add a space after first sentence.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a line break

///
/// For subscriptions, to get the offer token corresponding to the selected
/// offer call productDetails.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken
final String? offerToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ name: in_app_purchase_android
description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs.
repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
version: 0.4.0+1

version: 0.4.0+2

environment:
sdk: ^3.6.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,33 @@ import 'billing_client_wrappers/product_details_wrapper_test.dart';
import 'billing_client_wrappers/purchase_wrapper_test.dart';
import 'test_conversion_utils.dart';

const ProductDetailsWrapper dummySubscriptionProductDetails =
ProductDetailsWrapper(
description: 'description',
name: 'name',
productId: 'productId',
productType: ProductType.subs,
title: 'title',
subscriptionOfferDetails: <SubscriptionOfferDetailsWrapper>[
SubscriptionOfferDetailsWrapper(
basePlanId: 'basePlanId',
offerTags: <String>['offerTags'],
offerId: 'offerId',
offerIdToken: 'offerToken',
pricingPhases: <PricingPhaseWrapper>[
PricingPhaseWrapper(
billingCycleCount: 4,
billingPeriod: 'billingPeriod',
formattedPrice: r'$100',
priceAmountMicros: 100000000,
priceCurrencyCode: 'USD',
recurrenceMode: RecurrenceMode.finiteRecurring,
),
],
),
],
);

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

Expand Down Expand Up @@ -255,6 +282,67 @@ void main() {
});

group('make payment', () {
test('buy non consumable subscribe offer, serializes and deserializes data',
() async {
const ProductDetailsWrapper productDetails =
dummySubscriptionProductDetails;
const String accountId = 'hashedAccountId';
const String debugMessage = 'dummy message';
const BillingResponse sentCode = BillingResponse.ok;
const BillingResultWrapper expectedBillingResult = BillingResultWrapper(
responseCode: sentCode, debugMessage: debugMessage);

when(mockApi.launchBillingFlow(any)).thenAnswer((_) async {
// Mock java update purchase callback.
iapAndroidPlatform.billingClientManager.client.hostCallbackHandler
.onPurchasesUpdated(PlatformPurchasesResponse(
billingResult: convertToPigeonResult(expectedBillingResult),
purchases: <PlatformPurchase>[
PlatformPurchase(
orderId: 'orderID1',
products: <String>[productDetails.productId],
isAutoRenewing: false,
packageName: 'package',
purchaseTime: 1231231231,
purchaseToken: 'token',
signature: 'sign',
originalJson: 'json',
developerPayload: 'dummy payload',
isAcknowledged: true,
purchaseState: PlatformPurchaseState.purchased,
quantity: 1,
)
],
));

return convertToPigeonResult(expectedBillingResult);
});
final Completer<PurchaseDetails> completer = Completer<PurchaseDetails>();
PurchaseDetails purchaseDetails;
final Stream<List<PurchaseDetails>> purchaseStream =
iapAndroidPlatform.purchaseStream;
late StreamSubscription<List<PurchaseDetails>> subscription;
subscription = purchaseStream.listen((List<PurchaseDetails> details) {
purchaseDetails = details.first;
completer.complete(purchaseDetails);
subscription.cancel();
}, onDone: () {});
final GooglePlayPurchaseParam purchaseParam = GooglePlayPurchaseParam(
offerToken:
productDetails.subscriptionOfferDetails?.first.offerIdToken,
productDetails:
GooglePlayProductDetails.fromProductDetails(productDetails).first,
applicationUserName: accountId);
final bool launchResult = await iapAndroidPlatform.buyNonConsumable(
purchaseParam: purchaseParam);

final PurchaseDetails result = await completer.future;
expect(launchResult, isTrue);
expect(result.purchaseID, 'orderID1');
expect(result.status, PurchaseStatus.purchased);
expect(result.productID, productDetails.productId);
});

test('buy non consumable, serializes and deserializes data', () async {
const ProductDetailsWrapper productDetails = dummyOneTimeProductDetails;
const String accountId = 'hashedAccountId';
Expand Down