blob: 0d3beaea513aa213faef8faf95eef2a886be8482 [file] [log] [blame]
// Copyright 2013 The Flutter 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:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart';
import '../../billing_client_wrappers.dart';
/// The class represents the information of a product as registered in at
/// Google Play store front.
class GooglePlayProductDetails extends ProductDetails {
/// Creates a new Google Play specific product details object with the
/// provided details.
GooglePlayProductDetails._({
required super.id,
required super.title,
required super.description,
required super.price,
required super.rawPrice,
required super.currencyCode,
required this.productDetails,
required super.currencySymbol,
this.subscriptionIndex,
});
/// Generates a [GooglePlayProductDetails] object based on an Android
/// [ProductDetailsWrapper] object for an in-app product.
factory GooglePlayProductDetails._fromOneTimePurchaseProductDetails(
ProductDetailsWrapper productDetails,
) {
assert(productDetails.productType == ProductType.inapp);
assert(productDetails.oneTimePurchaseOfferDetails != null);
final OneTimePurchaseOfferDetailsWrapper oneTimePurchaseOfferDetails =
productDetails.oneTimePurchaseOfferDetails!;
final String formattedPrice = oneTimePurchaseOfferDetails.formattedPrice;
final double rawPrice =
oneTimePurchaseOfferDetails.priceAmountMicros / 1000000.0;
final String currencyCode = oneTimePurchaseOfferDetails.priceCurrencyCode;
final String? currencySymbol = _extractCurrencySymbol(formattedPrice);
return GooglePlayProductDetails._(
id: productDetails.productId,
title: productDetails.title,
description: productDetails.description,
price: formattedPrice,
rawPrice: rawPrice,
currencyCode: currencyCode,
currencySymbol: currencySymbol ?? currencyCode,
productDetails: productDetails,
);
}
/// Generates a [GooglePlayProductDetails] object based on an Android
/// [ProductDetailsWrapper] object for a subscription product.
///
/// Subscriptions can consist of multiple base plans, and base plans in turn
/// can consist of multiple offers. [subscriptionIndex] points to the index of
/// [productDetails.subscriptionOfferDetails] for which the
/// [GooglePlayProductDetails] is constructed.
factory GooglePlayProductDetails._fromSubscription(
ProductDetailsWrapper productDetails,
int subscriptionIndex,
) {
assert(productDetails.productType == ProductType.subs);
assert(productDetails.subscriptionOfferDetails != null);
assert(subscriptionIndex < productDetails.subscriptionOfferDetails!.length);
final SubscriptionOfferDetailsWrapper subscriptionOfferDetails =
productDetails.subscriptionOfferDetails![subscriptionIndex];
final PricingPhaseWrapper firstPricingPhase =
subscriptionOfferDetails.pricingPhases.first;
final String formattedPrice = firstPricingPhase.formattedPrice;
final double rawPrice = firstPricingPhase.priceAmountMicros / 1000000.0;
final String currencyCode = firstPricingPhase.priceCurrencyCode;
final String? currencySymbol = _extractCurrencySymbol(formattedPrice);
return GooglePlayProductDetails._(
id: productDetails.productId,
title: productDetails.title,
description: productDetails.description,
price: formattedPrice,
rawPrice: rawPrice,
currencyCode: currencyCode,
currencySymbol: currencySymbol ?? currencyCode,
productDetails: productDetails,
subscriptionIndex: subscriptionIndex,
);
}
/// Generates a list of [GooglePlayProductDetails] based on an Android
/// [ProductDetailsWrapper] object.
///
/// If [productDetails] is of type [ProductType.inapp], a single
/// [GooglePlayProductDetails] will be constructed.
/// If [productDetails] is of type [ProductType.subs], a list is returned
/// where every element corresponds to a base plan or its offer in
/// [productDetails.subscriptionOfferDetails].
static List<GooglePlayProductDetails> fromProductDetails(
ProductDetailsWrapper productDetails,
) {
if (productDetails.productType == ProductType.inapp) {
return <GooglePlayProductDetails>[
GooglePlayProductDetails._fromOneTimePurchaseProductDetails(
productDetails),
];
} else {
final List<GooglePlayProductDetails> productDetailList =
<GooglePlayProductDetails>[];
for (int subscriptionIndex = 0;
subscriptionIndex < productDetails.subscriptionOfferDetails!.length;
subscriptionIndex++) {
productDetailList.add(GooglePlayProductDetails._fromSubscription(
productDetails,
subscriptionIndex,
));
}
return productDetailList;
}
}
/// Extracts the currency symbol from [formattedPrice].
///
/// Note that a currency symbol might consist of more than a single character.
///
/// Just in case, we assume currency symbols can appear at the start or the
/// end of [formattedPrice].
///
/// The regex captures the characters from the start/end of the [String]
/// until the first/last digit or space.
static String? _extractCurrencySymbol(String formattedPrice) {
return RegExp(r'^[^\d ]*|[^\d ]*$').firstMatch(formattedPrice)?.group(0);
}
/// Points back to the [ProductDetailsWrapper] object that was used to
/// generate this [GooglePlayProductDetails] object.
final ProductDetailsWrapper productDetails;
/// The index pointing to the [SubscriptionOfferDetailsWrapper] this
/// [GooglePlayProductDetails] object was contructed for, or `null` if it was
/// not a subscription.
///
/// The original subscription can be accessed using this index:
///
/// ```dart
/// SubscriptionOfferDetailWrapper subscription = productDetail
/// .subscriptionOfferDetails[subscriptionIndex];
/// ```
final int? subscriptionIndex;
/// The offerToken of the subscription this [GooglePlayProductDetails]
/// object was contructed for, or `null` if it was not a subscription.
String? get offerToken => subscriptionIndex != null &&
productDetails.subscriptionOfferDetails != null
? productDetails
.subscriptionOfferDetails![subscriptionIndex!].offerIdToken
: null;
}