From 660029133de934bc190e503bb06f4b56f63cbb2a Mon Sep 17 00:00:00 2001
From: Hyo
Date: Thu, 23 Jul 2026 22:08:12 +0900
Subject: [PATCH 1/9] docs: clarify one-time discount offers
Align DiscountOffer documentation and generated API comments with the Google Play one-time offer contract. Refresh examples and search entries, and add regression audits for offer semantics and Kotlin purchase calls.
Closes #249
---
libraries/expo-iap/src/types.ts | 54 +-
.../flutter_inapp_purchase/lib/types.dart | 52 +-
libraries/godot-iap/addons/godot-iap/types.gd | 22 +-
.../io/github/hyochan/kmpiap/openiap/Types.kt | 52 +-
libraries/maui-iap/src/OpenIap.Maui/Types.cs | 52 +-
libraries/react-native-iap/src/types.ts | 54 +-
packages/apple/Sources/Models/Types.swift | 52 +-
packages/docs/src/lib/searchData.ts | 33 +-
.../docs/src/pages/docs/features/discount.tsx | 654 +++++++++---------
...one-time-purchase-offer-detail-android.tsx | 2 +-
.../src/pages/docs/types/discount-offer.tsx | 145 ++--
packages/docs/src/pages/docs/types/index.tsx | 2 +-
.../docs/src/pages/docs/types/product.tsx | 36 +-
.../docs/types/request-purchase-props.tsx | 3 +-
.../pages/docs/types/subscription-offer.tsx | 76 +-
.../pages/docs/types/subscription-product.tsx | 25 +-
.../src/main/java/dev/hyo/openiap/Types.kt | 52 +-
packages/gql/src/generated/Types.cs | 52 +-
packages/gql/src/generated/Types.kt | 52 +-
packages/gql/src/generated/Types.swift | 52 +-
packages/gql/src/generated/types.dart | 52 +-
packages/gql/src/generated/types.gd | 22 +-
packages/gql/src/generated/types.ts | 54 +-
packages/gql/src/type-android.graphql | 36 +-
packages/gql/src/type-ios.graphql | 10 +-
packages/gql/src/type.graphql | 12 +-
scripts/audit-docs.test.ts | 307 +++++++-
scripts/audit-docs.ts | 379 ++++++++++
28 files changed, 1538 insertions(+), 856 deletions(-)
diff --git a/libraries/expo-iap/src/types.ts b/libraries/expo-iap/src/types.ts
index 25c7de813..5866e2db8 100644
--- a/libraries/expo-iap/src/types.ts
+++ b/libraries/expo-iap/src/types.ts
@@ -328,7 +328,7 @@ export interface DiscountDisplayInfoAndroid {
/**
* Discount information returned from the store.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface DiscountIOS {
identifier: string;
@@ -343,10 +343,11 @@ export interface DiscountIOS {
/**
* Standardized one-time product discount offer.
- * Provides a unified interface for one-time purchase discounts across platforms.
+ * Provides a platform-neutral OpenIAP shape for Google Play one-time product
+ * purchase options and offers.
*
- * Currently supported on Android (Google Play Billing 8.0+).
- * iOS does not support one-time purchase discounts in the same way.
+ * Currently populated only on Android (Google Play Billing 8.0+).
+ * iOS does not populate this type.
*
* @see https://openiap.dev/docs/features/discount
*/
@@ -360,7 +361,7 @@ export interface DiscountOffer {
discountAmountMicrosAndroid?: (string | null);
/** Formatted display price string (e.g., "$4.99") */
displayPrice: string;
- /** [Android] Formatted discount amount string (e.g., "$5.00 OFF"). */
+ /** [Android] Formatted discount amount including its currency sign (e.g., "$5.00"). */
formattedDiscountAmountAndroid?: (string | null);
/**
* [Android] Original full price in micro-units before discount.
@@ -418,7 +419,7 @@ export interface DiscountOffer {
/**
* iOS DiscountOffer (output type).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface DiscountOfferIOS {
/** Discount identifier */
@@ -1093,9 +1094,9 @@ export interface ProductAndroid extends ProductCommon {
debugDescription?: (string | null);
description: string;
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Standardized Android one-time product purchase options and offers.
+ * Native metadata uses Android-suffixed fields.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
discountOffers?: (DiscountOffer[] | null);
displayName?: (string | null);
@@ -1105,7 +1106,7 @@ export interface ProductAndroid extends ProductCommon {
/**
* One-time purchase offer details including discounts (Android)
* Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * @deprecated Use the standardized discountOffers field instead.
* @deprecated Use discountOffers instead
*/
oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
@@ -1127,7 +1128,7 @@ export interface ProductAndroid extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
title: string;
@@ -1137,8 +1138,8 @@ export interface ProductAndroid extends ProductCommon {
/**
* One-time purchase offer details (Android).
* Available in Google Play Billing Library 8.0+
- * @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#discount-offer
+ * @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
export interface ProductAndroidOneTimePurchaseOfferDetail {
/**
@@ -1218,7 +1219,7 @@ export interface ProductIOS extends ProductCommon {
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
* Note: iOS does not support one-time product discounts.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
title: string;
@@ -1250,9 +1251,8 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
debugDescription?: (string | null);
description: string;
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Nullable compatibility field. Google Play does not return one-time purchase
+ * offer details for subscription products; use subscriptionOffers below.
*/
discountOffers?: (DiscountOffer[] | null);
displayName?: (string | null);
@@ -1260,10 +1260,11 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
id: string;
nameAndroid: string;
/**
- * One-time purchase offer details including discounts (Android)
- * Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
- * @deprecated Use discountOffers instead
+ * Legacy nullable compatibility field. Google Play does not populate one-time
+ * purchase offer details for subscription products.
+ * @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ * subscriptions use subscriptionOffers.
+ * @deprecated Use subscriptionOffers instead
*/
oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
platform: 'android';
@@ -1284,7 +1285,7 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers: SubscriptionOffer[];
title: string;
@@ -1294,7 +1295,7 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
/**
* Subscription offer details (Android).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface ProductSubscriptionAndroidOfferDetails {
basePlanId: string;
@@ -1347,7 +1348,7 @@ export interface ProductSubscriptionIOS extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
subscriptionPeriodNumberIOS?: (string | null);
@@ -2126,8 +2127,7 @@ export interface SubscriptionInfoIOS {
* - iOS: Introductory offers, promotional offers with server-side signatures
* - Android: Offer tokens with pricing phases
*
- * @see https://openiap.dev/docs/types/ios#discount-offer
- * @see https://openiap.dev/docs/types/android#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface SubscriptionOffer {
/**
@@ -2202,7 +2202,7 @@ export interface SubscriptionOffer {
/**
* iOS subscription offer details.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface SubscriptionOfferIOS {
displayPrice: string;
diff --git a/libraries/flutter_inapp_purchase/lib/types.dart b/libraries/flutter_inapp_purchase/lib/types.dart
index e3af72e5c..0dffa88a4 100644
--- a/libraries/flutter_inapp_purchase/lib/types.dart
+++ b/libraries/flutter_inapp_purchase/lib/types.dart
@@ -1982,7 +1982,7 @@ class DiscountDisplayInfoAndroid {
/// Discount information returned from the store.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class DiscountIOS {
const DiscountIOS({
required this.identifier,
@@ -2033,10 +2033,11 @@ class DiscountIOS {
}
/// Standardized one-time product discount offer.
-/// Provides a unified interface for one-time purchase discounts across platforms.
+/// Provides a platform-neutral OpenIAP shape for Google Play one-time product
+/// purchase options and offers.
///
-/// Currently supported on Android (Google Play Billing 8.0+).
-/// iOS does not support one-time purchase discounts in the same way.
+/// Currently populated only on Android (Google Play Billing 8.0+).
+/// iOS does not populate this type.
///
/// @see https://openiap.dev/docs/features/discount
class DiscountOffer {
@@ -2066,7 +2067,7 @@ class DiscountOffer {
final String? discountAmountMicrosAndroid;
/// Formatted display price string (e.g., "$4.99")
final String displayPrice;
- /// [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ /// [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
final String? formattedDiscountAmountAndroid;
/// [Android] Original full price in micro-units before discount.
/// Divide by 1,000,000 to get the actual price.
@@ -2150,7 +2151,7 @@ class DiscountOffer {
/// iOS DiscountOffer (output type).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class DiscountOfferIOS {
const DiscountOfferIOS({
required this.identifier,
@@ -2691,9 +2692,9 @@ class ProductAndroid extends Product implements ProductCommon {
final String currency;
final String? debugDescription;
final String description;
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Standardized Android one-time product purchase options and offers.
+ /// Native metadata uses Android-suffixed fields.
+ /// @see https://openiap.dev/docs/types/discount-offer
final List? discountOffers;
final String? displayName;
final String displayPrice;
@@ -2701,7 +2702,7 @@ class ProductAndroid extends Product implements ProductCommon {
final String nameAndroid;
/// One-time purchase offer details including discounts (Android)
/// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// @deprecated Use the standardized discountOffers field instead.
final List? oneTimePurchaseOfferDetailsAndroid;
final IapPlatform platform;
final double? price;
@@ -2715,7 +2716,7 @@ class ProductAndroid extends Product implements ProductCommon {
final List? subscriptionOfferDetailsAndroid;
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
final List? subscriptionOffers;
final String title;
final ProductType type;
@@ -2767,8 +2768,8 @@ class ProductAndroid extends Product implements ProductCommon {
/// One-time purchase offer details (Android).
/// Available in Google Play Billing Library 8.0+
-/// @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#discount-offer
+/// @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+/// @see https://openiap.dev/docs/types/discount-offer
class ProductAndroidOneTimePurchaseOfferDetail {
const ProductAndroidOneTimePurchaseOfferDetail({
this.discountDisplayInfo,
@@ -2893,7 +2894,7 @@ class ProductIOS extends Product implements ProductCommon {
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
/// Note: iOS does not support one-time product discounts.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
final List? subscriptionOffers;
final String title;
final ProductType type;
@@ -2969,17 +2970,17 @@ class ProductSubscriptionAndroid extends ProductSubscription implements ProductC
final String currency;
final String? debugDescription;
final String description;
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Nullable compatibility field. Google Play does not return one-time purchase
+ /// offer details for subscription products; use subscriptionOffers below.
final List? discountOffers;
final String? displayName;
final String displayPrice;
final String id;
final String nameAndroid;
- /// One-time purchase offer details including discounts (Android)
- /// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// Legacy nullable compatibility field. Google Play does not populate one-time
+ /// purchase offer details for subscription products.
+ /// @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ /// subscriptions use subscriptionOffers.
final List? oneTimePurchaseOfferDetailsAndroid;
final IapPlatform platform;
final double? price;
@@ -2993,7 +2994,7 @@ class ProductSubscriptionAndroid extends ProductSubscription implements ProductC
final List subscriptionOfferDetailsAndroid;
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
final List subscriptionOffers;
final String title;
final ProductType type;
@@ -3045,7 +3046,7 @@ class ProductSubscriptionAndroid extends ProductSubscription implements ProductC
/// Subscription offer details (Android).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class ProductSubscriptionAndroidOfferDetails {
const ProductSubscriptionAndroidOfferDetails({
required this.basePlanId,
@@ -3147,7 +3148,7 @@ class ProductSubscriptionIOS extends ProductSubscription implements ProductCommo
final SubscriptionInfoIOS? subscriptionInfoIOS;
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
final List? subscriptionOffers;
final String? subscriptionPeriodNumberIOS;
final SubscriptionPeriodIOS? subscriptionPeriodUnitIOS;
@@ -3915,8 +3916,7 @@ class SubscriptionInfoIOS {
/// - iOS: Introductory offers, promotional offers with server-side signatures
/// - Android: Offer tokens with pricing phases
///
-/// @see https://openiap.dev/docs/types/ios#discount-offer
-/// @see https://openiap.dev/docs/types/android#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class SubscriptionOffer {
const SubscriptionOffer({
this.basePlanIdAndroid,
@@ -4042,7 +4042,7 @@ class SubscriptionOffer {
/// iOS subscription offer details.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class SubscriptionOfferIOS {
const SubscriptionOfferIOS({
required this.displayPrice,
diff --git a/libraries/godot-iap/addons/godot-iap/types.gd b/libraries/godot-iap/addons/godot-iap/types.gd
index 6322882b8..17db2f5a1 100644
--- a/libraries/godot-iap/addons/godot-iap/types.gd
+++ b/libraries/godot-iap/addons/godot-iap/types.gd
@@ -1005,7 +1005,7 @@ class DiscountDisplayInfoAndroid:
dict["discountAmount"] = discount_amount
return dict
-## Discount information returned from the store. @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#subscription-offer
+## Discount information returned from the store. @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types/subscription-offer
class DiscountIOS:
var identifier: String = ""
var type: String = ""
@@ -1056,7 +1056,7 @@ class DiscountIOS:
dict["localizedPrice"] = localized_price
return dict
-## Standardized one-time product discount offer. Provides a unified interface for one-time purchase discounts across platforms. Currently supported on Android (Google Play Billing 8.0+). iOS does not support one-time purchase discounts in the same way. @see https://openiap.dev/docs/features/discount
+## Standardized one-time product discount offer. Provides a platform-neutral OpenIAP shape for Google Play one-time product purchase options and offers. Currently populated only on Android (Google Play Billing 8.0+). iOS does not populate this type. @see https://openiap.dev/docs/features/discount
class DiscountOffer:
## Unique identifier for the offer.
var id: Variant = null
@@ -1078,7 +1078,7 @@ class DiscountOffer:
var percentage_discount_android: Variant = null
## [Android] Fixed discount amount in micro-units.
var discount_amount_micros_android: Variant = null
- ## [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ ## [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
var formatted_discount_amount_android: Variant = null
## [Android] Valid time window for the offer.
var valid_time_window_android: ValidTimeWindowAndroid
@@ -1190,7 +1190,7 @@ class DiscountOffer:
dict["purchaseOptionIdAndroid"] = purchase_option_id_android
return dict
-## iOS DiscountOffer (output type). @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#subscription-offer
+## iOS DiscountOffer (output type). @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types/subscription-offer
class DiscountOfferIOS:
## Discount identifier
var identifier: String = ""
@@ -1612,7 +1612,7 @@ class ProductAndroid:
var name_android: String = ""
## Product-level status code indicating fetch result (Android 8.0+)
var product_status_android: Variant = null
- ## Standardized discount offers for one-time products.
+ ## Standardized Android one-time product purchase options and offers.
var discount_offers: Array[DiscountOffer] = []
## Standardized subscription offers.
var subscription_offers: Array[SubscriptionOffer] = []
@@ -1766,7 +1766,7 @@ class ProductAndroid:
dict["subscriptionOfferDetailsAndroid"] = null
return dict
-## One-time purchase offer details (Android). Available in Google Play Billing Library 8.0+ @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#discount-offer
+## One-time purchase offer details (Android). Available in Google Play Billing Library 8.0+ @deprecated Use the standardized DiscountOffer type for Android one-time offers. @see https://openiap.dev/docs/types/discount-offer
class ProductAndroidOneTimePurchaseOfferDetail:
## Offer ID
var offer_id: Variant = null
@@ -2034,11 +2034,11 @@ class ProductSubscriptionAndroid:
var name_android: String = ""
## Product-level status code indicating fetch result (Android 8.0+)
var product_status_android: Variant = null
- ## Standardized discount offers for one-time products.
+ ## Nullable compatibility field. Google Play does not return one-time purchase
var discount_offers: Array[DiscountOffer] = []
## Standardized subscription offers.
var subscription_offers: Array[SubscriptionOffer] = []
- ## One-time purchase offer details including discounts (Android)
+ ## Legacy nullable compatibility field. Google Play does not populate one-time
var one_time_purchase_offer_details_android: Array[ProductAndroidOneTimePurchaseOfferDetail] = []
## @deprecated Use subscriptionOffers instead for cross-platform compatibility.
var subscription_offer_details_android: Array[ProductSubscriptionAndroidOfferDetails] = []
@@ -2188,7 +2188,7 @@ class ProductSubscriptionAndroid:
dict["subscriptionOfferDetailsAndroid"] = null
return dict
-## Subscription offer details (Android). @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#subscription-offer
+## Subscription offer details (Android). @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types/subscription-offer
class ProductSubscriptionAndroidOfferDetails:
var base_plan_id: String = ""
var offer_id: Variant = null
@@ -3281,7 +3281,7 @@ class SubscriptionInfoIOS:
dict["subscriptionPeriod"] = subscription_period
return dict
-## Standardized subscription discount/promotional offer. Provides a unified interface for subscription offers across iOS and Android. Both platforms support subscription offers with different implementations: - iOS: Introductory offers, promotional offers with server-side signatures - Android: Offer tokens with pricing phases @see https://openiap.dev/docs/types/ios#discount-offer @see https://openiap.dev/docs/types/android#subscription-offer
+## Standardized subscription discount/promotional offer. Provides a unified interface for subscription offers across iOS and Android. Both platforms support subscription offers with different implementations: - iOS: Introductory offers, promotional offers with server-side signatures - Android: Offer tokens with pricing phases @see https://openiap.dev/docs/types/subscription-offer
class SubscriptionOffer:
## Unique identifier for the offer.
var id: String = ""
@@ -3435,7 +3435,7 @@ class SubscriptionOffer:
dict["installmentPlanDetailsAndroid"] = installment_plan_details_android
return dict
-## iOS subscription offer details. @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#subscription-offer
+## iOS subscription offer details. @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types/subscription-offer
class SubscriptionOfferIOS:
var display_price: String = ""
var id: String = ""
diff --git a/libraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/openiap/Types.kt b/libraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/openiap/Types.kt
index c57f504ea..a501815bb 100644
--- a/libraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/openiap/Types.kt
+++ b/libraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/openiap/Types.kt
@@ -2205,7 +2205,7 @@ public data class DiscountDisplayInfoAndroid(
/**
* Discount information returned from the store.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class DiscountIOS(
val identifier: String,
@@ -2248,10 +2248,11 @@ public data class DiscountIOS(
/**
* Standardized one-time product discount offer.
- * Provides a unified interface for one-time purchase discounts across platforms.
+ * Provides a platform-neutral OpenIAP shape for Google Play one-time product
+ * purchase options and offers.
*
- * Currently supported on Android (Google Play Billing 8.0+).
- * iOS does not support one-time purchase discounts in the same way.
+ * Currently populated only on Android (Google Play Billing 8.0+).
+ * iOS does not populate this type.
*
* @see https://openiap.dev/docs/features/discount
*/
@@ -2270,7 +2271,7 @@ public data class DiscountOffer(
*/
val displayPrice: String,
/**
- * [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ * [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
*/
val formattedDiscountAmountAndroid: String? = null,
/**
@@ -2381,7 +2382,7 @@ public data class DiscountOffer(
/**
* iOS DiscountOffer (output type).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class DiscountOfferIOS(
/**
@@ -2898,9 +2899,9 @@ public data class ProductAndroid(
override val debugDescription: String? = null,
override val description: String,
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Standardized Android one-time product purchase options and offers.
+ * Native metadata uses Android-suffixed fields.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
val discountOffers: List? = null,
override val displayName: String? = null,
@@ -2910,7 +2911,7 @@ public data class ProductAndroid(
/**
* One-time purchase offer details including discounts (Android)
* Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * @deprecated Use the standardized discountOffers field instead.
*/
val oneTimePurchaseOfferDetailsAndroid: List? = null,
override val platform: IapPlatform = IapPlatform.Android,
@@ -2930,7 +2931,7 @@ public data class ProductAndroid(
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
override val title: String,
@@ -2984,8 +2985,8 @@ public data class ProductAndroid(
/**
* One-time purchase offer details (Android).
* Available in Google Play Billing Library 8.0+
- * @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#discount-offer
+ * @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
public data class ProductAndroidOneTimePurchaseOfferDetail(
/**
@@ -3101,7 +3102,7 @@ public data class ProductIOS(
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
* Note: iOS does not support one-time product discounts.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
override val title: String,
@@ -3160,9 +3161,8 @@ public data class ProductSubscriptionAndroid(
override val debugDescription: String? = null,
override val description: String,
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Nullable compatibility field. Google Play does not return one-time purchase
+ * offer details for subscription products; use subscriptionOffers below.
*/
val discountOffers: List? = null,
override val displayName: String? = null,
@@ -3170,9 +3170,10 @@ public data class ProductSubscriptionAndroid(
override val id: String,
val nameAndroid: String,
/**
- * One-time purchase offer details including discounts (Android)
- * Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * Legacy nullable compatibility field. Google Play does not populate one-time
+ * purchase offer details for subscription products.
+ * @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ * subscriptions use subscriptionOffers.
*/
val oneTimePurchaseOfferDetailsAndroid: List? = null,
override val platform: IapPlatform = IapPlatform.Android,
@@ -3192,7 +3193,7 @@ public data class ProductSubscriptionAndroid(
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List,
override val title: String,
@@ -3246,7 +3247,7 @@ public data class ProductSubscriptionAndroid(
/**
* Subscription offer details (Android).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class ProductSubscriptionAndroidOfferDetails(
val basePlanId: String,
@@ -3323,7 +3324,7 @@ public data class ProductSubscriptionIOS(
/**
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
val subscriptionPeriodNumberIOS: String? = null,
@@ -4045,8 +4046,7 @@ public data class SubscriptionInfoIOS(
* - iOS: Introductory offers, promotional offers with server-side signatures
* - Android: Offer tokens with pricing phases
*
- * @see https://openiap.dev/docs/types/ios#discount-offer
- * @see https://openiap.dev/docs/types/android#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class SubscriptionOffer(
/**
@@ -4191,7 +4191,7 @@ public data class SubscriptionOffer(
/**
* iOS subscription offer details.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class SubscriptionOfferIOS(
val displayPrice: String,
diff --git a/libraries/maui-iap/src/OpenIap.Maui/Types.cs b/libraries/maui-iap/src/OpenIap.Maui/Types.cs
index 7677ef615..1da78ba5c 100644
--- a/libraries/maui-iap/src/OpenIap.Maui/Types.cs
+++ b/libraries/maui-iap/src/OpenIap.Maui/Types.cs
@@ -2944,7 +2944,7 @@ public sealed record DiscountDisplayInfoAndroid
/// Discount information returned from the store.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record DiscountIOS
{
[JsonPropertyName("identifier")]
@@ -2966,10 +2966,11 @@ public sealed record DiscountIOS
}
/// Standardized one-time product discount offer.
-/// Provides a unified interface for one-time purchase discounts across platforms.
+/// Provides a platform-neutral OpenIAP shape for Google Play one-time product
+/// purchase options and offers.
///
-/// Currently supported on Android (Google Play Billing 8.0+).
-/// iOS does not support one-time purchase discounts in the same way.
+/// Currently populated only on Android (Google Play Billing 8.0+).
+/// iOS does not populate this type.
///
/// @see https://openiap.dev/docs/features/discount
public sealed record DiscountOffer
@@ -2984,7 +2985,7 @@ public sealed record DiscountOffer
/// Formatted display price string (e.g., "$4.99")
[JsonPropertyName("displayPrice")]
public required string DisplayPrice { get; init; }
- /// [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ /// [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
[JsonPropertyName("formattedDiscountAmountAndroid")]
public string? FormattedDiscountAmountAndroid { get; init; }
/// [Android] Original full price in micro-units before discount.
@@ -3038,7 +3039,7 @@ public sealed record DiscountOffer
/// iOS DiscountOffer (output type).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record DiscountOfferIOS
{
/// Discount identifier
@@ -3265,9 +3266,9 @@ public sealed record ProductAndroid : Product, ProductCommon
public string? DebugDescription { get; init; }
[JsonPropertyName("description")]
public required string Description { get; init; }
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Standardized Android one-time product purchase options and offers.
+ /// Native metadata uses Android-suffixed fields.
+ /// @see https://openiap.dev/docs/types/discount-offer
[JsonPropertyName("discountOffers")]
public IReadOnlyList? DiscountOffers { get; init; }
[JsonPropertyName("displayName")]
@@ -3280,7 +3281,7 @@ public sealed record ProductAndroid : Product, ProductCommon
public required string NameAndroid { get; init; }
/// One-time purchase offer details including discounts (Android)
/// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// @deprecated Use the standardized discountOffers field instead.
[JsonPropertyName("oneTimePurchaseOfferDetailsAndroid")]
public IReadOnlyList? OneTimePurchaseOfferDetailsAndroid { get; init; }
[JsonPropertyName("platform")]
@@ -3299,7 +3300,7 @@ public sealed record ProductAndroid : Product, ProductCommon
public IReadOnlyList? SubscriptionOfferDetailsAndroid { get; init; }
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
[JsonPropertyName("subscriptionOffers")]
public IReadOnlyList? SubscriptionOffers { get; init; }
[JsonPropertyName("title")]
@@ -3310,8 +3311,8 @@ public sealed record ProductAndroid : Product, ProductCommon
/// One-time purchase offer details (Android).
/// Available in Google Play Billing Library 8.0+
-/// @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#discount-offer
+/// @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+/// @see https://openiap.dev/docs/types/discount-offer
public sealed record ProductAndroidOneTimePurchaseOfferDetail
{
/// Discount display information
@@ -3391,7 +3392,7 @@ public sealed record ProductIOS : Product, ProductCommon
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
/// Note: iOS does not support one-time product discounts.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
[JsonPropertyName("subscriptionOffers")]
public IReadOnlyList? SubscriptionOffers { get; init; }
[JsonPropertyName("title")]
@@ -3410,9 +3411,8 @@ public sealed record ProductSubscriptionAndroid : ProductSubscription, ProductCo
public string? DebugDescription { get; init; }
[JsonPropertyName("description")]
public required string Description { get; init; }
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Nullable compatibility field. Google Play does not return one-time purchase
+ /// offer details for subscription products; use subscriptionOffers below.
[JsonPropertyName("discountOffers")]
public IReadOnlyList? DiscountOffers { get; init; }
[JsonPropertyName("displayName")]
@@ -3423,9 +3423,10 @@ public sealed record ProductSubscriptionAndroid : ProductSubscription, ProductCo
public required string Id { get; init; }
[JsonPropertyName("nameAndroid")]
public required string NameAndroid { get; init; }
- /// One-time purchase offer details including discounts (Android)
- /// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// Legacy nullable compatibility field. Google Play does not populate one-time
+ /// purchase offer details for subscription products.
+ /// @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ /// subscriptions use subscriptionOffers.
[JsonPropertyName("oneTimePurchaseOfferDetailsAndroid")]
public IReadOnlyList? OneTimePurchaseOfferDetailsAndroid { get; init; }
[JsonPropertyName("platform")]
@@ -3444,7 +3445,7 @@ public sealed record ProductSubscriptionAndroid : ProductSubscription, ProductCo
public required IReadOnlyList SubscriptionOfferDetailsAndroid { get; init; }
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
[JsonPropertyName("subscriptionOffers")]
public required IReadOnlyList SubscriptionOffers { get; init; }
[JsonPropertyName("title")]
@@ -3455,7 +3456,7 @@ public sealed record ProductSubscriptionAndroid : ProductSubscription, ProductCo
/// Subscription offer details (Android).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record ProductSubscriptionAndroidOfferDetails
{
[JsonPropertyName("basePlanId")]
@@ -3524,7 +3525,7 @@ public sealed record ProductSubscriptionIOS : ProductSubscription, ProductCommon
public SubscriptionInfoIOS? SubscriptionInfoIOS { get; init; }
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
[JsonPropertyName("subscriptionOffers")]
public IReadOnlyList? SubscriptionOffers { get; init; }
[JsonPropertyName("subscriptionPeriodNumberIOS")]
@@ -3861,8 +3862,7 @@ public sealed record SubscriptionInfoIOS
/// - iOS: Introductory offers, promotional offers with server-side signatures
/// - Android: Offer tokens with pricing phases
///
-/// @see https://openiap.dev/docs/types/ios#discount-offer
-/// @see https://openiap.dev/docs/types/android#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record SubscriptionOffer
{
/// [Android] Base plan identifier.
@@ -3937,7 +3937,7 @@ public sealed record SubscriptionOffer
/// iOS subscription offer details.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record SubscriptionOfferIOS
{
[JsonPropertyName("displayPrice")]
diff --git a/libraries/react-native-iap/src/types.ts b/libraries/react-native-iap/src/types.ts
index 25c7de813..5866e2db8 100644
--- a/libraries/react-native-iap/src/types.ts
+++ b/libraries/react-native-iap/src/types.ts
@@ -328,7 +328,7 @@ export interface DiscountDisplayInfoAndroid {
/**
* Discount information returned from the store.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface DiscountIOS {
identifier: string;
@@ -343,10 +343,11 @@ export interface DiscountIOS {
/**
* Standardized one-time product discount offer.
- * Provides a unified interface for one-time purchase discounts across platforms.
+ * Provides a platform-neutral OpenIAP shape for Google Play one-time product
+ * purchase options and offers.
*
- * Currently supported on Android (Google Play Billing 8.0+).
- * iOS does not support one-time purchase discounts in the same way.
+ * Currently populated only on Android (Google Play Billing 8.0+).
+ * iOS does not populate this type.
*
* @see https://openiap.dev/docs/features/discount
*/
@@ -360,7 +361,7 @@ export interface DiscountOffer {
discountAmountMicrosAndroid?: (string | null);
/** Formatted display price string (e.g., "$4.99") */
displayPrice: string;
- /** [Android] Formatted discount amount string (e.g., "$5.00 OFF"). */
+ /** [Android] Formatted discount amount including its currency sign (e.g., "$5.00"). */
formattedDiscountAmountAndroid?: (string | null);
/**
* [Android] Original full price in micro-units before discount.
@@ -418,7 +419,7 @@ export interface DiscountOffer {
/**
* iOS DiscountOffer (output type).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface DiscountOfferIOS {
/** Discount identifier */
@@ -1093,9 +1094,9 @@ export interface ProductAndroid extends ProductCommon {
debugDescription?: (string | null);
description: string;
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Standardized Android one-time product purchase options and offers.
+ * Native metadata uses Android-suffixed fields.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
discountOffers?: (DiscountOffer[] | null);
displayName?: (string | null);
@@ -1105,7 +1106,7 @@ export interface ProductAndroid extends ProductCommon {
/**
* One-time purchase offer details including discounts (Android)
* Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * @deprecated Use the standardized discountOffers field instead.
* @deprecated Use discountOffers instead
*/
oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
@@ -1127,7 +1128,7 @@ export interface ProductAndroid extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
title: string;
@@ -1137,8 +1138,8 @@ export interface ProductAndroid extends ProductCommon {
/**
* One-time purchase offer details (Android).
* Available in Google Play Billing Library 8.0+
- * @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#discount-offer
+ * @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
export interface ProductAndroidOneTimePurchaseOfferDetail {
/**
@@ -1218,7 +1219,7 @@ export interface ProductIOS extends ProductCommon {
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
* Note: iOS does not support one-time product discounts.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
title: string;
@@ -1250,9 +1251,8 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
debugDescription?: (string | null);
description: string;
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Nullable compatibility field. Google Play does not return one-time purchase
+ * offer details for subscription products; use subscriptionOffers below.
*/
discountOffers?: (DiscountOffer[] | null);
displayName?: (string | null);
@@ -1260,10 +1260,11 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
id: string;
nameAndroid: string;
/**
- * One-time purchase offer details including discounts (Android)
- * Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
- * @deprecated Use discountOffers instead
+ * Legacy nullable compatibility field. Google Play does not populate one-time
+ * purchase offer details for subscription products.
+ * @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ * subscriptions use subscriptionOffers.
+ * @deprecated Use subscriptionOffers instead
*/
oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
platform: 'android';
@@ -1284,7 +1285,7 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers: SubscriptionOffer[];
title: string;
@@ -1294,7 +1295,7 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
/**
* Subscription offer details (Android).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface ProductSubscriptionAndroidOfferDetails {
basePlanId: string;
@@ -1347,7 +1348,7 @@ export interface ProductSubscriptionIOS extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
subscriptionPeriodNumberIOS?: (string | null);
@@ -2126,8 +2127,7 @@ export interface SubscriptionInfoIOS {
* - iOS: Introductory offers, promotional offers with server-side signatures
* - Android: Offer tokens with pricing phases
*
- * @see https://openiap.dev/docs/types/ios#discount-offer
- * @see https://openiap.dev/docs/types/android#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface SubscriptionOffer {
/**
@@ -2202,7 +2202,7 @@ export interface SubscriptionOffer {
/**
* iOS subscription offer details.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface SubscriptionOfferIOS {
displayPrice: string;
diff --git a/packages/apple/Sources/Models/Types.swift b/packages/apple/Sources/Models/Types.swift
index 40ffcd7ae..a46c07cd5 100644
--- a/packages/apple/Sources/Models/Types.swift
+++ b/packages/apple/Sources/Models/Types.swift
@@ -838,7 +838,7 @@ public struct DiscountDisplayInfoAndroid: Codable {
/// Discount information returned from the store.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct DiscountIOS: Codable {
public var identifier: String
public var localizedPrice: String? = nil
@@ -851,10 +851,11 @@ public struct DiscountIOS: Codable {
}
/// Standardized one-time product discount offer.
-/// Provides a unified interface for one-time purchase discounts across platforms.
+/// Provides a platform-neutral OpenIAP shape for Google Play one-time product
+/// purchase options and offers.
///
-/// Currently supported on Android (Google Play Billing 8.0+).
-/// iOS does not support one-time purchase discounts in the same way.
+/// Currently populated only on Android (Google Play Billing 8.0+).
+/// iOS does not populate this type.
///
/// @see https://openiap.dev/docs/features/discount
public struct DiscountOffer: Codable {
@@ -865,7 +866,7 @@ public struct DiscountOffer: Codable {
public var discountAmountMicrosAndroid: String? = nil
/// Formatted display price string (e.g., "$4.99")
public var displayPrice: String
- /// [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ /// [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
public var formattedDiscountAmountAndroid: String? = nil
/// [Android] Original full price in micro-units before discount.
/// Divide by 1,000,000 to get the actual price.
@@ -906,7 +907,7 @@ public struct DiscountOffer: Codable {
/// iOS DiscountOffer (output type).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct DiscountOfferIOS: Codable {
/// Discount identifier
public var identifier: String
@@ -1071,9 +1072,9 @@ public struct ProductAndroid: Codable, ProductCommon {
public var currency: String
public var debugDescription: String? = nil
public var description: String
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Standardized Android one-time product purchase options and offers.
+ /// Native metadata uses Android-suffixed fields.
+ /// @see https://openiap.dev/docs/types/discount-offer
public var discountOffers: [DiscountOffer]? = nil
public var displayName: String? = nil
public var displayPrice: String
@@ -1081,7 +1082,7 @@ public struct ProductAndroid: Codable, ProductCommon {
public var nameAndroid: String
/// One-time purchase offer details including discounts (Android)
/// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// @deprecated Use the standardized discountOffers field instead.
public var oneTimePurchaseOfferDetailsAndroid: [ProductAndroidOneTimePurchaseOfferDetail]? = nil
public var platform: IapPlatform = .android
public var price: Double? = nil
@@ -1095,7 +1096,7 @@ public struct ProductAndroid: Codable, ProductCommon {
public var subscriptionOfferDetailsAndroid: [ProductSubscriptionAndroidOfferDetails]? = nil
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
public var subscriptionOffers: [SubscriptionOffer]? = nil
public var title: String
public var type: ProductType = .inApp
@@ -1103,8 +1104,8 @@ public struct ProductAndroid: Codable, ProductCommon {
/// One-time purchase offer details (Android).
/// Available in Google Play Billing Library 8.0+
-/// @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#discount-offer
+/// @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+/// @see https://openiap.dev/docs/types/discount-offer
public struct ProductAndroidOneTimePurchaseOfferDetail: Codable {
/// Discount display information
/// Only available for discounted offers
@@ -1156,7 +1157,7 @@ public struct ProductIOS: Codable, ProductCommon {
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
/// Note: iOS does not support one-time product discounts.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
public var subscriptionOffers: [SubscriptionOffer]? = nil
public var title: String
public var type: ProductType = .inApp
@@ -1167,17 +1168,17 @@ public struct ProductSubscriptionAndroid: Codable, ProductCommon {
public var currency: String
public var debugDescription: String? = nil
public var description: String
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Nullable compatibility field. Google Play does not return one-time purchase
+ /// offer details for subscription products; use subscriptionOffers below.
public var discountOffers: [DiscountOffer]? = nil
public var displayName: String? = nil
public var displayPrice: String
public var id: String
public var nameAndroid: String
- /// One-time purchase offer details including discounts (Android)
- /// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// Legacy nullable compatibility field. Google Play does not populate one-time
+ /// purchase offer details for subscription products.
+ /// @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ /// subscriptions use subscriptionOffers.
public var oneTimePurchaseOfferDetailsAndroid: [ProductAndroidOneTimePurchaseOfferDetail]? = nil
public var platform: IapPlatform = .android
public var price: Double? = nil
@@ -1191,7 +1192,7 @@ public struct ProductSubscriptionAndroid: Codable, ProductCommon {
public var subscriptionOfferDetailsAndroid: [ProductSubscriptionAndroidOfferDetails]
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
public var subscriptionOffers: [SubscriptionOffer]
public var title: String
public var type: ProductType = .subs
@@ -1199,7 +1200,7 @@ public struct ProductSubscriptionAndroid: Codable, ProductCommon {
/// Subscription offer details (Android).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct ProductSubscriptionAndroidOfferDetails: Codable {
public var basePlanId: String
/// Installment plan details for this subscription offer.
@@ -1240,7 +1241,7 @@ public struct ProductSubscriptionIOS: Codable, ProductCommon {
public var subscriptionInfoIOS: SubscriptionInfoIOS? = nil
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
public var subscriptionOffers: [SubscriptionOffer]? = nil
public var subscriptionPeriodNumberIOS: String? = nil
public var subscriptionPeriodUnitIOS: SubscriptionPeriodIOS? = nil
@@ -1453,8 +1454,7 @@ public struct SubscriptionInfoIOS: Codable {
/// - iOS: Introductory offers, promotional offers with server-side signatures
/// - Android: Offer tokens with pricing phases
///
-/// @see https://openiap.dev/docs/types/ios#discount-offer
-/// @see https://openiap.dev/docs/types/android#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct SubscriptionOffer: Codable {
/// [Android] Base plan identifier.
/// Identifies which base plan this offer belongs to.
@@ -1509,7 +1509,7 @@ public struct SubscriptionOffer: Codable {
/// iOS subscription offer details.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct SubscriptionOfferIOS: Codable {
public var displayPrice: String
public var id: String
diff --git a/packages/docs/src/lib/searchData.ts b/packages/docs/src/lib/searchData.ts
index 190b6b541..417f27c85 100644
--- a/packages/docs/src/lib/searchData.ts
+++ b/packages/docs/src/lib/searchData.ts
@@ -726,14 +726,15 @@ export const apiData: ApiItem[] = [
title: 'iOS Types',
category: 'Types',
description:
- 'DiscountOffer, SubscriptionStatusIOS, PaymentMode, AppTransaction, SubscriptionBillingPlanTypeIOS',
+ 'DiscountOfferIOS, SubscriptionStatusIOS, PaymentMode, AppTransaction, SubscriptionBillingPlanTypeIOS',
path: '/docs/types#ios-types',
},
{
id: 'types-android',
title: 'Android Types',
category: 'Types',
- description: 'SubscriptionOffer, PricingPhase, PricingPhasesAndroid',
+ description:
+ 'ProductSubscriptionAndroidOfferDetails, PricingPhase, PricingPhasesAndroid',
path: '/docs/types#android-types',
},
{
@@ -744,14 +745,30 @@ export const apiData: ApiItem[] = [
'AlternativeBillingModeAndroid, InitConnectionConfig, External Purchase Link',
path: '/docs/types/alternative-billing-types',
},
-
- // iOS-Specific Types (from types/ios.tsx)
{
id: 'discount-offer',
title: 'DiscountOffer',
+ category: 'Types',
+ description:
+ 'Standardized Android one-time product purchase options and offers',
+ path: '/docs/types/discount-offer',
+ },
+ {
+ id: 'subscription-offer',
+ title: 'SubscriptionOffer',
+ category: 'Types',
+ description:
+ 'Standardized iOS and Android subscription introductory and promotional offers',
+ path: '/docs/types/subscription-offer',
+ },
+
+ // iOS-Specific Types (from types/ios.tsx)
+ {
+ id: 'discount-offer-ios',
+ title: 'DiscountOfferIOS',
category: 'Types (iOS)',
description:
- 'iOS promotional offer for purchase: identifier, keyIdentifier, nonce, signature, timestamp',
+ 'Deprecated iOS promotional offer: identifier, keyIdentifier, nonce, signature, timestamp',
path: '/docs/types/ios/discount-offer-ios',
},
{
@@ -811,11 +828,11 @@ export const apiData: ApiItem[] = [
// Android-Specific Types (from types/android.tsx)
{
- id: 'subscription-offer',
- title: 'SubscriptionOffer',
+ id: 'subscription-offer-android',
+ title: 'ProductSubscriptionAndroidOfferDetails',
category: 'Types (Android)',
description:
- 'Android subscription offer: sku, offerToken for Play Billing purchases',
+ 'Deprecated Android subscription offer details with Play Billing offer tokens',
path: '/docs/types/android/subscription-offer-android',
},
{
diff --git a/packages/docs/src/pages/docs/features/discount.tsx b/packages/docs/src/pages/docs/features/discount.tsx
index 6ca0203e5..781c52df0 100644
--- a/packages/docs/src/pages/docs/features/discount.tsx
+++ b/packages/docs/src/pages/docs/features/discount.tsx
@@ -31,12 +31,15 @@ function Discount() {
-
+
- Standardized Types: For cross-platform development,
- use the new DiscountOffer{' '}
- type which provides a unified interface with platform-specific fields
- via suffixes (e.g., offerTokenAndroid).
+ Standardized API: Read{' '}
+ ProductAndroid.discountOffers. Each{' '}
+ DiscountOffer exposes
+ common price fields and Android-only details through the{' '}
+ Android suffix (for example,{' '}
+ offerTokenAndroid). The older{' '}
+ oneTimePurchaseOfferDetailsAndroid field is deprecated.
@@ -78,224 +81,168 @@ function Discount() {
Data Structure
- The oneTimePurchaseOfferDetailsAndroid field is now an
- array containing all available offers for a product:
+ The discountOffers field contains standardized{' '}
+ DiscountOffer values. The following excerpts use the
+ generated field names:
{{
typescript: (
- {`interface ProductAndroidOneTimePurchaseOfferDetail {
- // Offer identification
- offerId: string | null;
- offerToken: string;
- offerTags: string[];
-
- // Pricing
- formattedPrice: string; // "$4.99"
- priceCurrencyCode: string; // "USD"
- priceAmountMicros: string; // "4990000"
-
- // Discount information (only for discounted offers)
- fullPriceMicros: string | null; // Original price: "9990000"
- discountDisplayInfo: DiscountDisplayInfoAndroid | null;
-
- // Time and quantity limits
- validTimeWindow: ValidTimeWindowAndroid | null;
- limitedQuantityInfo: LimitedQuantityInfoAndroid | null;
-
- // Special offer types
- preorderDetailsAndroid: PreorderDetailsAndroid | null;
- rentalDetailsAndroid: RentalDetailsAndroid | null;
-}
-
-interface DiscountDisplayInfoAndroid {
- percentageDiscount: number | null; // 50 for 50% off
- discountAmount: DiscountAmountAndroid | null;
-}
-
-interface DiscountAmountAndroid {
- discountAmountMicros: string; // "5000000"
- formattedDiscountAmount: string; // "$5.00"
-}
-
-interface ValidTimeWindowAndroid {
- startTimeMillis: string;
- endTimeMillis: string;
+ {`interface ProductAndroid {
+ discountOffers?: DiscountOffer[] | null;
}
-interface LimitedQuantityInfoAndroid {
- maximumQuantity: number;
- remainingQuantity: number;
+interface DiscountOffer {
+ id?: string | null;
+ displayPrice: string; // "$4.99"
+ price: number; // 4.99
+ currency: string; // "USD"
+ type: DiscountOfferType; // "one-time"
+ offerTokenAndroid?: string | null;
+ offerTagsAndroid?: string[] | null;
+ fullPriceMicrosAndroid?: string | null; // "9990000"
+ percentageDiscountAndroid?: number | null;
+ discountAmountMicrosAndroid?: string | null;
+ formattedDiscountAmountAndroid?: string | null;
+ validTimeWindowAndroid?: ValidTimeWindowAndroid | null;
+ limitedQuantityInfoAndroid?: LimitedQuantityInfoAndroid | null;
+ preorderDetailsAndroid?: PreorderDetailsAndroid | null;
+ rentalDetailsAndroid?: RentalDetailsAndroid | null;
+ purchaseOptionIdAndroid?: string | null;
}`}
),
swift: (
{`// iOS does not support one-time purchase discounts in the same way.
-// For iOS promotional offers, see the Subscription feature documentation.`}
+// For iOS subscription offers, use SubscriptionOffer instead.`}
),
kotlin: (
- {`data class ProductAndroidOneTimePurchaseOfferDetail(
- // Offer identification
- val offerId: String?,
- val offerToken: String,
- val offerTags: List,
-
- // Pricing
- val formattedPrice: String, // "$4.99"
- val priceCurrencyCode: String, // "USD"
- val priceAmountMicros: String, // "4990000"
-
- // Discount information (only for discounted offers)
- val fullPriceMicros: String?, // Original price: "9990000"
- val discountDisplayInfo: DiscountDisplayInfoAndroid?,
-
- // Time and quantity limits
- val validTimeWindow: ValidTimeWindowAndroid?,
- val limitedQuantityInfo: LimitedQuantityInfoAndroid?,
-
- // Special offer types
- val preorderDetailsAndroid: PreorderDetailsAndroid?,
- val rentalDetailsAndroid: RentalDetailsAndroid?
-)
-
-data class DiscountDisplayInfoAndroid(
- val percentageDiscount: Int?, // 50 for 50% off
- val discountAmount: DiscountAmountAndroid?
-)
-
-data class DiscountAmountAndroid(
- val discountAmountMicros: String, // "5000000"
- val formattedDiscountAmount: String // "$5.00"
-)
-
-data class ValidTimeWindowAndroid(
- val startTimeMillis: String,
- val endTimeMillis: String
+ {`data class ProductAndroid(
+ val discountOffers: List? = null,
+ // ...
)
-data class LimitedQuantityInfoAndroid(
- val maximumQuantity: Int,
- val remainingQuantity: Int
+data class DiscountOffer(
+ val id: String? = null,
+ val displayPrice: String,
+ val price: Double,
+ val currency: String,
+ val type: DiscountOfferType,
+ val offerTokenAndroid: String? = null,
+ val offerTagsAndroid: List? = null,
+ val fullPriceMicrosAndroid: String? = null,
+ val percentageDiscountAndroid: Int? = null,
+ val discountAmountMicrosAndroid: String? = null,
+ val formattedDiscountAmountAndroid: String? = null,
+ val validTimeWindowAndroid: ValidTimeWindowAndroid? = null,
+ val limitedQuantityInfoAndroid: LimitedQuantityInfoAndroid? = null,
+ val preorderDetailsAndroid: PreorderDetailsAndroid? = null,
+ val rentalDetailsAndroid: RentalDetailsAndroid? = null,
+ val purchaseOptionIdAndroid: String? = null
)`}
),
kmp: (
- {`data class ProductAndroidOneTimePurchaseOfferDetail(
- // Offer identification
- val offerId: String?,
- val offerToken: String,
- val offerTags: List,
-
- // Pricing
- val formattedPrice: String, // "$4.99"
- val priceCurrencyCode: String, // "USD"
- val priceAmountMicros: String, // "4990000"
-
- // Discount information (only for discounted offers)
- val fullPriceMicros: String?, // Original price: "9990000"
- val discountDisplayInfo: DiscountDisplayInfoAndroid?,
-
- // Time and quantity limits
- val validTimeWindow: ValidTimeWindowAndroid?,
- val limitedQuantityInfo: LimitedQuantityInfoAndroid?,
-
- // Special offer types
- val preorderDetailsAndroid: PreorderDetailsAndroid?,
- val rentalDetailsAndroid: RentalDetailsAndroid?
-)
-
-data class DiscountDisplayInfoAndroid(
- val percentageDiscount: Int?, // 50 for 50% off
- val discountAmount: DiscountAmountAndroid?
-)
-
-data class DiscountAmountAndroid(
- val discountAmountMicros: String, // "5000000"
- val formattedDiscountAmount: String // "$5.00"
-)
-
-data class ValidTimeWindowAndroid(
- val startTimeMillis: String,
- val endTimeMillis: String
+ {`data class ProductAndroid(
+ val discountOffers: List? = null,
+ // ...
)
-data class LimitedQuantityInfoAndroid(
- val maximumQuantity: Int,
- val remainingQuantity: Int
+data class DiscountOffer(
+ val id: String? = null,
+ val displayPrice: String,
+ val price: Double,
+ val currency: String,
+ val type: DiscountOfferType,
+ val offerTokenAndroid: String? = null,
+ val offerTagsAndroid: List? = null,
+ val fullPriceMicrosAndroid: String? = null,
+ val percentageDiscountAndroid: Int? = null,
+ val discountAmountMicrosAndroid: String? = null,
+ val formattedDiscountAmountAndroid: String? = null,
+ val validTimeWindowAndroid: ValidTimeWindowAndroid? = null,
+ val limitedQuantityInfoAndroid: LimitedQuantityInfoAndroid? = null,
+ val preorderDetailsAndroid: PreorderDetailsAndroid? = null,
+ val rentalDetailsAndroid: RentalDetailsAndroid? = null,
+ val purchaseOptionIdAndroid: String? = null
)`}
),
dart: (
- {`class ProductAndroidOneTimePurchaseOfferDetail {
- // Offer identification
- final String? offerId;
- final String offerToken;
- final List offerTags;
-
- // Pricing
- final String formattedPrice; // "\$4.99"
- final String priceCurrencyCode; // "USD"
- final String priceAmountMicros; // "4990000"
-
- // Discount information (only for discounted offers)
- final String? fullPriceMicros; // Original price: "9990000"
- final DiscountDisplayInfoAndroid? discountDisplayInfo;
-
- // Time and quantity limits
- final ValidTimeWindowAndroid? validTimeWindow;
- final LimitedQuantityInfoAndroid? limitedQuantityInfo;
+ {`class ProductAndroid {
+ final List? discountOffers;
+ // ...
+}
+
+class DiscountOffer {
+ final String? id;
+ final String displayPrice; // "\$4.99"
+ final double price; // 4.99
+ final String currency; // "USD"
+ final DiscountOfferType type;
+ final String? offerTokenAndroid;
+ final List? offerTagsAndroid;
+ final String? fullPriceMicrosAndroid; // "9990000"
+ final int? percentageDiscountAndroid;
+ final String? discountAmountMicrosAndroid;
+ final String? formattedDiscountAmountAndroid;
+ final ValidTimeWindowAndroid? validTimeWindowAndroid;
+ final LimitedQuantityInfoAndroid? limitedQuantityInfoAndroid;
+ final PreorderDetailsAndroid? preorderDetailsAndroid;
+ final RentalDetailsAndroid? rentalDetailsAndroid;
+ final String? purchaseOptionIdAndroid;
}`}
),
csharp: (
{`using OpenIap;
using System.Collections.Generic;
-public sealed record ProductAndroidOneTimePurchaseOfferDetail
-{
- public string? OfferId { get; init; }
- public required string OfferToken { get; init; }
- public required IReadOnlyList OfferTags { get; init; }
- public required string FormattedPrice { get; init; } // "$4.99"
- public required string PriceCurrencyCode { get; init; } // "USD"
- public required string PriceAmountMicros { get; init; } // "4990000"
- public string? FullPriceMicros { get; init; } // "9990000"
- public DiscountDisplayInfoAndroid? DiscountDisplayInfo { get; init; }
- public ValidTimeWindowAndroid? ValidTimeWindow { get; init; }
- public LimitedQuantityInfoAndroid? LimitedQuantityInfo { get; init; }
- public PreorderDetailsAndroid? PreorderDetailsAndroid { get; init; }
- public RentalDetailsAndroid? RentalDetailsAndroid { get; init; }
-}
-
-public sealed record DiscountDisplayInfoAndroid
+public sealed record ProductAndroid
{
- public int? PercentageDiscount { get; init; } // 50 for 50% off
- public DiscountAmountAndroid? DiscountAmount { get; init; }
+ public IReadOnlyList? DiscountOffers { get; init; }
+ // ...
}
-public sealed record DiscountAmountAndroid
+public sealed record DiscountOffer
{
- public required string DiscountAmountMicros { get; init; } // "5000000"
- public required string FormattedDiscountAmount { get; init; } // "$5.00"
+ public string? Id { get; init; }
+ public required string DisplayPrice { get; init; } // "$4.99"
+ public required double Price { get; init; } // 4.99
+ public required string Currency { get; init; } // "USD"
+ public required DiscountOfferType Type { get; init; }
+ public string? OfferTokenAndroid { get; init; }
+ public IReadOnlyList? OfferTagsAndroid { get; init; }
+ public string? FullPriceMicrosAndroid { get; init; } // "9990000"
+ public int? PercentageDiscountAndroid { get; init; }
+ public string? DiscountAmountMicrosAndroid { get; init; }
+ public string? FormattedDiscountAmountAndroid { get; init; }
+ public ValidTimeWindowAndroid? ValidTimeWindowAndroid { get; init; }
+ public LimitedQuantityInfoAndroid? LimitedQuantityInfoAndroid { get; init; }
+ public PreorderDetailsAndroid? PreorderDetailsAndroid { get; init; }
+ public RentalDetailsAndroid? RentalDetailsAndroid { get; init; }
+ public string? PurchaseOptionIdAndroid { get; init; }
}`}
),
gdscript: (
- {`class_name ProductAndroidOneTimePurchaseOfferDetail
-
-# Offer identification
-var offer_id: String # May be empty
-var offer_token: String # Required for purchase
-var offer_tags: Array[String] # Tags for categorization
-
-# Pricing
-var formatted_price: String # "$4.99"
-var price_currency_code: String # "USD"
-var price_amount_micros: String # "4990000"
-
-# Discount information (only for discounted offers)
-var full_price_micros: String # Original price: "9990000"
-var discount_display_info: DiscountDisplayInfoAndroid
-
-# Time and quantity limits
-var valid_time_window: ValidTimeWindowAndroid
-var limited_quantity_info: LimitedQuantityInfoAndroid`}
+ {`class ProductAndroid:
+ var discount_offers: Array[DiscountOffer] = []
+ # ...
+
+class DiscountOffer:
+ var id: Variant = null
+ var display_price: String = "" # "$4.99"
+ var price: float = 0.0 # 4.99
+ var currency: String = "" # "USD"
+ var type: DiscountOfferType
+ var offer_token_android: Variant = null
+ var offer_tags_android: Array[String] = []
+ var full_price_micros_android: Variant = null # "9990000"
+ var percentage_discount_android: Variant = null
+ var discount_amount_micros_android: Variant = null
+ var formatted_discount_amount_android: Variant = null
+ var valid_time_window_android: ValidTimeWindowAndroid
+ var limited_quantity_info_android: LimitedQuantityInfoAndroid
+ var preorder_details_android: PreorderDetailsAndroid
+ var rental_details_android: RentalDetailsAndroid
+ var purchase_option_id_android: Variant = null`}
),
}}
@@ -307,8 +254,8 @@ var limited_quantity_info: LimitedQuantityInfoAndroid`}
Fetch products normally using fetchProducts. Discounted
- offers will be included in the{' '}
- oneTimePurchaseOfferDetailsAndroid array:
+ one-time offers will be included in the discountOffers{' '}
+ array:
@@ -321,22 +268,33 @@ const products = await fetchProducts({
});
products.forEach((product) => {
- const offers = product.oneTimePurchaseOfferDetailsAndroid;
+ if (product.platform !== 'android') return;
+
+ const offers = product.discountOffers;
if (offers && offers.length > 0) {
const firstOffer = offers[0];
- const hasDiscount = firstOffer.discountDisplayInfo != null;
+ const hasDiscount =
+ firstOffer.percentageDiscountAndroid != null ||
+ firstOffer.discountAmountMicrosAndroid != null;
console.log('Product:', product.id);
- console.log('Display Price:', product.displayPrice);
+ console.log('Display Price:', firstOffer.displayPrice);
if (hasDiscount) {
- const discount = firstOffer.discountDisplayInfo;
- const fullPriceMicros = parseInt(firstOffer.fullPriceMicros || '0', 10);
+ const fullPriceMicros = parseInt(
+ firstOffer.fullPriceMicrosAndroid || '0',
+ 10,
+ );
const fullPrice = fullPriceMicros / 1_000_000;
console.log('Original Price:', fullPrice);
- console.log('Discount:', discount?.percentageDiscount + '% OFF');
+ console.log(
+ 'Discount:',
+ firstOffer.percentageDiscountAndroid != null
+ ? \`\${firstOffer.percentageDiscountAndroid}% OFF\`
+ : firstOffer.formattedDiscountAmountAndroid,
+ );
}
}
});`}
@@ -363,22 +321,28 @@ val products = (result as? FetchProductsResultProducts)
.filterIsInstance()
products.forEach { product ->
- val offers = product.oneTimePurchaseOfferDetailsAndroid
+ val offers = product.discountOffers
if (!offers.isNullOrEmpty()) {
val firstOffer = offers.first()
- val hasDiscount = firstOffer.discountDisplayInfo != null
+ val hasDiscount =
+ firstOffer.percentageDiscountAndroid != null ||
+ firstOffer.discountAmountMicrosAndroid != null
println("Product: \${product.id}")
- println("Display Price: \${product.displayPrice}")
+ println("Display Price: \${firstOffer.displayPrice}")
if (hasDiscount) {
- val discount = firstOffer.discountDisplayInfo
- val fullPriceMicros = firstOffer.fullPriceMicros?.toLongOrNull() ?: 0L
+ val fullPriceMicros =
+ firstOffer.fullPriceMicrosAndroid?.toLongOrNull() ?: 0L
val fullPrice = fullPriceMicros.toDouble() / 1_000_000.0
println("Original Price: $fullPrice")
- println("Discount: \${discount?.percentageDiscount}% OFF")
+ println(
+ "Discount: " +
+ (firstOffer.percentageDiscountAndroid?.let { "$it% OFF" }
+ ?: firstOffer.formattedDiscountAmountAndroid)
+ )
}
}
}`}
@@ -401,22 +365,28 @@ val products = (result as? FetchProductsResultProducts)
.filterIsInstance()
products.forEach { product ->
- val offers = product.oneTimePurchaseOfferDetailsAndroid
+ val offers = product.discountOffers
if (!offers.isNullOrEmpty()) {
val firstOffer = offers.first()
- val hasDiscount = firstOffer.discountDisplayInfo != null
+ val hasDiscount =
+ firstOffer.percentageDiscountAndroid != null ||
+ firstOffer.discountAmountMicrosAndroid != null
println("Product: \${product.id}")
- println("Display Price: \${product.displayPrice}")
+ println("Display Price: \${firstOffer.displayPrice}")
if (hasDiscount) {
- val discount = firstOffer.discountDisplayInfo
- val fullPriceMicros = firstOffer.fullPriceMicros?.toLongOrNull() ?: 0L
+ val fullPriceMicros =
+ firstOffer.fullPriceMicrosAndroid?.toLongOrNull() ?: 0L
val fullPrice = fullPriceMicros.toDouble() / 1_000_000.0
println("Original Price: $fullPrice")
- println("Discount: \${discount?.percentageDiscount}% OFF")
+ println(
+ "Discount: " +
+ (firstOffer.percentageDiscountAndroid?.let { "$it% OFF" }
+ ?: firstOffer.formattedDiscountAmountAndroid)
+ )
}
}
}`}
@@ -428,22 +398,27 @@ products.forEach { product ->
for (final product in products) {
if (product is! ProductAndroid) continue;
- final offers = product.oneTimePurchaseOfferDetailsAndroid;
+ final offers = product.discountOffers;
if (offers != null && offers.isNotEmpty) {
final firstOffer = offers.first;
- final hasDiscount = firstOffer.discountDisplayInfo != null;
+ final hasDiscount =
+ firstOffer.percentageDiscountAndroid != null ||
+ firstOffer.discountAmountMicrosAndroid != null;
print('Product: \${product.id}');
- print('Display Price: \${product.displayPrice}');
+ print('Display Price: \${firstOffer.displayPrice}');
if (hasDiscount) {
- final discount = firstOffer.discountDisplayInfo!;
- final fullPriceMicros = int.tryParse(firstOffer.fullPriceMicros ?? '0') ?? 0;
+ final fullPriceMicros =
+ int.tryParse(firstOffer.fullPriceMicrosAndroid ?? '0') ?? 0;
final fullPrice = fullPriceMicros / 1000000;
print('Original Price: $fullPrice');
- print('Discount: \${discount.percentageDiscount}% OFF');
+ final discountText = firstOffer.percentageDiscountAndroid != null
+ ? '\${firstOffer.percentageDiscountAndroid}% OFF'
+ : firstOffer.formattedDiscountAmountAndroid;
+ print('Discount: $discountText');
}
}
}`}
@@ -466,25 +441,31 @@ var products = result is FetchProductsResultProducts productResult
foreach (var product in products)
{
- var firstOffer = product.OneTimePurchaseOfferDetailsAndroid?.FirstOrDefault();
+ var firstOffer = product.DiscountOffers?.FirstOrDefault();
if (firstOffer is null)
{
continue;
}
Console.WriteLine($"Product: {product.Id}");
- Console.WriteLine($"Display Price: {product.DisplayPrice}");
+ Console.WriteLine($"Display Price: {firstOffer.DisplayPrice}");
- var discount = firstOffer.DiscountDisplayInfo;
- if (discount is not null)
+ var hasDiscount = firstOffer.PercentageDiscountAndroid is not null
+ || firstOffer.DiscountAmountMicrosAndroid is not null;
+ if (hasDiscount)
{
- var fullPriceMicros = long.TryParse(firstOffer.FullPriceMicros, out var micros)
+ var fullPriceMicros = long.TryParse(
+ firstOffer.FullPriceMicrosAndroid,
+ out var micros)
? micros
: 0;
var fullPrice = fullPriceMicros / 1_000_000.0;
+ var discountText = firstOffer.PercentageDiscountAndroid is { } percent
+ ? $"{percent}% OFF"
+ : firstOffer.FormattedDiscountAmountAndroid;
Console.WriteLine($"Original Price: {fullPrice}");
- Console.WriteLine($"Discount: {discount.PercentageDiscount}% OFF");
+ Console.WriteLine($"Discount: {discountText}");
}
}`}
),
@@ -495,22 +476,30 @@ request.type = ProductQueryType.IN_APP
var products = await iap.fetch_products(request)
for product in products:
- var offers = product.one_time_purchase_offer_details_android
+ var offers = product.discount_offers
if offers and offers.size() > 0:
var first_offer = offers[0]
- var has_discount = first_offer.discount_display_info != null
+ var has_discount = (
+ first_offer.percentage_discount_android != null
+ or first_offer.discount_amount_micros_android != null
+ )
print("Product: %s" % product.id)
- print("Display Price: %s" % product.display_price)
+ print("Display Price: %s" % first_offer.display_price)
if has_discount:
- var discount = first_offer.discount_display_info
- var full_price_micros = int(first_offer.full_price_micros) if first_offer.full_price_micros else 0
+ var full_price_micros = (
+ int(first_offer.full_price_micros_android)
+ if first_offer.full_price_micros_android else 0
+ )
var full_price = full_price_micros / 1000000.0
print("Original Price: %.2f" % full_price)
- print("Discount: %d%% OFF" % discount.percentage_discount)`}
+ if first_offer.percentage_discount_android != null:
+ print("Discount: %d%% OFF" % first_offer.percentage_discount_android)
+ else:
+ print("Discount: %s" % first_offer.formatted_discount_amount_android)`}
),
}}
@@ -532,24 +521,28 @@ for product in products:
import type { ProductAndroid } from 'expo-iap';
function ProductCard({ product }: { product: ProductAndroid }) {
- const offers = product.oneTimePurchaseOfferDetailsAndroid;
+ const offers = product.discountOffers;
const firstOffer = offers?.[0];
- const discount = firstOffer?.discountDisplayInfo;
- const hasDiscount = discount != null;
+ const hasDiscount =
+ firstOffer?.percentageDiscountAndroid != null ||
+ firstOffer?.discountAmountMicrosAndroid != null;
// Calculate original price for strikethrough
- const fullPriceMicros = parseInt(firstOffer?.fullPriceMicros || '0', 10);
+ const fullPriceMicros = parseInt(
+ firstOffer?.fullPriceMicrosAndroid || '0',
+ 10,
+ );
const fullPrice = fullPriceMicros / 1_000_000;
- const currency = firstOffer?.priceCurrencyCode || '';
+ const currency = firstOffer?.currency || '';
// Build discount text
const getDiscountText = () => {
- if (!discount) return null;
- if (discount.percentageDiscount) {
- return \`\${discount.percentageDiscount}% OFF\`;
+ if (!hasDiscount) return null;
+ if (firstOffer?.percentageDiscountAndroid != null) {
+ return \`\${firstOffer.percentageDiscountAndroid}% OFF\`;
}
- if (discount.discountAmount) {
- return \`\${discount.discountAmount.formattedDiscountAmount} OFF\`;
+ if (firstOffer?.formattedDiscountAmountAndroid) {
+ return \`\${firstOffer.formattedDiscountAmountAndroid} OFF\`;
}
return 'SALE';
};
@@ -569,7 +562,7 @@ function ProductCard({ product }: { product: ProductAndroid }) {
{/* Current (discounted) price */}
- {product.displayPrice}
+ {firstOffer?.displayPrice ?? product.displayPrice}
{/* Discount badge */}
@@ -641,9 +634,10 @@ fun ProductCard(
product: ProductAndroid,
onPurchase: () -> Unit
) {
- val firstOffer = product.oneTimePurchaseOfferDetailsAndroid?.firstOrNull()
- val discountInfo = firstOffer?.discountDisplayInfo
- val hasDiscount = discountInfo != null
+ val firstOffer = product.discountOffers?.firstOrNull()
+ val hasDiscount =
+ firstOffer?.percentageDiscountAndroid != null ||
+ firstOffer?.discountAmountMicrosAndroid != null
Card(
modifier = Modifier
@@ -673,11 +667,12 @@ fun ProductCard(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
// Original price with strikethrough
- if (hasDiscount && firstOffer?.fullPriceMicros != null) {
- val fullPriceMicros = firstOffer.fullPriceMicros?.toLongOrNull() ?: 0L
+ if (hasDiscount && firstOffer?.fullPriceMicrosAndroid != null) {
+ val fullPriceMicros =
+ firstOffer.fullPriceMicrosAndroid?.toLongOrNull() ?: 0L
val fullPrice = fullPriceMicros.toDouble() / 1_000_000.0
Text(
- text = "\${firstOffer.priceCurrencyCode} \${String.format("%.2f", fullPrice)}",
+ text = "\${firstOffer.currency} \${String.format("%.2f", fullPrice)}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textDecoration = TextDecoration.LineThrough
@@ -686,7 +681,7 @@ fun ProductCard(
// Current (discounted) price
Text(
- text = product.displayPrice,
+ text = firstOffer?.displayPrice ?: product.displayPrice,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
color = if (hasDiscount) Color(0xFF34C759) else MaterialTheme.colorScheme.primary
@@ -695,10 +690,10 @@ fun ProductCard(
// Discount badge
if (hasDiscount) {
val discountText = when {
- discountInfo?.percentageDiscount != null ->
- "\${discountInfo.percentageDiscount}% OFF"
- discountInfo?.discountAmount != null ->
- "\${discountInfo.discountAmount?.formattedDiscountAmount} OFF"
+ firstOffer?.percentageDiscountAndroid != null ->
+ "\${firstOffer.percentageDiscountAndroid}% OFF"
+ firstOffer?.formattedDiscountAmountAndroid != null ->
+ "\${firstOffer.formattedDiscountAmountAndroid} OFF"
else -> "SALE"
}
Surface(
@@ -734,9 +729,10 @@ fun ProductCard(
product: ProductAndroid,
onPurchase: () -> Unit
) {
- val firstOffer = product.oneTimePurchaseOfferDetailsAndroid?.firstOrNull()
- val discountInfo = firstOffer?.discountDisplayInfo
- val hasDiscount = discountInfo != null
+ val firstOffer = product.discountOffers?.firstOrNull()
+ val hasDiscount =
+ firstOffer?.percentageDiscountAndroid != null ||
+ firstOffer?.discountAmountMicrosAndroid != null
Card(
modifier = Modifier
@@ -766,11 +762,12 @@ fun ProductCard(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
// Original price with strikethrough
- if (hasDiscount && firstOffer?.fullPriceMicros != null) {
- val fullPriceMicros = firstOffer.fullPriceMicros?.toLongOrNull() ?: 0L
+ if (hasDiscount && firstOffer?.fullPriceMicrosAndroid != null) {
+ val fullPriceMicros =
+ firstOffer.fullPriceMicrosAndroid?.toLongOrNull() ?: 0L
val fullPrice = fullPriceMicros.toDouble() / 1_000_000.0
Text(
- text = "\${firstOffer.priceCurrencyCode} \${String.format("%.2f", fullPrice)}",
+ text = "\${firstOffer.currency} \${String.format("%.2f", fullPrice)}",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textDecoration = TextDecoration.LineThrough
@@ -779,7 +776,7 @@ fun ProductCard(
// Current (discounted) price
Text(
- text = product.displayPrice,
+ text = firstOffer?.displayPrice ?: product.displayPrice,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
color = if (hasDiscount) Color(0xFF34C759) else MaterialTheme.colorScheme.primary
@@ -788,10 +785,10 @@ fun ProductCard(
// Discount badge
if (hasDiscount) {
val discountText = when {
- discountInfo?.percentageDiscount != null ->
- "\${discountInfo.percentageDiscount}% OFF"
- discountInfo?.discountAmount != null ->
- "\${discountInfo.discountAmount?.formattedDiscountAmount} OFF"
+ firstOffer?.percentageDiscountAndroid != null ->
+ "\${firstOffer.percentageDiscountAndroid}% OFF"
+ firstOffer?.formattedDiscountAmountAndroid != null ->
+ "\${firstOffer.formattedDiscountAmountAndroid} OFF"
else -> "SALE"
}
Surface(
@@ -837,18 +834,18 @@ public sealed record ProductCardViewModel
ProductCardViewModel BuildProductCard(ProductAndroid product)
{
- var firstOffer = product.OneTimePurchaseOfferDetailsAndroid?.FirstOrDefault();
- var discountInfo = firstOffer?.DiscountDisplayInfo;
- var hasDiscount = discountInfo is not null;
+ var firstOffer = product.DiscountOffers?.FirstOrDefault();
+ var hasDiscount = firstOffer?.PercentageDiscountAndroid is not null
+ || firstOffer?.DiscountAmountMicrosAndroid is not null;
- var originalPrice = firstOffer is { FullPriceMicros: { } fullPriceMicros }
+ var originalPrice = firstOffer is { FullPriceMicrosAndroid: { } fullPriceMicros }
&& long.TryParse(fullPriceMicros, out var micros)
- ? $"{firstOffer.PriceCurrencyCode} {micros / 1_000_000.0:F2}"
+ ? $"{firstOffer.Currency} {micros / 1_000_000.0:F2}"
: null;
- var badge = discountInfo?.PercentageDiscount is { } percent
+ var badge = firstOffer?.PercentageDiscountAndroid is { } percent
? $"{percent}% OFF"
- : discountInfo?.DiscountAmount?.FormattedDiscountAmount is { } amount
+ : firstOffer?.FormattedDiscountAmountAndroid is { } amount
? $"{amount} OFF"
: hasDiscount ? "SALE" : null;
@@ -856,7 +853,7 @@ ProductCardViewModel BuildProductCard(ProductAndroid product)
{
Title = product.Title,
Description = product.Description,
- Price = product.DisplayPrice,
+ Price = firstOffer?.DisplayPrice ?? product.DisplayPrice,
OriginalPrice = originalPrice,
DiscountBadge = badge,
};
@@ -871,8 +868,8 @@ async Task PurchaseAsync(ProductAndroid product)
Google = new RequestPurchaseAndroidProps
{
Skus = new[] { product.Id },
- OfferToken = product.OneTimePurchaseOfferDetailsAndroid?
- .FirstOrDefault()?.OfferToken,
+ OfferToken = product.DiscountOffers?
+ .FirstOrDefault()?.OfferTokenAndroid,
},
},
Type = ProductQueryType.InApp,
@@ -894,25 +891,30 @@ var product: ProductAndroid
func setup(p: ProductAndroid) -> void:
product = p
- var offers = product.one_time_purchase_offer_details_android
+ var offers = product.discount_offers
var first_offer = offers[0] if offers and offers.size() > 0 else null
- var discount = first_offer.discount_display_info if first_offer else null
- var has_discount = discount != null
+ var has_discount = (
+ first_offer != null
+ and (
+ first_offer.percentage_discount_android != null
+ or first_offer.discount_amount_micros_android != null
+ )
+ )
title_label.text = product.title
- price_label.text = product.display_price
+ price_label.text = first_offer.display_price if first_offer else product.display_price
- if has_discount and first_offer.full_price_micros:
- var full_price_micros = int(first_offer.full_price_micros)
+ if has_discount and first_offer.full_price_micros_android:
+ var full_price_micros = int(first_offer.full_price_micros_android)
var full_price = full_price_micros / 1000000.0
- original_price_label.text = "%s %.2f" % [first_offer.price_currency_code, full_price]
+ original_price_label.text = "%s %.2f" % [first_offer.currency, full_price]
original_price_label.visible = true
# Set discount badge text
- if discount.percentage_discount:
- discount_badge.text = "%d%% OFF" % discount.percentage_discount
- elif discount.discount_amount:
- discount_badge.text = "%s OFF" % discount.discount_amount.formatted_discount_amount
+ if first_offer.percentage_discount_android != null:
+ discount_badge.text = "%d%% OFF" % first_offer.percentage_discount_android
+ elif first_offer.formatted_discount_amount_android:
+ discount_badge.text = "%s OFF" % first_offer.formatted_discount_amount_android
else:
discount_badge.text = "SALE"
discount_badge.visible = true
@@ -924,9 +926,9 @@ func setup(p: ProductAndroid) -> void:
discount_badge.visible = false
func _on_buy_button_pressed() -> void:
- var offers = product.one_time_purchase_offer_details_android
+ var offers = product.discount_offers
if offers and offers.size() > 0:
- emit_signal("purchase_requested", product, offers[0].offer_token)`}
+ emit_signal("purchase_requested", product, offers[0].offer_token_android)`}
),
}}
@@ -944,8 +946,8 @@ func _on_buy_button_pressed() -> void:
{{
typescript: (
- {`function checkOfferValidity(offer: ProductAndroidOneTimePurchaseOfferDetail) {
- const timeWindow = offer.validTimeWindow;
+ {`function checkOfferValidity(offer: DiscountOffer) {
+ const timeWindow = offer.validTimeWindowAndroid;
if (!timeWindow) {
return { isValid: true, message: 'Always available' };
@@ -987,8 +989,8 @@ func _on_buy_button_pressed() -> void:
val message: String
)
-fun checkOfferValidity(offer: ProductAndroidOneTimePurchaseOfferDetail): OfferValidity {
- val timeWindow = offer.validTimeWindow
+fun checkOfferValidity(offer: DiscountOffer): OfferValidity {
+ val timeWindow = offer.validTimeWindowAndroid
?: return OfferValidity(true, "Always available")
val now = System.currentTimeMillis()
@@ -1021,8 +1023,8 @@ fun checkOfferValidity(offer: ProductAndroidOneTimePurchaseOfferDetail): OfferVa
val message: String
)
-fun checkOfferValidity(offer: ProductAndroidOneTimePurchaseOfferDetail): OfferValidity {
- val timeWindow = offer.validTimeWindow
+fun checkOfferValidity(offer: DiscountOffer): OfferValidity {
+ val timeWindow = offer.validTimeWindowAndroid
?: return OfferValidity(true, "Always available")
val now = System.currentTimeMillis()
@@ -1055,9 +1057,9 @@ using System;
public sealed record OfferValidity(bool IsValid, string Message);
-OfferValidity CheckOfferValidity(ProductAndroidOneTimePurchaseOfferDetail offer)
+OfferValidity CheckOfferValidity(DiscountOffer offer)
{
- var timeWindow = offer.ValidTimeWindow;
+ var timeWindow = offer.ValidTimeWindowAndroid;
if (timeWindow is null)
{
return new OfferValidity(true, "Always available");
@@ -1092,8 +1094,8 @@ OfferValidity CheckOfferValidity(ProductAndroidOneTimePurchaseOfferDetail offer)
}`}
),
gdscript: (
- {`func check_offer_validity(offer: ProductAndroidOneTimePurchaseOfferDetail) -> Dictionary:
- var time_window = offer.valid_time_window
+ {`func check_offer_validity(offer: DiscountOffer) -> Dictionary:
+ var time_window = offer.valid_time_window_android
if not time_window:
return {"is_valid": true, "message": "Always available"}
@@ -1137,8 +1139,8 @@ OfferValidity CheckOfferValidity(ProductAndroidOneTimePurchaseOfferDetail offer)
{{
typescript: (
- {`function checkQuantityAvailability(offer: ProductAndroidOneTimePurchaseOfferDetail) {
- const quantityInfo = offer.limitedQuantityInfo;
+ {`function checkQuantityAvailability(offer: DiscountOffer) {
+ const quantityInfo = offer.limitedQuantityInfoAndroid;
if (!quantityInfo) {
return { isAvailable: true, message: null };
@@ -1175,8 +1177,8 @@ OfferValidity CheckOfferValidity(ProductAndroidOneTimePurchaseOfferDetail offer)
val message: String?
)
-fun checkQuantityAvailability(offer: ProductAndroidOneTimePurchaseOfferDetail): QuantityAvailability {
- val quantityInfo = offer.limitedQuantityInfo
+fun checkQuantityAvailability(offer: DiscountOffer): QuantityAvailability {
+ val quantityInfo = offer.limitedQuantityInfoAndroid
?: return QuantityAvailability(true, null)
val (maximumQuantity, remainingQuantity) = quantityInfo
@@ -1198,8 +1200,8 @@ fun checkQuantityAvailability(offer: ProductAndroidOneTimePurchaseOfferDetail):
val message: String?
)
-fun checkQuantityAvailability(offer: ProductAndroidOneTimePurchaseOfferDetail): QuantityAvailability {
- val quantityInfo = offer.limitedQuantityInfo
+fun checkQuantityAvailability(offer: DiscountOffer): QuantityAvailability {
+ val quantityInfo = offer.limitedQuantityInfoAndroid
?: return QuantityAvailability(true, null)
val (maximumQuantity, remainingQuantity) = quantityInfo
@@ -1220,9 +1222,9 @@ fun checkQuantityAvailability(offer: ProductAndroidOneTimePurchaseOfferDetail):
public sealed record QuantityAvailability(bool IsAvailable, string? Message);
-QuantityAvailability CheckQuantityAvailability(ProductAndroidOneTimePurchaseOfferDetail offer)
+QuantityAvailability CheckQuantityAvailability(DiscountOffer offer)
{
- var quantityInfo = offer.LimitedQuantityInfo;
+ var quantityInfo = offer.LimitedQuantityInfoAndroid;
if (quantityInfo is null)
{
return new QuantityAvailability(true, null);
@@ -1247,8 +1249,8 @@ QuantityAvailability CheckQuantityAvailability(ProductAndroidOneTimePurchaseOffe
}`}
),
gdscript: (
- {`func check_quantity_availability(offer: ProductAndroidOneTimePurchaseOfferDetail) -> Dictionary:
- var quantity_info = offer.limited_quantity_info
+ {`func check_quantity_availability(offer: DiscountOffer) -> Dictionary:
+ var quantity_info = offer.limited_quantity_info_android
if not quantity_info:
return {"is_available": true, "message": null}
@@ -1273,8 +1275,8 @@ QuantityAvailability CheckQuantityAvailability(ProductAndroidOneTimePurchaseOffe
Purchasing with Specific Offer
- When purchasing a discounted product, use the offerToken{' '}
- from the specific offer you want to apply:
+ When purchasing a discounted product, pass the selected offer's{' '}
+ offerTokenAndroid to the Android purchase request:
@@ -1286,7 +1288,7 @@ async function purchaseWithOffer(
product: ProductAndroid,
offerIndex: number = 0
) {
- const offers = product.oneTimePurchaseOfferDetailsAndroid;
+ const offers = product.discountOffers;
if (!offers || offers.length === 0) {
throw new Error('No offers available for this product');
@@ -1299,8 +1301,8 @@ async function purchaseWithOffer(
request: {
google: {
skus: [product.id],
- // Include offerTokenAndroid for discounted purchases (Android 8.0+)
- offerToken: selectedOffer.offerToken,
+ // Pass the standardized offer's Android token.
+ offerToken: selectedOffer.offerTokenAndroid,
},
},
});
@@ -1312,26 +1314,24 @@ async function purchaseWithOffer(
),
kotlin: (
{`suspend fun purchaseWithOffer(
- activity: Activity,
product: ProductAndroid,
offerIndex: Int = 0
) {
- val offers = product.oneTimePurchaseOfferDetailsAndroid
+ val offers = product.discountOffers
?: throw IllegalStateException("No offers available")
val selectedOffer = offers.getOrNull(offerIndex)
?: throw IllegalStateException("Invalid offer index")
iapStore.requestPurchase(
- activity = activity,
- props = RequestPurchaseProps(
+ RequestPurchaseProps(
type = ProductQueryType.InApp,
request = RequestPurchaseProps.Request.Purchase(
RequestPurchasePropsByPlatforms(
google = RequestPurchaseAndroidProps(
skus = listOf(product.id),
- // Include offerTokenAndroid for discounted purchases (Android 8.0+)
- offerToken = selectedOffer.offerToken
+ // Pass the standardized offer's Android token.
+ offerToken = selectedOffer.offerTokenAndroid
)
)
)
@@ -1341,26 +1341,24 @@ async function purchaseWithOffer(
),
kmp: (
{`suspend fun purchaseWithOffer(
- activity: Activity,
product: ProductAndroid,
offerIndex: Int = 0
) {
- val offers = product.oneTimePurchaseOfferDetailsAndroid
+ val offers = product.discountOffers
?: throw IllegalStateException("No offers available")
val selectedOffer = offers.getOrNull(offerIndex)
?: throw IllegalStateException("Invalid offer index")
kmpIAP.requestPurchase(
- activity = activity,
- props = RequestPurchaseProps(
+ RequestPurchaseProps(
type = ProductQueryType.InApp,
request = RequestPurchaseProps.Request.Purchase(
RequestPurchasePropsByPlatforms(
google = RequestPurchaseAndroidProps(
skus = listOf(product.id),
- // Include offerTokenAndroid for discounted purchases (Android 8.0+)
- offerToken = selectedOffer.offerToken
+ // Pass the standardized offer's Android token.
+ offerToken = selectedOffer.offerTokenAndroid
)
)
)
@@ -1376,7 +1374,7 @@ using System.Linq;
async Task PurchaseWithOfferAsync(ProductAndroid product, int offerIndex = 0)
{
- var offers = product.OneTimePurchaseOfferDetailsAndroid
+ var offers = product.DiscountOffers
?? throw new InvalidOperationException("No offers available");
var selectedOffer = offers.ElementAtOrDefault(offerIndex)
@@ -1390,8 +1388,8 @@ async Task PurchaseWithOfferAsync(ProductAndroid product, int offerIndex = 0)
Google = new RequestPurchaseAndroidProps
{
Skus = new[] { product.Id },
- // Include OfferToken for discounted purchases (Android 8.0+).
- OfferToken = selectedOffer.OfferToken,
+ // Pass the standardized offer's Android token.
+ OfferToken = selectedOffer.OfferTokenAndroid,
},
},
});
@@ -1399,7 +1397,7 @@ async Task PurchaseWithOfferAsync(ProductAndroid product, int offerIndex = 0)
),
gdscript: (
{`func purchase_with_offer(product: ProductAndroid, offer_index: int = 0) -> void:
- var offers = product.one_time_purchase_offer_details_android
+ var offers = product.discount_offers
if not offers or offers.size() == 0:
push_error("No offers available for this product")
@@ -1416,7 +1414,7 @@ async Task PurchaseWithOfferAsync(ProductAndroid product, int offerIndex = 0)
props.request = RequestPurchasePropsByPlatforms.new()
props.request.google = RequestPurchaseAndroidProps.new()
props.request.google.skus = [product.id]
- props.request.google.offer_token = selected_offer.offer_token
+ props.request.google.offer_token = selected_offer.offer_token_android
await iap.request_purchase(props)`}
),
@@ -1510,16 +1508,6 @@ async Task PurchaseWithOfferAsync(ProductAndroid product, int offerIndex = 0)
Native References
@@ -344,20 +343,13 @@ interface SubscriptionPeriod {
value: number;
}
-enum SubscriptionPeriodUnit {
- Day = 'Day',
- Week = 'Week',
- Month = 'Month',
- Year = 'Year',
- Unknown = 'Unknown',
-}
+type SubscriptionPeriodUnit = 'day' | 'week' | 'month' | 'year' | 'unknown';
-enum PaymentMode {
- FreeTrial = 'FreeTrial',
- PayAsYouGo = 'PayAsYouGo',
- PayUpFront = 'PayUpFront',
- Unknown = 'Unknown',
-}`}
+type PaymentMode =
+ | 'free-trial'
+ | 'pay-as-you-go'
+ | 'pay-up-front'
+ | 'unknown';`}
),
swift: (
{`struct SubscriptionOffer: Codable {
@@ -398,18 +390,18 @@ struct SubscriptionPeriod: Codable {
}
enum SubscriptionPeriodUnit: String, Codable {
- case day = "Day"
- case week = "Week"
- case month = "Month"
- case year = "Year"
- case unknown = "Unknown"
+ case day = "day"
+ case week = "week"
+ case month = "month"
+ case year = "year"
+ case unknown = "unknown"
}
enum PaymentMode: String, Codable {
- case freeTrial = "FreeTrial"
- case payAsYouGo = "PayAsYouGo"
- case payUpFront = "PayUpFront"
- case unknown = "Unknown"
+ case freeTrial = "free-trial"
+ case payAsYouGo = "pay-as-you-go"
+ case payUpFront = "pay-up-front"
+ case unknown = "unknown"
}`}
),
kotlin: (
@@ -450,12 +442,19 @@ data class SubscriptionPeriod(
val value: Int
)
-enum class SubscriptionPeriodUnit {
- Day, Week, Month, Year, Unknown
+enum class SubscriptionPeriodUnit(val rawValue: String) {
+ Day("day"),
+ Week("week"),
+ Month("month"),
+ Year("year"),
+ Unknown("unknown")
}
-enum class PaymentMode {
- FreeTrial, PayAsYouGo, PayUpFront, Unknown
+enum class PaymentMode(val rawValue: String) {
+ FreeTrial("free-trial"),
+ PayAsYouGo("pay-as-you-go"),
+ PayUpFront("pay-up-front"),
+ Unknown("unknown")
}`}
),
dart: (
@@ -525,9 +524,26 @@ class SubscriptionPeriod {
SubscriptionPeriod({required this.unit, required this.value});
}
-enum SubscriptionPeriodUnit { day, week, month, year, unknown }
+enum SubscriptionPeriodUnit {
+ Day('day'),
+ Week('week'),
+ Month('month'),
+ Year('year'),
+ Unknown('unknown');
+
+ const SubscriptionPeriodUnit(this.value);
+ final String value;
+}
-enum PaymentMode { freeTrial, payAsYouGo, payUpFront, unknown }`}
+enum PaymentMode {
+ FreeTrial('free-trial'),
+ PayAsYouGo('pay-as-you-go'),
+ PayUpFront('pay-up-front'),
+ Unknown('unknown');
+
+ const PaymentMode(this.value);
+ final String value;
+}`}
),
csharp: (
{`using OpenIap;
diff --git a/packages/docs/src/pages/docs/types/subscription-product.tsx b/packages/docs/src/pages/docs/types/subscription-product.tsx
index f2835a001..4b16385c5 100644
--- a/packages/docs/src/pages/docs/types/subscription-product.tsx
+++ b/packages/docs/src/pages/docs/types/subscription-product.tsx
@@ -80,7 +80,7 @@ function SubscriptionProduct() {
debugDescription,{' '}
platform (
Deprecated.)), plus the subscription-only override
- and the cross-platform offer arrays below.
+ and standardized subscription offer array below.
@@ -118,21 +118,6 @@ function SubscriptionProduct() {
offers on iOS and from Play Billing offer details on Android.
-
-
- discountOffers
-
-
-
- DiscountOffer[]
-
-
-
- Cross-platform discount list (introductory pricing, promo
- codes). Always present in the schema; iOS-only stores may return
- an empty array.
-
-
@@ -281,6 +266,14 @@ function SubscriptionProduct() {
ProductSubscriptionAndroid
Additional fields available on Android subscriptions:
+
+ The generated Android shape retains a nullable{' '}
+ discountOffers compatibility field, but Google
+ Play only returns one-time purchase offer details for{' '}
+ in-app products. It is not subscription discount
+ metadata; use subscriptionOffers for
+ subscriptions.
+
diff --git a/packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt b/packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
index 2a99c11d3..8a910f5b0 100644
--- a/packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
+++ b/packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
@@ -2077,7 +2077,7 @@ public data class DiscountDisplayInfoAndroid(
/**
* Discount information returned from the store.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class DiscountIOS(
val identifier: String,
@@ -2120,10 +2120,11 @@ public data class DiscountIOS(
/**
* Standardized one-time product discount offer.
- * Provides a unified interface for one-time purchase discounts across platforms.
+ * Provides a platform-neutral OpenIAP shape for Google Play one-time product
+ * purchase options and offers.
*
- * Currently supported on Android (Google Play Billing 8.0+).
- * iOS does not support one-time purchase discounts in the same way.
+ * Currently populated only on Android (Google Play Billing 8.0+).
+ * iOS does not populate this type.
*
* @see https://openiap.dev/docs/features/discount
*/
@@ -2142,7 +2143,7 @@ public data class DiscountOffer(
*/
val displayPrice: String,
/**
- * [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ * [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
*/
val formattedDiscountAmountAndroid: String? = null,
/**
@@ -2253,7 +2254,7 @@ public data class DiscountOffer(
/**
* iOS DiscountOffer (output type).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class DiscountOfferIOS(
/**
@@ -2770,9 +2771,9 @@ public data class ProductAndroid(
override val debugDescription: String? = null,
override val description: String,
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Standardized Android one-time product purchase options and offers.
+ * Native metadata uses Android-suffixed fields.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
val discountOffers: List? = null,
override val displayName: String? = null,
@@ -2782,7 +2783,7 @@ public data class ProductAndroid(
/**
* One-time purchase offer details including discounts (Android)
* Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * @deprecated Use the standardized discountOffers field instead.
*/
val oneTimePurchaseOfferDetailsAndroid: List? = null,
override val platform: IapPlatform = IapPlatform.Android,
@@ -2802,7 +2803,7 @@ public data class ProductAndroid(
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
override val title: String,
@@ -2856,8 +2857,8 @@ public data class ProductAndroid(
/**
* One-time purchase offer details (Android).
* Available in Google Play Billing Library 8.0+
- * @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#discount-offer
+ * @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
public data class ProductAndroidOneTimePurchaseOfferDetail(
/**
@@ -2973,7 +2974,7 @@ public data class ProductIOS(
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
* Note: iOS does not support one-time product discounts.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
override val title: String,
@@ -3032,9 +3033,8 @@ public data class ProductSubscriptionAndroid(
override val debugDescription: String? = null,
override val description: String,
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Nullable compatibility field. Google Play does not return one-time purchase
+ * offer details for subscription products; use subscriptionOffers below.
*/
val discountOffers: List? = null,
override val displayName: String? = null,
@@ -3042,9 +3042,10 @@ public data class ProductSubscriptionAndroid(
override val id: String,
val nameAndroid: String,
/**
- * One-time purchase offer details including discounts (Android)
- * Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * Legacy nullable compatibility field. Google Play does not populate one-time
+ * purchase offer details for subscription products.
+ * @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ * subscriptions use subscriptionOffers.
*/
val oneTimePurchaseOfferDetailsAndroid: List? = null,
override val platform: IapPlatform = IapPlatform.Android,
@@ -3064,7 +3065,7 @@ public data class ProductSubscriptionAndroid(
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List,
override val title: String,
@@ -3118,7 +3119,7 @@ public data class ProductSubscriptionAndroid(
/**
* Subscription offer details (Android).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class ProductSubscriptionAndroidOfferDetails(
val basePlanId: String,
@@ -3195,7 +3196,7 @@ public data class ProductSubscriptionIOS(
/**
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
val subscriptionPeriodNumberIOS: String? = null,
@@ -3917,8 +3918,7 @@ public data class SubscriptionInfoIOS(
* - iOS: Introductory offers, promotional offers with server-side signatures
* - Android: Offer tokens with pricing phases
*
- * @see https://openiap.dev/docs/types/ios#discount-offer
- * @see https://openiap.dev/docs/types/android#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class SubscriptionOffer(
/**
@@ -4063,7 +4063,7 @@ public data class SubscriptionOffer(
/**
* iOS subscription offer details.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class SubscriptionOfferIOS(
val displayPrice: String,
diff --git a/packages/gql/src/generated/Types.cs b/packages/gql/src/generated/Types.cs
index 7677ef615..1da78ba5c 100644
--- a/packages/gql/src/generated/Types.cs
+++ b/packages/gql/src/generated/Types.cs
@@ -2944,7 +2944,7 @@ public sealed record DiscountDisplayInfoAndroid
/// Discount information returned from the store.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record DiscountIOS
{
[JsonPropertyName("identifier")]
@@ -2966,10 +2966,11 @@ public sealed record DiscountIOS
}
/// Standardized one-time product discount offer.
-/// Provides a unified interface for one-time purchase discounts across platforms.
+/// Provides a platform-neutral OpenIAP shape for Google Play one-time product
+/// purchase options and offers.
///
-/// Currently supported on Android (Google Play Billing 8.0+).
-/// iOS does not support one-time purchase discounts in the same way.
+/// Currently populated only on Android (Google Play Billing 8.0+).
+/// iOS does not populate this type.
///
/// @see https://openiap.dev/docs/features/discount
public sealed record DiscountOffer
@@ -2984,7 +2985,7 @@ public sealed record DiscountOffer
/// Formatted display price string (e.g., "$4.99")
[JsonPropertyName("displayPrice")]
public required string DisplayPrice { get; init; }
- /// [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ /// [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
[JsonPropertyName("formattedDiscountAmountAndroid")]
public string? FormattedDiscountAmountAndroid { get; init; }
/// [Android] Original full price in micro-units before discount.
@@ -3038,7 +3039,7 @@ public sealed record DiscountOffer
/// iOS DiscountOffer (output type).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record DiscountOfferIOS
{
/// Discount identifier
@@ -3265,9 +3266,9 @@ public sealed record ProductAndroid : Product, ProductCommon
public string? DebugDescription { get; init; }
[JsonPropertyName("description")]
public required string Description { get; init; }
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Standardized Android one-time product purchase options and offers.
+ /// Native metadata uses Android-suffixed fields.
+ /// @see https://openiap.dev/docs/types/discount-offer
[JsonPropertyName("discountOffers")]
public IReadOnlyList? DiscountOffers { get; init; }
[JsonPropertyName("displayName")]
@@ -3280,7 +3281,7 @@ public sealed record ProductAndroid : Product, ProductCommon
public required string NameAndroid { get; init; }
/// One-time purchase offer details including discounts (Android)
/// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// @deprecated Use the standardized discountOffers field instead.
[JsonPropertyName("oneTimePurchaseOfferDetailsAndroid")]
public IReadOnlyList? OneTimePurchaseOfferDetailsAndroid { get; init; }
[JsonPropertyName("platform")]
@@ -3299,7 +3300,7 @@ public sealed record ProductAndroid : Product, ProductCommon
public IReadOnlyList? SubscriptionOfferDetailsAndroid { get; init; }
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
[JsonPropertyName("subscriptionOffers")]
public IReadOnlyList? SubscriptionOffers { get; init; }
[JsonPropertyName("title")]
@@ -3310,8 +3311,8 @@ public sealed record ProductAndroid : Product, ProductCommon
/// One-time purchase offer details (Android).
/// Available in Google Play Billing Library 8.0+
-/// @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#discount-offer
+/// @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+/// @see https://openiap.dev/docs/types/discount-offer
public sealed record ProductAndroidOneTimePurchaseOfferDetail
{
/// Discount display information
@@ -3391,7 +3392,7 @@ public sealed record ProductIOS : Product, ProductCommon
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
/// Note: iOS does not support one-time product discounts.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
[JsonPropertyName("subscriptionOffers")]
public IReadOnlyList? SubscriptionOffers { get; init; }
[JsonPropertyName("title")]
@@ -3410,9 +3411,8 @@ public sealed record ProductSubscriptionAndroid : ProductSubscription, ProductCo
public string? DebugDescription { get; init; }
[JsonPropertyName("description")]
public required string Description { get; init; }
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Nullable compatibility field. Google Play does not return one-time purchase
+ /// offer details for subscription products; use subscriptionOffers below.
[JsonPropertyName("discountOffers")]
public IReadOnlyList? DiscountOffers { get; init; }
[JsonPropertyName("displayName")]
@@ -3423,9 +3423,10 @@ public sealed record ProductSubscriptionAndroid : ProductSubscription, ProductCo
public required string Id { get; init; }
[JsonPropertyName("nameAndroid")]
public required string NameAndroid { get; init; }
- /// One-time purchase offer details including discounts (Android)
- /// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// Legacy nullable compatibility field. Google Play does not populate one-time
+ /// purchase offer details for subscription products.
+ /// @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ /// subscriptions use subscriptionOffers.
[JsonPropertyName("oneTimePurchaseOfferDetailsAndroid")]
public IReadOnlyList? OneTimePurchaseOfferDetailsAndroid { get; init; }
[JsonPropertyName("platform")]
@@ -3444,7 +3445,7 @@ public sealed record ProductSubscriptionAndroid : ProductSubscription, ProductCo
public required IReadOnlyList SubscriptionOfferDetailsAndroid { get; init; }
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
[JsonPropertyName("subscriptionOffers")]
public required IReadOnlyList SubscriptionOffers { get; init; }
[JsonPropertyName("title")]
@@ -3455,7 +3456,7 @@ public sealed record ProductSubscriptionAndroid : ProductSubscription, ProductCo
/// Subscription offer details (Android).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record ProductSubscriptionAndroidOfferDetails
{
[JsonPropertyName("basePlanId")]
@@ -3524,7 +3525,7 @@ public sealed record ProductSubscriptionIOS : ProductSubscription, ProductCommon
public SubscriptionInfoIOS? SubscriptionInfoIOS { get; init; }
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
[JsonPropertyName("subscriptionOffers")]
public IReadOnlyList? SubscriptionOffers { get; init; }
[JsonPropertyName("subscriptionPeriodNumberIOS")]
@@ -3861,8 +3862,7 @@ public sealed record SubscriptionInfoIOS
/// - iOS: Introductory offers, promotional offers with server-side signatures
/// - Android: Offer tokens with pricing phases
///
-/// @see https://openiap.dev/docs/types/ios#discount-offer
-/// @see https://openiap.dev/docs/types/android#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record SubscriptionOffer
{
/// [Android] Base plan identifier.
@@ -3937,7 +3937,7 @@ public sealed record SubscriptionOffer
/// iOS subscription offer details.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public sealed record SubscriptionOfferIOS
{
[JsonPropertyName("displayPrice")]
diff --git a/packages/gql/src/generated/Types.kt b/packages/gql/src/generated/Types.kt
index fb59190ea..a222be9b9 100644
--- a/packages/gql/src/generated/Types.kt
+++ b/packages/gql/src/generated/Types.kt
@@ -2203,7 +2203,7 @@ public data class DiscountDisplayInfoAndroid(
/**
* Discount information returned from the store.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class DiscountIOS(
val identifier: String,
@@ -2246,10 +2246,11 @@ public data class DiscountIOS(
/**
* Standardized one-time product discount offer.
- * Provides a unified interface for one-time purchase discounts across platforms.
+ * Provides a platform-neutral OpenIAP shape for Google Play one-time product
+ * purchase options and offers.
*
- * Currently supported on Android (Google Play Billing 8.0+).
- * iOS does not support one-time purchase discounts in the same way.
+ * Currently populated only on Android (Google Play Billing 8.0+).
+ * iOS does not populate this type.
*
* @see https://openiap.dev/docs/features/discount
*/
@@ -2268,7 +2269,7 @@ public data class DiscountOffer(
*/
val displayPrice: String,
/**
- * [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ * [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
*/
val formattedDiscountAmountAndroid: String? = null,
/**
@@ -2379,7 +2380,7 @@ public data class DiscountOffer(
/**
* iOS DiscountOffer (output type).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class DiscountOfferIOS(
/**
@@ -2896,9 +2897,9 @@ public data class ProductAndroid(
override val debugDescription: String? = null,
override val description: String,
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Standardized Android one-time product purchase options and offers.
+ * Native metadata uses Android-suffixed fields.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
val discountOffers: List? = null,
override val displayName: String? = null,
@@ -2908,7 +2909,7 @@ public data class ProductAndroid(
/**
* One-time purchase offer details including discounts (Android)
* Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * @deprecated Use the standardized discountOffers field instead.
*/
val oneTimePurchaseOfferDetailsAndroid: List? = null,
override val platform: IapPlatform = IapPlatform.Android,
@@ -2928,7 +2929,7 @@ public data class ProductAndroid(
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
override val title: String,
@@ -2982,8 +2983,8 @@ public data class ProductAndroid(
/**
* One-time purchase offer details (Android).
* Available in Google Play Billing Library 8.0+
- * @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#discount-offer
+ * @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
public data class ProductAndroidOneTimePurchaseOfferDetail(
/**
@@ -3099,7 +3100,7 @@ public data class ProductIOS(
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
* Note: iOS does not support one-time product discounts.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
override val title: String,
@@ -3158,9 +3159,8 @@ public data class ProductSubscriptionAndroid(
override val debugDescription: String? = null,
override val description: String,
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Nullable compatibility field. Google Play does not return one-time purchase
+ * offer details for subscription products; use subscriptionOffers below.
*/
val discountOffers: List? = null,
override val displayName: String? = null,
@@ -3168,9 +3168,10 @@ public data class ProductSubscriptionAndroid(
override val id: String,
val nameAndroid: String,
/**
- * One-time purchase offer details including discounts (Android)
- * Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * Legacy nullable compatibility field. Google Play does not populate one-time
+ * purchase offer details for subscription products.
+ * @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ * subscriptions use subscriptionOffers.
*/
val oneTimePurchaseOfferDetailsAndroid: List? = null,
override val platform: IapPlatform = IapPlatform.Android,
@@ -3190,7 +3191,7 @@ public data class ProductSubscriptionAndroid(
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List,
override val title: String,
@@ -3244,7 +3245,7 @@ public data class ProductSubscriptionAndroid(
/**
* Subscription offer details (Android).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class ProductSubscriptionAndroidOfferDetails(
val basePlanId: String,
@@ -3321,7 +3322,7 @@ public data class ProductSubscriptionIOS(
/**
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
val subscriptionOffers: List? = null,
val subscriptionPeriodNumberIOS: String? = null,
@@ -4043,8 +4044,7 @@ public data class SubscriptionInfoIOS(
* - iOS: Introductory offers, promotional offers with server-side signatures
* - Android: Offer tokens with pricing phases
*
- * @see https://openiap.dev/docs/types/ios#discount-offer
- * @see https://openiap.dev/docs/types/android#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class SubscriptionOffer(
/**
@@ -4189,7 +4189,7 @@ public data class SubscriptionOffer(
/**
* iOS subscription offer details.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
public data class SubscriptionOfferIOS(
val displayPrice: String,
diff --git a/packages/gql/src/generated/Types.swift b/packages/gql/src/generated/Types.swift
index 40ffcd7ae..a46c07cd5 100644
--- a/packages/gql/src/generated/Types.swift
+++ b/packages/gql/src/generated/Types.swift
@@ -838,7 +838,7 @@ public struct DiscountDisplayInfoAndroid: Codable {
/// Discount information returned from the store.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct DiscountIOS: Codable {
public var identifier: String
public var localizedPrice: String? = nil
@@ -851,10 +851,11 @@ public struct DiscountIOS: Codable {
}
/// Standardized one-time product discount offer.
-/// Provides a unified interface for one-time purchase discounts across platforms.
+/// Provides a platform-neutral OpenIAP shape for Google Play one-time product
+/// purchase options and offers.
///
-/// Currently supported on Android (Google Play Billing 8.0+).
-/// iOS does not support one-time purchase discounts in the same way.
+/// Currently populated only on Android (Google Play Billing 8.0+).
+/// iOS does not populate this type.
///
/// @see https://openiap.dev/docs/features/discount
public struct DiscountOffer: Codable {
@@ -865,7 +866,7 @@ public struct DiscountOffer: Codable {
public var discountAmountMicrosAndroid: String? = nil
/// Formatted display price string (e.g., "$4.99")
public var displayPrice: String
- /// [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ /// [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
public var formattedDiscountAmountAndroid: String? = nil
/// [Android] Original full price in micro-units before discount.
/// Divide by 1,000,000 to get the actual price.
@@ -906,7 +907,7 @@ public struct DiscountOffer: Codable {
/// iOS DiscountOffer (output type).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct DiscountOfferIOS: Codable {
/// Discount identifier
public var identifier: String
@@ -1071,9 +1072,9 @@ public struct ProductAndroid: Codable, ProductCommon {
public var currency: String
public var debugDescription: String? = nil
public var description: String
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Standardized Android one-time product purchase options and offers.
+ /// Native metadata uses Android-suffixed fields.
+ /// @see https://openiap.dev/docs/types/discount-offer
public var discountOffers: [DiscountOffer]? = nil
public var displayName: String? = nil
public var displayPrice: String
@@ -1081,7 +1082,7 @@ public struct ProductAndroid: Codable, ProductCommon {
public var nameAndroid: String
/// One-time purchase offer details including discounts (Android)
/// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// @deprecated Use the standardized discountOffers field instead.
public var oneTimePurchaseOfferDetailsAndroid: [ProductAndroidOneTimePurchaseOfferDetail]? = nil
public var platform: IapPlatform = .android
public var price: Double? = nil
@@ -1095,7 +1096,7 @@ public struct ProductAndroid: Codable, ProductCommon {
public var subscriptionOfferDetailsAndroid: [ProductSubscriptionAndroidOfferDetails]? = nil
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
public var subscriptionOffers: [SubscriptionOffer]? = nil
public var title: String
public var type: ProductType = .inApp
@@ -1103,8 +1104,8 @@ public struct ProductAndroid: Codable, ProductCommon {
/// One-time purchase offer details (Android).
/// Available in Google Play Billing Library 8.0+
-/// @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#discount-offer
+/// @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+/// @see https://openiap.dev/docs/types/discount-offer
public struct ProductAndroidOneTimePurchaseOfferDetail: Codable {
/// Discount display information
/// Only available for discounted offers
@@ -1156,7 +1157,7 @@ public struct ProductIOS: Codable, ProductCommon {
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
/// Note: iOS does not support one-time product discounts.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
public var subscriptionOffers: [SubscriptionOffer]? = nil
public var title: String
public var type: ProductType = .inApp
@@ -1167,17 +1168,17 @@ public struct ProductSubscriptionAndroid: Codable, ProductCommon {
public var currency: String
public var debugDescription: String? = nil
public var description: String
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Nullable compatibility field. Google Play does not return one-time purchase
+ /// offer details for subscription products; use subscriptionOffers below.
public var discountOffers: [DiscountOffer]? = nil
public var displayName: String? = nil
public var displayPrice: String
public var id: String
public var nameAndroid: String
- /// One-time purchase offer details including discounts (Android)
- /// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// Legacy nullable compatibility field. Google Play does not populate one-time
+ /// purchase offer details for subscription products.
+ /// @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ /// subscriptions use subscriptionOffers.
public var oneTimePurchaseOfferDetailsAndroid: [ProductAndroidOneTimePurchaseOfferDetail]? = nil
public var platform: IapPlatform = .android
public var price: Double? = nil
@@ -1191,7 +1192,7 @@ public struct ProductSubscriptionAndroid: Codable, ProductCommon {
public var subscriptionOfferDetailsAndroid: [ProductSubscriptionAndroidOfferDetails]
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
public var subscriptionOffers: [SubscriptionOffer]
public var title: String
public var type: ProductType = .subs
@@ -1199,7 +1200,7 @@ public struct ProductSubscriptionAndroid: Codable, ProductCommon {
/// Subscription offer details (Android).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct ProductSubscriptionAndroidOfferDetails: Codable {
public var basePlanId: String
/// Installment plan details for this subscription offer.
@@ -1240,7 +1241,7 @@ public struct ProductSubscriptionIOS: Codable, ProductCommon {
public var subscriptionInfoIOS: SubscriptionInfoIOS? = nil
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
public var subscriptionOffers: [SubscriptionOffer]? = nil
public var subscriptionPeriodNumberIOS: String? = nil
public var subscriptionPeriodUnitIOS: SubscriptionPeriodIOS? = nil
@@ -1453,8 +1454,7 @@ public struct SubscriptionInfoIOS: Codable {
/// - iOS: Introductory offers, promotional offers with server-side signatures
/// - Android: Offer tokens with pricing phases
///
-/// @see https://openiap.dev/docs/types/ios#discount-offer
-/// @see https://openiap.dev/docs/types/android#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct SubscriptionOffer: Codable {
/// [Android] Base plan identifier.
/// Identifies which base plan this offer belongs to.
@@ -1509,7 +1509,7 @@ public struct SubscriptionOffer: Codable {
/// iOS subscription offer details.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
public struct SubscriptionOfferIOS: Codable {
public var displayPrice: String
public var id: String
diff --git a/packages/gql/src/generated/types.dart b/packages/gql/src/generated/types.dart
index e3af72e5c..0dffa88a4 100644
--- a/packages/gql/src/generated/types.dart
+++ b/packages/gql/src/generated/types.dart
@@ -1982,7 +1982,7 @@ class DiscountDisplayInfoAndroid {
/// Discount information returned from the store.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class DiscountIOS {
const DiscountIOS({
required this.identifier,
@@ -2033,10 +2033,11 @@ class DiscountIOS {
}
/// Standardized one-time product discount offer.
-/// Provides a unified interface for one-time purchase discounts across platforms.
+/// Provides a platform-neutral OpenIAP shape for Google Play one-time product
+/// purchase options and offers.
///
-/// Currently supported on Android (Google Play Billing 8.0+).
-/// iOS does not support one-time purchase discounts in the same way.
+/// Currently populated only on Android (Google Play Billing 8.0+).
+/// iOS does not populate this type.
///
/// @see https://openiap.dev/docs/features/discount
class DiscountOffer {
@@ -2066,7 +2067,7 @@ class DiscountOffer {
final String? discountAmountMicrosAndroid;
/// Formatted display price string (e.g., "$4.99")
final String displayPrice;
- /// [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ /// [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
final String? formattedDiscountAmountAndroid;
/// [Android] Original full price in micro-units before discount.
/// Divide by 1,000,000 to get the actual price.
@@ -2150,7 +2151,7 @@ class DiscountOffer {
/// iOS DiscountOffer (output type).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class DiscountOfferIOS {
const DiscountOfferIOS({
required this.identifier,
@@ -2691,9 +2692,9 @@ class ProductAndroid extends Product implements ProductCommon {
final String currency;
final String? debugDescription;
final String description;
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Standardized Android one-time product purchase options and offers.
+ /// Native metadata uses Android-suffixed fields.
+ /// @see https://openiap.dev/docs/types/discount-offer
final List? discountOffers;
final String? displayName;
final String displayPrice;
@@ -2701,7 +2702,7 @@ class ProductAndroid extends Product implements ProductCommon {
final String nameAndroid;
/// One-time purchase offer details including discounts (Android)
/// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// @deprecated Use the standardized discountOffers field instead.
final List? oneTimePurchaseOfferDetailsAndroid;
final IapPlatform platform;
final double? price;
@@ -2715,7 +2716,7 @@ class ProductAndroid extends Product implements ProductCommon {
final List? subscriptionOfferDetailsAndroid;
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
final List? subscriptionOffers;
final String title;
final ProductType type;
@@ -2767,8 +2768,8 @@ class ProductAndroid extends Product implements ProductCommon {
/// One-time purchase offer details (Android).
/// Available in Google Play Billing Library 8.0+
-/// @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#discount-offer
+/// @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+/// @see https://openiap.dev/docs/types/discount-offer
class ProductAndroidOneTimePurchaseOfferDetail {
const ProductAndroidOneTimePurchaseOfferDetail({
this.discountDisplayInfo,
@@ -2893,7 +2894,7 @@ class ProductIOS extends Product implements ProductCommon {
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
/// Note: iOS does not support one-time product discounts.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
final List? subscriptionOffers;
final String title;
final ProductType type;
@@ -2969,17 +2970,17 @@ class ProductSubscriptionAndroid extends ProductSubscription implements ProductC
final String currency;
final String? debugDescription;
final String description;
- /// Standardized discount offers for one-time products.
- /// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#discount-offer
+ /// Nullable compatibility field. Google Play does not return one-time purchase
+ /// offer details for subscription products; use subscriptionOffers below.
final List? discountOffers;
final String? displayName;
final String displayPrice;
final String id;
final String nameAndroid;
- /// One-time purchase offer details including discounts (Android)
- /// Returns all eligible offers. Available in Google Play Billing Library 8.0+
- /// @deprecated Use discountOffers instead for cross-platform compatibility.
+ /// Legacy nullable compatibility field. Google Play does not populate one-time
+ /// purchase offer details for subscription products.
+ /// @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ /// subscriptions use subscriptionOffers.
final List? oneTimePurchaseOfferDetailsAndroid;
final IapPlatform platform;
final double? price;
@@ -2993,7 +2994,7 @@ class ProductSubscriptionAndroid extends ProductSubscription implements ProductC
final List subscriptionOfferDetailsAndroid;
/// Standardized subscription offers.
/// Cross-platform type with Android-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
final List subscriptionOffers;
final String title;
final ProductType type;
@@ -3045,7 +3046,7 @@ class ProductSubscriptionAndroid extends ProductSubscription implements ProductC
/// Subscription offer details (Android).
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class ProductSubscriptionAndroidOfferDetails {
const ProductSubscriptionAndroidOfferDetails({
required this.basePlanId,
@@ -3147,7 +3148,7 @@ class ProductSubscriptionIOS extends ProductSubscription implements ProductCommo
final SubscriptionInfoIOS? subscriptionInfoIOS;
/// Standardized subscription offers.
/// Cross-platform type with iOS-specific fields using suffix.
- /// @see https://openiap.dev/docs/types#subscription-offer
+ /// @see https://openiap.dev/docs/types/subscription-offer
final List? subscriptionOffers;
final String? subscriptionPeriodNumberIOS;
final SubscriptionPeriodIOS? subscriptionPeriodUnitIOS;
@@ -3915,8 +3916,7 @@ class SubscriptionInfoIOS {
/// - iOS: Introductory offers, promotional offers with server-side signatures
/// - Android: Offer tokens with pricing phases
///
-/// @see https://openiap.dev/docs/types/ios#discount-offer
-/// @see https://openiap.dev/docs/types/android#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class SubscriptionOffer {
const SubscriptionOffer({
this.basePlanIdAndroid,
@@ -4042,7 +4042,7 @@ class SubscriptionOffer {
/// iOS subscription offer details.
/// @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-/// @see https://openiap.dev/docs/types#subscription-offer
+/// @see https://openiap.dev/docs/types/subscription-offer
class SubscriptionOfferIOS {
const SubscriptionOfferIOS({
required this.displayPrice,
diff --git a/packages/gql/src/generated/types.gd b/packages/gql/src/generated/types.gd
index 6322882b8..17db2f5a1 100644
--- a/packages/gql/src/generated/types.gd
+++ b/packages/gql/src/generated/types.gd
@@ -1005,7 +1005,7 @@ class DiscountDisplayInfoAndroid:
dict["discountAmount"] = discount_amount
return dict
-## Discount information returned from the store. @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#subscription-offer
+## Discount information returned from the store. @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types/subscription-offer
class DiscountIOS:
var identifier: String = ""
var type: String = ""
@@ -1056,7 +1056,7 @@ class DiscountIOS:
dict["localizedPrice"] = localized_price
return dict
-## Standardized one-time product discount offer. Provides a unified interface for one-time purchase discounts across platforms. Currently supported on Android (Google Play Billing 8.0+). iOS does not support one-time purchase discounts in the same way. @see https://openiap.dev/docs/features/discount
+## Standardized one-time product discount offer. Provides a platform-neutral OpenIAP shape for Google Play one-time product purchase options and offers. Currently populated only on Android (Google Play Billing 8.0+). iOS does not populate this type. @see https://openiap.dev/docs/features/discount
class DiscountOffer:
## Unique identifier for the offer.
var id: Variant = null
@@ -1078,7 +1078,7 @@ class DiscountOffer:
var percentage_discount_android: Variant = null
## [Android] Fixed discount amount in micro-units.
var discount_amount_micros_android: Variant = null
- ## [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ ## [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
var formatted_discount_amount_android: Variant = null
## [Android] Valid time window for the offer.
var valid_time_window_android: ValidTimeWindowAndroid
@@ -1190,7 +1190,7 @@ class DiscountOffer:
dict["purchaseOptionIdAndroid"] = purchase_option_id_android
return dict
-## iOS DiscountOffer (output type). @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#subscription-offer
+## iOS DiscountOffer (output type). @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types/subscription-offer
class DiscountOfferIOS:
## Discount identifier
var identifier: String = ""
@@ -1612,7 +1612,7 @@ class ProductAndroid:
var name_android: String = ""
## Product-level status code indicating fetch result (Android 8.0+)
var product_status_android: Variant = null
- ## Standardized discount offers for one-time products.
+ ## Standardized Android one-time product purchase options and offers.
var discount_offers: Array[DiscountOffer] = []
## Standardized subscription offers.
var subscription_offers: Array[SubscriptionOffer] = []
@@ -1766,7 +1766,7 @@ class ProductAndroid:
dict["subscriptionOfferDetailsAndroid"] = null
return dict
-## One-time purchase offer details (Android). Available in Google Play Billing Library 8.0+ @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#discount-offer
+## One-time purchase offer details (Android). Available in Google Play Billing Library 8.0+ @deprecated Use the standardized DiscountOffer type for Android one-time offers. @see https://openiap.dev/docs/types/discount-offer
class ProductAndroidOneTimePurchaseOfferDetail:
## Offer ID
var offer_id: Variant = null
@@ -2034,11 +2034,11 @@ class ProductSubscriptionAndroid:
var name_android: String = ""
## Product-level status code indicating fetch result (Android 8.0+)
var product_status_android: Variant = null
- ## Standardized discount offers for one-time products.
+ ## Nullable compatibility field. Google Play does not return one-time purchase
var discount_offers: Array[DiscountOffer] = []
## Standardized subscription offers.
var subscription_offers: Array[SubscriptionOffer] = []
- ## One-time purchase offer details including discounts (Android)
+ ## Legacy nullable compatibility field. Google Play does not populate one-time
var one_time_purchase_offer_details_android: Array[ProductAndroidOneTimePurchaseOfferDetail] = []
## @deprecated Use subscriptionOffers instead for cross-platform compatibility.
var subscription_offer_details_android: Array[ProductSubscriptionAndroidOfferDetails] = []
@@ -2188,7 +2188,7 @@ class ProductSubscriptionAndroid:
dict["subscriptionOfferDetailsAndroid"] = null
return dict
-## Subscription offer details (Android). @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#subscription-offer
+## Subscription offer details (Android). @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types/subscription-offer
class ProductSubscriptionAndroidOfferDetails:
var base_plan_id: String = ""
var offer_id: Variant = null
@@ -3281,7 +3281,7 @@ class SubscriptionInfoIOS:
dict["subscriptionPeriod"] = subscription_period
return dict
-## Standardized subscription discount/promotional offer. Provides a unified interface for subscription offers across iOS and Android. Both platforms support subscription offers with different implementations: - iOS: Introductory offers, promotional offers with server-side signatures - Android: Offer tokens with pricing phases @see https://openiap.dev/docs/types/ios#discount-offer @see https://openiap.dev/docs/types/android#subscription-offer
+## Standardized subscription discount/promotional offer. Provides a unified interface for subscription offers across iOS and Android. Both platforms support subscription offers with different implementations: - iOS: Introductory offers, promotional offers with server-side signatures - Android: Offer tokens with pricing phases @see https://openiap.dev/docs/types/subscription-offer
class SubscriptionOffer:
## Unique identifier for the offer.
var id: String = ""
@@ -3435,7 +3435,7 @@ class SubscriptionOffer:
dict["installmentPlanDetailsAndroid"] = installment_plan_details_android
return dict
-## iOS subscription offer details. @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types#subscription-offer
+## iOS subscription offer details. @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility. @see https://openiap.dev/docs/types/subscription-offer
class SubscriptionOfferIOS:
var display_price: String = ""
var id: String = ""
diff --git a/packages/gql/src/generated/types.ts b/packages/gql/src/generated/types.ts
index 25c7de813..5866e2db8 100644
--- a/packages/gql/src/generated/types.ts
+++ b/packages/gql/src/generated/types.ts
@@ -328,7 +328,7 @@ export interface DiscountDisplayInfoAndroid {
/**
* Discount information returned from the store.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface DiscountIOS {
identifier: string;
@@ -343,10 +343,11 @@ export interface DiscountIOS {
/**
* Standardized one-time product discount offer.
- * Provides a unified interface for one-time purchase discounts across platforms.
+ * Provides a platform-neutral OpenIAP shape for Google Play one-time product
+ * purchase options and offers.
*
- * Currently supported on Android (Google Play Billing 8.0+).
- * iOS does not support one-time purchase discounts in the same way.
+ * Currently populated only on Android (Google Play Billing 8.0+).
+ * iOS does not populate this type.
*
* @see https://openiap.dev/docs/features/discount
*/
@@ -360,7 +361,7 @@ export interface DiscountOffer {
discountAmountMicrosAndroid?: (string | null);
/** Formatted display price string (e.g., "$4.99") */
displayPrice: string;
- /** [Android] Formatted discount amount string (e.g., "$5.00 OFF"). */
+ /** [Android] Formatted discount amount including its currency sign (e.g., "$5.00"). */
formattedDiscountAmountAndroid?: (string | null);
/**
* [Android] Original full price in micro-units before discount.
@@ -418,7 +419,7 @@ export interface DiscountOffer {
/**
* iOS DiscountOffer (output type).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface DiscountOfferIOS {
/** Discount identifier */
@@ -1093,9 +1094,9 @@ export interface ProductAndroid extends ProductCommon {
debugDescription?: (string | null);
description: string;
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Standardized Android one-time product purchase options and offers.
+ * Native metadata uses Android-suffixed fields.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
discountOffers?: (DiscountOffer[] | null);
displayName?: (string | null);
@@ -1105,7 +1106,7 @@ export interface ProductAndroid extends ProductCommon {
/**
* One-time purchase offer details including discounts (Android)
* Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
+ * @deprecated Use the standardized discountOffers field instead.
* @deprecated Use discountOffers instead
*/
oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
@@ -1127,7 +1128,7 @@ export interface ProductAndroid extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
title: string;
@@ -1137,8 +1138,8 @@ export interface ProductAndroid extends ProductCommon {
/**
* One-time purchase offer details (Android).
* Available in Google Play Billing Library 8.0+
- * @deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#discount-offer
+ * @deprecated Use the standardized DiscountOffer type for Android one-time offers.
+ * @see https://openiap.dev/docs/types/discount-offer
*/
export interface ProductAndroidOneTimePurchaseOfferDetail {
/**
@@ -1218,7 +1219,7 @@ export interface ProductIOS extends ProductCommon {
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
* Note: iOS does not support one-time product discounts.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
title: string;
@@ -1250,9 +1251,8 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
debugDescription?: (string | null);
description: string;
/**
- * Standardized discount offers for one-time products.
- * Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#discount-offer
+ * Nullable compatibility field. Google Play does not return one-time purchase
+ * offer details for subscription products; use subscriptionOffers below.
*/
discountOffers?: (DiscountOffer[] | null);
displayName?: (string | null);
@@ -1260,10 +1260,11 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
id: string;
nameAndroid: string;
/**
- * One-time purchase offer details including discounts (Android)
- * Returns all eligible offers. Available in Google Play Billing Library 8.0+
- * @deprecated Use discountOffers instead for cross-platform compatibility.
- * @deprecated Use discountOffers instead
+ * Legacy nullable compatibility field. Google Play does not populate one-time
+ * purchase offer details for subscription products.
+ * @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ * subscriptions use subscriptionOffers.
+ * @deprecated Use subscriptionOffers instead
*/
oneTimePurchaseOfferDetailsAndroid?: (ProductAndroidOneTimePurchaseOfferDetail[] | null);
platform: 'android';
@@ -1284,7 +1285,7 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with Android-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers: SubscriptionOffer[];
title: string;
@@ -1294,7 +1295,7 @@ export interface ProductSubscriptionAndroid extends ProductCommon {
/**
* Subscription offer details (Android).
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface ProductSubscriptionAndroidOfferDetails {
basePlanId: string;
@@ -1347,7 +1348,7 @@ export interface ProductSubscriptionIOS extends ProductCommon {
/**
* Standardized subscription offers.
* Cross-platform type with iOS-specific fields using suffix.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
subscriptionOffers?: (SubscriptionOffer[] | null);
subscriptionPeriodNumberIOS?: (string | null);
@@ -2126,8 +2127,7 @@ export interface SubscriptionInfoIOS {
* - iOS: Introductory offers, promotional offers with server-side signatures
* - Android: Offer tokens with pricing phases
*
- * @see https://openiap.dev/docs/types/ios#discount-offer
- * @see https://openiap.dev/docs/types/android#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface SubscriptionOffer {
/**
@@ -2202,7 +2202,7 @@ export interface SubscriptionOffer {
/**
* iOS subscription offer details.
* @deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
- * @see https://openiap.dev/docs/types#subscription-offer
+ * @see https://openiap.dev/docs/types/subscription-offer
*/
export interface SubscriptionOfferIOS {
displayPrice: string;
diff --git a/packages/gql/src/type-android.graphql b/packages/gql/src/type-android.graphql
index 373c8d5d5..fc2ad015c 100644
--- a/packages/gql/src/type-android.graphql
+++ b/packages/gql/src/type-android.graphql
@@ -138,8 +138,8 @@ type DiscountDisplayInfoAndroid {
"""
One-time purchase offer details (Android).
Available in Google Play Billing Library 8.0+
-@deprecated Use the standardized DiscountOffer type instead for cross-platform compatibility.
-@see https://openiap.dev/docs/types#discount-offer
+@deprecated Use the standardized DiscountOffer type for Android one-time offers.
+@see https://openiap.dev/docs/types/discount-offer
"""
type ProductAndroidOneTimePurchaseOfferDetail
@deprecated(reason: "Use DiscountOffer type instead") {
@@ -217,7 +217,7 @@ type InstallmentPlanDetailsAndroid {
"""
Subscription offer details (Android).
@deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-@see https://openiap.dev/docs/types#subscription-offer
+@see https://openiap.dev/docs/types/subscription-offer
"""
type ProductSubscriptionAndroidOfferDetails
@deprecated(reason: "Use SubscriptionOffer type instead") {
@@ -258,18 +258,18 @@ type ProductAndroid implements ProductCommon {
"""
productStatusAndroid: ProductStatusAndroid
- # Standardized cross-platform fields
+ # Standardized offer fields
"""
- Standardized discount offers for one-time products.
- Cross-platform type with Android-specific fields using suffix.
- @see https://openiap.dev/docs/types#discount-offer
+ Standardized Android one-time product purchase options and offers.
+ Native metadata uses Android-suffixed fields.
+ @see https://openiap.dev/docs/types/discount-offer
"""
discountOffers: [DiscountOffer!]
"""
Standardized subscription offers.
Cross-platform type with Android-specific fields using suffix.
- @see https://openiap.dev/docs/types#subscription-offer
+ @see https://openiap.dev/docs/types/subscription-offer
"""
subscriptionOffers: [SubscriptionOffer!]
@@ -277,7 +277,7 @@ type ProductAndroid implements ProductCommon {
"""
One-time purchase offer details including discounts (Android)
Returns all eligible offers. Available in Google Play Billing Library 8.0+
- @deprecated Use discountOffers instead for cross-platform compatibility.
+ @deprecated Use the standardized discountOffers field instead.
"""
oneTimePurchaseOfferDetailsAndroid: [ProductAndroidOneTimePurchaseOfferDetail!]
@deprecated(reason: "Use discountOffers instead")
@@ -312,29 +312,29 @@ type ProductSubscriptionAndroid implements ProductCommon {
"""
productStatusAndroid: ProductStatusAndroid
- # Standardized cross-platform fields
+ # Standardized offer fields
"""
- Standardized discount offers for one-time products.
- Cross-platform type with Android-specific fields using suffix.
- @see https://openiap.dev/docs/types#discount-offer
+ Nullable compatibility field. Google Play does not return one-time purchase
+ offer details for subscription products; use subscriptionOffers below.
"""
discountOffers: [DiscountOffer!]
"""
Standardized subscription offers.
Cross-platform type with Android-specific fields using suffix.
- @see https://openiap.dev/docs/types#subscription-offer
+ @see https://openiap.dev/docs/types/subscription-offer
"""
subscriptionOffers: [SubscriptionOffer!]!
# Deprecated platform-specific fields
"""
- One-time purchase offer details including discounts (Android)
- Returns all eligible offers. Available in Google Play Billing Library 8.0+
- @deprecated Use discountOffers instead for cross-platform compatibility.
+ Legacy nullable compatibility field. Google Play does not populate one-time
+ purchase offer details for subscription products.
+ @deprecated One-time offers belong to ProductAndroid.discountOffers;
+ subscriptions use subscriptionOffers.
"""
oneTimePurchaseOfferDetailsAndroid: [ProductAndroidOneTimePurchaseOfferDetail!]
- @deprecated(reason: "Use discountOffers instead")
+ @deprecated(reason: "Use subscriptionOffers instead")
"""
@deprecated Use subscriptionOffers instead for cross-platform compatibility.
"""
diff --git a/packages/gql/src/type-ios.graphql b/packages/gql/src/type-ios.graphql
index 0c2e85c0c..52a081a46 100644
--- a/packages/gql/src/type-ios.graphql
+++ b/packages/gql/src/type-ios.graphql
@@ -61,7 +61,7 @@ type SubscriptionPeriodValueIOS {
"""
iOS subscription offer details.
@deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-@see https://openiap.dev/docs/types#subscription-offer
+@see https://openiap.dev/docs/types/subscription-offer
"""
type SubscriptionOfferIOS
@deprecated(reason: "Use SubscriptionOffer type instead") {
@@ -122,7 +122,7 @@ type ProductIOS implements ProductCommon {
Standardized subscription offers.
Cross-platform type with iOS-specific fields using suffix.
Note: iOS does not support one-time product discounts.
- @see https://openiap.dev/docs/types#subscription-offer
+ @see https://openiap.dev/docs/types/subscription-offer
"""
subscriptionOffers: [SubscriptionOffer!]
@@ -164,7 +164,7 @@ type ProductSubscriptionIOS implements ProductCommon {
"""
Standardized subscription offers.
Cross-platform type with iOS-specific fields using suffix.
- @see https://openiap.dev/docs/types#subscription-offer
+ @see https://openiap.dev/docs/types/subscription-offer
"""
subscriptionOffers: [SubscriptionOffer!]
@@ -205,7 +205,7 @@ type ProductSubscriptionIOS implements ProductCommon {
"""
Discount information returned from the store.
@deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-@see https://openiap.dev/docs/types#subscription-offer
+@see https://openiap.dev/docs/types/subscription-offer
"""
type DiscountIOS @deprecated(reason: "Use SubscriptionOffer type instead") {
identifier: String!
@@ -536,7 +536,7 @@ type SubscriptionStatusIOS {
"""
iOS DiscountOffer (output type).
@deprecated Use the standardized SubscriptionOffer type instead for cross-platform compatibility.
-@see https://openiap.dev/docs/types#subscription-offer
+@see https://openiap.dev/docs/types/subscription-offer
"""
type DiscountOfferIOS
@deprecated(reason: "Use SubscriptionOffer type instead") {
diff --git a/packages/gql/src/type.graphql b/packages/gql/src/type.graphql
index a63d5e56d..46b2b22ff 100644
--- a/packages/gql/src/type.graphql
+++ b/packages/gql/src/type.graphql
@@ -602,10 +602,11 @@ type SubscriptionPeriod {
"""
Standardized one-time product discount offer.
-Provides a unified interface for one-time purchase discounts across platforms.
+Provides a platform-neutral OpenIAP shape for Google Play one-time product
+purchase options and offers.
-Currently supported on Android (Google Play Billing 8.0+).
-iOS does not support one-time purchase discounts in the same way.
+Currently populated only on Android (Google Play Billing 8.0+).
+iOS does not populate this type.
@see https://openiap.dev/docs/features/discount
"""
@@ -672,7 +673,7 @@ type DiscountOffer {
discountAmountMicrosAndroid: String
"""
- [Android] Formatted discount amount string (e.g., "$5.00 OFF").
+ [Android] Formatted discount amount including its currency sign (e.g., "$5.00").
"""
formattedDiscountAmountAndroid: String
@@ -715,8 +716,7 @@ Both platforms support subscription offers with different implementations:
- iOS: Introductory offers, promotional offers with server-side signatures
- Android: Offer tokens with pricing phases
-@see https://openiap.dev/docs/types/ios#discount-offer
-@see https://openiap.dev/docs/types/android#subscription-offer
+@see https://openiap.dev/docs/types/subscription-offer
"""
type SubscriptionOffer {
"""
diff --git a/scripts/audit-docs.test.ts b/scripts/audit-docs.test.ts
index 5fe9ce83c..0f865f522 100644
--- a/scripts/audit-docs.test.ts
+++ b/scripts/audit-docs.test.ts
@@ -1,5 +1,34 @@
import { describe, expect, test } from 'bun:test';
-import { auditActiveCodeExampleSource } from './audit-docs';
+import {
+ auditActiveCodeExampleSource,
+ auditCanonicalOfferDocs,
+ type CanonicalOfferDocsSources,
+} from './audit-docs';
+
+const VALID_DISCOUNT_OFFER_TYPE_BLOCKS = `{\`
+type DiscountOfferType = 'introductory' | 'promotional' | 'one-time';
+\`}
+{\`
+enum DiscountOfferType: String {
+ case introductory = "introductory"
+ case promotional = "promotional"
+ case oneTime = "one-time"
+}
+\`}
+{\`
+enum class DiscountOfferType(val rawValue: String) {
+ Introductory("introductory"),
+ Promotional("promotional"),
+ OneTime("one-time")
+}
+\`}
+{\`
+enum DiscountOfferType {
+ Introductory('introductory'),
+ Promotional('promotional'),
+ OneTime('one-time');
+}
+\`}`;
describe('active docs code-example audit', () => {
test('flags recurring cross-language phantom patterns', () => {
@@ -61,4 +90,280 @@ props.sku = "premium"\`}`;
expect.objectContaining({ rule: 'R11', line: 1 }),
]);
});
+
+ test('flags obsolete Kotlin and KMP requestPurchase named arguments', () => {
+ const source = [
+ '{`iapStore.requestPurchase(activity = activity, props = request)`}',
+ '{`kmpIAP.requestPurchase(props = request)`}',
+ ].join('\n');
+
+ expect(auditActiveCodeExampleSource('/tmp/active.tsx', source)).toEqual([
+ expect.objectContaining({ rule: 'R11' }),
+ expect.objectContaining({ rule: 'R11' }),
+ ]);
+ });
+
+ test('accepts current Kotlin and KMP requestPurchase calls', () => {
+ const source = [
+ '{`iapStore.requestPurchase(request)`}',
+ '{`kmpIAP.requestPurchase(RequestPurchaseProps(...))`}',
+ ].join('\n');
+
+ expect(auditActiveCodeExampleSource('/tmp/active.tsx', source)).toEqual([]);
+ });
+});
+
+const validOfferDocsSources = (
+ overrides: Partial> = {}
+): CanonicalOfferDocsSources => ({
+ discountOffer: {
+ file: '/tmp/discount-offer.tsx',
+ source:
+ overrides.discountOffer ??
+ `
DiscountOffer represents one-time products on Android via ProductDetails.OneTimePurchaseOfferDetails.