From b49067bc04a73764345f24135f8ad1364e27bbcd Mon Sep 17 00:00:00 2001 From: hyochan Date: Sun, 19 Apr 2026 17:57:38 +0900 Subject: [PATCH 1/5] fix(apple): migrate IAPKit verification host to kit.openiap.dev Update the IAPKit provider endpoint from api.iapkit.com to kit.openiap.dev. Request/response shape, bearer auth, and the /v1/purchase/verify path are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/apple/Sources/OpenIapModule.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apple/Sources/OpenIapModule.swift b/packages/apple/Sources/OpenIapModule.swift index 6c60a193..7cd95b39 100644 --- a/packages/apple/Sources/OpenIapModule.swift +++ b/packages/apple/Sources/OpenIapModule.swift @@ -706,7 +706,7 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol { // but it is never invoked from this module. private func verifyPurchaseWithIapkit(props: RequestVerifyPurchaseWithIapkitProps) async throws -> RequestVerifyPurchaseWithIapkitResult { // URL is a constant and cannot fail, so force unwrap is safe - let url = URL(string: "https://api.iapkit.com/v1/purchase/verify")! + let url = URL(string: "https://kit.openiap.dev/v1/purchase/verify")! // On Apple, only Apple verification is supported guard props.apple != nil else { From a95a2c9cca35f40c6c3920a2928d58562409b1d4 Mon Sep 17 00:00:00 2001 From: hyochan Date: Sun, 19 Apr 2026 17:57:42 +0900 Subject: [PATCH 2/5] fix(google): migrate IAPKit verification host to kit.openiap.dev Update the DEFAULT_IAPKIT_ENDPOINT from api.iapkit.com to kit.openiap.dev. Request/response shape, bearer auth, and the /v1/purchase/verify path are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 118b798c..cf829a81 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 @@ -18,7 +18,7 @@ import java.net.HttpURLConnection import java.net.URL import java.net.URLEncoder -private const val DEFAULT_IAPKIT_ENDPOINT = "https://api.iapkit.com/v1/purchase/verify" +private const val DEFAULT_IAPKIT_ENDPOINT = "https://kit.openiap.dev/v1/purchase/verify" private val gson = Gson() private fun openConnection(url: String): HttpURLConnection { From 52c7eed736b66c326f8d5f33906c13dd36f82737 Mon Sep 17 00:00:00 2001 From: hyochan Date: Sun, 19 Apr 2026 17:58:16 +0900 Subject: [PATCH 3/5] docs: migrate iapkit.com references to kit.openiap.dev - Update IAPKIT_URL constant and all inline href/text references - Add IAPKit nav button (top-right) linking to kit.openiap.dev - Add "Verify Purchase with IAPKit" section to /docs/features/purchase with examples across TypeScript, Swift, Kotlin, KMP, Dart, GDScript - Add 2026-04-19 release note documenting the host migration Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/docs/src/components/Navigation.tsx | 13 +- packages/docs/src/lib/config.ts | 2 +- packages/docs/src/pages/docs/example.tsx | 4 +- .../docs/src/pages/docs/features/purchase.tsx | 238 ++++++++++++++++++ .../src/pages/docs/lifecycle/subscription.tsx | 2 +- .../src/pages/docs/types/verification.tsx | 2 +- .../docs/src/pages/docs/updates/releases.tsx | 61 +++++ packages/docs/src/styles/navigation.css | 30 +++ 8 files changed, 346 insertions(+), 6 deletions(-) diff --git a/packages/docs/src/components/Navigation.tsx b/packages/docs/src/components/Navigation.tsx index cd78234a..1fde9da5 100644 --- a/packages/docs/src/components/Navigation.tsx +++ b/packages/docs/src/components/Navigation.tsx @@ -4,7 +4,7 @@ import { DarkModeToggle } from './DarkModeToggle'; import { Menu, X } from 'lucide-react'; import { FaGithub, FaSearch } from 'react-icons/fa'; import { openSearchModal } from '../lib/signals'; -import { LOGO_PATH } from '../lib/config'; +import { IAPKIT_URL, LOGO_PATH, trackIapKitClick } from '../lib/config'; function Navigation() { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); @@ -96,6 +96,17 @@ function Navigation() { + {/* IAPKit Link */} + + IAPKit + + {/* GitHub Link */} - iapkit.com + kit.openiap.dev {' '} and configure it in Info.plist:
                     className="external-link"
                     onClick={trackIapKitClick}
                   >
-                    iapkit.com
+                    kit.openiap.dev
                   {' '}
                   and configure it in local.properties:
                   
 verifyOnServer(ProductPurchase purchase) async {
         
       
 
+      
+ + Verify Purchase with IAPKit + +

+ Don't want to run a verification backend?{' '} + + IAPKit + {' '} + is a hosted purchase verification service that validates App Store and + Google Play purchases for you. Use{' '} + verifyPurchaseWithProvider with the{' '} + 'iapkit' provider and pass the + platform-specific token (iOS JWS or Android purchase token) — no + server code required. +

+ +
+

+ ℹ️ Get an API key: Sign up at{' '} + + kit.openiap.dev + {' '} + to obtain an IAPKIT_API_KEY. You can pass it directly, + or configure it once in your app (Expo extra, Info.plist, + AndroidManifest, etc.) so the SDK picks it up automatically. +

+
+ + + {{ + typescript: ( + {`import { Platform } from 'react-native'; +import { verifyPurchaseWithProvider, type Purchase } from 'expo-iap'; + +const verifyWithIapkit = async (purchase: Purchase) => { + const result = await verifyPurchaseWithProvider({ + provider: 'iapkit', + iapkit: { + // apiKey is optional when configured via app config / Info.plist / AndroidManifest + apiKey: process.env.EXPO_PUBLIC_IAPKIT_API_KEY, + ...(Platform.OS === 'ios' + ? { apple: { jws: purchase.purchaseToken ?? '' } } + : { google: { purchaseToken: purchase.purchaseToken ?? '' } }), + }, + }); + + if (result.iapkit?.isValid) { + console.log('IAPKit verified:', result.iapkit.state); + return true; + } + + console.error('IAPKit verification failed'); + return false; +};`} + ), + swift: ( + {`import OpenIap + +func verifyWithIapkit(_ purchase: PurchaseIOS) async -> Bool { + let iapStore = OpenIapStore.shared + + do { + let result = try await iapStore.verifyPurchaseWithProvider( + VerifyPurchaseWithProviderProps( + provider: .iapkit, + iapkit: RequestVerifyPurchaseWithIapkitProps( + apiKey: Bundle.main.object(forInfoDictionaryKey: "IAPKIT_API_KEY") as? String, + apple: RequestVerifyPurchaseWithIapkitApple(jws: purchase.purchaseToken ?? "") + ) + ) + ) + + if result.iapkit?.isValid == true { + print("IAPKit verified: \\(result.iapkit?.state.rawValue ?? "")") + return true + } + + print("IAPKit verification failed") + return false + } catch { + print("IAPKit verification error: \\(error.localizedDescription)") + return false + } +}`} + ), + kotlin: ( + {`import dev.hyo.openiap.OpenIapStore +import dev.hyo.openiap.models.* + +suspend fun verifyWithIapkit(purchase: PurchaseAndroid): Boolean { + return try { + val result = iapStore.verifyPurchaseWithProvider( + VerifyPurchaseWithProviderProps( + provider = PurchaseVerificationProvider.Iapkit, + iapkit = RequestVerifyPurchaseWithIapkitProps( + // apiKey is optional when configured via AndroidManifest meta-data + apiKey = BuildConfig.IAPKIT_API_KEY, + google = RequestVerifyPurchaseWithIapkitGoogle( + purchaseToken = purchase.purchaseToken.orEmpty() + ) + ) + ) + ) + + if (result.iapkit?.isValid == true) { + println("IAPKit verified: \${result.iapkit?.state}") + true + } else { + println("IAPKit verification failed") + false + } + } catch (e: Exception) { + println("IAPKit verification error: \${e.message}") + false + } +}`} + ), + kmp: ( + {`import io.github.hyochan.kmpiap.KmpIAP +import io.github.hyochan.kmpiap.* + +suspend fun verifyWithIapkit(purchase: PurchaseAndroid): Boolean { + return try { + val result = kmpIAP.verifyPurchaseWithProvider( + VerifyPurchaseWithProviderProps( + provider = PurchaseVerificationProvider.Iapkit, + iapkit = RequestVerifyPurchaseWithIapkitProps( + apiKey = AppConfig.iapkitApiKey, + google = RequestVerifyPurchaseWithIapkitGoogle( + purchaseToken = purchase.purchaseToken.orEmpty() + ) + ) + ) + ) + + if (result.iapkit?.isValid == true) { + println("IAPKit verified: \${result.iapkit?.state}") + true + } else { + println("IAPKit verification failed") + false + } + } catch (e: Exception) { + println("IAPKit verification error: \${e.message}") + false + } +}`} + ), + dart: ( + {`import 'dart:io'; +import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart'; + +Future verifyWithIapkit(ProductPurchase purchase) async { + final iap = FlutterInappPurchase.instance; + + try { + final result = await iap.verifyPurchaseWithProvider( + VerifyPurchaseWithProviderProps( + provider: PurchaseVerificationProvider.iapkit, + iapkit: RequestVerifyPurchaseWithIapkitProps( + apiKey: IapConstants.iapkitApiKey, + apple: Platform.isIOS + ? RequestVerifyPurchaseWithIapkitApple( + jws: purchase.purchaseToken ?? '', + ) + : null, + google: Platform.isAndroid + ? RequestVerifyPurchaseWithIapkitGoogle( + purchaseToken: purchase.purchaseToken ?? '', + ) + : null, + ), + ), + ); + + if (result.iapkit?.isValid == true) { + print('IAPKit verified: \${result.iapkit?.state}'); + return true; + } + + print('IAPKit verification failed'); + return false; + } catch (e) { + print('IAPKit verification error: $e'); + return false; + } +}`} + ), + gdscript: ( + {`func verify_with_iapkit(purchase: Purchase) -> bool: + var props = VerifyPurchaseWithProviderProps.new() + props.provider = PurchaseVerificationProvider.IAPKIT + props.iapkit = RequestVerifyPurchaseWithIapkitProps.new() + props.iapkit.api_key = AppConfig.iapkit_api_key + + if OS.get_name() == "iOS": + props.iapkit.apple = RequestVerifyPurchaseWithIapkitApple.new() + props.iapkit.apple.jws = purchase.purchase_token + else: + props.iapkit.google = RequestVerifyPurchaseWithIapkitGoogle.new() + props.iapkit.google.purchase_token = purchase.purchase_token + + var result = await iap.verify_purchase_with_provider(props) + + if result.iapkit and result.iapkit.is_valid: + print("IAPKit verified: %s" % result.iapkit.state) + return true + + print("IAPKit verification failed") + return false`} + ), + }} + + +
+

+ ℹ️ Endpoint: Requests are sent to{' '} + https://kit.openiap.dev/v1/purchase/verify with{' '} + Authorization: Bearer <apiKey>. See the{' '} + + PurchaseVerificationProvider + {' '} + type reference for the full response shape. +

+
+
+
Finish Transaction diff --git a/packages/docs/src/pages/docs/lifecycle/subscription.tsx b/packages/docs/src/pages/docs/lifecycle/subscription.tsx index 745d8aef..95f2840b 100644 --- a/packages/docs/src/pages/docs/lifecycle/subscription.tsx +++ b/packages/docs/src/pages/docs/lifecycle/subscription.tsx @@ -141,7 +141,7 @@ function Subscription() {

Recommendation: Use{' '} diff --git a/packages/docs/src/pages/docs/types/verification.tsx b/packages/docs/src/pages/docs/types/verification.tsx index 36fb2032..c49a96cb 100644 --- a/packages/docs/src/pages/docs/types/verification.tsx +++ b/packages/docs/src/pages/docs/types/verification.tsx @@ -646,7 +646,7 @@ function TypesVerification() { diff --git a/packages/docs/src/pages/docs/updates/releases.tsx b/packages/docs/src/pages/docs/updates/releases.tsx index 3d4327b8..58d82fb9 100644 --- a/packages/docs/src/pages/docs/updates/releases.tsx +++ b/packages/docs/src/pages/docs/updates/releases.tsx @@ -25,6 +25,67 @@ function Releases() { useScrollToHash(); const allNotes: Note[] = [ + // April 19, 2026 — IAPKit verification host migration + { + id: 'releases-2026-04-19', + date: new Date('2026-04-19'), + element: ( +

+ ), + }, + // April 17, 2026 — Advanced Commerce & Transaction History { id: 'releases-2026-04-17', diff --git a/packages/docs/src/styles/navigation.css b/packages/docs/src/styles/navigation.css index 5a2fed55..2ac864ce 100644 --- a/packages/docs/src/styles/navigation.css +++ b/packages/docs/src/styles/navigation.css @@ -87,6 +87,36 @@ background: var(--bg-secondary); } +/* IAPKit Link */ +.iapkit-link { + display: inline-flex; + align-items: center; + justify-content: center; + height: 32px; + padding: 0 0.75rem; + border-radius: 6px; + font-size: 0.85rem; + font-weight: 600; + color: #ffffff; + background: linear-gradient( + 135deg, + var(--primary-color), + var(--accent-color) + ); + text-decoration: none; + transition: + transform 0.15s, + box-shadow 0.15s, + opacity 0.15s; + white-space: nowrap; +} + +.iapkit-link:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + opacity: 0.95; +} + /* Mobile menu button */ .mobile-menu-button { display: none; From e94d8e52dfd787d3438230bc9d32dcaf1521c10d Mon Sep 17 00:00:00 2001 From: hyochan Date: Sun, 19 Apr 2026 17:58:27 +0900 Subject: [PATCH 4/5] chore(libraries): update IAPKit URL references to kit.openiap.dev Update comments, env examples, and plugin JSDoc across expo-iap, react-native-iap, flutter_inapp_purchase, and kmp-iap so all "Get your API key from" pointers target kit.openiap.dev. Co-Authored-By: Claude Opus 4.7 (1M context) --- bun.lock | 4 ++-- libraries/expo-iap/example/.env.example | 2 +- libraries/expo-iap/example/app.config.ts | 2 +- libraries/expo-iap/plugin/src/withIAP.ts | 2 +- libraries/flutter_inapp_purchase/example/env.example | 2 +- .../flutter_inapp_purchase/example/lib/src/constants.dart | 2 +- libraries/kmp-iap/.env.example | 2 +- libraries/kmp-iap/example/.env.example | 2 +- .../src/commonMain/kotlin/dev/hyo/martie/config/AppConfig.kt | 2 +- .../example/iosApp/Configuration/Secrets.xcconfig.example | 2 +- libraries/react-native-iap/example-expo/.env.example | 2 +- libraries/react-native-iap/example-expo/app.config.ts | 2 +- libraries/react-native-iap/example/.env.example | 2 +- libraries/react-native-iap/plugin/src/withIAP.ts | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bun.lock b/bun.lock index 9e78d072..ee54ca7c 100644 --- a/bun.lock +++ b/bun.lock @@ -16,7 +16,7 @@ }, "packages/docs": { "name": "@hyodotdev/openiap-docs", - "version": "1.0.0", + "version": "2.0.0", "dependencies": { "@preact/signals-react": "^3.2.1", "@types/prismjs": "^1.26.5", @@ -61,7 +61,7 @@ }, "packages/gql": { "name": "@hyodotdev/openiap-gql", - "version": "1.0.0", + "version": "2.0.0", "devDependencies": { "@graphql-codegen/add": "^6.0.0", "@graphql-codegen/cli": "^6.0.0", diff --git a/libraries/expo-iap/example/.env.example b/libraries/expo-iap/example/.env.example index 79be8836..619c5fa5 100644 --- a/libraries/expo-iap/example/.env.example +++ b/libraries/expo-iap/example/.env.example @@ -1,3 +1,3 @@ # IAPKit Configuration -# Get your API key from https://iapkit.com +# Get your API key from https://kit.openiap.dev EXPO_PUBLIC_IAPKIT_API_KEY=your_iapkit_api_key_here diff --git a/libraries/expo-iap/example/app.config.ts b/libraries/expo-iap/example/app.config.ts index e75c0954..4cada57a 100644 --- a/libraries/expo-iap/example/app.config.ts +++ b/libraries/expo-iap/example/app.config.ts @@ -30,7 +30,7 @@ export default ({config}: ConfigContext): ExpoConfig => { '../app.plugin.js', { // IAPKit API key for server-side receipt verification - // Get your API key from https://iapkit.com + // Get your API key from https://kit.openiap.dev iapkitApiKey: process.env.EXPO_PUBLIC_IAPKIT_API_KEY, enableLocalDev: useLocalDev, localPath: { diff --git a/libraries/expo-iap/plugin/src/withIAP.ts b/libraries/expo-iap/plugin/src/withIAP.ts index 3a7d17a1..af7364f2 100644 --- a/libraries/expo-iap/plugin/src/withIAP.ts +++ b/libraries/expo-iap/plugin/src/withIAP.ts @@ -489,7 +489,7 @@ const withIapIOS: ConfigPlugin = ( export interface ExpoIapPluginOptions { /** * IAPKit API key for server-side receipt verification. - * Get your API key from https://iapkit.com + * Get your API key from https://kit.openiap.dev * This will be available via `Constants.expoConfig?.extra?.iapkitApiKey` */ iapkitApiKey?: string; diff --git a/libraries/flutter_inapp_purchase/example/env.example b/libraries/flutter_inapp_purchase/example/env.example index ec6aa10e..a52704fc 100644 --- a/libraries/flutter_inapp_purchase/example/env.example +++ b/libraries/flutter_inapp_purchase/example/env.example @@ -1,3 +1,3 @@ # IAPKit API Key for purchase verification -# Get your API key from https://iapkit.com +# Get your API key from https://kit.openiap.dev IAPKIT_API_KEY=your_iapkit_api_key_here diff --git a/libraries/flutter_inapp_purchase/example/lib/src/constants.dart b/libraries/flutter_inapp_purchase/example/lib/src/constants.dart index 16e75fdd..a0002793 100644 --- a/libraries/flutter_inapp_purchase/example/lib/src/constants.dart +++ b/libraries/flutter_inapp_purchase/example/lib/src/constants.dart @@ -3,7 +3,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; // Product IDs for testing in the example app class IapConstants { // IAPKit API Key for purchase verification - // Get your API key from https://iapkit.com + // Get your API key from https://kit.openiap.dev static String get iapkitApiKey => dotenv.env['IAPKIT_API_KEY'] ?? ''; // Consumable Product IDs diff --git a/libraries/kmp-iap/.env.example b/libraries/kmp-iap/.env.example index ec6aa10e..a52704fc 100644 --- a/libraries/kmp-iap/.env.example +++ b/libraries/kmp-iap/.env.example @@ -1,3 +1,3 @@ # IAPKit API Key for purchase verification -# Get your API key from https://iapkit.com +# Get your API key from https://kit.openiap.dev IAPKIT_API_KEY=your_iapkit_api_key_here diff --git a/libraries/kmp-iap/example/.env.example b/libraries/kmp-iap/example/.env.example index 2a220323..be95248c 100644 --- a/libraries/kmp-iap/example/.env.example +++ b/libraries/kmp-iap/example/.env.example @@ -1,3 +1,3 @@ # IAPKit Configuration -# Get your API key from https://iapkit.com +# Get your API key from https://kit.openiap.dev IAPKIT_API_KEY=your_iapkit_api_key_here diff --git a/libraries/kmp-iap/example/composeApp/src/commonMain/kotlin/dev/hyo/martie/config/AppConfig.kt b/libraries/kmp-iap/example/composeApp/src/commonMain/kotlin/dev/hyo/martie/config/AppConfig.kt index a8a03a2d..6c99154d 100644 --- a/libraries/kmp-iap/example/composeApp/src/commonMain/kotlin/dev/hyo/martie/config/AppConfig.kt +++ b/libraries/kmp-iap/example/composeApp/src/commonMain/kotlin/dev/hyo/martie/config/AppConfig.kt @@ -9,7 +9,7 @@ package dev.hyo.martie.config expect object AppConfig { /** * IAPKit API key for purchase verification. - * Get your API key from https://iapkit.com + * Get your API key from https://kit.openiap.dev */ val iapkitApiKey: String } diff --git a/libraries/kmp-iap/example/iosApp/Configuration/Secrets.xcconfig.example b/libraries/kmp-iap/example/iosApp/Configuration/Secrets.xcconfig.example index 8beb5532..7bfbf77b 100644 --- a/libraries/kmp-iap/example/iosApp/Configuration/Secrets.xcconfig.example +++ b/libraries/kmp-iap/example/iosApp/Configuration/Secrets.xcconfig.example @@ -2,5 +2,5 @@ // Copy this file to Secrets.xcconfig and add your API keys // IAPKit API Key for purchase verification -// Get your API key from https://iapkit.com +// Get your API key from https://kit.openiap.dev IAPKIT_API_KEY=your_iapkit_api_key_here diff --git a/libraries/react-native-iap/example-expo/.env.example b/libraries/react-native-iap/example-expo/.env.example index 79be8836..619c5fa5 100644 --- a/libraries/react-native-iap/example-expo/.env.example +++ b/libraries/react-native-iap/example-expo/.env.example @@ -1,3 +1,3 @@ # IAPKit Configuration -# Get your API key from https://iapkit.com +# Get your API key from https://kit.openiap.dev EXPO_PUBLIC_IAPKIT_API_KEY=your_iapkit_api_key_here diff --git a/libraries/react-native-iap/example-expo/app.config.ts b/libraries/react-native-iap/example-expo/app.config.ts index ada2f4f8..385c2066 100644 --- a/libraries/react-native-iap/example-expo/app.config.ts +++ b/libraries/react-native-iap/example-expo/app.config.ts @@ -6,7 +6,7 @@ export default ({config}: ConfigContext): ExpoConfig => { 'react-native-iap', { // IAPKit API key for purchase verification (optional) - // Get your API key from https://iapkit.com + // Get your API key from https://kit.openiap.dev iapkitApiKey: process.env.EXPO_PUBLIC_IAPKIT_API_KEY, // iOS Alternative Billing configuration (optional) diff --git a/libraries/react-native-iap/example/.env.example b/libraries/react-native-iap/example/.env.example index 2a220323..be95248c 100644 --- a/libraries/react-native-iap/example/.env.example +++ b/libraries/react-native-iap/example/.env.example @@ -1,3 +1,3 @@ # IAPKit Configuration -# Get your API key from https://iapkit.com +# Get your API key from https://kit.openiap.dev IAPKIT_API_KEY=your_iapkit_api_key_here diff --git a/libraries/react-native-iap/plugin/src/withIAP.ts b/libraries/react-native-iap/plugin/src/withIAP.ts index fc0d9b8c..8075bdb4 100644 --- a/libraries/react-native-iap/plugin/src/withIAP.ts +++ b/libraries/react-native-iap/plugin/src/withIAP.ts @@ -363,7 +363,7 @@ type IapPluginProps = { /** * IAPKit API key for purchase verification. * This key will be added to AndroidManifest.xml (as meta-data) and Info.plist. - * Get your API key from https://iapkit.com + * Get your API key from https://kit.openiap.dev */ iapkitApiKey?: string; }; From 5a135d6ac73ff458b991a673a0e68967192f93e9 Mon Sep 17 00:00:00 2001 From: hyochan Date: Sun, 19 Apr 2026 18:26:31 +0900 Subject: [PATCH 5/5] docs: address PR #102 review feedback - Navigation: mirror IAPKit link in mobile menu (gemini-code-assist) - navigation.css: switch CTA text to #000000 for WCAG AA contrast over gradient (coderabbitai) - purchase.tsx IAPKit section: - Soften "no server code required" to clarify that accounts/entitlements should still be granted from a trusted backend (coderabbitai) - Swift: rename type to RequestVerifyPurchaseWithIapkitAppleProps and use the "IAPKitAPIKey" Info.plist key that matches the Expo plugin output (gemini-code-assist) - Kotlin/KMP/Dart/GDScript: rename Apple/Google props types to ...AppleProps / ...GoogleProps to match types/verification.tsx (gemini-code-assist) Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/docs/src/components/Navigation.tsx | 14 +++++++++++ .../docs/src/pages/docs/features/purchase.tsx | 24 +++++++++++-------- packages/docs/src/styles/navigation.css | 2 +- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/packages/docs/src/components/Navigation.tsx b/packages/docs/src/components/Navigation.tsx index 1fde9da5..fd05f7e5 100644 --- a/packages/docs/src/components/Navigation.tsx +++ b/packages/docs/src/components/Navigation.tsx @@ -180,6 +180,20 @@ function Navigation() { Sponsors + +
  • + { + trackIapKitClick(); + closeMobileMenu(); + }} + > + IAPKit + +
  • diff --git a/packages/docs/src/pages/docs/features/purchase.tsx b/packages/docs/src/pages/docs/features/purchase.tsx index a15f1b93..d7aa8801 100644 --- a/packages/docs/src/pages/docs/features/purchase.tsx +++ b/packages/docs/src/pages/docs/features/purchase.tsx @@ -691,7 +691,8 @@ Future verifyOnServer(ProductPurchase purchase) async { Verify Purchase with IAPKit

    - Don't want to run a verification backend?{' '} + Don't want to implement App Store / Google Play verification + yourself?{' '} verifyOnServer(ProductPurchase purchase) async { verifyPurchaseWithProvider with the{' '} 'iapkit' provider and pass the platform-specific token (iOS JWS or Android purchase token) — no - server code required. + store-verification code required. If your app has server-side accounts + or entitlements, keep the final grant decision on your backend and + call IAPKit from that trusted path; client-only use is convenient, but + not tamper-proof.

    @@ -763,8 +767,8 @@ func verifyWithIapkit(_ purchase: PurchaseIOS) async -> Bool { VerifyPurchaseWithProviderProps( provider: .iapkit, iapkit: RequestVerifyPurchaseWithIapkitProps( - apiKey: Bundle.main.object(forInfoDictionaryKey: "IAPKIT_API_KEY") as? String, - apple: RequestVerifyPurchaseWithIapkitApple(jws: purchase.purchaseToken ?? "") + apiKey: Bundle.main.object(forInfoDictionaryKey: "IAPKitAPIKey") as? String, + apple: RequestVerifyPurchaseWithIapkitAppleProps(jws: purchase.purchaseToken ?? "") ) ) ) @@ -794,7 +798,7 @@ suspend fun verifyWithIapkit(purchase: PurchaseAndroid): Boolean { iapkit = RequestVerifyPurchaseWithIapkitProps( // apiKey is optional when configured via AndroidManifest meta-data apiKey = BuildConfig.IAPKIT_API_KEY, - google = RequestVerifyPurchaseWithIapkitGoogle( + google = RequestVerifyPurchaseWithIapkitGoogleProps( purchaseToken = purchase.purchaseToken.orEmpty() ) ) @@ -825,7 +829,7 @@ suspend fun verifyWithIapkit(purchase: PurchaseAndroid): Boolean { provider = PurchaseVerificationProvider.Iapkit, iapkit = RequestVerifyPurchaseWithIapkitProps( apiKey = AppConfig.iapkitApiKey, - google = RequestVerifyPurchaseWithIapkitGoogle( + google = RequestVerifyPurchaseWithIapkitGoogleProps( purchaseToken = purchase.purchaseToken.orEmpty() ) ) @@ -859,12 +863,12 @@ Future verifyWithIapkit(ProductPurchase purchase) async { iapkit: RequestVerifyPurchaseWithIapkitProps( apiKey: IapConstants.iapkitApiKey, apple: Platform.isIOS - ? RequestVerifyPurchaseWithIapkitApple( + ? RequestVerifyPurchaseWithIapkitAppleProps( jws: purchase.purchaseToken ?? '', ) : null, google: Platform.isAndroid - ? RequestVerifyPurchaseWithIapkitGoogle( + ? RequestVerifyPurchaseWithIapkitGoogleProps( purchaseToken: purchase.purchaseToken ?? '', ) : null, @@ -893,10 +897,10 @@ Future verifyWithIapkit(ProductPurchase purchase) async { props.iapkit.api_key = AppConfig.iapkit_api_key if OS.get_name() == "iOS": - props.iapkit.apple = RequestVerifyPurchaseWithIapkitApple.new() + props.iapkit.apple = RequestVerifyPurchaseWithIapkitAppleProps.new() props.iapkit.apple.jws = purchase.purchase_token else: - props.iapkit.google = RequestVerifyPurchaseWithIapkitGoogle.new() + props.iapkit.google = RequestVerifyPurchaseWithIapkitGoogleProps.new() props.iapkit.google.purchase_token = purchase.purchase_token var result = await iap.verify_purchase_with_provider(props) diff --git a/packages/docs/src/styles/navigation.css b/packages/docs/src/styles/navigation.css index 2ac864ce..f9cad7cb 100644 --- a/packages/docs/src/styles/navigation.css +++ b/packages/docs/src/styles/navigation.css @@ -97,7 +97,7 @@ border-radius: 6px; font-size: 0.85rem; font-weight: 600; - color: #ffffff; + color: #000000; background: linear-gradient( 135deg, var(--primary-color),