diff --git a/packages/apple/Sources/Models/Types.swift b/packages/apple/Sources/Models/Types.swift index 42365f341..a56f06024 100644 --- a/packages/apple/Sources/Models/Types.swift +++ b/packages/apple/Sources/Models/Types.swift @@ -1337,17 +1337,14 @@ public struct SubscriptionProductReplacementParamsAndroid: Codable { /// Apple App Store verification parameters. /// Used for server-side receipt validation via App Store Server API. -/// -/// ⚠️ SECURITY: Contains sensitive token (jws). Do not log or persist this data. public struct VerifyPurchaseAppleOptions: Codable { - /// The JWS (JSON Web Signature) representation of the transaction. - /// ⚠️ Sensitive: Do not log this value. - public var jws: String + /// Product SKU to validate + public var sku: String public init( - jws: String + sku: String ) { - self.jws = jws + self.sku = sku } } @@ -1366,17 +1363,21 @@ public struct VerifyPurchaseGoogleOptions: Codable { /// Purchase token from the purchase response. /// ⚠️ Sensitive: Do not log this value. public var purchaseToken: String + /// Product SKU to validate + public var sku: String public init( accessToken: String, isSub: Bool? = nil, packageName: String, - purchaseToken: String + purchaseToken: String, + sku: String ) { self.accessToken = accessToken self.isSub = isSub self.packageName = packageName self.purchaseToken = purchaseToken + self.sku = sku } } @@ -1417,19 +1418,15 @@ public struct VerifyPurchaseProps: Codable { public var google: VerifyPurchaseGoogleOptions? /// Meta Horizon (Quest) verification parameters. public var horizon: VerifyPurchaseHorizonOptions? - /// Product SKU to validate - public var sku: String public init( apple: VerifyPurchaseAppleOptions? = nil, google: VerifyPurchaseGoogleOptions? = nil, - horizon: VerifyPurchaseHorizonOptions? = nil, - sku: String + horizon: VerifyPurchaseHorizonOptions? = nil ) { self.apple = apple self.google = google self.horizon = horizon - self.sku = sku } } diff --git a/packages/apple/Sources/OpenIapModule.swift b/packages/apple/Sources/OpenIapModule.swift index 281696e5e..7d12c4631 100644 --- a/packages/apple/Sources/OpenIapModule.swift +++ b/packages/apple/Sources/OpenIapModule.swift @@ -598,25 +598,24 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol { var jws: String = "" var isValid = false - // If apple options with JWS are provided, use that directly - // Otherwise, fetch the latest transaction from StoreKit - if let appleOptions = props.apple, !appleOptions.jws.isEmpty { - jws = appleOptions.jws - // When JWS is provided externally, we trust it's valid - // The caller should verify the JWS on their server - isValid = true - } else { - do { - let product = try await storeProduct(for: props.sku) - if let result = await product.latestTransaction { - jws = result.jwsRepresentation - let transaction = try checkVerified(result) - latestPurchase = .purchaseIos(await StoreKitTypesBridge.purchaseIOS(from: transaction, jwsRepresentation: result.jwsRepresentation)) - isValid = true - } - } catch { - isValid = false + // Apple options with sku is required + guard let appleOptions = props.apple, !appleOptions.sku.isEmpty else { + throw makePurchaseError( + code: .developerError, + message: "Apple verification requires apple options with sku" + ) + } + + do { + let product = try await storeProduct(for: appleOptions.sku) + if let result = await product.latestTransaction { + jws = result.jwsRepresentation + let transaction = try checkVerified(result) + latestPurchase = .purchaseIos(await StoreKitTypesBridge.purchaseIOS(from: transaction, jwsRepresentation: result.jwsRepresentation)) + isValid = true } + } catch { + isValid = false } return VerifyPurchaseResultIOS( diff --git a/packages/apple/Sources/OpenIapProtocol.swift b/packages/apple/Sources/OpenIapProtocol.swift index 083b799e9..295469203 100644 --- a/packages/apple/Sources/OpenIapProtocol.swift +++ b/packages/apple/Sources/OpenIapProtocol.swift @@ -92,7 +92,7 @@ public extension OpenIapModuleProtocol { throw PurchaseError( code: .featureNotSupported, message: "Expected iOS validation result", - productId: props.sku + productId: props.apple?.sku ) } diff --git a/packages/apple/Sources/OpenIapStore.swift b/packages/apple/Sources/OpenIapStore.swift index 85516e8d6..5a7671873 100644 --- a/packages/apple/Sources/OpenIapStore.swift +++ b/packages/apple/Sources/OpenIapStore.swift @@ -374,7 +374,7 @@ public final class OpenIapStore: ObservableObject { } public func verifyPurchase(sku: String) async throws -> VerifyPurchaseResultIOS { - let result = try await module.verifyPurchase(VerifyPurchaseProps(sku: sku)) + let result = try await module.verifyPurchase(VerifyPurchaseProps(apple: VerifyPurchaseAppleOptions(sku: sku))) if case let .verifyPurchaseResultIos(iosResult) = result { return iosResult } diff --git a/packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift b/packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift index 63c73fbf2..ebfe50f49 100644 --- a/packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift +++ b/packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift @@ -101,7 +101,7 @@ private final class FakeOpenIapModule: OpenIapModuleProtocol { func getReceiptDataIOS() async throws -> String? { "receipt" } func validateReceiptIOS(_ props: VerifyPurchaseProps) async throws -> VerifyPurchaseResultIOS { guard case let .verifyPurchaseResultIos(ios) = validateResult else { - throw PurchaseError(code: .featureNotSupported, message: "Android validation not supported", productId: props.sku) + throw PurchaseError(code: .featureNotSupported, message: "Android validation not supported", productId: props.apple?.sku) } return ios } diff --git a/packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift b/packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift index deeb3f74c..11cf9515f 100644 --- a/packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift +++ b/packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift @@ -116,7 +116,7 @@ private final class FakeVerifyPurchaseModule: OpenIapModuleProtocol { func getReceiptDataIOS() async throws -> String? { "receipt" } func validateReceiptIOS(_ props: VerifyPurchaseProps) async throws -> VerifyPurchaseResultIOS { guard case let .verifyPurchaseResultIos(ios) = validateResult else { - throw PurchaseError(code: .featureNotSupported, message: "Expected iOS validation result", productId: props.sku) + throw PurchaseError(code: .featureNotSupported, message: "Expected iOS validation result", productId: props.apple?.sku) } return ios } diff --git a/packages/docs/src/pages/docs.tsx b/packages/docs/src/pages/docs.tsx index 2b740cc84..4164bc530 100644 --- a/packages/docs/src/pages/docs.tsx +++ b/packages/docs/src/pages/docs.tsx @@ -225,7 +225,7 @@ function Docs() { className={({ isActive }) => (isActive ? 'active' : '')} onClick={closeSidebar} > - Notes + Updates
  • diff --git a/packages/docs/src/pages/docs/types.tsx b/packages/docs/src/pages/docs/types.tsx index 6e1f99ad7..f5c479d7f 100644 --- a/packages/docs/src/pages/docs/types.tsx +++ b/packages/docs/src/pages/docs/types.tsx @@ -2426,8 +2426,8 @@ Future handleExternalPurchase(String externalUrl) async { verification.

    - - PurchaseVerificationProps + + VerifyPurchaseProps @@ -2439,35 +2439,49 @@ Future handleExternalPurchase(String externalUrl) async { + - + + + +
    + apple + + Apple App Store verification options. Contains:{' '} sku Product identifier to verify
    - androidOptions + google + + Google Play verification options. Contains:{' '} + sku, packageName,{' '} + purchaseToken, accessToken,{' '} + isSub +
    + horizon - Android Play Developer API options. Contains:{' '} - packageName, productToken,{' '} - accessToken, isSub + Meta Horizon (Quest) verification options. Contains:{' '} + sku, userId, accessToken
    - - PurchaseVerificationResult + + VerifyPurchaseResult

    - Union of PurchaseVerificationResultIOS and{' '} - PurchaseVerificationResultAndroid. + Union of VerifyPurchaseResultIOS,{' '} + VerifyPurchaseResultAndroid, and{' '} + VerifyPurchaseResultHorizon.

    {{ ios: ( <> -

    PurchaseVerificationResultIOS

    +

    VerifyPurchaseResultIOS

    @@ -2506,7 +2520,7 @@ Future handleExternalPurchase(String externalUrl) async { ), android: ( <> -

    PurchaseVerificationResultAndroid

    +

    VerifyPurchaseResultAndroid (Google Play)

    @@ -2601,6 +2615,35 @@ Future handleExternalPurchase(String externalUrl) async {
    + +

    + VerifyPurchaseResultHorizon (Meta Quest) +

    + + + + + + + + + + + + + + + + + +
    NameSummary
    + success + Whether the entitlement verification succeeded
    + grantTime + + Unix timestamp when the entitlement was granted (null if + verification failed) +
    ), }} diff --git a/packages/docs/src/pages/docs/updates/notes.tsx b/packages/docs/src/pages/docs/updates/notes.tsx index fa7c0f9e4..cf171e520 100644 --- a/packages/docs/src/pages/docs/updates/notes.tsx +++ b/packages/docs/src/pages/docs/updates/notes.tsx @@ -8,12 +8,12 @@ function Notes() { return (
    -

    Updates

    +

    Notes

    Important changes and deprecations in IAP libraries and platforms.

    @@ -29,29 +29,31 @@ function Notes() { }} >

    - 📅 openiap-gql v1.3.3 / openiap-google v1.3.13 / openiap-apple v1.3.1 + 📅 openiap-gql v1.3.4 / openiap-google v1.3.14 / openiap-apple v1.3.2 - Platform-Specific Verification Options

    - verifyPurchase API Refactored: + verifyPurchase API Refactored (Breaking Change):

    - The verifyPurchase API now supports platform-specific - options for Apple, Google, and Meta Horizon stores. + The verifyPurchase API now requires platform-specific + options for Apple, Google, and Meta Horizon stores. The{' '} + sku field has been moved inside each platform-specific + options object.

    • VerifyPurchaseAppleOptions {' '} - - Apple App Store verification with JWS token + - Apple App Store verification with sku
    • VerifyPurchaseGoogleOptions {' '} - - Google Play verification with packageName, purchaseToken, and - accessToken + - Google Play verification with sku, packageName, purchaseToken, + and accessToken
    • @@ -65,11 +67,11 @@ function Notes() { New VerifyPurchaseProps Structure:

      - {`// Platform-specific verification (recommended) + {`// Platform-specific verification verifyPurchase({ - sku: 'premium_monthly', - apple: { jws: 'eyJ...' }, // iOS App Store + apple: { sku: 'premium_monthly' }, // iOS App Store google: { // Google Play + sku: 'premium_monthly', packageName: 'com.example.app', purchaseToken: 'token...', accessToken: 'oauth_token...', @@ -80,20 +82,18 @@ verifyPurchase({ userId: '123456789', accessToken: 'OC|app_id|app_secret' } -}) - -// Legacy format still supported (deprecated) -verifyPurchase({ - sku: 'premium_monthly', - androidOptions: { ... } // @deprecated - use google instead })`}

      - Deprecations: + Breaking Changes:

      • - androidOptions in VerifyPurchaseProps → Use{' '} + sku removed from VerifyPurchaseProps{' '} + root level → Now inside each platform options +
      • +
      • + androidOptions completely removed → Use{' '} google instead
      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 b4ae42335..4aefb97f0 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 @@ -3087,26 +3087,23 @@ public data class SubscriptionProductReplacementParamsAndroid( /** * Apple App Store verification parameters. * Used for server-side receipt validation via App Store Server API. - * - * ⚠️ SECURITY: Contains sensitive token (jws). Do not log or persist this data. */ public data class VerifyPurchaseAppleOptions( /** - * The JWS (JSON Web Signature) representation of the transaction. - * ⚠️ Sensitive: Do not log this value. + * Product SKU to validate */ - val jws: String + val sku: String ) { companion object { fun fromJson(json: Map): VerifyPurchaseAppleOptions { return VerifyPurchaseAppleOptions( - jws = json["jws"] as String, + sku = json["sku"] as String, ) } } fun toJson(): Map = mapOf( - "jws" to jws, + "sku" to sku, ) } @@ -3134,7 +3131,11 @@ public data class VerifyPurchaseGoogleOptions( * Purchase token from the purchase response. * ⚠️ Sensitive: Do not log this value. */ - val purchaseToken: String + val purchaseToken: String, + /** + * Product SKU to validate + */ + val sku: String ) { companion object { fun fromJson(json: Map): VerifyPurchaseGoogleOptions { @@ -3143,6 +3144,7 @@ public data class VerifyPurchaseGoogleOptions( isSub = json["isSub"] as Boolean?, packageName = json["packageName"] as String, purchaseToken = json["purchaseToken"] as String, + sku = json["sku"] as String, ) } } @@ -3152,6 +3154,7 @@ public data class VerifyPurchaseGoogleOptions( "isSub" to isSub, "packageName" to packageName, "purchaseToken" to purchaseToken, + "sku" to sku, ) } @@ -3213,11 +3216,7 @@ public data class VerifyPurchaseProps( /** * Meta Horizon (Quest) verification parameters. */ - val horizon: VerifyPurchaseHorizonOptions? = null, - /** - * Product SKU to validate - */ - val sku: String + val horizon: VerifyPurchaseHorizonOptions? = null ) { companion object { fun fromJson(json: Map): VerifyPurchaseProps { @@ -3225,7 +3224,6 @@ public data class VerifyPurchaseProps( apple = (json["apple"] as Map?)?.let { VerifyPurchaseAppleOptions.fromJson(it) }, google = (json["google"] as Map?)?.let { VerifyPurchaseGoogleOptions.fromJson(it) }, horizon = (json["horizon"] as Map?)?.let { VerifyPurchaseHorizonOptions.fromJson(it) }, - sku = json["sku"] as String, ) } } @@ -3234,7 +3232,6 @@ public data class VerifyPurchaseProps( "apple" to apple?.toJson(), "google" to google?.toJson(), "horizon" to horizon?.toJson(), - "sku" to sku, ) } 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 b3e978750..118b798cf 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 @@ -49,10 +49,11 @@ suspend fun verifyPurchaseWithGooglePlay( ) } + val sku = googleOptions.sku val typeSegment = if (isSub == true) "subscriptions" else "products" val baseUrl = "https://androidpublisher.googleapis.com/androidpublisher/v3/applications" val url = "$baseUrl/${encodePathSegment(packageName)}/purchases/$typeSegment/" + - "${encodePathSegment(props.sku)}/tokens/${encodePathSegment(purchaseToken)}" + "${encodePathSegment(sku)}/tokens/${encodePathSegment(purchaseToken)}" val connection = connectionFactory(url).apply { requestMethod = "GET" 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 940265d40..549edd02f 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 @@ -26,7 +26,7 @@ class PurchaseVerificationValidatorTest { @Test fun `verifyPurchaseWithGooglePlay throws without google options`() = runTest { - val props = VerifyPurchaseProps(sku = "product.sku") + val props = VerifyPurchaseProps() try { verifyPurchaseWithGooglePlay(props, "TEST_TAG") { _ -> @@ -44,9 +44,10 @@ class PurchaseVerificationValidatorTest { accessToken = "token", isSub = true, packageName = "dev.hyo.app", - purchaseToken = "purchaseToken" + purchaseToken = "purchaseToken", + sku = "premium_monthly" ) - val props = VerifyPurchaseProps(google = googleOptions, sku = "premium_monthly") + val props = VerifyPurchaseProps(google = googleOptions) val body = """ { "autoRenewing": true, @@ -86,9 +87,10 @@ class PurchaseVerificationValidatorTest { accessToken = "token", isSub = true, packageName = "dev.hyo.app", - purchaseToken = "purchaseToken" + purchaseToken = "purchaseToken", + sku = "premium_monthly" ) - val props = VerifyPurchaseProps(google = googleOptions, sku = "premium_monthly") + val props = VerifyPurchaseProps(google = googleOptions) val body = """ { "autoRenewing": true, @@ -130,9 +132,10 @@ class PurchaseVerificationValidatorTest { accessToken = "token", isSub = false, packageName = "dev.hyo.app", - purchaseToken = "purchaseToken" + purchaseToken = "purchaseToken", + sku = "premium_monthly" ) - val props = VerifyPurchaseProps(google = googleOptions, sku = "premium_monthly") + val props = VerifyPurchaseProps(google = googleOptions) try { verifyPurchaseWithGooglePlay( @@ -268,7 +271,7 @@ class PurchaseVerificationValidatorTest { @Test fun `verifyPurchaseWithHorizon throws without horizon options`() = runTest { - val props = VerifyPurchaseProps(sku = "50_gems") + val props = VerifyPurchaseProps() try { verifyPurchaseWithHorizon(props, "test-app-id", "TEST") { _ -> @@ -287,7 +290,7 @@ class PurchaseVerificationValidatorTest { userId = "123456789", accessToken = "OC|app_id|app_secret" ) - val props = VerifyPurchaseProps(horizon = horizonOptions, sku = "50_gems") + val props = VerifyPurchaseProps(horizon = horizonOptions) val result = verifyPurchaseWithHorizon( props, @@ -306,7 +309,7 @@ class PurchaseVerificationValidatorTest { userId = "123456789", accessToken = "OC|app_id|app_secret" ) - val props = VerifyPurchaseProps(horizon = horizonOptions, sku = "50_gems") + val props = VerifyPurchaseProps(horizon = horizonOptions) try { verifyPurchaseWithHorizon( diff --git a/packages/gql/src/generated/Types.kt b/packages/gql/src/generated/Types.kt index a060f3183..304fe050a 100644 --- a/packages/gql/src/generated/Types.kt +++ b/packages/gql/src/generated/Types.kt @@ -3159,26 +3159,23 @@ public data class SubscriptionProductReplacementParamsAndroid( /** * Apple App Store verification parameters. * Used for server-side receipt validation via App Store Server API. - * - * ⚠️ SECURITY: Contains sensitive token (jws). Do not log or persist this data. */ public data class VerifyPurchaseAppleOptions( /** - * The JWS (JSON Web Signature) representation of the transaction. - * ⚠️ Sensitive: Do not log this value. + * Product SKU to validate */ - val jws: String + val sku: String ) { companion object { fun fromJson(json: Map): VerifyPurchaseAppleOptions { return VerifyPurchaseAppleOptions( - jws = json["jws"] as String, + sku = json["sku"] as String, ) } } fun toJson(): Map = mapOf( - "jws" to jws, + "sku" to sku, ) } @@ -3206,7 +3203,11 @@ public data class VerifyPurchaseGoogleOptions( * Purchase token from the purchase response. * ⚠️ Sensitive: Do not log this value. */ - val purchaseToken: String + val purchaseToken: String, + /** + * Product SKU to validate + */ + val sku: String ) { companion object { fun fromJson(json: Map): VerifyPurchaseGoogleOptions { @@ -3215,6 +3216,7 @@ public data class VerifyPurchaseGoogleOptions( isSub = json["isSub"] as Boolean?, packageName = json["packageName"] as String, purchaseToken = json["purchaseToken"] as String, + sku = json["sku"] as String, ) } } @@ -3224,6 +3226,7 @@ public data class VerifyPurchaseGoogleOptions( "isSub" to isSub, "packageName" to packageName, "purchaseToken" to purchaseToken, + "sku" to sku, ) } @@ -3285,11 +3288,7 @@ public data class VerifyPurchaseProps( /** * Meta Horizon (Quest) verification parameters. */ - val horizon: VerifyPurchaseHorizonOptions? = null, - /** - * Product SKU to validate - */ - val sku: String + val horizon: VerifyPurchaseHorizonOptions? = null ) { companion object { fun fromJson(json: Map): VerifyPurchaseProps { @@ -3297,7 +3296,6 @@ public data class VerifyPurchaseProps( apple = (json["apple"] as Map?)?.let { VerifyPurchaseAppleOptions.fromJson(it) }, google = (json["google"] as Map?)?.let { VerifyPurchaseGoogleOptions.fromJson(it) }, horizon = (json["horizon"] as Map?)?.let { VerifyPurchaseHorizonOptions.fromJson(it) }, - sku = json["sku"] as String, ) } } @@ -3306,7 +3304,6 @@ public data class VerifyPurchaseProps( "apple" to apple?.toJson(), "google" to google?.toJson(), "horizon" to horizon?.toJson(), - "sku" to sku, ) } diff --git a/packages/gql/src/generated/Types.swift b/packages/gql/src/generated/Types.swift index 42365f341..a56f06024 100644 --- a/packages/gql/src/generated/Types.swift +++ b/packages/gql/src/generated/Types.swift @@ -1337,17 +1337,14 @@ public struct SubscriptionProductReplacementParamsAndroid: Codable { /// Apple App Store verification parameters. /// Used for server-side receipt validation via App Store Server API. -/// -/// ⚠️ SECURITY: Contains sensitive token (jws). Do not log or persist this data. public struct VerifyPurchaseAppleOptions: Codable { - /// The JWS (JSON Web Signature) representation of the transaction. - /// ⚠️ Sensitive: Do not log this value. - public var jws: String + /// Product SKU to validate + public var sku: String public init( - jws: String + sku: String ) { - self.jws = jws + self.sku = sku } } @@ -1366,17 +1363,21 @@ public struct VerifyPurchaseGoogleOptions: Codable { /// Purchase token from the purchase response. /// ⚠️ Sensitive: Do not log this value. public var purchaseToken: String + /// Product SKU to validate + public var sku: String public init( accessToken: String, isSub: Bool? = nil, packageName: String, - purchaseToken: String + purchaseToken: String, + sku: String ) { self.accessToken = accessToken self.isSub = isSub self.packageName = packageName self.purchaseToken = purchaseToken + self.sku = sku } } @@ -1417,19 +1418,15 @@ public struct VerifyPurchaseProps: Codable { public var google: VerifyPurchaseGoogleOptions? /// Meta Horizon (Quest) verification parameters. public var horizon: VerifyPurchaseHorizonOptions? - /// Product SKU to validate - public var sku: String public init( apple: VerifyPurchaseAppleOptions? = nil, google: VerifyPurchaseGoogleOptions? = nil, - horizon: VerifyPurchaseHorizonOptions? = nil, - sku: String + horizon: VerifyPurchaseHorizonOptions? = nil ) { self.apple = apple self.google = google self.horizon = horizon - self.sku = sku } } diff --git a/packages/gql/src/generated/types.dart b/packages/gql/src/generated/types.dart index ecde6bfed..6e9c8c2f9 100644 --- a/packages/gql/src/generated/types.dart +++ b/packages/gql/src/generated/types.dart @@ -3624,28 +3624,24 @@ class SubscriptionProductReplacementParamsAndroid { /// Apple App Store verification parameters. /// Used for server-side receipt validation via App Store Server API. -/// -/// ⚠️ SECURITY: Contains sensitive token (jws). Do not log or persist this data. class VerifyPurchaseAppleOptions { const VerifyPurchaseAppleOptions({ - /// The JWS (JSON Web Signature) representation of the transaction. - /// ⚠️ Sensitive: Do not log this value. - required this.jws, + /// Product SKU to validate + required this.sku, }); - /// The JWS (JSON Web Signature) representation of the transaction. - /// ⚠️ Sensitive: Do not log this value. - final String jws; + /// Product SKU to validate + final String sku; factory VerifyPurchaseAppleOptions.fromJson(Map json) { return VerifyPurchaseAppleOptions( - jws: json['jws'] as String, + sku: json['sku'] as String, ); } Map toJson() { return { - 'jws': jws, + 'sku': sku, }; } } @@ -3666,6 +3662,8 @@ class VerifyPurchaseGoogleOptions { /// Purchase token from the purchase response. /// ⚠️ Sensitive: Do not log this value. required this.purchaseToken, + /// Product SKU to validate + required this.sku, }); /// Google OAuth2 access token for API authentication. @@ -3678,6 +3676,8 @@ class VerifyPurchaseGoogleOptions { /// Purchase token from the purchase response. /// ⚠️ Sensitive: Do not log this value. final String purchaseToken; + /// Product SKU to validate + final String sku; factory VerifyPurchaseGoogleOptions.fromJson(Map json) { return VerifyPurchaseGoogleOptions( @@ -3685,6 +3685,7 @@ class VerifyPurchaseGoogleOptions { isSub: json['isSub'] as bool?, packageName: json['packageName'] as String, purchaseToken: json['purchaseToken'] as String, + sku: json['sku'] as String, ); } @@ -3694,6 +3695,7 @@ class VerifyPurchaseGoogleOptions { 'isSub': isSub, 'packageName': packageName, 'purchaseToken': purchaseToken, + 'sku': sku, }; } } @@ -3752,8 +3754,6 @@ class VerifyPurchaseProps { this.google, /// Meta Horizon (Quest) verification parameters. this.horizon, - /// Product SKU to validate - required this.sku, }); /// Apple App Store verification parameters. @@ -3762,15 +3762,12 @@ class VerifyPurchaseProps { final VerifyPurchaseGoogleOptions? google; /// Meta Horizon (Quest) verification parameters. final VerifyPurchaseHorizonOptions? horizon; - /// Product SKU to validate - final String sku; factory VerifyPurchaseProps.fromJson(Map json) { return VerifyPurchaseProps( apple: json['apple'] != null ? VerifyPurchaseAppleOptions.fromJson(json['apple'] as Map) : null, google: json['google'] != null ? VerifyPurchaseGoogleOptions.fromJson(json['google'] as Map) : null, horizon: json['horizon'] != null ? VerifyPurchaseHorizonOptions.fromJson(json['horizon'] as Map) : null, - sku: json['sku'] as String, ); } @@ -3779,7 +3776,6 @@ class VerifyPurchaseProps { 'apple': apple?.toJson(), 'google': google?.toJson(), 'horizon': horizon?.toJson(), - 'sku': sku, }; } } @@ -4058,14 +4054,12 @@ abstract class MutationResolver { VerifyPurchaseAppleOptions? apple, VerifyPurchaseGoogleOptions? google, VerifyPurchaseHorizonOptions? horizon, - required String sku, }); /// Verify purchases with the configured providers Future verifyPurchase({ VerifyPurchaseAppleOptions? apple, VerifyPurchaseGoogleOptions? google, VerifyPurchaseHorizonOptions? horizon, - required String sku, }); /// Verify purchases with a specific provider (e.g., IAPKit) Future verifyPurchaseWithProvider({ @@ -4121,7 +4115,6 @@ abstract class QueryResolver { VerifyPurchaseAppleOptions? apple, VerifyPurchaseGoogleOptions? google, VerifyPurchaseHorizonOptions? horizon, - required String sku, }); } @@ -4173,13 +4166,11 @@ typedef MutationValidateReceiptHandler = Future Function({ VerifyPurchaseAppleOptions? apple, VerifyPurchaseGoogleOptions? google, VerifyPurchaseHorizonOptions? horizon, - required String sku, }); typedef MutationVerifyPurchaseHandler = Future Function({ VerifyPurchaseAppleOptions? apple, VerifyPurchaseGoogleOptions? google, VerifyPurchaseHorizonOptions? horizon, - required String sku, }); typedef MutationVerifyPurchaseWithProviderHandler = Future Function({ RequestVerifyPurchaseWithIapkitProps? iapkit, @@ -4265,7 +4256,6 @@ typedef QueryValidateReceiptIOSHandler = Future Functio VerifyPurchaseAppleOptions? apple, VerifyPurchaseGoogleOptions? google, VerifyPurchaseHorizonOptions? horizon, - required String sku, }); class QueryHandlers { diff --git a/packages/gql/src/generated/types.ts b/packages/gql/src/generated/types.ts index 2f7ebf1a6..65eb2f933 100644 --- a/packages/gql/src/generated/types.ts +++ b/packages/gql/src/generated/types.ts @@ -1113,15 +1113,10 @@ export interface ValidTimeWindowAndroid { /** * Apple App Store verification parameters. * Used for server-side receipt validation via App Store Server API. - * - * ⚠️ SECURITY: Contains sensitive token (jws). Do not log or persist this data. */ export interface VerifyPurchaseAppleOptions { - /** - * The JWS (JSON Web Signature) representation of the transaction. - * ⚠️ Sensitive: Do not log this value. - */ - jws: string; + /** Product SKU to validate */ + sku: string; } /** @@ -1145,6 +1140,8 @@ export interface VerifyPurchaseGoogleOptions { * ⚠️ Sensitive: Do not log this value. */ purchaseToken: string; + /** Product SKU to validate */ + sku: string; } /** @@ -1180,8 +1177,6 @@ export interface VerifyPurchaseProps { google?: (VerifyPurchaseGoogleOptions | null); /** Meta Horizon (Quest) verification parameters. */ horizon?: (VerifyPurchaseHorizonOptions | null); - /** Product SKU to validate */ - sku: string; } export type VerifyPurchaseResult = VerifyPurchaseResultAndroid | VerifyPurchaseResultHorizon | VerifyPurchaseResultIOS; diff --git a/packages/gql/src/type-android.graphql b/packages/gql/src/type-android.graphql index 57a9175b0..eac0b2f06 100644 --- a/packages/gql/src/type-android.graphql +++ b/packages/gql/src/type-android.graphql @@ -377,6 +377,10 @@ Used for server-side receipt validation via Google Play Developer API. ⚠️ SECURITY: Contains sensitive tokens (accessToken, purchaseToken). Do not log or persist this data. """ input VerifyPurchaseGoogleOptions { + """ + Product SKU to validate + """ + sku: String! """ Android package name (e.g., com.example.app) """ diff --git a/packages/gql/src/type-ios.graphql b/packages/gql/src/type-ios.graphql index d4b036a11..a67bfefdc 100644 --- a/packages/gql/src/type-ios.graphql +++ b/packages/gql/src/type-ios.graphql @@ -230,15 +230,12 @@ input DiscountOfferInputIOS { """ Apple App Store verification parameters. Used for server-side receipt validation via App Store Server API. - -⚠️ SECURITY: Contains sensitive token (jws). Do not log or persist this data. """ input VerifyPurchaseAppleOptions { """ - The JWS (JSON Web Signature) representation of the transaction. - ⚠️ Sensitive: Do not log this value. + Product SKU to validate """ - jws: String! + sku: String! } type VerifyPurchaseResultIOS { diff --git a/packages/gql/src/type.graphql b/packages/gql/src/type.graphql index 08e573234..80f291941 100644 --- a/packages/gql/src/type.graphql +++ b/packages/gql/src/type.graphql @@ -250,10 +250,6 @@ Platform-specific purchase verification parameters. - horizon: Verifies via Meta's S2S API (verify_entitlement endpoint) """ input VerifyPurchaseProps { - """ - Product SKU to validate - """ - sku: String! """ Apple App Store verification parameters. """