@capgo/native-purchases 7.18.0-alpha.0 → 7.18.0

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.
@@ -2,10 +2,6 @@ import Foundation
2
2
  import Capacitor
3
3
  import StoreKit
4
4
 
5
- /**
6
- * Please read the Capacitor iOS Plugin Development Guide
7
- * here: https://capacitorjs.com/docs/plugins/ios
8
- */
9
5
  @objc(NativePurchasesPlugin)
10
6
  public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
11
7
  public let identifier = "NativePurchasesPlugin"
@@ -25,7 +21,7 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
25
21
  CAPPluginMethod(name: "isEntitledToOldBusinessModel", returnType: CAPPluginReturnPromise)
26
22
  ]
27
23
 
28
- private let pluginVersion: String = "7.18.0-alpha.0"
24
+ private let pluginVersion: String = "7.18.0"
29
25
  private var transactionUpdatesTask: Task<Void, Never>?
30
26
 
31
27
  @objc func getPluginVersion(_ call: CAPPluginCall) {
@@ -34,38 +30,28 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
34
30
 
35
31
  override public func load() {
36
32
  super.load()
37
- // Start listening to StoreKit transaction updates as early as possible
38
- if #available(iOS 15.0, *) {
39
- startTransactionUpdatesListener()
40
- }
33
+ startTransactionUpdatesListener()
41
34
  }
42
35
 
43
36
  deinit {
44
- if #available(iOS 15.0, *) { cancelTransactionUpdatesListener() }
45
- }
46
-
47
- private func cancelTransactionUpdatesListener() {
48
- self.transactionUpdatesTask?.cancel()
49
- self.transactionUpdatesTask = nil
37
+ transactionUpdatesTask?.cancel()
38
+ transactionUpdatesTask = nil
50
39
  }
51
40
 
52
- @available(iOS 15.0, *)
53
41
  private func startTransactionUpdatesListener() {
54
- // Ensure only one listener is running
55
- cancelTransactionUpdatesListener()
56
- let task = Task.detached { [weak self] in
42
+ transactionUpdatesTask?.cancel()
43
+ transactionUpdatesTask = Task.detached { [weak self] in
57
44
  for await result in Transaction.updates {
58
45
  guard !Task.isCancelled else { break }
59
46
  switch result {
60
47
  case .verified(let transaction):
61
- // Build payload similar to purchase response
62
- let payload = await TransactionHelpers.buildTransactionResponse(from: transaction, jwsRepresentation: result.jwsRepresentation, alwaysIncludeWillCancel: true)
63
-
64
- // Finish the transaction to avoid blocking future purchases
48
+ let payload = await TransactionHelpers.buildTransactionResponse(
49
+ from: transaction,
50
+ jwsRepresentation: result.jwsRepresentation,
51
+ alwaysIncludeWillCancel: true
52
+ )
65
53
  await transaction.finish()
66
-
67
- // Notify JS listeners on main thread, after slight delay
68
- try? await Task.sleep(nanoseconds: 500_000_000) // 0.5s delay
54
+ try? await Task.sleep(nanoseconds: 500_000_000)
69
55
  await MainActor.run {
70
56
  self?.notifyListeners("transactionUpdated", data: payload)
71
57
  }
@@ -79,93 +65,70 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
79
65
  }
80
66
  }
81
67
  }
82
- transactionUpdatesTask = task
83
68
  }
84
69
 
85
- // MARK: - Plugin Methods
86
-
87
70
  @objc func isBillingSupported(_ call: CAPPluginCall) {
88
- if #available(iOS 15, *) {
89
- call.resolve([
90
- "isBillingSupported": true
91
- ])
92
- } else {
93
- call.resolve([
94
- "isBillingSupported": false
95
- ])
96
- }
71
+ call.resolve(["isBillingSupported": true])
97
72
  }
98
73
 
99
74
  @objc func purchaseProduct(_ call: CAPPluginCall) {
100
- if #available(iOS 15, *) {
101
- print("purchaseProduct")
102
- let productIdentifier = call.getString("productIdentifier", "")
103
- let quantity = call.getInt("quantity", 1)
104
- let appAccountToken = call.getString("appAccountToken")
105
- let autoAcknowledge = call.getBool("autoAcknowledgePurchases") ?? true
106
-
107
- if productIdentifier.isEmpty {
108
- call.reject("productIdentifier is Empty, give an id")
109
- return
110
- }
111
-
112
- print("Auto-acknowledge enabled: \(autoAcknowledge)")
113
-
114
- Task { @MainActor in
115
- do {
116
- let products = try await Product.products(for: [productIdentifier])
117
- guard let product = products.first else {
118
- call.reject("Cannot find product for id \(productIdentifier)")
119
- return
120
- }
75
+ print("purchaseProduct")
76
+ let productIdentifier = call.getString("productIdentifier", "")
77
+ let quantity = call.getInt("quantity", 1)
78
+ let appAccountToken = call.getString("appAccountToken")
79
+ let autoAcknowledge = call.getBool("autoAcknowledgePurchases") ?? true
80
+
81
+ if productIdentifier.isEmpty {
82
+ call.reject("productIdentifier is Empty, give an id")
83
+ return
84
+ }
121
85
 
122
- let purchaseOptions = self.buildPurchaseOptions(quantity: quantity, appAccountToken: appAccountToken)
123
- let result = try await product.purchase(options: purchaseOptions)
124
- print("purchaseProduct result \(result)")
86
+ print("Auto-acknowledge enabled: \(autoAcknowledge)")
125
87
 
126
- await self.handlePurchaseResult(result, call: call, autoFinish: autoAcknowledge)
127
- } catch {
128
- print(error)
129
- call.reject(error.localizedDescription)
88
+ Task { @MainActor in
89
+ do {
90
+ let products = try await Product.products(for: [productIdentifier])
91
+ guard let product = products.first else {
92
+ call.reject("Cannot find product for id \(productIdentifier)")
93
+ return
130
94
  }
131
- }
132
- } else {
133
- print("Not implemented under ios 15")
134
- call.reject("Not implemented under ios 15")
135
- }
136
- }
137
95
 
138
- @available(iOS 15.0, *)
139
- private func buildPurchaseOptions(quantity: Int, appAccountToken: String?) -> Set<Product.PurchaseOption> {
140
- var purchaseOptions = Set<Product.PurchaseOption>()
141
- purchaseOptions.insert(Product.PurchaseOption.quantity(quantity))
96
+ var purchaseOptions = Set<Product.PurchaseOption>()
97
+ purchaseOptions.insert(.quantity(quantity))
98
+ if let token = appAccountToken, !token.isEmpty, let uuid = UUID(uuidString: token) {
99
+ purchaseOptions.insert(.appAccountToken(uuid))
100
+ }
142
101
 
143
- if let accountToken = appAccountToken, !accountToken.isEmpty, let tokenData = UUID(uuidString: accountToken) {
144
- purchaseOptions.insert(Product.PurchaseOption.appAccountToken(tokenData))
102
+ let result = try await product.purchase(options: purchaseOptions)
103
+ print("purchaseProduct result \(result)")
104
+ await self.handlePurchaseResult(result, call: call, autoFinish: autoAcknowledge)
105
+ } catch {
106
+ print(error)
107
+ call.reject(error.localizedDescription)
108
+ }
145
109
  }
146
-
147
- return purchaseOptions
148
110
  }
149
111
 
150
- @available(iOS 15.0, *)
151
112
  @MainActor
152
- private func handlePurchaseResult(_ result: Product.PurchaseResult, call: CAPPluginCall, autoFinish: Bool) async {
113
+ private func handlePurchaseResult(
114
+ _ result: Product.PurchaseResult,
115
+ call: CAPPluginCall,
116
+ autoFinish: Bool
117
+ ) async {
153
118
  switch result {
154
119
  case let .success(verificationResult):
155
120
  switch verificationResult {
156
121
  case .verified(let transaction):
157
- let response = await TransactionHelpers.buildTransactionResponse(from: transaction, jwsRepresentation: verificationResult.jwsRepresentation)
158
-
122
+ let response = await TransactionHelpers.buildTransactionResponse(
123
+ from: transaction,
124
+ jwsRepresentation: verificationResult.jwsRepresentation
125
+ )
159
126
  if autoFinish {
160
127
  print("Auto-finishing transaction: \(transaction.id)")
161
128
  await transaction.finish()
162
129
  } else {
163
130
  print("Manual finish required for transaction: \(transaction.id)")
164
- print("Transaction will remain unfinished until acknowledgePurchase() is called")
165
- // Don't finish - transaction remains in StoreKit's queue
166
- // Can be retrieved later via Transaction.all
167
131
  }
168
-
169
132
  call.resolve(response)
170
133
  case .unverified(_, let error):
171
134
  call.reject(error.localizedDescription)
@@ -180,185 +143,127 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
180
143
  }
181
144
 
182
145
  @objc func restorePurchases(_ call: CAPPluginCall) {
183
- if #available(iOS 15.0, *) {
184
- print("restorePurchases")
185
- Task {
186
- do {
187
- try await AppStore.sync()
188
- // make finish() calls for all transactions and consume all consumables
189
- for transaction in SKPaymentQueue.default().transactions {
190
- SKPaymentQueue.default().finishTransaction(transaction)
191
- }
192
- await MainActor.run {
193
- call.resolve()
194
- }
195
- } catch {
196
- await MainActor.run {
197
- call.reject(error.localizedDescription)
198
- }
146
+ print("restorePurchases")
147
+ Task {
148
+ do {
149
+ try await AppStore.sync()
150
+ for transaction in SKPaymentQueue.default().transactions {
151
+ SKPaymentQueue.default().finishTransaction(transaction)
199
152
  }
153
+ await MainActor.run { call.resolve() }
154
+ } catch {
155
+ await MainActor.run { call.reject(error.localizedDescription) }
200
156
  }
201
- } else {
202
- print("Not implemented under ios 15")
203
- call.reject("Not implemented under ios 15")
204
157
  }
205
158
  }
206
159
 
207
160
  @objc func getProducts(_ call: CAPPluginCall) {
208
- if #available(iOS 15.0, *) {
209
- let productIdentifiers = call.getArray("productIdentifiers", String.self) ?? []
210
- print("productIdentifiers \(productIdentifiers)")
211
- Task {
212
- do {
213
- let products = try await Product.products(for: productIdentifiers)
214
- print("products \(products)")
215
- let productsJson: [[String: Any]] = products.map { $0.dictionary }
216
- await MainActor.run {
217
- call.resolve([
218
- "products": productsJson
219
- ])
220
- }
221
- } catch {
222
- print("error \(error)")
223
- await MainActor.run {
224
- call.reject(error.localizedDescription)
225
- }
226
- }
161
+ let productIdentifiers = call.getArray("productIdentifiers", String.self) ?? []
162
+ let productType = call.getString("productType", "inapp")
163
+ print("productIdentifiers \(productIdentifiers)")
164
+ print("productType \(productType)")
165
+ Task {
166
+ do {
167
+ let products = try await Product.products(for: productIdentifiers)
168
+ print("products \(products)")
169
+ let productsJson: [[String: Any]] = products.map { $0.dictionary }
170
+ await MainActor.run { call.resolve(["products": productsJson]) }
171
+ } catch {
172
+ print("error \(error)")
173
+ await MainActor.run { call.reject(error.localizedDescription) }
227
174
  }
228
- } else {
229
- print("Not implemented under ios 15")
230
- call.reject("Not implemented under ios 15")
231
175
  }
232
176
  }
233
177
 
234
178
  @objc func getProduct(_ call: CAPPluginCall) {
235
- if #available(iOS 15.0, *) {
236
- let productIdentifier = call.getString("productIdentifier") ?? ""
237
- print("productIdentifier \(productIdentifier)")
238
- if productIdentifier.isEmpty {
239
- call.reject("productIdentifier is empty")
240
- return
241
- }
179
+ let productIdentifier = call.getString("productIdentifier") ?? ""
180
+ let productType = call.getString("productType", "inapp")
181
+ print("productIdentifier \(productIdentifier)")
182
+ print("productType \(productType)")
183
+ if productIdentifier.isEmpty {
184
+ call.reject("productIdentifier is empty")
185
+ return
186
+ }
242
187
 
243
- Task {
244
- do {
245
- let products = try await Product.products(for: [productIdentifier])
246
- print("products \(products)")
247
- if let product = products.first {
248
- let productJson = product.dictionary
249
- await MainActor.run {
250
- call.resolve(["product": productJson])
251
- }
252
- } else {
253
- await MainActor.run {
254
- call.reject("Product not found")
255
- }
256
- }
257
- } catch {
258
- print(error)
259
- await MainActor.run {
260
- call.reject(error.localizedDescription)
261
- }
188
+ Task {
189
+ do {
190
+ let products = try await Product.products(for: [productIdentifier])
191
+ print("products \(products)")
192
+ if let product = products.first {
193
+ await MainActor.run { call.resolve(["product": product.dictionary]) }
194
+ } else {
195
+ await MainActor.run { call.reject("Product not found") }
262
196
  }
197
+ } catch {
198
+ print(error)
199
+ await MainActor.run { call.reject(error.localizedDescription) }
263
200
  }
264
- } else {
265
- print("Not implemented under iOS 15")
266
- call.reject("Not implemented under iOS 15")
267
201
  }
268
202
  }
269
203
 
270
204
  @objc func getPurchases(_ call: CAPPluginCall) {
205
+ print("getPurchases")
271
206
  let appAccountTokenFilter = call.getString("appAccountToken")
272
- if #available(iOS 15.0, *) {
273
- print("getPurchases")
274
- Task {
275
- let allPurchases = await TransactionHelpers.collectAllPurchases(appAccountTokenFilter: appAccountTokenFilter)
276
- await MainActor.run {
277
- call.resolve(["purchases": allPurchases])
278
- }
279
- }
280
- } else {
281
- print("Not implemented under iOS 15")
282
- call.reject("Not implemented under iOS 15")
207
+ Task {
208
+ let allPurchases = await TransactionHelpers.collectAllPurchases(
209
+ appAccountTokenFilter: appAccountTokenFilter
210
+ )
211
+ await MainActor.run { call.resolve(["purchases": allPurchases]) }
283
212
  }
284
213
  }
285
214
 
286
215
  @objc func manageSubscriptions(_ call: CAPPluginCall) {
287
- if #available(iOS 15.0, *) {
288
- print("manageSubscriptions")
289
- Task { @MainActor in
290
- do {
291
- // Get the current window scene
292
- guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
293
- call.reject("Unable to get window scene")
294
- return
295
- }
296
- // Open the App Store subscription management page
297
- try await AppStore.showManageSubscriptions(in: windowScene)
298
- call.resolve()
299
- } catch {
300
- print("manageSubscriptions error: \(error)")
301
- call.reject(error.localizedDescription)
216
+ print("manageSubscriptions")
217
+ Task { @MainActor in
218
+ do {
219
+ guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
220
+ call.reject("Unable to get window scene")
221
+ return
302
222
  }
223
+ try await AppStore.showManageSubscriptions(in: windowScene)
224
+ call.resolve()
225
+ } catch {
226
+ print("manageSubscriptions error: \(error)")
227
+ call.reject(error.localizedDescription)
303
228
  }
304
- } else {
305
- print("Not implemented under iOS 15")
306
- call.reject("Not implemented under iOS 15")
307
229
  }
308
230
  }
309
231
 
310
232
  @objc func acknowledgePurchase(_ call: CAPPluginCall) {
311
- if #available(iOS 15.0, *) {
312
- print("acknowledgePurchase called on iOS")
233
+ print("acknowledgePurchase called on iOS")
313
234
 
314
- guard let purchaseToken = call.getString("purchaseToken") else {
315
- call.reject("purchaseToken is required")
316
- return
317
- }
318
-
319
- // On iOS, purchaseToken is the transactionId (UInt64 as string)
320
- guard let transactionId = UInt64(purchaseToken) else {
321
- call.reject("Invalid purchaseToken format")
322
- return
323
- }
235
+ guard let purchaseToken = call.getString("purchaseToken") else {
236
+ call.reject("purchaseToken is required")
237
+ return
238
+ }
324
239
 
325
- Task {
326
- // Search for the transaction in StoreKit's unfinished transactions
327
- // This works even after app restart because StoreKit persists them
328
- var foundTransaction: Transaction?
329
-
330
- for await verificationResult in Transaction.all {
331
- switch verificationResult {
332
- case .verified(let transaction):
333
- if transaction.id == transactionId {
334
- foundTransaction = transaction
335
- break
336
- }
337
- case .unverified:
338
- continue
339
- }
340
- if foundTransaction != nil {
341
- break
342
- }
343
- }
240
+ guard let transactionId = UInt64(purchaseToken) else {
241
+ call.reject("Invalid purchaseToken format")
242
+ return
243
+ }
344
244
 
345
- guard let transaction = foundTransaction else {
346
- await MainActor.run {
347
- call.reject("Transaction not found or already finished. Transaction ID: \(transactionId)")
348
- }
349
- return
245
+ Task {
246
+ var foundTransaction: Transaction?
247
+ for await verificationResult in Transaction.all {
248
+ if case .verified(let transaction) = verificationResult, transaction.id == transactionId {
249
+ foundTransaction = transaction
250
+ break
350
251
  }
252
+ }
351
253
 
352
- print("Manually finishing transaction: \(transaction.id)")
353
- await transaction.finish()
354
-
254
+ guard let transaction = foundTransaction else {
355
255
  await MainActor.run {
356
- print("Transaction finished successfully")
357
- call.resolve()
256
+ call.reject("Transaction not found or already finished. Transaction ID: \(transactionId)")
358
257
  }
258
+ return
259
+ }
260
+
261
+ print("Manually finishing transaction: \(transaction.id)")
262
+ await transaction.finish()
263
+ await MainActor.run {
264
+ print("Transaction finished successfully")
265
+ call.resolve()
359
266
  }
360
- } else {
361
- call.reject("Not implemented under iOS 15")
362
267
  }
363
268
  }
364
269
 
@@ -366,55 +271,16 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
366
271
  call.reject("consumePurchase is only available on Android")
367
272
  }
368
273
 
274
+ }
275
+
276
+ // MARK: - iOS 16+ App Transaction Methods
277
+ extension NativePurchasesPlugin {
369
278
  @objc func getAppTransaction(_ call: CAPPluginCall) {
370
279
  if #available(iOS 16.0, *) {
371
- print("getAppTransaction called on iOS")
372
280
  Task { @MainActor in
373
- do {
374
- let verificationResult = try await AppTransaction.shared
375
- switch verificationResult {
376
- case .verified(let appTransaction):
377
- var response: [String: Any] = [:]
378
-
379
- // originalAppVersion is the CFBundleVersion (build number) at the time of original download
380
- response["originalAppVersion"] = appTransaction.originalAppVersion
381
-
382
- // Original purchase date
383
- response["originalPurchaseDate"] = ISO8601DateFormatter().string(from: appTransaction.originalPurchaseDate)
384
-
385
- // Bundle ID
386
- response["bundleId"] = appTransaction.bundleID
387
-
388
- // Current app version (build number)
389
- response["appVersion"] = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""
390
-
391
- // Environment
392
- switch appTransaction.environment {
393
- case .sandbox:
394
- response["environment"] = "Sandbox"
395
- case .production:
396
- response["environment"] = "Production"
397
- case .xcode:
398
- response["environment"] = "Xcode"
399
- default:
400
- response["environment"] = "Production"
401
- }
402
-
403
- // JWS representation for server-side verification
404
- response["jwsRepresentation"] = verificationResult.jwsRepresentation
405
-
406
- call.resolve(["appTransaction": response])
407
-
408
- case .unverified(_, let error):
409
- call.reject("App transaction verification failed: \(error.localizedDescription)")
410
- }
411
- } catch {
412
- print("getAppTransaction error: \(error)")
413
- call.reject("Failed to get app transaction: \(error.localizedDescription)")
414
- }
281
+ await self.handleGetAppTransaction(call)
415
282
  }
416
283
  } else {
417
- print("getAppTransaction not implemented under iOS 16")
418
284
  call.reject("App Transaction requires iOS 16.0 or later")
419
285
  }
420
286
  }
@@ -426,44 +292,78 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
426
292
  }
427
293
 
428
294
  if #available(iOS 16.0, *) {
429
- print("isEntitledToOldBusinessModel called with targetBuildNumber: \(targetBuildNumber)")
430
295
  Task { @MainActor in
431
- do {
432
- let verificationResult = try await AppTransaction.shared
433
- switch verificationResult {
434
- case .verified(let appTransaction):
435
- let originalBuildNumber = appTransaction.originalAppVersion
436
-
437
- // Compare build numbers (integers)
438
- let isOlder = self.compareVersions(originalBuildNumber, targetBuildNumber) < 0
439
-
440
- call.resolve([
441
- "isOlderVersion": isOlder,
442
- "originalAppVersion": originalBuildNumber
443
- ])
444
-
445
- case .unverified(_, let error):
446
- call.reject("App transaction verification failed: \(error.localizedDescription)")
447
- }
448
- } catch {
449
- print("isEntitledToOldBusinessModel error: \(error)")
450
- call.reject("Failed to get app transaction: \(error.localizedDescription)")
451
- }
296
+ await self.handleIsEntitledToOldBusinessModel(call, targetBuildNumber: targetBuildNumber)
452
297
  }
453
298
  } else {
454
- print("isEntitledToOldBusinessModel not implemented under iOS 16")
455
299
  call.reject("App Transaction requires iOS 16.0 or later")
456
300
  }
457
301
  }
458
302
 
459
- // MARK: - Version Comparison Helper
303
+ @available(iOS 16.0, *)
304
+ @MainActor
305
+ private func handleGetAppTransaction(_ call: CAPPluginCall) async {
306
+ print("getAppTransaction called on iOS")
307
+ do {
308
+ let verificationResult = try await AppTransaction.shared
309
+ switch verificationResult {
310
+ case .verified(let appTransaction):
311
+ let response: [String: Any] = [
312
+ "originalAppVersion": appTransaction.originalAppVersion,
313
+ "originalPurchaseDate": ISO8601DateFormatter().string(
314
+ from: appTransaction.originalPurchaseDate
315
+ ),
316
+ "bundleId": appTransaction.bundleID,
317
+ "appVersion": Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "",
318
+ "jwsRepresentation": verificationResult.jwsRepresentation,
319
+ "environment": appTransaction.environment.environmentString
320
+ ]
321
+ call.resolve(["appTransaction": response])
322
+ case .unverified(_, let error):
323
+ call.reject("App transaction verification failed: \(error.localizedDescription)")
324
+ }
325
+ } catch {
326
+ print("getAppTransaction error: \(error)")
327
+ call.reject("Failed to get app transaction: \(error.localizedDescription)")
328
+ }
329
+ }
460
330
 
461
- /// Compares two build numbers as integers.
462
- /// Returns: negative if v1 < v2, zero if v1 == v2, positive if v1 > v2
463
- private func compareVersions(_ version1: String, _ version2: String) -> Int {
464
- let v1Int = Int(version1) ?? 0
465
- let v2Int = Int(version2) ?? 0
466
- return v1Int - v2Int
331
+ @available(iOS 16.0, *)
332
+ @MainActor
333
+ private func handleIsEntitledToOldBusinessModel(
334
+ _ call: CAPPluginCall,
335
+ targetBuildNumber: String
336
+ ) async {
337
+ print("isEntitledToOldBusinessModel called with targetBuildNumber: \(targetBuildNumber)")
338
+ do {
339
+ let verificationResult = try await AppTransaction.shared
340
+ switch verificationResult {
341
+ case .verified(let appTransaction):
342
+ let originalBuildNumber = appTransaction.originalAppVersion
343
+ let originalInt = Int(originalBuildNumber) ?? 0
344
+ let targetInt = Int(targetBuildNumber) ?? 0
345
+ call.resolve([
346
+ "isOlderVersion": originalInt < targetInt,
347
+ "originalAppVersion": originalBuildNumber
348
+ ])
349
+ case .unverified(_, let error):
350
+ call.reject("App transaction verification failed: \(error.localizedDescription)")
351
+ }
352
+ } catch {
353
+ print("isEntitledToOldBusinessModel error: \(error)")
354
+ call.reject("Failed to get app transaction: \(error.localizedDescription)")
355
+ }
467
356
  }
357
+ }
468
358
 
359
+ @available(iOS 16.0, *)
360
+ private extension AppStore.Environment {
361
+ var environmentString: String {
362
+ switch self {
363
+ case .sandbox: return "Sandbox"
364
+ case .production: return "Production"
365
+ case .xcode: return "Xcode"
366
+ default: return "Production"
367
+ }
368
+ }
469
369
  }
@@ -8,7 +8,6 @@
8
8
  import Foundation
9
9
  import StoreKit
10
10
 
11
- @available(iOS 15.0, *)
12
11
  extension Product {
13
12
 
14
13
  var dictionary: [String: Any] {