diff --git a/docs/blog/2026-02-11-8.2.6-openiap-1.3.17.md b/docs/blog/2026-02-11-8.2.6-openiap-1.3.17.md new file mode 100644 index 000000000..694239ff2 --- /dev/null +++ b/docs/blog/2026-02-11-8.2.6-openiap-1.3.17.md @@ -0,0 +1,77 @@ +--- +slug: 8.2.6 +title: 8.2.6 - OpenIAP 1.3.17 Sync +authors: [hyochan] +tags: [release, openiap, android, billing-library] +date: 2026-02-11 +--- + +# 8.2.6 Release Notes + +This release syncs with [OpenIAP v1.3.17](https://www.openiap.dev/docs/updates/notes#gql-1317), adding new types for Google Play Billing Library 5.0+ and 7.0+ features. + + + +## New Types + +### InstallmentPlanDetailsAndroid (Billing Library 7.0+) + +Subscription installment plan details for plans that allow users to pay in installments. + +```dart +class InstallmentPlanDetailsAndroid { + /// Committed payments count after signup (e.g., 12 monthly payments) + final int commitmentPaymentsCount; + /// Subsequent commitment payments when plan renews (0 if reverts to normal) + final int subsequentCommitmentPaymentsCount; +} +``` + +This is available on `ProductSubscriptionAndroidOfferDetails.installmentPlanDetails`. + +### PendingPurchaseUpdateAndroid (Billing Library 5.0+) + +Details about pending subscription upgrades/downgrades. + +```dart +class PendingPurchaseUpdateAndroid { + /// Product IDs for the pending purchase update + final List products; + /// Purchase token for the pending transaction + final String purchaseToken; +} +``` + +This is available on `PurchaseAndroid.pendingPurchaseUpdateAndroid`. + +### purchaseOptionIdAndroid (Billing Library 7.0+) + +New field on `DiscountOffer` and `ProductAndroidOneTimePurchaseOfferDetail` to identify which purchase option the user selected. + +```dart +// Available on DiscountOffer +discountOffer.purchaseOptionIdAndroid // String? + +// Available on ProductAndroidOneTimePurchaseOfferDetail +// Note: The field is named purchaseOptionId on this class. +productAndroidOneTimePurchaseOfferDetail.purchaseOptionId // String? +``` + +## OpenIAP Versions + +| Package | Version | +|---------|---------| +| openiap-gql | 1.3.17 | +| openiap-google | 1.3.28 | +| openiap-apple | 1.3.14 | + +## Installation + +```yaml +dependencies: + flutter_inapp_purchase: ^8.2.6 +``` + +## Related + +- [OpenIAP Release Notes](https://www.openiap.dev/docs/updates/notes#gql-1317) diff --git a/docs/static/llms.txt b/docs/static/llms.txt index 692b49461..1a1af4ab7 100644 --- a/docs/static/llms.txt +++ b/docs/static/llms.txt @@ -250,6 +250,19 @@ class DiscountOffer { String? offerTokenAndroid; int? percentageDiscountAndroid; String? discountAmountMicrosAndroid; + String? purchaseOptionIdAndroid; // v8.2.6+, Billing Library 7.0+ +} + +// Installment plan details (v8.2.6+, Billing Library 7.0+) +class InstallmentPlanDetailsAndroid { + int commitmentPaymentsCount; // Initial commitment (e.g., 12 months) + int subsequentCommitmentPaymentsCount; // Renewal commitment +} + +// Pending subscription update (v8.2.6+, Billing Library 5.0+) +class PendingPurchaseUpdateAndroid { + List products; // New products being switched to + String purchaseToken; // Pending transaction token } enum DiscountOfferType { diff --git a/lib/types.dart b/lib/types.dart index b774e1706..9741c8be9 100644 --- a/lib/types.dart +++ b/lib/types.dart @@ -1362,6 +1362,7 @@ class DiscountOffer { this.percentageDiscountAndroid, this.preorderDetailsAndroid, required this.price, + this.purchaseOptionIdAndroid, this.rentalDetailsAndroid, required this.type, this.validTimeWindowAndroid, @@ -1400,6 +1401,10 @@ class DiscountOffer { final PreorderDetailsAndroid? preorderDetailsAndroid; /// Numeric price value final double price; + /// [Android] Purchase option ID for this offer. + /// Used to identify which purchase option the user selected. + /// Available in Google Play Billing Library 7.0+ + final String? purchaseOptionIdAndroid; /// [Android] Rental details if this is a rental offer. final RentalDetailsAndroid? rentalDetailsAndroid; /// Type of discount offer @@ -1422,6 +1427,7 @@ class DiscountOffer { percentageDiscountAndroid: json['percentageDiscountAndroid'] as int?, preorderDetailsAndroid: json['preorderDetailsAndroid'] != null ? PreorderDetailsAndroid.fromJson(json['preorderDetailsAndroid'] as Map) : null, price: (json['price'] as num).toDouble(), + purchaseOptionIdAndroid: json['purchaseOptionIdAndroid'] as String?, rentalDetailsAndroid: json['rentalDetailsAndroid'] != null ? RentalDetailsAndroid.fromJson(json['rentalDetailsAndroid'] as Map) : null, type: DiscountOfferType.fromJson(json['type'] as String), validTimeWindowAndroid: json['validTimeWindowAndroid'] != null ? ValidTimeWindowAndroid.fromJson(json['validTimeWindowAndroid'] as Map) : null, @@ -1443,6 +1449,7 @@ class DiscountOffer { 'percentageDiscountAndroid': percentageDiscountAndroid, 'preorderDetailsAndroid': preorderDetailsAndroid?.toJson(), 'price': price, + 'purchaseOptionIdAndroid': purchaseOptionIdAndroid, 'rentalDetailsAndroid': rentalDetailsAndroid?.toJson(), 'type': type.toJson(), 'validTimeWindowAndroid': validTimeWindowAndroid?.toJson(), @@ -1714,6 +1721,41 @@ class FetchProductsResultSubscriptions extends FetchProductsResult { final List? value; } +/// Installment plan details for subscription offers (Android) +/// Contains information about the installment plan commitment. +/// Available in Google Play Billing Library 7.0+ +class InstallmentPlanDetailsAndroid { + const InstallmentPlanDetailsAndroid({ + required this.commitmentPaymentsCount, + required this.subsequentCommitmentPaymentsCount, + }); + + /// Committed payments count after a user signs up for this subscription plan. + /// For example, for a monthly subscription with commitmentPaymentsCount of 12, + /// users will be charged monthly for 12 months after signup. + final int commitmentPaymentsCount; + /// Subsequent committed payments count after the subscription plan renews. + /// For example, for a monthly subscription with subsequentCommitmentPaymentsCount of 12, + /// users will be committed to another 12 monthly payments when the plan renews. + /// Returns 0 if the installment plan has no subsequent commitment (reverts to normal plan). + final int subsequentCommitmentPaymentsCount; + + factory InstallmentPlanDetailsAndroid.fromJson(Map json) { + return InstallmentPlanDetailsAndroid( + commitmentPaymentsCount: json['commitmentPaymentsCount'] as int, + subsequentCommitmentPaymentsCount: json['subsequentCommitmentPaymentsCount'] as int, + ); + } + + Map toJson() { + return { + '__typename': 'InstallmentPlanDetailsAndroid', + 'commitmentPaymentsCount': commitmentPaymentsCount, + 'subsequentCommitmentPaymentsCount': subsequentCommitmentPaymentsCount, + }; + } +} + /// Limited quantity information for one-time purchase offers (Android) /// Available in Google Play Billing Library 7.0+ class LimitedQuantityInfoAndroid { @@ -1743,6 +1785,40 @@ class LimitedQuantityInfoAndroid { } } +/// Pending purchase update for subscription upgrades/downgrades (Android) +/// When a user initiates a subscription change (upgrade/downgrade), the new purchase +/// may be pending until the current billing period ends. This type contains the +/// details of the pending change. +/// Available in Google Play Billing Library 5.0+ +class PendingPurchaseUpdateAndroid { + const PendingPurchaseUpdateAndroid({ + required this.products, + required this.purchaseToken, + }); + + /// Product IDs for the pending purchase update. + /// These are the new products the user is switching to. + final List products; + /// Purchase token for the pending transaction. + /// Use this token to track or manage the pending purchase update. + final String purchaseToken; + + factory PendingPurchaseUpdateAndroid.fromJson(Map json) { + return PendingPurchaseUpdateAndroid( + products: (json['products'] as List).map((e) => e as String).toList(), + purchaseToken: json['purchaseToken'] as String, + ); + } + + Map toJson() { + return { + '__typename': 'PendingPurchaseUpdateAndroid', + 'products': products, + 'purchaseToken': purchaseToken, + }; + } +} + /// Pre-order details for one-time purchase products (Android) /// Available in Google Play Billing Library 8.1.0+ class PreorderDetailsAndroid { @@ -1949,6 +2025,7 @@ class ProductAndroidOneTimePurchaseOfferDetail { this.preorderDetailsAndroid, required this.priceAmountMicros, required this.priceCurrencyCode, + this.purchaseOptionId, this.rentalDetailsAndroid, this.validTimeWindow, }); @@ -1973,6 +2050,10 @@ class ProductAndroidOneTimePurchaseOfferDetail { final PreorderDetailsAndroid? preorderDetailsAndroid; final String priceAmountMicros; final String priceCurrencyCode; + /// Purchase option ID for this offer (Android) + /// Used to identify which purchase option the user selected. + /// Available in Google Play Billing Library 7.0+ + final String? purchaseOptionId; /// Rental details for rental offers final RentalDetailsAndroid? rentalDetailsAndroid; /// Valid time window for the offer @@ -1990,6 +2071,7 @@ class ProductAndroidOneTimePurchaseOfferDetail { preorderDetailsAndroid: json['preorderDetailsAndroid'] != null ? PreorderDetailsAndroid.fromJson(json['preorderDetailsAndroid'] as Map) : null, priceAmountMicros: json['priceAmountMicros'] as String, priceCurrencyCode: json['priceCurrencyCode'] as String, + purchaseOptionId: json['purchaseOptionId'] as String?, rentalDetailsAndroid: json['rentalDetailsAndroid'] != null ? RentalDetailsAndroid.fromJson(json['rentalDetailsAndroid'] as Map) : null, validTimeWindow: json['validTimeWindow'] != null ? ValidTimeWindowAndroid.fromJson(json['validTimeWindow'] as Map) : null, ); @@ -2008,6 +2090,7 @@ class ProductAndroidOneTimePurchaseOfferDetail { 'preorderDetailsAndroid': preorderDetailsAndroid?.toJson(), 'priceAmountMicros': priceAmountMicros, 'priceCurrencyCode': priceCurrencyCode, + 'purchaseOptionId': purchaseOptionId, 'rentalDetailsAndroid': rentalDetailsAndroid?.toJson(), 'validTimeWindow': validTimeWindow?.toJson(), }; @@ -2204,6 +2287,7 @@ class ProductSubscriptionAndroid extends ProductSubscription implements ProductC class ProductSubscriptionAndroidOfferDetails { const ProductSubscriptionAndroidOfferDetails({ required this.basePlanId, + this.installmentPlanDetails, this.offerId, required this.offerTags, required this.offerToken, @@ -2211,6 +2295,10 @@ class ProductSubscriptionAndroidOfferDetails { }); final String basePlanId; + /// Installment plan details for this subscription offer. + /// Only set for installment subscription plans; null for non-installment plans. + /// Available in Google Play Billing Library 7.0+ + final InstallmentPlanDetailsAndroid? installmentPlanDetails; final String? offerId; final List offerTags; final String offerToken; @@ -2219,6 +2307,7 @@ class ProductSubscriptionAndroidOfferDetails { factory ProductSubscriptionAndroidOfferDetails.fromJson(Map json) { return ProductSubscriptionAndroidOfferDetails( basePlanId: json['basePlanId'] as String, + installmentPlanDetails: json['installmentPlanDetails'] != null ? InstallmentPlanDetailsAndroid.fromJson(json['installmentPlanDetails'] as Map) : null, offerId: json['offerId'] as String?, offerTags: (json['offerTags'] as List).map((e) => e as String).toList(), offerToken: json['offerToken'] as String, @@ -2230,6 +2319,7 @@ class ProductSubscriptionAndroidOfferDetails { return { '__typename': 'ProductSubscriptionAndroidOfferDetails', 'basePlanId': basePlanId, + 'installmentPlanDetails': installmentPlanDetails?.toJson(), 'offerId': offerId, 'offerTags': offerTags, 'offerToken': offerToken, @@ -2371,6 +2461,7 @@ class PurchaseAndroid extends Purchase implements PurchaseCommon { this.obfuscatedAccountIdAndroid, this.obfuscatedProfileIdAndroid, this.packageNameAndroid, + this.pendingPurchaseUpdateAndroid, required this.platform, required this.productId, required this.purchaseState, @@ -2400,6 +2491,11 @@ class PurchaseAndroid extends Purchase implements PurchaseCommon { final String? obfuscatedAccountIdAndroid; final String? obfuscatedProfileIdAndroid; final String? packageNameAndroid; + /// Pending purchase update for uncommitted subscription upgrade/downgrade (Android) + /// Contains the new products and purchase token for the pending transaction. + /// Returns null if no pending update exists. + /// Available in Google Play Billing Library 5.0+ + final PendingPurchaseUpdateAndroid? pendingPurchaseUpdateAndroid; final IapPlatform platform; final String productId; final PurchaseState purchaseState; @@ -2426,6 +2522,7 @@ class PurchaseAndroid extends Purchase implements PurchaseCommon { obfuscatedAccountIdAndroid: json['obfuscatedAccountIdAndroid'] as String?, obfuscatedProfileIdAndroid: json['obfuscatedProfileIdAndroid'] as String?, packageNameAndroid: json['packageNameAndroid'] as String?, + pendingPurchaseUpdateAndroid: json['pendingPurchaseUpdateAndroid'] != null ? PendingPurchaseUpdateAndroid.fromJson(json['pendingPurchaseUpdateAndroid'] as Map) : null, platform: IapPlatform.fromJson(json['platform'] as String), productId: json['productId'] as String, purchaseState: PurchaseState.fromJson(json['purchaseState'] as String), @@ -2455,6 +2552,7 @@ class PurchaseAndroid extends Purchase implements PurchaseCommon { 'obfuscatedAccountIdAndroid': obfuscatedAccountIdAndroid, 'obfuscatedProfileIdAndroid': obfuscatedProfileIdAndroid, 'packageNameAndroid': packageNameAndroid, + 'pendingPurchaseUpdateAndroid': pendingPurchaseUpdateAndroid?.toJson(), 'platform': platform.toJson(), 'productId': productId, 'purchaseState': purchaseState.toJson(), @@ -2912,6 +3010,7 @@ class SubscriptionOffer { this.currency, required this.displayPrice, required this.id, + this.installmentPlanDetailsAndroid, this.keyIdentifierIOS, this.localizedPriceIOS, this.nonceIOS, @@ -2939,6 +3038,10 @@ class SubscriptionOffer { /// - iOS: Discount identifier from App Store Connect /// - Android: offerId from ProductSubscriptionAndroidOfferDetails final String id; + /// [Android] Installment plan details for this subscription offer. + /// Only set for installment subscription plans; null for non-installment plans. + /// Available in Google Play Billing Library 7.0+ + final InstallmentPlanDetailsAndroid? installmentPlanDetailsAndroid; /// [iOS] Key identifier for signature validation. /// Used with server-side signature generation for promotional offers. final String? keyIdentifierIOS; @@ -2980,6 +3083,7 @@ class SubscriptionOffer { currency: json['currency'] as String?, displayPrice: json['displayPrice'] as String, id: json['id'] as String, + installmentPlanDetailsAndroid: json['installmentPlanDetailsAndroid'] != null ? InstallmentPlanDetailsAndroid.fromJson(json['installmentPlanDetailsAndroid'] as Map) : null, keyIdentifierIOS: json['keyIdentifierIOS'] as String?, localizedPriceIOS: json['localizedPriceIOS'] as String?, nonceIOS: json['nonceIOS'] as String?, @@ -3004,6 +3108,7 @@ class SubscriptionOffer { 'currency': currency, 'displayPrice': displayPrice, 'id': id, + 'installmentPlanDetailsAndroid': installmentPlanDetailsAndroid?.toJson(), 'keyIdentifierIOS': keyIdentifierIOS, 'localizedPriceIOS': localizedPriceIOS, 'nonceIOS': nonceIOS, diff --git a/openiap-versions.json b/openiap-versions.json index a571838cd..c6bc0c0f5 100755 --- a/openiap-versions.json +++ b/openiap-versions.json @@ -1,5 +1,5 @@ { "apple": "1.3.14", - "google": "1.3.27", - "gql": "1.3.16" + "google": "1.3.28", + "gql": "1.3.17" }