From 9284b554e155c87bcf64db90665482fff22da066 Mon Sep 17 00:00:00 2001 From: Hyo Date: Tue, 9 Dec 2025 03:31:32 +0900 Subject: [PATCH 1/4] feat: deprecate platform field and ios/android props - Add IapStore enum (Unknown, Apple, Google, Horizon) - Add store field to PurchaseCommon interface - Deprecate platform field in favor of store - Deprecate ios/android props in requestPurchase/requestSubscription - Add apple/google props as replacements - Update all example codes and documentation - Add v1.3.0 release notes with migration guide --- .../Sources/Helpers/StoreKitTypesBridge.swift | 1 + packages/apple/Sources/Models/Types.swift | 58 ++++- .../apple/Sources/OpenIapModule+ObjC.swift | 1 + packages/apple/Sources/OpenIapModule.swift | 14 +- .../VerifyPurchaseWithProviderTests.swift | 2 +- packages/docs/src/pages/docs/apis.tsx | 16 +- .../pages/docs/features/external-purchase.tsx | 4 +- .../docs/src/pages/docs/features/purchase.tsx | 4 +- .../subscription-upgrade-downgrade.tsx | 6 +- .../src/pages/docs/features/subscription.tsx | 16 +- packages/docs/src/pages/docs/types.tsx | 94 +++++--- .../docs/src/pages/docs/updates/notes.tsx | 69 ++++++ .../hyo/openiap/utils/BillingConverters.kt | 2 + .../src/main/java/dev/hyo/openiap/Types.kt | 101 ++++++-- .../utils/PurchaseVerificationValidator.kt | 10 +- .../hyo/openiap/utils/BillingConverters.kt | 6 +- .../gql/scripts/generate-kotlin-types.mjs | 15 +- packages/gql/src/generated/Types.kt | 227 +++++++++++------- packages/gql/src/generated/Types.swift | 58 ++++- packages/gql/src/generated/types.dart | 125 +++++++--- packages/gql/src/generated/types.ts | 40 ++- packages/gql/src/type-android.graphql | 9 +- packages/gql/src/type-ios.graphql | 9 +- packages/gql/src/type.graphql | 48 +++- 24 files changed, 687 insertions(+), 248 deletions(-) diff --git a/packages/apple/Sources/Helpers/StoreKitTypesBridge.swift b/packages/apple/Sources/Helpers/StoreKitTypesBridge.swift index 1f84ebb8..dd4f9d7b 100644 --- a/packages/apple/Sources/Helpers/StoreKitTypesBridge.swift +++ b/packages/apple/Sources/Helpers/StoreKitTypesBridge.swift @@ -157,6 +157,7 @@ enum StoreKitTypesBridge { renewalInfoIOS: renewalInfoIOS, revocationDateIOS: revocationDate, revocationReasonIOS: transaction.revocationReason?.rawValue.description, + store: .apple, storefrontCountryCodeIOS: { if #available(iOS 17.0, tvOS 17.0, watchOS 10.0, *) { transaction.storefront.countryCode diff --git a/packages/apple/Sources/Models/Types.swift b/packages/apple/Sources/Models/Types.swift index f56eaaa3..28d56a4d 100644 --- a/packages/apple/Sources/Models/Types.swift +++ b/packages/apple/Sources/Models/Types.swift @@ -181,16 +181,18 @@ public enum IapkitPurchaseState: String, Codable, CaseIterable { case inauthentic = "inauthentic" } -public enum IapkitStore: String, Codable, CaseIterable { - case apple = "apple" - case google = "google" -} - public enum IapPlatform: String, Codable, CaseIterable { case ios = "ios" case android = "android" } +public enum IapStore: String, Codable, CaseIterable { + case unknown = "unknown" + case apple = "apple" + case google = "google" + case horizon = "horizon" +} + public enum PaymentModeIOS: String, Codable, CaseIterable { case empty = "empty" case freeTrial = "free-trial" @@ -266,12 +268,15 @@ public protocol PurchaseCommon: Codable { var id: String { get } var ids: [String]? { get } var isAutoRenewing: Bool { get } + /// @deprecated Use store instead var platform: IapPlatform { get } var productId: String { get } var purchaseState: PurchaseState { get } /// Unified purchase token (iOS JWS, Android purchaseToken) var purchaseToken: String? { get } var quantity: Int { get } + /// Store where purchase was made + var store: IapStore { get } var transactionDate: Double { get } } @@ -487,12 +492,15 @@ public struct PurchaseAndroid: Codable, PurchaseCommon { public var obfuscatedAccountIdAndroid: String? public var obfuscatedProfileIdAndroid: String? public var packageNameAndroid: String? + /// @deprecated Use store instead public var platform: IapPlatform public var productId: String public var purchaseState: PurchaseState public var purchaseToken: String? public var quantity: Int public var signatureAndroid: String? + /// Store where purchase was made + public var store: IapStore public var transactionDate: Double public var transactionId: String? } @@ -520,6 +528,7 @@ public struct PurchaseIOS: Codable, PurchaseCommon { public var originalTransactionDateIOS: Double? public var originalTransactionIdentifierIOS: String? public var ownershipTypeIOS: String? + /// @deprecated Use store instead public var platform: IapPlatform public var productId: String public var purchaseState: PurchaseState @@ -531,6 +540,8 @@ public struct PurchaseIOS: Codable, PurchaseCommon { public var renewalInfoIOS: RenewalInfoIOS? public var revocationDateIOS: Double? public var revocationReasonIOS: String? + /// Store where purchase was made + public var store: IapStore public var storefrontCountryCodeIOS: String? public var subscriptionGroupIdIOS: String? public var transactionDate: Double @@ -591,7 +602,7 @@ public struct RequestVerifyPurchaseWithIapkitResult: Codable { public var isValid: Bool /// The current state of the purchase. public var state: IapkitPurchaseState - public var store: IapkitStore + public var store: IapStore } public struct SubscriptionInfoIOS: Codable { @@ -924,16 +935,24 @@ public struct RequestPurchaseProps: Codable { } public struct RequestPurchasePropsByPlatforms: Codable { - /// Android-specific purchase parameters + /// @deprecated Use google instead public var android: RequestPurchaseAndroidProps? - /// iOS-specific purchase parameters + /// Apple-specific purchase parameters + public var apple: RequestPurchaseIosProps? + /// Google-specific purchase parameters + public var google: RequestPurchaseAndroidProps? + /// @deprecated Use apple instead public var ios: RequestPurchaseIosProps? public init( android: RequestPurchaseAndroidProps? = nil, + apple: RequestPurchaseIosProps? = nil, + google: RequestPurchaseAndroidProps? = nil, ios: RequestPurchaseIosProps? = nil ) { self.android = android + self.apple = apple + self.google = google self.ios = ios } } @@ -996,16 +1015,24 @@ public struct RequestSubscriptionIosProps: Codable { } public struct RequestSubscriptionPropsByPlatforms: Codable { - /// Android-specific subscription parameters + /// @deprecated Use google instead public var android: RequestSubscriptionAndroidProps? - /// iOS-specific subscription parameters + /// Apple-specific subscription parameters + public var apple: RequestSubscriptionIosProps? + /// Google-specific subscription parameters + public var google: RequestSubscriptionAndroidProps? + /// @deprecated Use apple instead public var ios: RequestSubscriptionIosProps? public init( android: RequestSubscriptionAndroidProps? = nil, + apple: RequestSubscriptionIosProps? = nil, + google: RequestSubscriptionAndroidProps? = nil, ios: RequestSubscriptionIosProps? = nil ) { self.android = android + self.apple = apple + self.google = google self.ios = ios } } @@ -1339,6 +1366,7 @@ public enum Purchase: Codable, PurchaseCommon { } } + /// @deprecated Use store instead public var platform: IapPlatform { switch self { case let .purchaseAndroid(value): @@ -1385,6 +1413,16 @@ public enum Purchase: Codable, PurchaseCommon { } } + /// Store where purchase was made + public var store: IapStore { + switch self { + case let .purchaseAndroid(value): + return value.store + case let .purchaseIos(value): + return value.store + } + } + public var transactionDate: Double { switch self { case let .purchaseAndroid(value): diff --git a/packages/apple/Sources/OpenIapModule+ObjC.swift b/packages/apple/Sources/OpenIapModule+ObjC.swift index 3737ca92..6746e6f0 100644 --- a/packages/apple/Sources/OpenIapModule+ObjC.swift +++ b/packages/apple/Sources/OpenIapModule+ObjC.swift @@ -297,6 +297,7 @@ import StoreKit reasonStringRepresentationIOS: nil, revocationDateIOS: nil, revocationReasonIOS: nil, + store: .apple, storefrontCountryCodeIOS: nil, subscriptionGroupIdIOS: nil, transactionDate: Date().timeIntervalSince1970, diff --git a/packages/apple/Sources/OpenIapModule.swift b/packages/apple/Sources/OpenIapModule.swift index e317bb4d..8c30fff6 100644 --- a/packages/apple/Sources/OpenIapModule.swift +++ b/packages/apple/Sources/OpenIapModule.swift @@ -653,7 +653,7 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol { guard props.apple != nil else { throw makePurchaseError(code: .developerError, message: "IAPKit verification on Apple requires an apple payload") } - let store: IapkitStore = .apple + let store: IapStore = .apple let body = try buildIapkitPayload(props: props, store: store) var request = URLRequest(url: url) @@ -716,22 +716,22 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol { let normalizedState = stateString.lowercased().replacingOccurrences(of: "_", with: "-") let parsedState = IapkitPurchaseState(rawValue: normalizedState) ?? .unknown let storeString = json["store"] as? String - let parsedStore = storeString.flatMap { IapkitStore(rawValue: $0) } ?? store + let parsedStore = storeString.flatMap { IapStore(rawValue: $0) } ?? store OpenIapLog.info("IAPKit verification result: store=\(parsedStore.rawValue), isValid=\(isValid), state=\(parsedState.rawValue)") return RequestVerifyPurchaseWithIapkitResult(isValid: isValid, state: parsedState, store: parsedStore) } private struct IapkitApplePayload: Codable { - let store: IapkitStore + let store: IapStore let jws: String } private struct IapkitGooglePayload: Codable { - let store: IapkitStore + let store: IapStore let purchaseToken: String } - private func buildIapkitPayload(props: RequestVerifyPurchaseWithIapkitProps, store: IapkitStore) throws -> Data { + private func buildIapkitPayload(props: RequestVerifyPurchaseWithIapkitProps, store: IapStore) throws -> Data { let encoder = JSONEncoder() encoder.outputFormatting = [.withoutEscapingSlashes] switch store { @@ -747,7 +747,7 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol { jws: apple.jws ) return try encoder.encode(payload) - case .google: + case .google, .horizon: guard let google = props.google else { throw makePurchaseError(code: .developerError, message: "Google verification parameters are required") } @@ -759,6 +759,8 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol { purchaseToken: google.purchaseToken ) return try encoder.encode(payload) + case .unknown: + throw makePurchaseError(code: .developerError, message: "Unknown store type") } } diff --git a/packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift b/packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift index 0ad6c88a..deeb3f74 100644 --- a/packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift +++ b/packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift @@ -40,7 +40,7 @@ final class VerifyPurchaseWithProviderTests: XCTestCase { let result = try await store.verifyPurchaseWithProvider(props) XCTAssertNotNil(result) - XCTAssertEqual(IapkitStore.apple, result?.store) + XCTAssertEqual(IapStore.apple, result?.store) XCTAssertEqual(true, result?.isValid) XCTAssertEqual(.entitled, result?.state) } diff --git a/packages/docs/src/pages/docs/apis.tsx b/packages/docs/src/pages/docs/apis.tsx index 1cc155f6..182be877 100644 --- a/packages/docs/src/pages/docs/apis.tsx +++ b/packages/docs/src/pages/docs/apis.tsx @@ -595,7 +595,7 @@ if (subscription?.renewalInfoIOS?.willAutoRenew === false) { try { await requestPurchase({ - request: { ios: { sku: 'premium_monthly' } }, + request: { apple: { sku: 'premium_monthly' } }, type: 'subs' }); } catch (error) { @@ -621,7 +621,7 @@ if let subscription = subscriptions.first { try await OpenIapModule.shared.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - ios: RequestPurchaseIosProps(sku: "premium_monthly") + apple: RequestPurchaseIosProps(sku: "premium_monthly") ), type: .subs ) @@ -645,7 +645,7 @@ subscriptions.firstOrNull()?.let { subscription -> openIapStore.requestPurchase( RequestPurchaseProps( request = RequestPurchasePropsByPlatforms( - android = RequestPurchaseAndroidProps( + google = RequestPurchaseAndroidProps( skus = listOf("premium_monthly") ) ), @@ -671,8 +671,8 @@ if (subscription?.renewalInfoIOS?.willAutoRenew == false) { await FlutterInappPurchase.instance.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - ios: RequestPurchaseIosProps(sku: 'premium_monthly'), - android: RequestPurchaseAndroidProps(skus: ['premium_monthly']), + apple: RequestPurchaseIosProps(sku: 'premium_monthly'), + google: RequestPurchaseAndroidProps(skus: ['premium_monthly']), ), type: ProductType.subs, ), @@ -2128,15 +2128,15 @@ const handlePurchase = async (basePlanId: string) => { purchasedBasePlanId = basePlanId; await requestPurchase({ - request: { - android: { + requestSubscription: { + google: { skus: [subscriptionGroupId], subscriptionOffers: [ { sku: subscriptionGroupId, offerToken: offer.offerToken }, ], }, }, - type: 'subs', + type: 'Subs', }); }; diff --git a/packages/docs/src/pages/docs/features/external-purchase.tsx b/packages/docs/src/pages/docs/features/external-purchase.tsx index 6b8f52f2..2ea8b28f 100644 --- a/packages/docs/src/pages/docs/features/external-purchase.tsx +++ b/packages/docs/src/pages/docs/features/external-purchase.tsx @@ -832,7 +832,7 @@ const purchaseSubscription = purchaseUpdatedListener( async function handleUserChoicePurchase(productId: string) { try { await requestPurchase({ - android: { skus: [productId] }, + google: { skus: [productId] }, }); // If user selects Google Play → purchaseUpdatedListener callback @@ -910,7 +910,7 @@ suspend fun handleUserChoicePurchase(productId: String) { val props = RequestPurchaseProps( request = RequestPurchaseProps.Request.Purchase( RequestPurchasePropsByPlatforms( - android = RequestPurchaseAndroidProps( + google = RequestPurchaseAndroidProps( skus = listOf(productId) ) ) diff --git a/packages/docs/src/pages/docs/features/purchase.tsx b/packages/docs/src/pages/docs/features/purchase.tsx index cb1ed13e..317d182e 100644 --- a/packages/docs/src/pages/docs/features/purchase.tsx +++ b/packages/docs/src/pages/docs/features/purchase.tsx @@ -276,8 +276,8 @@ const purchaseProduct = async (productId: string) => { try { await requestPurchase({ request: { - ios: { sku: productId }, - android: { skus: [productId] }, + apple: { sku: productId }, + google: { skus: [productId] }, }, type: 'inapp', // 'inapp' for consumables/non-consumables }); diff --git a/packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx b/packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx index e423115d..e8da24ac 100644 --- a/packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx +++ b/packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx @@ -1108,7 +1108,7 @@ if (currentSub != null) { await FlutterInappPurchase.instance.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - android: RequestPurchaseAndroidProps( + google: RequestPurchaseAndroidProps( skus: ['premium_monthly'], purchaseToken: currentSub.purchaseToken, replacementMode: 1, // WITH_TIME_PRORATION @@ -1249,7 +1249,7 @@ if (premiumPurchase != null) { await FlutterInappPurchase.instance.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - android: RequestPurchaseAndroidProps( + google: RequestPurchaseAndroidProps( skus: ['basic_monthly'], purchaseToken: premiumPurchase.purchaseToken, replacementMode: 6, // DEFERRED - Change at renewal @@ -1536,7 +1536,7 @@ Future changeSubscription( await FlutterInappPurchase.instance.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - android: RequestPurchaseAndroidProps( + google: RequestPurchaseAndroidProps( skus: [newSku], purchaseToken: currentSub.purchaseToken, replacementMode: replacementMode, diff --git a/packages/docs/src/pages/docs/features/subscription.tsx b/packages/docs/src/pages/docs/features/subscription.tsx index 91d05133..47127b06 100644 --- a/packages/docs/src/pages/docs/features/subscription.tsx +++ b/packages/docs/src/pages/docs/features/subscription.tsx @@ -353,7 +353,7 @@ const purchaseWithPromoOffer = async ( // 2. Purchase with the promotional offer await requestPurchase({ request: { - ios: { + apple: { sku: subscriptionId, withOffer: { identifier: offerId, @@ -363,7 +363,7 @@ const purchaseWithPromoOffer = async ( timestamp, }, }, - android: { skus: [subscriptionId] }, + google: { skus: [subscriptionId] }, }, type: 'subs', }); @@ -489,8 +489,8 @@ const purchaseSubscription = async (subscriptionId: string) => { // iOS: Base plan with auto-applied intro offer await requestPurchase({ request: { - ios: { sku: subscriptionId }, - android: { skus: [subscriptionId] }, + apple: { sku: subscriptionId }, + google: { skus: [subscriptionId] }, }, type: 'subs', }); @@ -758,8 +758,8 @@ const purchaseSubscription = async (subscriptionId: string) => { await requestPurchase({ request: { - ios: { sku: subscriptionId }, - android: { + apple: { sku: subscriptionId }, + google: { skus: [subscriptionId], subscriptionOffers, // Required for Android }, @@ -894,8 +894,8 @@ const purchaseWithOffer = async ( await requestPurchase({ request: { - ios: { sku: subscriptionId }, - android: { + apple: { sku: subscriptionId }, + google: { skus: [subscriptionId], subscriptionOffers: [{ sku: subscriptionId, diff --git a/packages/docs/src/pages/docs/types.tsx b/packages/docs/src/pages/docs/types.tsx index 2cde1387..0d0fdfee 100644 --- a/packages/docs/src/pages/docs/types.tsx +++ b/packages/docs/src/pages/docs/types.tsx @@ -269,11 +269,22 @@ function Types() { - platform + store + + + Store discriminator: "apple",{' '} + "google", or "horizon" + + + + + platform{' '} + + (deprecated) + - Platform discriminator: "ios" or{' '} - "android" + Use store instead @@ -531,43 +542,57 @@ function Types() {

These types combine platform-specific types with a{' '} - platform discriminator for type-safe handling across iOS - and Android. + store discriminator for type-safe handling across Apple, + Google, and Horizon stores.

- - Platform Discriminators + + Store Discriminators

- Each unified type includes a platform field that - identifies the source platform: + Each unified type includes a store field that identifies + the source store:

- + + + + + + + + +
NameValue Summary
- IosPlatform + "apple" Apple App Store (iOS/macOS)
- Contains platform: 'ios' + "google" Google Play Store (Android)
- AndroidPlatform + "horizon" Meta Horizon Store (Quest)
- Contains platform: 'android' + "unknown" Unknown store (default)
+
+

+ Note: The platform field is deprecated. + Use store instead. +

+
Union Types @@ -764,11 +789,22 @@ function Types() { - platform + store + + + Store discriminator: "apple",{' '} + "google", or "horizon" + + + + + platform{' '} + + (deprecated) + - Platform discriminator: "ios" or{' '} - "android" + Use store instead @@ -1521,8 +1557,8 @@ final allProducts = await FlutterInappPurchase.instance.fetchProducts( {`// Standard in-app purchase await requestPurchase({ params: { - ios: { sku: 'premium' }, - android: { skus: ['premium'] } + apple: { sku: 'premium' }, + google: { skus: ['premium'] } }, type: 'in-app' }); @@ -1530,8 +1566,8 @@ await requestPurchase({ // Subscription purchase await requestPurchase({ params: { - ios: { sku: 'monthly_sub' }, - android: { skus: ['monthly_sub'] } + apple: { sku: 'monthly_sub' }, + google: { skus: ['monthly_sub'] } }, type: 'subs' });`} @@ -1541,7 +1577,7 @@ await requestPurchase({ try await OpenIapModule.shared.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - ios: RequestPurchaseIosProps(sku: "premium") + apple: RequestPurchaseIosProps(sku: "premium") ), type: .inApp ) @@ -1551,7 +1587,7 @@ try await OpenIapModule.shared.requestPurchase( try await OpenIapModule.shared.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - ios: RequestPurchaseIosProps(sku: "monthly_sub") + apple: RequestPurchaseIosProps(sku: "monthly_sub") ), type: .subs ) @@ -1562,7 +1598,7 @@ try await OpenIapModule.shared.requestPurchase( openIapStore.requestPurchase( RequestPurchaseProps( request = RequestPurchasePropsByPlatforms( - android = RequestPurchaseAndroidProps(skus = listOf("premium")) + google = RequestPurchaseAndroidProps(skus = listOf("premium")) ), type = ProductType.InApp ) @@ -1572,7 +1608,7 @@ openIapStore.requestPurchase( openIapStore.requestPurchase( RequestPurchaseProps( request = RequestPurchasePropsByPlatforms( - android = RequestPurchaseAndroidProps(skus = listOf("monthly_sub")) + google = RequestPurchaseAndroidProps(skus = listOf("monthly_sub")) ), type = ProductType.Subs ) @@ -1583,8 +1619,8 @@ openIapStore.requestPurchase( await FlutterInappPurchase.instance.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - ios: RequestPurchaseIosProps(sku: 'premium'), - android: RequestPurchaseAndroidProps(skus: ['premium']), + apple: RequestPurchaseIosProps(sku: 'premium'), + google: RequestPurchaseAndroidProps(skus: ['premium']), ), type: ProductType.inApp, ), @@ -1594,8 +1630,8 @@ await FlutterInappPurchase.instance.requestPurchase( await FlutterInappPurchase.instance.requestPurchase( RequestPurchaseProps( request: RequestPurchasePropsByPlatforms( - ios: RequestPurchaseIosProps(sku: 'monthly_sub'), - android: RequestPurchaseAndroidProps(skus: ['monthly_sub']), + apple: RequestPurchaseIosProps(sku: 'monthly_sub'), + google: RequestPurchaseAndroidProps(skus: ['monthly_sub']), ), type: ProductType.subs, ), diff --git a/packages/docs/src/pages/docs/updates/notes.tsx b/packages/docs/src/pages/docs/updates/notes.tsx index 685ae79d..286a11e0 100644 --- a/packages/docs/src/pages/docs/updates/notes.tsx +++ b/packages/docs/src/pages/docs/updates/notes.tsx @@ -19,6 +19,75 @@ function Notes() {

📝 API & Terminology Changes

+
+

+ 📅 openiap v1.3.0 - Platform Props & Store Field Updates +

+

+ Breaking Changes: +

+
    +
  • + Purchase.platform → Purchase.store - The{' '} + platform field is deprecated. Use store{' '} + instead which returns 'apple' or 'google'. +
  • +
  • + requestPurchase props - The ios and{' '} + android props are deprecated. Use apple{' '} + and google instead. +
  • +
+

+ New Feature: +

+
    +
  • + verifyPurchaseWithProvider - New API for purchase + verification with external providers like IAPKit. Supports both + Apple App Store and Google Play Store. +
  • +
+

+ Migration: +

+ + {`// Before (deprecated) +requestPurchase({ + requestPurchase: { + ios: { sku: 'product_id' }, + android: { skus: ['product_id'] } + } +}) + +// After (recommended) +requestPurchase({ + requestPurchase: { + apple: { sku: 'product_id' }, + google: { skus: ['product_id'] } + } +}) + +// Purchase store field +purchase.store // 'apple' | 'google' +purchase.platform // deprecated`} + +

+ See:{' '} + + verifyPurchaseWithProvider API + +

+
+
IapkitStore.Apple - "Apple" -> IapkitStore.Apple - "google" -> IapkitStore.Google - "Google" -> IapkitStore.Google - else -> throw IllegalArgumentException("Unknown IapkitStore value: $value") - } - } - - fun toJson(): String = rawValue -} - public enum class IapPlatform(val rawValue: String) { Ios("ios"), Android("android"); @@ -323,6 +306,29 @@ public enum class IapPlatform(val rawValue: String) { fun toJson(): String = rawValue } +public enum class IapStore(val rawValue: String) { + Unknown("unknown"), + Apple("apple"), + Google("google"), + Horizon("horizon"); + + companion object { + fun fromJson(value: String): IapStore = when (value) { + "unknown" -> IapStore.Unknown + "Unknown" -> IapStore.Unknown + "apple" -> IapStore.Apple + "Apple" -> IapStore.Apple + "google" -> IapStore.Google + "Google" -> IapStore.Google + "horizon" -> IapStore.Horizon + "Horizon" -> IapStore.Horizon + else -> throw IllegalArgumentException("Unknown IapStore value: $value") + } + } + + fun toJson(): String = rawValue +} + public enum class PaymentModeIOS(val rawValue: String) { Empty("empty"), FreeTrial("free-trial"), @@ -518,6 +524,9 @@ public interface PurchaseCommon { val id: String val ids: List? val isAutoRenewing: Boolean + /** + * @deprecated Use store instead + */ val platform: IapPlatform val productId: String val purchaseState: PurchaseState @@ -526,6 +535,10 @@ public interface PurchaseCommon { */ val purchaseToken: String? val quantity: Int + /** + * Store where purchase was made + */ + val store: IapStore val transactionDate: Double } @@ -1205,12 +1218,19 @@ public data class PurchaseAndroid( val obfuscatedAccountIdAndroid: String? = null, val obfuscatedProfileIdAndroid: String? = null, val packageNameAndroid: String? = null, + /** + * @deprecated Use store instead + */ override val platform: IapPlatform, override val productId: String, override val purchaseState: PurchaseState, override val purchaseToken: String? = null, override val quantity: Int, val signatureAndroid: String? = null, + /** + * Store where purchase was made + */ + override val store: IapStore, override val transactionDate: Double, val transactionId: String? = null ) : PurchaseCommon, Purchase { @@ -1235,6 +1255,7 @@ public data class PurchaseAndroid( purchaseToken = json["purchaseToken"] as String?, quantity = (json["quantity"] as Number).toInt(), signatureAndroid = json["signatureAndroid"] as String?, + store = IapStore.fromJson(json["store"] as String), transactionDate = (json["transactionDate"] as Number).toDouble(), transactionId = json["transactionId"] as String?, ) @@ -1260,6 +1281,7 @@ public data class PurchaseAndroid( "purchaseToken" to purchaseToken, "quantity" to quantity, "signatureAndroid" to signatureAndroid, + "store" to store.toJson(), "transactionDate" to transactionDate, "transactionId" to transactionId, ) @@ -1306,6 +1328,9 @@ public data class PurchaseIOS( val originalTransactionDateIOS: Double? = null, val originalTransactionIdentifierIOS: String? = null, val ownershipTypeIOS: String? = null, + /** + * @deprecated Use store instead + */ override val platform: IapPlatform, override val productId: String, override val purchaseState: PurchaseState, @@ -1317,6 +1342,10 @@ public data class PurchaseIOS( val renewalInfoIOS: RenewalInfoIOS? = null, val revocationDateIOS: Double? = null, val revocationReasonIOS: String? = null, + /** + * Store where purchase was made + */ + override val store: IapStore, val storefrontCountryCodeIOS: String? = null, val subscriptionGroupIdIOS: String? = null, override val transactionDate: Double, @@ -1355,6 +1384,7 @@ public data class PurchaseIOS( renewalInfoIOS = (json["renewalInfoIOS"] as Map?)?.let { RenewalInfoIOS.fromJson(it) }, revocationDateIOS = (json["revocationDateIOS"] as Number?)?.toDouble(), revocationReasonIOS = json["revocationReasonIOS"] as String?, + store = IapStore.fromJson(json["store"] as String), storefrontCountryCodeIOS = json["storefrontCountryCodeIOS"] as String?, subscriptionGroupIdIOS = json["subscriptionGroupIdIOS"] as String?, transactionDate = (json["transactionDate"] as Number).toDouble(), @@ -1394,6 +1424,7 @@ public data class PurchaseIOS( "renewalInfoIOS" to renewalInfoIOS?.toJson(), "revocationDateIOS" to revocationDateIOS, "revocationReasonIOS" to revocationReasonIOS, + "store" to store.toJson(), "storefrontCountryCodeIOS" to storefrontCountryCodeIOS, "subscriptionGroupIdIOS" to subscriptionGroupIdIOS, "transactionDate" to transactionDate, @@ -1546,7 +1577,7 @@ public data class RequestVerifyPurchaseWithIapkitResult( * The current state of the purchase. */ val state: IapkitPurchaseState, - val store: IapkitStore + val store: IapStore ) { companion object { @@ -1554,7 +1585,7 @@ public data class RequestVerifyPurchaseWithIapkitResult( return RequestVerifyPurchaseWithIapkitResult( isValid = json["isValid"] as Boolean, state = IapkitPurchaseState.fromJson(json["state"] as String), - store = IapkitStore.fromJson(json["store"] as String), + store = IapStore.fromJson(json["store"] as String), ) } } @@ -2163,11 +2194,19 @@ public data class RequestPurchaseProps( public data class RequestPurchasePropsByPlatforms( /** - * Android-specific purchase parameters + * @deprecated Use google instead */ val android: RequestPurchaseAndroidProps? = null, /** - * iOS-specific purchase parameters + * Apple-specific purchase parameters + */ + val apple: RequestPurchaseIosProps? = null, + /** + * Google-specific purchase parameters + */ + val google: RequestPurchaseAndroidProps? = null, + /** + * @deprecated Use apple instead */ val ios: RequestPurchaseIosProps? = null ) { @@ -2175,6 +2214,8 @@ public data class RequestPurchasePropsByPlatforms( fun fromJson(json: Map): RequestPurchasePropsByPlatforms { return RequestPurchasePropsByPlatforms( android = (json["android"] as Map?)?.let { RequestPurchaseAndroidProps.fromJson(it) }, + apple = (json["apple"] as Map?)?.let { RequestPurchaseIosProps.fromJson(it) }, + google = (json["google"] as Map?)?.let { RequestPurchaseAndroidProps.fromJson(it) }, ios = (json["ios"] as Map?)?.let { RequestPurchaseIosProps.fromJson(it) }, ) } @@ -2182,6 +2223,8 @@ public data class RequestPurchasePropsByPlatforms( fun toJson(): Map = mapOf( "android" to android?.toJson(), + "apple" to apple?.toJson(), + "google" to google?.toJson(), "ios" to ios?.toJson(), ) } @@ -2271,11 +2314,19 @@ public data class RequestSubscriptionIosProps( public data class RequestSubscriptionPropsByPlatforms( /** - * Android-specific subscription parameters + * @deprecated Use google instead */ val android: RequestSubscriptionAndroidProps? = null, /** - * iOS-specific subscription parameters + * Apple-specific subscription parameters + */ + val apple: RequestSubscriptionIosProps? = null, + /** + * Google-specific subscription parameters + */ + val google: RequestSubscriptionAndroidProps? = null, + /** + * @deprecated Use apple instead */ val ios: RequestSubscriptionIosProps? = null ) { @@ -2283,6 +2334,8 @@ public data class RequestSubscriptionPropsByPlatforms( fun fromJson(json: Map): RequestSubscriptionPropsByPlatforms { return RequestSubscriptionPropsByPlatforms( android = (json["android"] as Map?)?.let { RequestSubscriptionAndroidProps.fromJson(it) }, + apple = (json["apple"] as Map?)?.let { RequestSubscriptionIosProps.fromJson(it) }, + google = (json["google"] as Map?)?.let { RequestSubscriptionAndroidProps.fromJson(it) }, ios = (json["ios"] as Map?)?.let { RequestSubscriptionIosProps.fromJson(it) }, ) } @@ -2290,6 +2343,8 @@ public data class RequestSubscriptionPropsByPlatforms( fun toJson(): Map = mapOf( "android" to android?.toJson(), + "apple" to apple?.toJson(), + "google" to google?.toJson(), "ios" to ios?.toJson(), ) } diff --git a/packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt b/packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt index 3c8c2052..b275c262 100644 --- a/packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt +++ b/packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt @@ -3,7 +3,7 @@ package dev.hyo.openiap.utils import com.google.gson.Gson import com.google.gson.JsonSyntaxException import com.google.gson.reflect.TypeToken -import dev.hyo.openiap.IapkitStore +import dev.hyo.openiap.IapStore import dev.hyo.openiap.OpenIapError import dev.hyo.openiap.OpenIapLog import dev.hyo.openiap.RequestVerifyPurchaseWithIapkitProps @@ -92,7 +92,7 @@ suspend fun verifyPurchaseWithIapkit( throw IllegalArgumentException("IAPKit verification on Android requires google payload") } - val store = IapkitStore.Google + val store = IapStore.Google val payload = buildPayload(props, store) val connection = connectionFactory(endpoint).apply { @@ -168,10 +168,10 @@ suspend fun verifyPurchaseWithIapkit( private fun buildPayload( props: RequestVerifyPurchaseWithIapkitProps, - store: IapkitStore + store: IapStore ): Map { return when (store) { - IapkitStore.Google -> { + IapStore.Google, IapStore.Horizon -> { val google = props.google ?: throw IllegalArgumentException("IAPKit Google verification requires google options") if (google.purchaseToken.isBlank()) { @@ -180,7 +180,7 @@ private fun buildPayload( ) } mutableMapOf( - "store" to store.toJson(), + "store" to store.rawValue, "purchaseToken" to google.purchaseToken ) } diff --git a/packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt b/packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt index 59e5f600..3d68e18d 100644 --- a/packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt +++ b/packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt @@ -2,6 +2,7 @@ package dev.hyo.openiap.utils import dev.hyo.openiap.ActiveSubscription import dev.hyo.openiap.IapPlatform +import dev.hyo.openiap.IapStore import dev.hyo.openiap.PricingPhaseAndroid import dev.hyo.openiap.PricingPhasesAndroid import dev.hyo.openiap.Product @@ -117,8 +118,9 @@ internal object BillingConverters { purchaseToken = purchaseToken, quantity = quantity, signatureAndroid = signature, - transactionId = orderId, - transactionDate = purchaseTime.toDouble() + store = IapStore.Google, + transactionDate = purchaseTime.toDouble(), + transactionId = orderId ) } diff --git a/packages/gql/scripts/generate-kotlin-types.mjs b/packages/gql/scripts/generate-kotlin-types.mjs index 3b5df2d3..69e79c2e 100644 --- a/packages/gql/scripts/generate-kotlin-types.mjs +++ b/packages/gql/scripts/generate-kotlin-types.mjs @@ -485,18 +485,27 @@ const printDataClass = (objectType) => { lines.push(''); return; } + // Collect all fields from interfaces to determine which need 'override' + const interfaceFields = new Set(); + for (const iface of objectType.getInterfaces()) { + for (const fieldName of Object.keys(iface.getFields())) { + interfaceFields.add(fieldName); + } + } const fieldInfos = fields.map((field) => { const { type, nullable, metadata } = getKotlinType(field.type); const propertyType = type + (nullable ? '?' : ''); const propertyName = escapeKotlinName(field.name); const defaultValue = nullable ? ' = null' : ''; - return { field, propertyName, propertyType, defaultValue, metadata }; + const needsOverride = interfaceFields.has(field.name); + return { field, propertyName, propertyType, defaultValue, metadata, needsOverride }; }); lines.push(`public data class ${objectType.name}(`); - fieldInfos.forEach(({ field, propertyName, propertyType, defaultValue }, index) => { + fieldInfos.forEach(({ field, propertyName, propertyType, defaultValue, needsOverride }, index) => { addDocComment(lines, field.description, ' '); const suffix = index === fieldInfos.length - 1 ? '' : ','; - lines.push(` val ${propertyName}: ${propertyType}${defaultValue}${suffix}`); + const overrideKeyword = needsOverride ? 'override ' : ''; + lines.push(` ${overrideKeyword}val ${propertyName}: ${propertyType}${defaultValue}${suffix}`); }); const implementsList = [...interfacesForObject, ...unionInterfaces]; if (implementsList.length > 0) { diff --git a/packages/gql/src/generated/Types.kt b/packages/gql/src/generated/Types.kt index 169c2d90..cf6473fc 100644 --- a/packages/gql/src/generated/Types.kt +++ b/packages/gql/src/generated/Types.kt @@ -324,25 +324,6 @@ public enum class IapkitPurchaseState(val rawValue: String) { fun toJson(): String = rawValue } -public enum class IapkitStore(val rawValue: String) { - Apple("apple"), - Google("google") - - companion object { - fun fromJson(value: String): IapkitStore = when (value) { - "apple" -> IapkitStore.Apple - "APPLE" -> IapkitStore.Apple - "Apple" -> IapkitStore.Apple - "google" -> IapkitStore.Google - "GOOGLE" -> IapkitStore.Google - "Google" -> IapkitStore.Google - else -> throw IllegalArgumentException("Unknown IapkitStore value: $value") - } - } - - fun toJson(): String = rawValue -} - public enum class IapPlatform(val rawValue: String) { Ios("ios"), Android("android") @@ -361,6 +342,33 @@ public enum class IapPlatform(val rawValue: String) { fun toJson(): String = rawValue } +public enum class IapStore(val rawValue: String) { + Unknown("unknown"), + Apple("apple"), + Google("google"), + Horizon("horizon") + + companion object { + fun fromJson(value: String): IapStore = when (value) { + "unknown" -> IapStore.Unknown + "UNKNOWN" -> IapStore.Unknown + "Unknown" -> IapStore.Unknown + "apple" -> IapStore.Apple + "APPLE" -> IapStore.Apple + "Apple" -> IapStore.Apple + "google" -> IapStore.Google + "GOOGLE" -> IapStore.Google + "Google" -> IapStore.Google + "horizon" -> IapStore.Horizon + "HORIZON" -> IapStore.Horizon + "Horizon" -> IapStore.Horizon + else -> throw IllegalArgumentException("Unknown IapStore value: $value") + } + } + + fun toJson(): String = rawValue +} + public enum class PaymentModeIOS(val rawValue: String) { Empty("empty"), FreeTrial("free-trial"), @@ -583,6 +591,9 @@ public interface PurchaseCommon { val id: String val ids: List? val isAutoRenewing: Boolean + /** + * @deprecated Use store instead + */ val platform: IapPlatform val productId: String val purchaseState: PurchaseState @@ -591,6 +602,10 @@ public interface PurchaseCommon { */ val purchaseToken: String? val quantity: Int + /** + * Store where purchase was made + */ + val store: IapStore val transactionDate: Double } @@ -953,19 +968,19 @@ public data class PricingPhasesAndroid( } public data class ProductAndroid( - val currency: String, - val debugDescription: String? = null, - val description: String, - val displayName: String? = null, - val displayPrice: String, - val id: String, + override val currency: String, + override val debugDescription: String? = null, + override val description: String, + override val displayName: String? = null, + override val displayPrice: String, + override val id: String, val nameAndroid: String, val oneTimePurchaseOfferDetailsAndroid: ProductAndroidOneTimePurchaseOfferDetail? = null, - val platform: IapPlatform = IapPlatform.Android, - val price: Double? = null, + override val platform: IapPlatform = IapPlatform.Android, + override val price: Double? = null, val subscriptionOfferDetailsAndroid: List? = null, - val title: String, - val type: ProductType = ProductType.InApp + override val title: String, + override val type: ProductType = ProductType.InApp ) : ProductCommon, Product { companion object { @@ -1031,20 +1046,20 @@ public data class ProductAndroidOneTimePurchaseOfferDetail( } public data class ProductIOS( - val currency: String, - val debugDescription: String? = null, - val description: String, - val displayName: String? = null, + override val currency: String, + override val debugDescription: String? = null, + override val description: String, + override val displayName: String? = null, val displayNameIOS: String, - val displayPrice: String, - val id: String, + override val displayPrice: String, + override val id: String, val isFamilyShareableIOS: Boolean, val jsonRepresentationIOS: String, - val platform: IapPlatform = IapPlatform.Ios, - val price: Double? = null, + override val platform: IapPlatform = IapPlatform.Ios, + override val price: Double? = null, val subscriptionInfoIOS: SubscriptionInfoIOS? = null, - val title: String, - val type: ProductType = ProductType.InApp, + override val title: String, + override val type: ProductType = ProductType.InApp, val typeIOS: ProductTypeIOS ) : ProductCommon, Product { @@ -1091,19 +1106,19 @@ public data class ProductIOS( } public data class ProductSubscriptionAndroid( - val currency: String, - val debugDescription: String? = null, - val description: String, - val displayName: String? = null, - val displayPrice: String, - val id: String, + override val currency: String, + override val debugDescription: String? = null, + override val description: String, + override val displayName: String? = null, + override val displayPrice: String, + override val id: String, val nameAndroid: String, val oneTimePurchaseOfferDetailsAndroid: ProductAndroidOneTimePurchaseOfferDetail? = null, - val platform: IapPlatform = IapPlatform.Android, - val price: Double? = null, + override val platform: IapPlatform = IapPlatform.Android, + override val price: Double? = null, val subscriptionOfferDetailsAndroid: List, - val title: String, - val type: ProductType = ProductType.Subs + override val title: String, + override val type: ProductType = ProductType.Subs ) : ProductCommon, ProductSubscription { companion object { @@ -1175,14 +1190,14 @@ public data class ProductSubscriptionAndroidOfferDetails( } public data class ProductSubscriptionIOS( - val currency: String, - val debugDescription: String? = null, - val description: String, + override val currency: String, + override val debugDescription: String? = null, + override val description: String, val discountsIOS: List? = null, - val displayName: String? = null, + override val displayName: String? = null, val displayNameIOS: String, - val displayPrice: String, - val id: String, + override val displayPrice: String, + override val id: String, val introductoryPriceAsAmountIOS: String? = null, val introductoryPriceIOS: String? = null, val introductoryPriceNumberOfPeriodsIOS: String? = null, @@ -1190,13 +1205,13 @@ public data class ProductSubscriptionIOS( val introductoryPriceSubscriptionPeriodIOS: SubscriptionPeriodIOS? = null, val isFamilyShareableIOS: Boolean, val jsonRepresentationIOS: String, - val platform: IapPlatform = IapPlatform.Ios, - val price: Double? = null, + override val platform: IapPlatform = IapPlatform.Ios, + override val price: Double? = null, val subscriptionInfoIOS: SubscriptionInfoIOS? = null, val subscriptionPeriodNumberIOS: String? = null, val subscriptionPeriodUnitIOS: SubscriptionPeriodIOS? = null, - val title: String, - val type: ProductType = ProductType.Subs, + override val title: String, + override val type: ProductType = ProductType.Subs, val typeIOS: ProductTypeIOS ) : ProductCommon, ProductSubscription { @@ -1260,23 +1275,30 @@ public data class ProductSubscriptionIOS( public data class PurchaseAndroid( val autoRenewingAndroid: Boolean? = null, - val currentPlanId: String? = null, + override val currentPlanId: String? = null, val dataAndroid: String? = null, val developerPayloadAndroid: String? = null, - val id: String, - val ids: List? = null, + override val id: String, + override val ids: List? = null, val isAcknowledgedAndroid: Boolean? = null, - val isAutoRenewing: Boolean, + override val isAutoRenewing: Boolean, val obfuscatedAccountIdAndroid: String? = null, val obfuscatedProfileIdAndroid: String? = null, val packageNameAndroid: String? = null, - val platform: IapPlatform, - val productId: String, - val purchaseState: PurchaseState, - val purchaseToken: String? = null, - val quantity: Int, + /** + * @deprecated Use store instead + */ + override val platform: IapPlatform, + override val productId: String, + override val purchaseState: PurchaseState, + override val purchaseToken: String? = null, + override val quantity: Int, val signatureAndroid: String? = null, - val transactionDate: Double, + /** + * Store where purchase was made + */ + override val store: IapStore, + override val transactionDate: Double, val transactionId: String? = null ) : PurchaseCommon, Purchase { @@ -1300,6 +1322,7 @@ public data class PurchaseAndroid( purchaseToken = json["purchaseToken"] as String?, quantity = (json["quantity"] as Number).toInt(), signatureAndroid = json["signatureAndroid"] as String?, + store = IapStore.fromJson(json["store"] as String), transactionDate = (json["transactionDate"] as Number).toDouble(), transactionId = json["transactionId"] as String?, ) @@ -1325,6 +1348,7 @@ public data class PurchaseAndroid( "purchaseToken" to purchaseToken, "quantity" to quantity, "signatureAndroid" to signatureAndroid, + "store" to store.toJson(), "transactionDate" to transactionDate, "transactionId" to transactionId, ) @@ -1360,31 +1384,38 @@ public data class PurchaseIOS( val countryCodeIOS: String? = null, val currencyCodeIOS: String? = null, val currencySymbolIOS: String? = null, - val currentPlanId: String? = null, + override val currentPlanId: String? = null, val environmentIOS: String? = null, val expirationDateIOS: Double? = null, - val id: String, - val ids: List? = null, - val isAutoRenewing: Boolean, + override val id: String, + override val ids: List? = null, + override val isAutoRenewing: Boolean, val isUpgradedIOS: Boolean? = null, val offerIOS: PurchaseOfferIOS? = null, val originalTransactionDateIOS: Double? = null, val originalTransactionIdentifierIOS: String? = null, val ownershipTypeIOS: String? = null, - val platform: IapPlatform, - val productId: String, - val purchaseState: PurchaseState, - val purchaseToken: String? = null, - val quantity: Int, + /** + * @deprecated Use store instead + */ + override val platform: IapPlatform, + override val productId: String, + override val purchaseState: PurchaseState, + override val purchaseToken: String? = null, + override val quantity: Int, val quantityIOS: Int? = null, val reasonIOS: String? = null, val reasonStringRepresentationIOS: String? = null, val renewalInfoIOS: RenewalInfoIOS? = null, val revocationDateIOS: Double? = null, val revocationReasonIOS: String? = null, + /** + * Store where purchase was made + */ + override val store: IapStore, val storefrontCountryCodeIOS: String? = null, val subscriptionGroupIdIOS: String? = null, - val transactionDate: Double, + override val transactionDate: Double, val transactionId: String, val transactionReasonIOS: String? = null, val webOrderLineItemIdIOS: String? = null @@ -1420,6 +1451,7 @@ public data class PurchaseIOS( renewalInfoIOS = (json["renewalInfoIOS"] as Map?)?.let { RenewalInfoIOS.fromJson(it) }, revocationDateIOS = (json["revocationDateIOS"] as Number?)?.toDouble(), revocationReasonIOS = json["revocationReasonIOS"] as String?, + store = IapStore.fromJson(json["store"] as String), storefrontCountryCodeIOS = json["storefrontCountryCodeIOS"] as String?, subscriptionGroupIdIOS = json["subscriptionGroupIdIOS"] as String?, transactionDate = (json["transactionDate"] as Number).toDouble(), @@ -1459,6 +1491,7 @@ public data class PurchaseIOS( "renewalInfoIOS" to renewalInfoIOS?.toJson(), "revocationDateIOS" to revocationDateIOS, "revocationReasonIOS" to revocationReasonIOS, + "store" to store.toJson(), "storefrontCountryCodeIOS" to storefrontCountryCodeIOS, "subscriptionGroupIdIOS" to subscriptionGroupIdIOS, "transactionDate" to transactionDate, @@ -1611,7 +1644,7 @@ public data class RequestVerifyPurchaseWithIapkitResult( * The current state of the purchase. */ val state: IapkitPurchaseState, - val store: IapkitStore + val store: IapStore ) { companion object { @@ -1619,7 +1652,7 @@ public data class RequestVerifyPurchaseWithIapkitResult( return RequestVerifyPurchaseWithIapkitResult( isValid = json["isValid"] as Boolean, state = IapkitPurchaseState.fromJson(json["state"] as String), - store = IapkitStore.fromJson(json["store"] as String), + store = IapStore.fromJson(json["store"] as String), ) } } @@ -2228,11 +2261,19 @@ public data class RequestPurchaseProps( public data class RequestPurchasePropsByPlatforms( /** - * Android-specific purchase parameters + * @deprecated Use google instead */ val android: RequestPurchaseAndroidProps? = null, /** - * iOS-specific purchase parameters + * Apple-specific purchase parameters + */ + val apple: RequestPurchaseIosProps? = null, + /** + * Google-specific purchase parameters + */ + val google: RequestPurchaseAndroidProps? = null, + /** + * @deprecated Use apple instead */ val ios: RequestPurchaseIosProps? = null ) { @@ -2240,6 +2281,8 @@ public data class RequestPurchasePropsByPlatforms( fun fromJson(json: Map): RequestPurchasePropsByPlatforms { return RequestPurchasePropsByPlatforms( android = (json["android"] as Map?)?.let { RequestPurchaseAndroidProps.fromJson(it) }, + apple = (json["apple"] as Map?)?.let { RequestPurchaseIosProps.fromJson(it) }, + google = (json["google"] as Map?)?.let { RequestPurchaseAndroidProps.fromJson(it) }, ios = (json["ios"] as Map?)?.let { RequestPurchaseIosProps.fromJson(it) }, ) } @@ -2247,6 +2290,8 @@ public data class RequestPurchasePropsByPlatforms( fun toJson(): Map = mapOf( "android" to android?.toJson(), + "apple" to apple?.toJson(), + "google" to google?.toJson(), "ios" to ios?.toJson(), ) } @@ -2336,11 +2381,19 @@ public data class RequestSubscriptionIosProps( public data class RequestSubscriptionPropsByPlatforms( /** - * Android-specific subscription parameters + * @deprecated Use google instead */ val android: RequestSubscriptionAndroidProps? = null, /** - * iOS-specific subscription parameters + * Apple-specific subscription parameters + */ + val apple: RequestSubscriptionIosProps? = null, + /** + * Google-specific subscription parameters + */ + val google: RequestSubscriptionAndroidProps? = null, + /** + * @deprecated Use apple instead */ val ios: RequestSubscriptionIosProps? = null ) { @@ -2348,6 +2401,8 @@ public data class RequestSubscriptionPropsByPlatforms( fun fromJson(json: Map): RequestSubscriptionPropsByPlatforms { return RequestSubscriptionPropsByPlatforms( android = (json["android"] as Map?)?.let { RequestSubscriptionAndroidProps.fromJson(it) }, + apple = (json["apple"] as Map?)?.let { RequestSubscriptionIosProps.fromJson(it) }, + google = (json["google"] as Map?)?.let { RequestSubscriptionAndroidProps.fromJson(it) }, ios = (json["ios"] as Map?)?.let { RequestSubscriptionIosProps.fromJson(it) }, ) } @@ -2355,6 +2410,8 @@ public data class RequestSubscriptionPropsByPlatforms( fun toJson(): Map = mapOf( "android" to android?.toJson(), + "apple" to apple?.toJson(), + "google" to google?.toJson(), "ios" to ios?.toJson(), ) } diff --git a/packages/gql/src/generated/Types.swift b/packages/gql/src/generated/Types.swift index f56eaaa3..28d56a4d 100644 --- a/packages/gql/src/generated/Types.swift +++ b/packages/gql/src/generated/Types.swift @@ -181,16 +181,18 @@ public enum IapkitPurchaseState: String, Codable, CaseIterable { case inauthentic = "inauthentic" } -public enum IapkitStore: String, Codable, CaseIterable { - case apple = "apple" - case google = "google" -} - public enum IapPlatform: String, Codable, CaseIterable { case ios = "ios" case android = "android" } +public enum IapStore: String, Codable, CaseIterable { + case unknown = "unknown" + case apple = "apple" + case google = "google" + case horizon = "horizon" +} + public enum PaymentModeIOS: String, Codable, CaseIterable { case empty = "empty" case freeTrial = "free-trial" @@ -266,12 +268,15 @@ public protocol PurchaseCommon: Codable { var id: String { get } var ids: [String]? { get } var isAutoRenewing: Bool { get } + /// @deprecated Use store instead var platform: IapPlatform { get } var productId: String { get } var purchaseState: PurchaseState { get } /// Unified purchase token (iOS JWS, Android purchaseToken) var purchaseToken: String? { get } var quantity: Int { get } + /// Store where purchase was made + var store: IapStore { get } var transactionDate: Double { get } } @@ -487,12 +492,15 @@ public struct PurchaseAndroid: Codable, PurchaseCommon { public var obfuscatedAccountIdAndroid: String? public var obfuscatedProfileIdAndroid: String? public var packageNameAndroid: String? + /// @deprecated Use store instead public var platform: IapPlatform public var productId: String public var purchaseState: PurchaseState public var purchaseToken: String? public var quantity: Int public var signatureAndroid: String? + /// Store where purchase was made + public var store: IapStore public var transactionDate: Double public var transactionId: String? } @@ -520,6 +528,7 @@ public struct PurchaseIOS: Codable, PurchaseCommon { public var originalTransactionDateIOS: Double? public var originalTransactionIdentifierIOS: String? public var ownershipTypeIOS: String? + /// @deprecated Use store instead public var platform: IapPlatform public var productId: String public var purchaseState: PurchaseState @@ -531,6 +540,8 @@ public struct PurchaseIOS: Codable, PurchaseCommon { public var renewalInfoIOS: RenewalInfoIOS? public var revocationDateIOS: Double? public var revocationReasonIOS: String? + /// Store where purchase was made + public var store: IapStore public var storefrontCountryCodeIOS: String? public var subscriptionGroupIdIOS: String? public var transactionDate: Double @@ -591,7 +602,7 @@ public struct RequestVerifyPurchaseWithIapkitResult: Codable { public var isValid: Bool /// The current state of the purchase. public var state: IapkitPurchaseState - public var store: IapkitStore + public var store: IapStore } public struct SubscriptionInfoIOS: Codable { @@ -924,16 +935,24 @@ public struct RequestPurchaseProps: Codable { } public struct RequestPurchasePropsByPlatforms: Codable { - /// Android-specific purchase parameters + /// @deprecated Use google instead public var android: RequestPurchaseAndroidProps? - /// iOS-specific purchase parameters + /// Apple-specific purchase parameters + public var apple: RequestPurchaseIosProps? + /// Google-specific purchase parameters + public var google: RequestPurchaseAndroidProps? + /// @deprecated Use apple instead public var ios: RequestPurchaseIosProps? public init( android: RequestPurchaseAndroidProps? = nil, + apple: RequestPurchaseIosProps? = nil, + google: RequestPurchaseAndroidProps? = nil, ios: RequestPurchaseIosProps? = nil ) { self.android = android + self.apple = apple + self.google = google self.ios = ios } } @@ -996,16 +1015,24 @@ public struct RequestSubscriptionIosProps: Codable { } public struct RequestSubscriptionPropsByPlatforms: Codable { - /// Android-specific subscription parameters + /// @deprecated Use google instead public var android: RequestSubscriptionAndroidProps? - /// iOS-specific subscription parameters + /// Apple-specific subscription parameters + public var apple: RequestSubscriptionIosProps? + /// Google-specific subscription parameters + public var google: RequestSubscriptionAndroidProps? + /// @deprecated Use apple instead public var ios: RequestSubscriptionIosProps? public init( android: RequestSubscriptionAndroidProps? = nil, + apple: RequestSubscriptionIosProps? = nil, + google: RequestSubscriptionAndroidProps? = nil, ios: RequestSubscriptionIosProps? = nil ) { self.android = android + self.apple = apple + self.google = google self.ios = ios } } @@ -1339,6 +1366,7 @@ public enum Purchase: Codable, PurchaseCommon { } } + /// @deprecated Use store instead public var platform: IapPlatform { switch self { case let .purchaseAndroid(value): @@ -1385,6 +1413,16 @@ public enum Purchase: Codable, PurchaseCommon { } } + /// Store where purchase was made + public var store: IapStore { + switch self { + case let .purchaseAndroid(value): + return value.store + case let .purchaseIos(value): + return value.store + } + } + public var transactionDate: Double { switch self { case let .purchaseAndroid(value): diff --git a/packages/gql/src/generated/types.dart b/packages/gql/src/generated/types.dart index 4384ca85..2c75e198 100644 --- a/packages/gql/src/generated/types.dart +++ b/packages/gql/src/generated/types.dart @@ -362,30 +362,6 @@ enum IapkitPurchaseState { String toJson() => value; } -enum IapkitStore { - Apple('apple'), - Google('google'); - - const IapkitStore(this.value); - final String value; - - factory IapkitStore.fromJson(String value) { - switch (value) { - case 'apple': - case 'APPLE': - case 'Apple': - return IapkitStore.Apple; - case 'google': - case 'GOOGLE': - case 'Google': - return IapkitStore.Google; - } - throw ArgumentError('Unknown IapkitStore value: $value'); - } - - String toJson() => value; -} - enum IapPlatform { IOS('ios'), Android('android'); @@ -409,6 +385,40 @@ enum IapPlatform { String toJson() => value; } +enum IapStore { + Unknown('unknown'), + Apple('apple'), + Google('google'), + Horizon('horizon'); + + const IapStore(this.value); + final String value; + + factory IapStore.fromJson(String value) { + switch (value) { + case 'unknown': + case 'UNKNOWN': + case 'Unknown': + return IapStore.Unknown; + case 'apple': + case 'APPLE': + case 'Apple': + return IapStore.Apple; + case 'google': + case 'GOOGLE': + case 'Google': + return IapStore.Google; + case 'horizon': + case 'HORIZON': + case 'Horizon': + return IapStore.Horizon; + } + throw ArgumentError('Unknown IapStore value: $value'); + } + + String toJson() => value; +} + enum PaymentModeIOS { Empty('empty'), FreeTrial('free-trial'), @@ -680,12 +690,15 @@ abstract class PurchaseCommon { String get id; List? get ids; bool get isAutoRenewing; + /// @deprecated Use store instead IapPlatform get platform; String get productId; PurchaseState get purchaseState; /// Unified purchase token (iOS JWS, Android purchaseToken) String? get purchaseToken; int get quantity; + /// Store where purchase was made + IapStore get store; double get transactionDate; } @@ -1526,12 +1539,15 @@ class PurchaseAndroid extends Purchase implements PurchaseCommon { this.obfuscatedAccountIdAndroid, this.obfuscatedProfileIdAndroid, this.packageNameAndroid, + /// @deprecated Use store instead required this.platform, required this.productId, required this.purchaseState, this.purchaseToken, required this.quantity, this.signatureAndroid, + /// Store where purchase was made + required this.store, required this.transactionDate, this.transactionId, this.isAlternativeBilling, @@ -1548,12 +1564,15 @@ class PurchaseAndroid extends Purchase implements PurchaseCommon { final String? obfuscatedAccountIdAndroid; final String? obfuscatedProfileIdAndroid; final String? packageNameAndroid; + /// @deprecated Use store instead final IapPlatform platform; final String productId; final PurchaseState purchaseState; final String? purchaseToken; final int quantity; final String? signatureAndroid; + /// Store where purchase was made + final IapStore store; final double transactionDate; final String? transactionId; final bool? isAlternativeBilling; @@ -1577,6 +1596,7 @@ class PurchaseAndroid extends Purchase implements PurchaseCommon { purchaseToken: json['purchaseToken'] as String?, quantity: json['quantity'] as int, signatureAndroid: json['signatureAndroid'] as String?, + store: IapStore.fromJson(json['store'] as String), transactionDate: (json['transactionDate'] as num).toDouble(), transactionId: json['transactionId'] as String?, isAlternativeBilling: json['isAlternativeBilling'] as bool?, @@ -1604,6 +1624,7 @@ class PurchaseAndroid extends Purchase implements PurchaseCommon { 'purchaseToken': purchaseToken, 'quantity': quantity, 'signatureAndroid': signatureAndroid, + 'store': store.toJson(), 'transactionDate': transactionDate, 'transactionId': transactionId, 'isAlternativeBilling': isAlternativeBilling, @@ -1658,6 +1679,7 @@ class PurchaseIOS extends Purchase implements PurchaseCommon { this.originalTransactionDateIOS, this.originalTransactionIdentifierIOS, this.ownershipTypeIOS, + /// @deprecated Use store instead required this.platform, required this.productId, required this.purchaseState, @@ -1669,6 +1691,8 @@ class PurchaseIOS extends Purchase implements PurchaseCommon { this.renewalInfoIOS, this.revocationDateIOS, this.revocationReasonIOS, + /// Store where purchase was made + required this.store, this.storefrontCountryCodeIOS, this.subscriptionGroupIdIOS, required this.transactionDate, @@ -1694,6 +1718,7 @@ class PurchaseIOS extends Purchase implements PurchaseCommon { final double? originalTransactionDateIOS; final String? originalTransactionIdentifierIOS; final String? ownershipTypeIOS; + /// @deprecated Use store instead final IapPlatform platform; final String productId; final PurchaseState purchaseState; @@ -1705,6 +1730,8 @@ class PurchaseIOS extends Purchase implements PurchaseCommon { final RenewalInfoIOS? renewalInfoIOS; final double? revocationDateIOS; final String? revocationReasonIOS; + /// Store where purchase was made + final IapStore store; final String? storefrontCountryCodeIOS; final String? subscriptionGroupIdIOS; final double transactionDate; @@ -1742,6 +1769,7 @@ class PurchaseIOS extends Purchase implements PurchaseCommon { renewalInfoIOS: json['renewalInfoIOS'] != null ? RenewalInfoIOS.fromJson(json['renewalInfoIOS'] as Map) : null, revocationDateIOS: (json['revocationDateIOS'] as num?)?.toDouble(), revocationReasonIOS: json['revocationReasonIOS'] as String?, + store: IapStore.fromJson(json['store'] as String), storefrontCountryCodeIOS: json['storefrontCountryCodeIOS'] as String?, subscriptionGroupIdIOS: json['subscriptionGroupIdIOS'] as String?, transactionDate: (json['transactionDate'] as num).toDouble(), @@ -1783,6 +1811,7 @@ class PurchaseIOS extends Purchase implements PurchaseCommon { 'renewalInfoIOS': renewalInfoIOS?.toJson(), 'revocationDateIOS': revocationDateIOS, 'revocationReasonIOS': revocationReasonIOS, + 'store': store.toJson(), 'storefrontCountryCodeIOS': storefrontCountryCodeIOS, 'subscriptionGroupIdIOS': subscriptionGroupIdIOS, 'transactionDate': transactionDate, @@ -1968,13 +1997,13 @@ class RequestVerifyPurchaseWithIapkitResult { final bool isValid; /// The current state of the purchase. final IapkitPurchaseState state; - final IapkitStore store; + final IapStore store; factory RequestVerifyPurchaseWithIapkitResult.fromJson(Map json) { return RequestVerifyPurchaseWithIapkitResult( isValid: json['isValid'] as bool, state: IapkitPurchaseState.fromJson(json['state'] as String), - store: IapkitStore.fromJson(json['store'] as String), + store: IapStore.fromJson(json['store'] as String), ); } @@ -2670,20 +2699,30 @@ class _SubsPurchase extends RequestPurchaseProps { class RequestPurchasePropsByPlatforms { const RequestPurchasePropsByPlatforms({ - /// Android-specific purchase parameters + /// @deprecated Use google instead this.android, - /// iOS-specific purchase parameters + /// Apple-specific purchase parameters + this.apple, + /// Google-specific purchase parameters + this.google, + /// @deprecated Use apple instead this.ios, }); - /// Android-specific purchase parameters + /// @deprecated Use google instead final RequestPurchaseAndroidProps? android; - /// iOS-specific purchase parameters + /// Apple-specific purchase parameters + final RequestPurchaseIosProps? apple; + /// Google-specific purchase parameters + final RequestPurchaseAndroidProps? google; + /// @deprecated Use apple instead final RequestPurchaseIosProps? ios; factory RequestPurchasePropsByPlatforms.fromJson(Map json) { return RequestPurchasePropsByPlatforms( android: json['android'] != null ? RequestPurchaseAndroidProps.fromJson(json['android'] as Map) : null, + apple: json['apple'] != null ? RequestPurchaseIosProps.fromJson(json['apple'] as Map) : null, + google: json['google'] != null ? RequestPurchaseAndroidProps.fromJson(json['google'] as Map) : null, ios: json['ios'] != null ? RequestPurchaseIosProps.fromJson(json['ios'] as Map) : null, ); } @@ -2691,6 +2730,8 @@ class RequestPurchasePropsByPlatforms { Map toJson() { return { 'android': android?.toJson(), + 'apple': apple?.toJson(), + 'google': google?.toJson(), 'ios': ios?.toJson(), }; } @@ -2792,20 +2833,30 @@ class RequestSubscriptionIosProps { class RequestSubscriptionPropsByPlatforms { const RequestSubscriptionPropsByPlatforms({ - /// Android-specific subscription parameters + /// @deprecated Use google instead this.android, - /// iOS-specific subscription parameters + /// Apple-specific subscription parameters + this.apple, + /// Google-specific subscription parameters + this.google, + /// @deprecated Use apple instead this.ios, }); - /// Android-specific subscription parameters + /// @deprecated Use google instead final RequestSubscriptionAndroidProps? android; - /// iOS-specific subscription parameters + /// Apple-specific subscription parameters + final RequestSubscriptionIosProps? apple; + /// Google-specific subscription parameters + final RequestSubscriptionAndroidProps? google; + /// @deprecated Use apple instead final RequestSubscriptionIosProps? ios; factory RequestSubscriptionPropsByPlatforms.fromJson(Map json) { return RequestSubscriptionPropsByPlatforms( android: json['android'] != null ? RequestSubscriptionAndroidProps.fromJson(json['android'] as Map) : null, + apple: json['apple'] != null ? RequestSubscriptionIosProps.fromJson(json['apple'] as Map) : null, + google: json['google'] != null ? RequestSubscriptionAndroidProps.fromJson(json['google'] as Map) : null, ios: json['ios'] != null ? RequestSubscriptionIosProps.fromJson(json['ios'] as Map) : null, ); } @@ -2813,6 +2864,8 @@ class RequestSubscriptionPropsByPlatforms { Map toJson() { return { 'android': android?.toJson(), + 'apple': apple?.toJson(), + 'google': google?.toJson(), 'ios': ios?.toJson(), }; } @@ -3121,6 +3174,7 @@ sealed class Purchase implements PurchaseCommon { List? get ids; @override bool get isAutoRenewing; + /// @deprecated Use store instead @override IapPlatform get platform; @override @@ -3132,6 +3186,9 @@ sealed class Purchase implements PurchaseCommon { String? get purchaseToken; @override int get quantity; + /// Store where purchase was made + @override + IapStore get store; @override double get transactionDate; diff --git a/packages/gql/src/generated/types.ts b/packages/gql/src/generated/types.ts index 82349641..3dcbbf91 100644 --- a/packages/gql/src/generated/types.ts +++ b/packages/gql/src/generated/types.ts @@ -180,11 +180,11 @@ export type IapEvent = 'purchase-updated' | 'purchase-error' | 'promoted-product export type IapPlatform = 'ios' | 'android'; +export type IapStore = 'unknown' | 'apple' | 'google' | 'horizon'; + /** Unified purchase states from IAPKit verification response. */ export type IapkitPurchaseState = 'entitled' | 'pending-acknowledgment' | 'pending' | 'canceled' | 'expired' | 'ready-to-consume' | 'consumed' | 'unknown' | 'inauthentic'; -export type IapkitStore = 'apple' | 'google'; - /** Connection initialization configuration */ export interface InitConnectionConfig { /** @@ -457,12 +457,18 @@ export interface PurchaseAndroid extends PurchaseCommon { obfuscatedAccountIdAndroid?: (string | null); obfuscatedProfileIdAndroid?: (string | null); packageNameAndroid?: (string | null); + /** + * @deprecated Use store instead + * @deprecated Use store instead + */ platform: IapPlatform; productId: string; purchaseState: PurchaseState; purchaseToken?: (string | null); quantity: number; signatureAndroid?: (string | null); + /** Store where purchase was made */ + store: IapStore; transactionDate: number; transactionId?: (string | null); } @@ -478,12 +484,18 @@ export interface PurchaseCommon { id: string; ids?: (string[] | null); isAutoRenewing: boolean; + /** + * @deprecated Use store instead + * @deprecated Use store instead + */ platform: IapPlatform; productId: string; purchaseState: PurchaseState; /** Unified purchase token (iOS JWS, Android purchaseToken) */ purchaseToken?: (string | null); quantity: number; + /** Store where purchase was made */ + store: IapStore; transactionDate: number; } @@ -510,6 +522,10 @@ export interface PurchaseIOS extends PurchaseCommon { originalTransactionDateIOS?: (number | null); originalTransactionIdentifierIOS?: (string | null); ownershipTypeIOS?: (string | null); + /** + * @deprecated Use store instead + * @deprecated Use store instead + */ platform: IapPlatform; productId: string; purchaseState: PurchaseState; @@ -521,6 +537,8 @@ export interface PurchaseIOS extends PurchaseCommon { renewalInfoIOS?: (RenewalInfoIOS | null); revocationDateIOS?: (number | null); revocationReasonIOS?: (string | null); + /** Store where purchase was made */ + store: IapStore; storefrontCountryCodeIOS?: (string | null); subscriptionGroupIdIOS?: (string | null); transactionDate: number; @@ -710,9 +728,13 @@ export type RequestPurchaseProps = }; export interface RequestPurchasePropsByPlatforms { - /** Android-specific purchase parameters */ + /** @deprecated Use google instead */ android?: (RequestPurchaseAndroidProps | null); - /** iOS-specific purchase parameters */ + /** Apple-specific purchase parameters */ + apple?: (RequestPurchaseIosProps | null); + /** Google-specific purchase parameters */ + google?: (RequestPurchaseAndroidProps | null); + /** @deprecated Use apple instead */ ios?: (RequestPurchaseIosProps | null); } @@ -744,9 +766,13 @@ export interface RequestSubscriptionIosProps { } export interface RequestSubscriptionPropsByPlatforms { - /** Android-specific subscription parameters */ + /** @deprecated Use google instead */ android?: (RequestSubscriptionAndroidProps | null); - /** iOS-specific subscription parameters */ + /** Apple-specific subscription parameters */ + apple?: (RequestSubscriptionIosProps | null); + /** Google-specific subscription parameters */ + google?: (RequestSubscriptionAndroidProps | null); + /** @deprecated Use apple instead */ ios?: (RequestSubscriptionIosProps | null); } @@ -774,7 +800,7 @@ export interface RequestVerifyPurchaseWithIapkitResult { isValid: boolean; /** The current state of the purchase. */ state: IapkitPurchaseState; - store: IapkitStore; + store: IapStore; } export interface Subscription { diff --git a/packages/gql/src/type-android.graphql b/packages/gql/src/type-android.graphql index cd8859a7..dea802a4 100644 --- a/packages/gql/src/type-android.graphql +++ b/packages/gql/src/type-android.graphql @@ -74,7 +74,14 @@ type PurchaseAndroid implements PurchaseCommon { transactionId: String # Optional in Android unlike iOS transactionDate: Float! purchaseToken: String - platform: IapPlatform! + """ + Store where purchase was made + """ + store: IapStore! + """ + @deprecated Use store instead + """ + platform: IapPlatform! @deprecated(reason: "Use store instead") quantity: Int! purchaseState: PurchaseState! isAutoRenewing: Boolean! diff --git a/packages/gql/src/type-ios.graphql b/packages/gql/src/type-ios.graphql index b8fa6852..f77c353c 100644 --- a/packages/gql/src/type-ios.graphql +++ b/packages/gql/src/type-ios.graphql @@ -128,7 +128,14 @@ type PurchaseIOS implements PurchaseCommon { ids: [String!] transactionDate: Float! purchaseToken: String - platform: IapPlatform! + """ + Store where purchase was made + """ + store: IapStore! + """ + @deprecated Use store instead + """ + platform: IapPlatform! @deprecated(reason: "Use store instead") quantity: Int! purchaseState: PurchaseState! isAutoRenewing: Boolean! diff --git a/packages/gql/src/type.graphql b/packages/gql/src/type.graphql index 7a694fa2..9ac97015 100644 --- a/packages/gql/src/type.graphql +++ b/packages/gql/src/type.graphql @@ -35,9 +35,11 @@ enum PurchaseVerificationProvider { Iapkit } -enum IapkitStore { +enum IapStore { + Unknown Apple Google + Horizon } # Common product fields @@ -64,7 +66,14 @@ interface PurchaseCommon { Unified purchase token (iOS JWS, Android purchaseToken) """ purchaseToken: String - platform: IapPlatform! + """ + Store where purchase was made + """ + store: IapStore! + """ + @deprecated Use store instead + """ + platform: IapPlatform! @deprecated(reason: "Use store instead") quantity: Int! purchaseState: PurchaseState! isAutoRenewing: Boolean! @@ -156,7 +165,14 @@ input PurchaseInput { ids: [String!] transactionDate: Float! purchaseToken: String - platform: IapPlatform! + """ + Store where purchase was made + """ + store: IapStore + """ + @deprecated Use store instead + """ + platform: IapPlatform quantity: Int! purchaseState: PurchaseState! isAutoRenewing: Boolean! @@ -177,22 +193,38 @@ input DeepLinkOptions { # Request props (platform-specific containers) input RequestPurchasePropsByPlatforms { """ - iOS-specific purchase parameters + Apple-specific purchase parameters + """ + apple: RequestPurchaseIosProps + """ + Google-specific purchase parameters + """ + google: RequestPurchaseAndroidProps + """ + @deprecated Use apple instead """ ios: RequestPurchaseIosProps """ - Android-specific purchase parameters + @deprecated Use google instead """ android: RequestPurchaseAndroidProps } input RequestSubscriptionPropsByPlatforms { """ - iOS-specific subscription parameters + Apple-specific subscription parameters + """ + apple: RequestSubscriptionIosProps + """ + Google-specific subscription parameters + """ + google: RequestSubscriptionAndroidProps + """ + @deprecated Use apple instead """ ios: RequestSubscriptionIosProps """ - Android-specific subscription parameters + @deprecated Use google instead """ android: RequestSubscriptionAndroidProps } @@ -285,7 +317,7 @@ enum IapkitPurchaseState { } type RequestVerifyPurchaseWithIapkitResult { - store: IapkitStore! + store: IapStore! """ Whether the purchase is valid (not falsified). """ From 34871eb8b3022b794e587092ad308464a2ce558d Mon Sep 17 00:00:00 2001 From: Hyo Date: Tue, 9 Dec 2025 03:37:27 +0900 Subject: [PATCH 2/4] fix: add missing store parameter to test PurchaseIOS initializers --- packages/apple/Tests/OpenIapTests.swift | 3 +++ packages/apple/Tests/OpenIapTests/RenewalInfoTests.swift | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packages/apple/Tests/OpenIapTests.swift b/packages/apple/Tests/OpenIapTests.swift index 308c0eb8..d3878288 100644 --- a/packages/apple/Tests/OpenIapTests.swift +++ b/packages/apple/Tests/OpenIapTests.swift @@ -86,6 +86,7 @@ final class OpenIapTests: XCTestCase { renewalInfoIOS: renewalInfo, revocationDateIOS: nil, revocationReasonIOS: nil, + store: .apple, storefrontCountryCodeIOS: "US", subscriptionGroupIdIOS: "21686373", transactionDate: 1729083955000, @@ -385,6 +386,7 @@ final class OpenIapTests: XCTestCase { renewalInfoIOS: nil, revocationDateIOS: nil, revocationReasonIOS: nil, + store: .apple, storefrontCountryCodeIOS: "US", subscriptionGroupIdIOS: "group", transactionDate: 2, @@ -422,6 +424,7 @@ final class OpenIapTests: XCTestCase { renewalInfoIOS: renewalInfo, revocationDateIOS: nil, revocationReasonIOS: nil, + store: .apple, storefrontCountryCodeIOS: "US", subscriptionGroupIdIOS: "21686373", transactionDate: 1729083955000, diff --git a/packages/apple/Tests/OpenIapTests/RenewalInfoTests.swift b/packages/apple/Tests/OpenIapTests/RenewalInfoTests.swift index bdbd6a55..a71ce55b 100644 --- a/packages/apple/Tests/OpenIapTests/RenewalInfoTests.swift +++ b/packages/apple/Tests/OpenIapTests/RenewalInfoTests.swift @@ -206,6 +206,7 @@ final class RenewalInfoTests: XCTestCase { renewalInfoIOS: renewalInfo, revocationDateIOS: nil, revocationReasonIOS: nil, + store: .apple, storefrontCountryCodeIOS: "US", subscriptionGroupIdIOS: "21686373", transactionDate: 1729083955000, @@ -271,6 +272,7 @@ final class RenewalInfoTests: XCTestCase { renewalInfoIOS: renewalInfo, revocationDateIOS: nil, revocationReasonIOS: nil, + store: .apple, storefrontCountryCodeIOS: "US", subscriptionGroupIdIOS: "21686373", transactionDate: 1729083955000, From 2231de31a501c874f8cd7fa68c7fb8d6ebe0bf95 Mon Sep 17 00:00:00 2001 From: Hyo Date: Tue, 9 Dec 2025 03:43:26 +0900 Subject: [PATCH 3/4] fix: update IapkitStore to IapStore in Android test --- .../dev/hyo/openiap/PurchaseVerificationValidatorTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt b/packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt index c2534b4b..9d53b45e 100644 --- a/packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt +++ b/packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt @@ -3,7 +3,7 @@ package dev.hyo.openiap import com.google.gson.Gson import dev.hyo.openiap.utils.verifyPurchaseWithGooglePlay import dev.hyo.openiap.utils.verifyPurchaseWithIapkit -import dev.hyo.openiap.IapkitStore +import dev.hyo.openiap.IapStore import dev.hyo.openiap.IapkitPurchaseState import dev.hyo.openiap.RequestVerifyPurchaseWithIapkitGoogleProps import dev.hyo.openiap.RequestVerifyPurchaseWithIapkitProps @@ -168,7 +168,7 @@ class PurchaseVerificationValidatorTest { val connection = FakeHttpURLConnection(200, """{"store":"google","isValid":true,"state":"ENTITLED"}""") val result = verifyPurchaseWithIapkit(props, "TEST") { _ -> connection } - assertEquals(IapkitStore.Google, result.store) + assertEquals(IapStore.Google, result.store) assertTrue(result.isValid) assertEquals("Bearer secret", connection.headers["Authorization"]) @@ -190,7 +190,7 @@ class PurchaseVerificationValidatorTest { val connection = FakeHttpURLConnection(200, """{"store":"google","isValid":false,"state":"INAUTHENTIC"}""") val result = verifyPurchaseWithIapkit(props, "TEST") { _ -> connection } - assertEquals(IapkitStore.Google, result.store) + assertEquals(IapStore.Google, result.store) assertEquals(false, result.isValid) val bodyMap = Gson().fromJson(requireNotNull(connection.writtenBody), Map::class.java) as Map<*, *> From 93e0bbb411b600ba699b1fe123112f02156cab38 Mon Sep 17 00:00:00 2001 From: Hyo Date: Tue, 9 Dec 2025 03:44:59 +0900 Subject: [PATCH 4/4] fix: address code review feedback for docs --- packages/docs/src/pages/docs/apis.tsx | 2 +- packages/docs/src/pages/docs/types.tsx | 52 ++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/packages/docs/src/pages/docs/apis.tsx b/packages/docs/src/pages/docs/apis.tsx index 182be877..6031f116 100644 --- a/packages/docs/src/pages/docs/apis.tsx +++ b/packages/docs/src/pages/docs/apis.tsx @@ -2136,7 +2136,7 @@ const handlePurchase = async (basePlanId: string) => { ], }, }, - type: 'Subs', + type: 'subs', }); }; diff --git a/packages/docs/src/pages/docs/types.tsx b/packages/docs/src/pages/docs/types.tsx index 0d0fdfee..3596931d 100644 --- a/packages/docs/src/pages/docs/types.tsx +++ b/packages/docs/src/pages/docs/types.tsx @@ -1656,15 +1656,33 @@ await FlutterInappPurchase.instance.requestPurchase( - ios + apple + + Apple purchase parameters (RequestPurchaseIosProps) + + + + google + + Google purchase parameters (RequestPurchaseAndroidProps) + + + + ios{' '} + + (deprecated) + - iOS purchase parameters (RequestPurchaseIosProps) + Use apple instead - android + android{' '} + + (deprecated) + - Android purchase parameters (RequestPurchaseAndroidProps) + Use google instead @@ -1683,19 +1701,37 @@ await FlutterInappPurchase.instance.requestPurchase( - ios + apple - iOS subscription parameters (RequestSubscriptionIosProps) + Apple subscription parameters (RequestSubscriptionIosProps) - android + google - Android subscription parameters + Google subscription parameters (RequestSubscriptionAndroidProps) + + + ios{' '} + + (deprecated) + + + Use apple instead + + + + android{' '} + + (deprecated) + + + Use google instead +