1+ import Foundation
2+ import StoreKit
3+
4+ // MARK: - Objective-C Bridge for Kotlin Multiplatform
5+
6+ @available ( iOS 15 . 0 , macOS 14 . 0 , * )
7+ @objc public extension OpenIapModule {
8+
9+ // MARK: - Connection Management
10+
11+ @objc func initConnectionWithCompletion( _ completion: @escaping ( Bool , Error ? ) -> Void ) {
12+ Task {
13+ do {
14+ let result = try await initConnection ( )
15+ completion ( result, nil )
16+ } catch {
17+ completion ( false , error)
18+ }
19+ }
20+ }
21+
22+ @objc func endConnectionWithCompletion( _ completion: @escaping ( Bool , Error ? ) -> Void ) {
23+ Task {
24+ do {
25+ let result = try await endConnection ( )
26+ completion ( result, nil )
27+ } catch {
28+ completion ( false , error)
29+ }
30+ }
31+ }
32+
33+ // MARK: - Product Management
34+
35+ @objc func fetchProductsWithSkus(
36+ _ skus: [ String ] ,
37+ type: String ? ,
38+ completion: @escaping ( [ Any ] ? , Error ? ) -> Void
39+ ) {
40+ Task {
41+ do {
42+ let productType = type. flatMap { ProductQueryType ( rawValue: $0) }
43+ let request = ProductRequest ( skus: skus, type: productType)
44+ let result = try await fetchProducts ( request)
45+
46+ switch result {
47+ case . products( let products) :
48+ // Extract ProductIOS from Product enum and convert to dictionaries
49+ let productIOS = ( products ?? [ ] ) . compactMap { product -> ProductIOS ? in
50+ guard case let . productIos( value) = product else { return nil }
51+ return value
52+ }
53+ print ( " [OpenIAP] Fetched \( productIOS. count) products " )
54+ let dictionaries = productIOS. map { OpenIapSerialization . encode ( $0) }
55+ completion ( dictionaries, nil )
56+
57+ case . subscriptions( let subscriptions) :
58+ // Extract ProductSubscriptionIOS from ProductSubscription enum and convert to dictionaries
59+ let subscriptionIOS = ( subscriptions ?? [ ] ) . compactMap { subscription -> ProductSubscriptionIOS ? in
60+ guard case let . productSubscriptionIos( value) = subscription else { return nil }
61+ return value
62+ }
63+ print ( " [OpenIAP] Fetched \( subscriptionIOS. count) subscriptions " )
64+ let dictionaries = subscriptionIOS. map { OpenIapSerialization . encode ( $0) }
65+ completion ( dictionaries, nil )
66+ }
67+ } catch {
68+ completion ( nil , error)
69+ }
70+ }
71+ }
72+
73+ @objc func getPromotedProductIOSWithCompletion( _ completion: @escaping ( Any ? , Error ? ) -> Void ) {
74+ Task {
75+ do {
76+ let product = try await getPromotedProductIOS ( )
77+ if let productIOS = product {
78+ // Convert ProductIOS to dictionary
79+ let dictionary = OpenIapSerialization . encode ( productIOS)
80+ completion ( dictionary, nil )
81+ } else {
82+ completion ( nil , nil )
83+ }
84+ } catch {
85+ completion ( nil , error)
86+ }
87+ }
88+ }
89+
90+ // MARK: - Purchase Management
91+
92+ @objc func requestPurchaseWithSku(
93+ _ sku: String ,
94+ quantity: Int ,
95+ type: String ? ,
96+ completion: @escaping ( Any ? , Error ? ) -> Void
97+ ) {
98+ Task {
99+ do {
100+ let productType = type. flatMap { ProductQueryType ( rawValue: $0) } ?? . inApp
101+ let iosProps = RequestPurchaseIosProps (
102+ andDangerouslyFinishTransactionAutomatically: nil ,
103+ appAccountToken: nil ,
104+ quantity: quantity,
105+ sku: sku,
106+ withOffer: nil
107+ )
108+ let props = RequestPurchaseProps (
109+ request: . purchase(
110+ RequestPurchasePropsByPlatforms ( android: nil , ios: iosProps)
111+ ) ,
112+ type: productType
113+ )
114+
115+ let result = try await requestPurchase ( props)
116+
117+ switch result {
118+ case . purchase( let purchase) :
119+ if let purchase = purchase {
120+ let dictionary = OpenIapSerialization . purchase ( purchase)
121+ completion ( dictionary, nil )
122+ } else {
123+ completion ( nil , nil )
124+ }
125+ case . purchases( let purchases) :
126+ if let firstPurchase = purchases? . first {
127+ let dictionary = OpenIapSerialization . purchase ( firstPurchase)
128+ completion ( dictionary, nil )
129+ } else {
130+ completion ( nil , nil )
131+ }
132+ case . none:
133+ completion ( nil , nil )
134+ }
135+ } catch {
136+ completion ( nil , error)
137+ }
138+ }
139+ }
140+
141+ @objc func restorePurchasesWithCompletion( _ completion: @escaping ( Error ? ) -> Void ) {
142+ Task {
143+ do {
144+ try await restorePurchases ( )
145+ completion ( nil )
146+ } catch {
147+ completion ( error)
148+ }
149+ }
150+ }
151+
152+ @objc func getAvailablePurchasesWithCompletion( _ completion: @escaping ( [ Any ] ? , Error ? ) -> Void ) {
153+ Task {
154+ do {
155+ let purchases = try await getAvailablePurchases ( nil )
156+ let dictionaries = OpenIapSerialization . purchases ( purchases)
157+ completion ( dictionaries, nil )
158+ } catch {
159+ completion ( nil , error)
160+ }
161+ }
162+ }
163+
164+ // MARK: - Transaction Management
165+
166+ @objc func finishTransactionWithPurchaseId(
167+ _ purchaseId: String ,
168+ productId: String ,
169+ isConsumable: Bool ,
170+ completion: @escaping ( Error ? ) -> Void
171+ ) {
172+ Task {
173+ do {
174+ let purchaseInput = PurchaseInput (
175+ id: purchaseId,
176+ ids: nil ,
177+ isAutoRenewing: false ,
178+ platform: . ios,
179+ productId: productId,
180+ purchaseState: . purchased,
181+ purchaseToken: nil ,
182+ quantity: 1 ,
183+ transactionDate: Date ( ) . timeIntervalSince1970
184+ )
185+ try await finishTransaction ( purchase: purchaseInput, isConsumable: isConsumable)
186+ completion ( nil )
187+ } catch {
188+ completion ( error)
189+ }
190+ }
191+ }
192+
193+ @objc func getPendingTransactionsIOSWithCompletion( _ completion: @escaping ( [ Any ] ? , Error ? ) -> Void ) {
194+ Task {
195+ do {
196+ let transactions = try await getPendingTransactionsIOS ( )
197+ // Convert [PurchaseIOS] to dictionaries directly
198+ let dictionaries = transactions. map { OpenIapSerialization . encode ( $0) }
199+ completion ( dictionaries, nil )
200+ } catch {
201+ completion ( nil , error)
202+ }
203+ }
204+ }
205+
206+ @objc func clearTransactionIOSWithCompletion( _ completion: @escaping ( Bool , Error ? ) -> Void ) {
207+ Task {
208+ do {
209+ let result = try await clearTransactionIOS ( )
210+ completion ( result, nil )
211+ } catch {
212+ completion ( false , error)
213+ }
214+ }
215+ }
216+
217+ // MARK: - Validation
218+
219+ @objc func getReceiptDataIOSWithCompletion( _ completion: @escaping ( String ? , Error ? ) -> Void ) {
220+ Task {
221+ do {
222+ let receipt = try await getReceiptDataIOS ( )
223+ completion ( receipt, nil )
224+ } catch {
225+ completion ( nil , error)
226+ }
227+ }
228+ }
229+
230+ // MARK: - Store Information
231+
232+ @objc func getStorefrontIOSWithCompletion( _ completion: @escaping ( String ? , Error ? ) -> Void ) {
233+ Task {
234+ do {
235+ let storefront = try await getStorefrontIOS ( )
236+ completion ( storefront, nil )
237+ } catch {
238+ completion ( nil , error)
239+ }
240+ }
241+ }
242+
243+ // MARK: - Subscription Management
244+
245+ @objc func getActiveSubscriptionsWithCompletion( _ completion: @escaping ( [ Any ] ? , Error ? ) -> Void ) {
246+ Task {
247+ do {
248+ let subscriptions = try await getActiveSubscriptions ( nil )
249+ let dictionaries = subscriptions. map { OpenIapSerialization . encode ( $0) }
250+ completion ( dictionaries, nil )
251+ } catch {
252+ completion ( nil , error)
253+ }
254+ }
255+ }
256+
257+ // MARK: - UI
258+
259+ @objc func presentCodeRedemptionSheetIOSWithCompletion( _ completion: @escaping ( Bool , Error ? ) -> Void ) {
260+ Task {
261+ do {
262+ let result = try await presentCodeRedemptionSheetIOS ( )
263+ completion ( result, nil )
264+ } catch {
265+ completion ( false , error)
266+ }
267+ }
268+ }
269+
270+ @objc func showManageSubscriptionsIOSWithCompletion( _ completion: @escaping ( [ Any ] ? , Error ? ) -> Void ) {
271+ Task {
272+ do {
273+ let purchases = try await showManageSubscriptionsIOS ( )
274+ // Convert [PurchaseIOS] to dictionaries directly
275+ let dictionaries = purchases. map { OpenIapSerialization . encode ( $0) }
276+ completion ( dictionaries, nil )
277+ } catch {
278+ completion ( nil , error)
279+ }
280+ }
281+ }
282+ }
0 commit comments