expo-iap 3.1.1-rc.2 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +0 -11
- package/build/ExpoIapModule.d.ts +0 -1
- package/build/ExpoIapModule.d.ts.map +1 -1
- package/build/ExpoIapModule.js +4 -29
- package/build/ExpoIapModule.js.map +1 -1
- package/build/useIAP.d.ts +0 -2
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +3 -8
- package/build/useIAP.js.map +1 -1
- package/coverage/clover.xml +7 -7
- package/coverage/coverage-final.json +5 -5
- package/coverage/lcov-report/index.html +1 -1
- package/coverage/lcov-report/src/helpers/index.html +1 -1
- package/coverage/lcov-report/src/helpers/subscription.ts.html +1 -1
- package/coverage/lcov-report/src/index.html +1 -1
- package/coverage/lcov-report/src/index.ts.html +1 -1
- package/coverage/lcov-report/src/modules/android.ts.html +1 -1
- package/coverage/lcov-report/src/modules/index.html +1 -1
- package/coverage/lcov-report/src/modules/ios.ts.html +1 -1
- package/coverage/lcov-report/src/utils/errorMapping.ts.html +1 -1
- package/coverage/lcov-report/src/utils/index.html +1 -1
- package/expo-module.config.json +3 -10
- package/jest.config.js +0 -1
- package/openiap-versions.json +1 -1
- package/package.json +3 -4
- package/plugin/build/withIAP.d.ts +9 -22
- package/plugin/build/withIAP.js +9 -157
- package/plugin/jest.config.js +3 -13
- package/plugin/src/expoConfig.augmentation.d.ts +1 -30
- package/plugin/src/withIAP.ts +18 -258
- package/plugin/tsconfig.json +1 -2
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/ExpoIapModule.ts +4 -45
- package/src/useIAP.ts +3 -11
- package/build/utils/constants.d.ts +0 -6
- package/build/utils/constants.d.ts.map +0 -1
- package/build/utils/constants.js +0 -19
- package/build/utils/constants.js.map +0 -1
- package/coverage/lcov-report/src/ExpoIap.types.ts.html +0 -1243
- package/coverage/lcov-report/src/PurchaseError.ts.html +0 -787
- package/coverage/lcov-report/src/purchase-error.ts.html +0 -880
- package/coverage/lcov-report/src/types/ExpoIapAndroid.types.ts.html +0 -493
- package/coverage/lcov-report/src/types/index.html +0 -116
- package/coverage/lcov-report/src/useIap.ts.html +0 -1483
- package/coverage/lcov-report/src/utils/purchase.ts.html +0 -241
- package/ios/onside/OnsideIapModule.swift +0 -489
- package/src/utils/constants.ts +0 -23
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
import ExpoModulesCore
|
|
2
|
-
import Foundation
|
|
3
|
-
import OpenIAP
|
|
4
|
-
|
|
5
|
-
private enum OnsideEvent: String {
|
|
6
|
-
case purchaseUpdated = "purchase-updated"
|
|
7
|
-
case purchaseError = "purchase-error"
|
|
8
|
-
case promotedProductIOS = "promoted-product-ios"
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
private enum OnsideBridgeError: Error, LocalizedError {
|
|
12
|
-
case sdkUnavailable
|
|
13
|
-
case notInitialized
|
|
14
|
-
case emptySkuList
|
|
15
|
-
case productNotFound(String)
|
|
16
|
-
case transactionNotFound(String)
|
|
17
|
-
case restoreInProgress
|
|
18
|
-
case queueError(String)
|
|
19
|
-
|
|
20
|
-
var errorDescription: String? {
|
|
21
|
-
switch self {
|
|
22
|
-
case .sdkUnavailable:
|
|
23
|
-
return "OnsideKit is not installed. Enable ios.onside.enabled to use this functionality."
|
|
24
|
-
case .notInitialized:
|
|
25
|
-
return "Connection not initialized. Call initConnection() first."
|
|
26
|
-
case .emptySkuList:
|
|
27
|
-
return "No product identifiers provided."
|
|
28
|
-
case .productNotFound(let sku):
|
|
29
|
-
return "Product with identifier \(sku) was not fetched. Call fetchProducts() first."
|
|
30
|
-
case .transactionNotFound(let id):
|
|
31
|
-
return "Could not locate transaction with id \(id)."
|
|
32
|
-
case .restoreInProgress:
|
|
33
|
-
return "A restore operation is already in progress."
|
|
34
|
-
case .queueError(let message):
|
|
35
|
-
return message
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
#if canImport(OnsideKit)
|
|
41
|
-
import OnsideKit
|
|
42
|
-
|
|
43
|
-
@available(iOS 16.0, *)
|
|
44
|
-
@MainActor
|
|
45
|
-
public final class OnsideIapModule: Module {
|
|
46
|
-
private var isInitialized = false
|
|
47
|
-
private var restoreContinuation: CheckedContinuation<Bool, Error>?
|
|
48
|
-
private let transactionObserver = OnsideTransactionObserverBridge()
|
|
49
|
-
private let productFetcher = OnsideProductFetcher()
|
|
50
|
-
private var productCache: [String: OnsideProduct] = [:]
|
|
51
|
-
private var transactionCache: [UUID: OnsidePaymentTransaction] = [:]
|
|
52
|
-
|
|
53
|
-
private let encoder: JSONEncoder = {
|
|
54
|
-
let encoder = JSONEncoder()
|
|
55
|
-
encoder.dateEncodingStrategy = .millisecondsSince1970
|
|
56
|
-
return encoder
|
|
57
|
-
}()
|
|
58
|
-
|
|
59
|
-
nonisolated public func definition() -> ModuleDefinition {
|
|
60
|
-
Name("ExpoIapOnside")
|
|
61
|
-
|
|
62
|
-
Constants {
|
|
63
|
-
OpenIapSerialization.errorCodes()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
Events(
|
|
67
|
-
OnsideEvent.purchaseUpdated.rawValue,
|
|
68
|
-
OnsideEvent.purchaseError.rawValue,
|
|
69
|
-
OnsideEvent.promotedProductIOS.rawValue
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
OnCreate {
|
|
73
|
-
Task { @MainActor in
|
|
74
|
-
self.configureObserverCallbacks()
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
OnDestroy {
|
|
79
|
-
Task { @MainActor in
|
|
80
|
-
self.cleanup()
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
AsyncFunction("initConnection") { () async throws -> Bool in
|
|
85
|
-
ExpoIapLog.payload("initConnectionOnside", payload: nil)
|
|
86
|
-
try await ensureObserverRegistered()
|
|
87
|
-
return true
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
AsyncFunction("endConnection") { () async throws -> Bool in
|
|
91
|
-
ExpoIapLog.payload("endConnectionOnside", payload: nil)
|
|
92
|
-
cleanup()
|
|
93
|
-
return true
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
AsyncFunction("fetchProducts") { (params: [String: Any]) async throws -> [[String: Any]] in
|
|
97
|
-
ExpoIapLog.payload("fetchProductsOnside", payload: params)
|
|
98
|
-
try await ensureObserverRegistered()
|
|
99
|
-
|
|
100
|
-
let request = try ExpoIapHelper.decodeProductRequest(from: params)
|
|
101
|
-
guard !request.skus.isEmpty else {
|
|
102
|
-
throw OnsideBridgeError.emptySkuList
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let response = try await productFetcher.fetch(identifiers: Set(request.skus))
|
|
106
|
-
|
|
107
|
-
if !response.invalidProductIdentifiers.isEmpty {
|
|
108
|
-
throw OnsideBridgeError.productNotFound(response.invalidProductIdentifiers.joined(separator: ", "))
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
response.products.forEach { productCache[$0.productIdentifier] = $0 }
|
|
112
|
-
|
|
113
|
-
let payload = try response.products.map { try serializeProduct($0) }
|
|
114
|
-
ExpoIapLog.result("fetchProductsOnside", value: payload)
|
|
115
|
-
return payload
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
AsyncFunction("requestPurchase") { (payload: [String: Any]) async throws -> Any? in
|
|
119
|
-
ExpoIapLog.payload("requestPurchaseOnside", payload: payload)
|
|
120
|
-
try await ensureObserverRegistered()
|
|
121
|
-
|
|
122
|
-
guard let sku = resolveSku(from: payload) else {
|
|
123
|
-
throw OnsideBridgeError.emptySkuList
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
try await ensureProductAvailable(sku: sku)
|
|
127
|
-
guard let product = productCache[sku] else {
|
|
128
|
-
throw OnsideBridgeError.productNotFound(sku)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
|
|
132
|
-
Onside.paymentQueue().add(product) { result in
|
|
133
|
-
switch result {
|
|
134
|
-
case .success:
|
|
135
|
-
continuation.resume()
|
|
136
|
-
case .failure(let error):
|
|
137
|
-
continuation.resume(throwing: OnsideBridgeError.queueError(error.localizedDescription))
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
ExpoIapLog.result("requestPurchaseOnside", value: nil as Any?)
|
|
143
|
-
return nil
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
AsyncFunction("finishTransaction") {
|
|
147
|
-
(purchasePayload: [String: Any], _: Bool?) async throws -> Bool in
|
|
148
|
-
ExpoIapLog.payload("finishTransactionOnside", payload: purchasePayload)
|
|
149
|
-
try await ensureObserverRegistered()
|
|
150
|
-
|
|
151
|
-
guard let transactionId = purchasePayload["transactionId"] as? String,
|
|
152
|
-
let uuid = UUID(uuidString: transactionId),
|
|
153
|
-
let transaction = transactionCache[uuid] else {
|
|
154
|
-
throw OnsideBridgeError.transactionNotFound(purchasePayload["transactionId"] as? String ?? "")
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
Onside.paymentQueue().finishTransaction(transaction)
|
|
158
|
-
transactionCache.removeValue(forKey: uuid)
|
|
159
|
-
ExpoIapLog.result("finishTransactionOnside", value: true)
|
|
160
|
-
return true
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
AsyncFunction("restorePurchases") { () async throws -> Bool in
|
|
164
|
-
ExpoIapLog.payload("restorePurchasesOnside", payload: nil)
|
|
165
|
-
try await ensureObserverRegistered()
|
|
166
|
-
|
|
167
|
-
if restoreContinuation != nil {
|
|
168
|
-
throw OnsideBridgeError.restoreInProgress
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return try await withCheckedThrowingContinuation { continuation in
|
|
172
|
-
restoreContinuation = continuation
|
|
173
|
-
Onside.paymentQueue().restoreCompletedTransactions { [weak self] result in
|
|
174
|
-
guard let self else {
|
|
175
|
-
continuation.resume(returning: true)
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
switch result {
|
|
179
|
-
case .success:
|
|
180
|
-
continuation.resume(returning: true)
|
|
181
|
-
case .failure(let error):
|
|
182
|
-
continuation.resume(throwing: OnsideBridgeError.queueError(error.localizedDescription))
|
|
183
|
-
}
|
|
184
|
-
restoreContinuation = nil
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
AsyncFunction("getStorefrontIOS") { () async throws -> String in
|
|
190
|
-
ExpoIapLog.payload("getStorefrontOnside", payload: nil)
|
|
191
|
-
try await ensureObserverRegistered()
|
|
192
|
-
let storefront = Onside.paymentQueue().storefront?.countryCode ?? ""
|
|
193
|
-
ExpoIapLog.result("getStorefrontOnside", value: storefront)
|
|
194
|
-
return storefront
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
private func ensureObserverRegistered() async throws {
|
|
199
|
-
if !isInitialized {
|
|
200
|
-
Onside.paymentQueue().add(observer: transactionObserver)
|
|
201
|
-
isInitialized = true
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private func ensureProductAvailable(sku: String) async throws {
|
|
206
|
-
if productCache[sku] != nil {
|
|
207
|
-
return
|
|
208
|
-
}
|
|
209
|
-
let response = try await productFetcher.fetch(identifiers: [sku])
|
|
210
|
-
if !response.invalidProductIdentifiers.isEmpty {
|
|
211
|
-
throw OnsideBridgeError.productNotFound(sku)
|
|
212
|
-
}
|
|
213
|
-
response.products.forEach { productCache[$0.productIdentifier] = $0 }
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private func configureObserverCallbacks() {
|
|
217
|
-
transactionObserver.onTransactionsUpdated = { [weak self] transactions in
|
|
218
|
-
guard let self else { return }
|
|
219
|
-
transactions.forEach { transaction in
|
|
220
|
-
transactionCache[transaction.id] = transaction
|
|
221
|
-
handle(transaction: transaction)
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
transactionObserver.onRestoreFinished = { [weak self] in
|
|
226
|
-
guard let self else { return }
|
|
227
|
-
restoreContinuation?.resume(returning: true)
|
|
228
|
-
restoreContinuation = nil
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
transactionObserver.onRestoreFailed = { [weak self] error in
|
|
232
|
-
guard let self else { return }
|
|
233
|
-
restoreContinuation?.resume(throwing: OnsideBridgeError.queueError(error.localizedDescription))
|
|
234
|
-
restoreContinuation = nil
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
private func cleanup() {
|
|
239
|
-
if isInitialized {
|
|
240
|
-
Onside.paymentQueue().remove(observer: transactionObserver)
|
|
241
|
-
isInitialized = false
|
|
242
|
-
}
|
|
243
|
-
transactionCache.removeAll()
|
|
244
|
-
restoreContinuation?.resume(returning: false)
|
|
245
|
-
restoreContinuation = nil
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
private func handle(transaction: OnsidePaymentTransaction) {
|
|
249
|
-
do {
|
|
250
|
-
let payload = try serialize(transaction: transaction)
|
|
251
|
-
switch transaction.transactionState {
|
|
252
|
-
case .purchased, .restored:
|
|
253
|
-
sendEvent(OnsideEvent.purchaseUpdated.rawValue, payload)
|
|
254
|
-
case .failed:
|
|
255
|
-
let errorPayload: [String: Any] = [
|
|
256
|
-
"code": ErrorCode.PurchaseError.rawValue,
|
|
257
|
-
"message": (transaction.error?.localizedDescription ?? "Purchase failed"),
|
|
258
|
-
"productId": transaction.product.productIdentifier
|
|
259
|
-
]
|
|
260
|
-
sendEvent(OnsideEvent.purchaseError.rawValue, errorPayload)
|
|
261
|
-
case .purchasing:
|
|
262
|
-
break
|
|
263
|
-
@unknown default:
|
|
264
|
-
break
|
|
265
|
-
}
|
|
266
|
-
} catch {
|
|
267
|
-
ExpoIapLog.failure("handleTransactionOnside", error: error)
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
private func serializeProduct(_ product: OnsideProduct) throws -> [String: Any] {
|
|
272
|
-
var dictionary: [String: Any?] = [:]
|
|
273
|
-
dictionary["id"] = product.productIdentifier
|
|
274
|
-
dictionary["platform"] = "ios"
|
|
275
|
-
dictionary["title"] = product.localizedTitle
|
|
276
|
-
dictionary["description"] = product.localizedDescription
|
|
277
|
-
dictionary["displayName"] = product.localizedTitle
|
|
278
|
-
dictionary["displayNameIOS"] = product.localizedTitle
|
|
279
|
-
dictionary["displayPrice"] = product.price.formatted
|
|
280
|
-
dictionary["currency"] = product.price.currencyCode
|
|
281
|
-
dictionary["price"] = product.price.value
|
|
282
|
-
dictionary["type"] = "in-app"
|
|
283
|
-
dictionary["typeIOS"] = "non-consumable"
|
|
284
|
-
dictionary["isFamilyShareableIOS"] = false
|
|
285
|
-
dictionary["jsonRepresentationIOS"] = try encodeToJSONString(product)
|
|
286
|
-
dictionary["debugDescription"] = product.description
|
|
287
|
-
return sanitize(dictionary)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
private func serialize(transaction: OnsidePaymentTransaction) throws -> [String: Any] {
|
|
291
|
-
let product = transaction.product
|
|
292
|
-
var dictionary: [String: Any?] = [:]
|
|
293
|
-
dictionary["id"] = transaction.id.uuidString
|
|
294
|
-
dictionary["transactionId"] = transaction.id.uuidString
|
|
295
|
-
dictionary["productId"] = product.productIdentifier
|
|
296
|
-
dictionary["platform"] = "ios"
|
|
297
|
-
dictionary["quantity"] = 1
|
|
298
|
-
dictionary["isAutoRenewing"] = false
|
|
299
|
-
dictionary["purchaseState"] = mapPurchaseState(transaction.transactionState)
|
|
300
|
-
dictionary["transactionDate"] = Int(Date().timeIntervalSince1970 * 1000)
|
|
301
|
-
dictionary["currencyCodeIOS"] = product.price.currencyCode
|
|
302
|
-
dictionary["currencySymbolIOS"] = product.price.formatted
|
|
303
|
-
dictionary["storefrontCountryCodeIOS"] = transaction.storefront.countryCode
|
|
304
|
-
dictionary["purchaseToken"] = nil
|
|
305
|
-
dictionary["environmentIOS"] = transaction.storefront.id
|
|
306
|
-
if let error = transaction.error {
|
|
307
|
-
dictionary["reasonIOS"] = error.localizedDescription
|
|
308
|
-
}
|
|
309
|
-
return sanitize(dictionary)
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
private func encodeToJSONString<T: Encodable>(_ value: T) throws -> String {
|
|
313
|
-
let data = try encoder.encode(value)
|
|
314
|
-
guard let json = String(data: data, encoding: .utf8) else {
|
|
315
|
-
throw OnsideBridgeError.queueError("Unable to encode JSON string")
|
|
316
|
-
}
|
|
317
|
-
return json
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private func sanitize(_ dictionary: [String: Any?]) -> [String: Any] {
|
|
321
|
-
var result: [String: Any] = [:]
|
|
322
|
-
for (key, value) in dictionary {
|
|
323
|
-
if let value {
|
|
324
|
-
result[key] = value
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
return result
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
private func mapPurchaseState(_ state: OnsidePaymentTransactionState) -> String {
|
|
331
|
-
switch state {
|
|
332
|
-
case .purchased:
|
|
333
|
-
return "purchased"
|
|
334
|
-
case .restored:
|
|
335
|
-
return "restored"
|
|
336
|
-
case .failed:
|
|
337
|
-
return "failed"
|
|
338
|
-
case .purchasing:
|
|
339
|
-
return "pending"
|
|
340
|
-
@unknown default:
|
|
341
|
-
return "unknown"
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
private func resolveSku(from payload: [String: Any]) -> String? {
|
|
346
|
-
if let sku = payload["sku"] as? String, !sku.isEmpty {
|
|
347
|
-
return sku
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if let request = payload["request"] as? [String: Any] {
|
|
351
|
-
if let ios = request["ios"] as? [String: Any] {
|
|
352
|
-
if let sku = ios["sku"] as? String, !sku.isEmpty {
|
|
353
|
-
return sku
|
|
354
|
-
}
|
|
355
|
-
if let skus = ios["skus"] as? [String], let first = skus.first, !first.isEmpty {
|
|
356
|
-
return first
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if let requestPurchase = payload["requestPurchase"] as? [String: Any],
|
|
362
|
-
let ios = requestPurchase["ios"] as? [String: Any],
|
|
363
|
-
let sku = ios["sku"] as? String, !sku.isEmpty {
|
|
364
|
-
return sku
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if let requestSubscription = payload["requestSubscription"] as? [String: Any],
|
|
368
|
-
let ios = requestSubscription["ios"] as? [String: Any],
|
|
369
|
-
let sku = ios["sku"] as? String, !sku.isEmpty {
|
|
370
|
-
return sku
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if let skus = payload["skus"] as? [String], let first = skus.first, !first.isEmpty {
|
|
374
|
-
return first
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
return nil
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
@available(iOS 16.0, *)
|
|
382
|
-
private final class OnsideTransactionObserverBridge: NSObject, OnsidePaymentTransactionObserver {
|
|
383
|
-
var onTransactionsUpdated: (([OnsidePaymentTransaction]) -> Void)?
|
|
384
|
-
var onRestoreFinished: (() -> Void)?
|
|
385
|
-
var onRestoreFailed: ((OnsideTransactionsRestoreError) -> Void)?
|
|
386
|
-
|
|
387
|
-
func onsidePaymentQueue(_ queue: OnsidePaymentQueue, updatedTransactions transactions: [OnsidePaymentTransaction]) {
|
|
388
|
-
onTransactionsUpdated?(transactions)
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
func onsidePaymentQueue(_ queue: OnsidePaymentQueue, removedTransactions: [OnsidePaymentTransaction]) {}
|
|
392
|
-
|
|
393
|
-
func onsidePaymentQueueRestoreCompletedTransactionsFinished(_ queue: OnsidePaymentQueue) {
|
|
394
|
-
onRestoreFinished?()
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
func onsidePaymentQueue(_ queue: OnsidePaymentQueue, restoreCompletedTransactionsFailedWithError error: OnsideTransactionsRestoreError) {
|
|
398
|
-
onRestoreFailed?(error)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
func onsidePaymentQueueDidChangeStorefront(_ queue: OnsidePaymentQueue) {}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
@available(iOS 16.0, *)
|
|
405
|
-
private final class OnsideProductFetcher: NSObject, OnsideProductsRequestDelegate {
|
|
406
|
-
private var continuation: CheckedContinuation<OnsideProductsResponse, Error>?
|
|
407
|
-
private var request: OnsideProductsRequest?
|
|
408
|
-
|
|
409
|
-
func fetch(identifiers: Set<String>) async throws -> OnsideProductsResponse {
|
|
410
|
-
guard !identifiers.isEmpty else {
|
|
411
|
-
throw OnsideBridgeError.emptySkuList
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
return try await withCheckedThrowingContinuation { continuation in
|
|
415
|
-
let request = Onside.makeProductsRequest(productIdentifiers: identifiers)
|
|
416
|
-
self.request = request
|
|
417
|
-
self.continuation = continuation
|
|
418
|
-
request.delegate = self
|
|
419
|
-
request.start()
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
func onsideProductsRequest(_ request: OnsideProductsRequest, didReceive response: OnsideProductsResponse) {
|
|
424
|
-
continuation?.resume(returning: response)
|
|
425
|
-
cleanup()
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
func onsideProductsRequestRequest(_ request: OnsideProductsRequest, didFailWithError error: OnsideProductsRequestError) {
|
|
429
|
-
continuation?.resume(throwing: OnsideBridgeError.queueError(error.localizedDescription))
|
|
430
|
-
cleanup()
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
func onsideProductsRequestDidFinish(_ request: OnsideProductsRequest) {
|
|
434
|
-
cleanup()
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
private func cleanup() {
|
|
438
|
-
request?.delegate = nil
|
|
439
|
-
request?.stop()
|
|
440
|
-
request = nil
|
|
441
|
-
continuation = nil
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
#else
|
|
446
|
-
|
|
447
|
-
@available(iOS 15.0, tvOS 15.0, *)
|
|
448
|
-
@MainActor
|
|
449
|
-
public final class OnsideIapModule: Module {
|
|
450
|
-
nonisolated public func definition() -> ModuleDefinition {
|
|
451
|
-
Name("ExpoIapOnside")
|
|
452
|
-
|
|
453
|
-
Events(
|
|
454
|
-
OnsideEvent.purchaseUpdated.rawValue,
|
|
455
|
-
OnsideEvent.purchaseError.rawValue,
|
|
456
|
-
OnsideEvent.promotedProductIOS.rawValue
|
|
457
|
-
)
|
|
458
|
-
|
|
459
|
-
AsyncFunction("initConnection") { () async throws -> Bool in
|
|
460
|
-
throw OnsideBridgeError.sdkUnavailable
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
AsyncFunction("endConnection") { () async throws -> Bool in
|
|
464
|
-
throw OnsideBridgeError.sdkUnavailable
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
AsyncFunction("fetchProducts") { (_: [String: Any]) async throws -> [[String: Any]] in
|
|
468
|
-
throw OnsideBridgeError.sdkUnavailable
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
AsyncFunction("requestPurchase") { (_: [String: Any]) async throws -> Any? in
|
|
472
|
-
throw OnsideBridgeError.sdkUnavailable
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
AsyncFunction("finishTransaction") { (_: [String: Any], _: Bool?) async throws -> Bool in
|
|
476
|
-
throw OnsideBridgeError.sdkUnavailable
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
AsyncFunction("restorePurchases") { () async throws -> Bool in
|
|
480
|
-
throw OnsideBridgeError.sdkUnavailable
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
AsyncFunction("getStorefrontIOS") { () async throws -> String in
|
|
484
|
-
throw OnsideBridgeError.sdkUnavailable
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
#endif
|
package/src/utils/constants.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// Centralized product ID constants for examples and internal usage
|
|
2
|
-
// Rename guide: subscriptionIds -> SUBSCRIPTION_PRODUCT_IDS, PRODUCT_IDS remains the same name
|
|
3
|
-
|
|
4
|
-
// One-time purchase product IDs split by consumption behavior
|
|
5
|
-
export const CONSUMABLE_PRODUCT_IDS: string[] = [
|
|
6
|
-
'dev.hyo.martie.10bulbs',
|
|
7
|
-
'dev.hyo.martie.30bulbs',
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
export const NON_CONSUMABLE_PRODUCT_IDS: string[] = [
|
|
11
|
-
'dev.hyo.martie.certified',
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
export const PRODUCT_IDS: string[] = [
|
|
15
|
-
...CONSUMABLE_PRODUCT_IDS,
|
|
16
|
-
...NON_CONSUMABLE_PRODUCT_IDS,
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
// Subscription product IDs
|
|
20
|
-
export const SUBSCRIPTION_PRODUCT_IDS: string[] = ['dev.hyo.martie.premium'];
|
|
21
|
-
|
|
22
|
-
// Optionally export a single default subscription for convenience
|
|
23
|
-
export const DEFAULT_SUBSCRIPTION_PRODUCT_ID = SUBSCRIPTION_PRODUCT_IDS[0];
|