react-native-iap 14.6.1 → 14.6.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/ios/HybridRnIap.swift +86 -46
- package/ios/RnIapHelper.swift +36 -3
- package/package.json +1 -1
package/ios/HybridRnIap.swift
CHANGED
|
@@ -88,7 +88,7 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
88
88
|
])
|
|
89
89
|
|
|
90
90
|
if skus.isEmpty {
|
|
91
|
-
throw
|
|
91
|
+
throw OpenIapException.make(code: .emptySkuList)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
var productsById: [String: NitroProduct] = [:]
|
|
@@ -242,9 +242,12 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
242
242
|
let payloads = RnIapHelper.sanitizeArray(OpenIapSerialization.purchases(purchases))
|
|
243
243
|
RnIapLog.result("getAvailablePurchases", payloads)
|
|
244
244
|
return payloads.map { RnIapHelper.convertPurchaseDictionary($0) }
|
|
245
|
+
} catch let purchaseError as PurchaseError {
|
|
246
|
+
RnIapLog.failure("getAvailablePurchases", error: purchaseError)
|
|
247
|
+
throw OpenIapException.from(purchaseError)
|
|
245
248
|
} catch {
|
|
246
249
|
RnIapLog.failure("getAvailablePurchases", error: error)
|
|
247
|
-
throw error
|
|
250
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
248
251
|
}
|
|
249
252
|
}
|
|
250
253
|
}
|
|
@@ -259,9 +262,12 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
259
262
|
let payloads = RnIapHelper.sanitizeArray(subscriptions.map { OpenIapSerialization.encode($0) })
|
|
260
263
|
RnIapLog.result("getActiveSubscriptions", payloads)
|
|
261
264
|
return payloads.map { RnIapHelper.convertActiveSubscriptionDictionary($0) }
|
|
265
|
+
} catch let purchaseError as PurchaseError {
|
|
266
|
+
RnIapLog.failure("getActiveSubscriptions", error: purchaseError)
|
|
267
|
+
throw OpenIapException.from(purchaseError)
|
|
262
268
|
} catch {
|
|
263
269
|
RnIapLog.failure("getActiveSubscriptions", error: error)
|
|
264
|
-
throw error
|
|
270
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
265
271
|
}
|
|
266
272
|
}
|
|
267
273
|
}
|
|
@@ -274,9 +280,12 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
274
280
|
let hasActive = try await OpenIapModule.shared.hasActiveSubscriptions(subscriptionIds)
|
|
275
281
|
RnIapLog.result("hasActiveSubscriptions", hasActive)
|
|
276
282
|
return hasActive
|
|
283
|
+
} catch let purchaseError as PurchaseError {
|
|
284
|
+
RnIapLog.failure("hasActiveSubscriptions", error: purchaseError)
|
|
285
|
+
throw OpenIapException.from(purchaseError)
|
|
277
286
|
} catch {
|
|
278
287
|
RnIapLog.failure("hasActiveSubscriptions", error: error)
|
|
279
|
-
throw error
|
|
288
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
280
289
|
}
|
|
281
290
|
}
|
|
282
291
|
}
|
|
@@ -297,7 +306,7 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
297
306
|
purchasePayload = ["transactionIdentifier": iosParams.transactionId]
|
|
298
307
|
}
|
|
299
308
|
guard let purchasePayload else {
|
|
300
|
-
throw
|
|
309
|
+
throw OpenIapException.make(code: .purchaseError, message: "Missing purchase context for \(iosParams.transactionId)")
|
|
301
310
|
}
|
|
302
311
|
let sanitizedPayload = RnIapHelper.sanitizeDictionary(purchasePayload)
|
|
303
312
|
RnIapLog.payload("finishTransaction.nativePayload", sanitizedPayload)
|
|
@@ -311,7 +320,7 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
311
320
|
} catch {
|
|
312
321
|
RnIapLog.failure("finishTransaction", error: error)
|
|
313
322
|
let tid = iosParams.transactionId
|
|
314
|
-
throw
|
|
323
|
+
throw OpenIapException.make(code: .purchaseError, message: "Transaction not found: \(tid)")
|
|
315
324
|
}
|
|
316
325
|
}
|
|
317
326
|
}
|
|
@@ -321,7 +330,7 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
321
330
|
do {
|
|
322
331
|
// Extract SKU from apple options (new platform-specific structure)
|
|
323
332
|
guard let appleOptions = params.apple, !appleOptions.sku.isEmpty else {
|
|
324
|
-
throw
|
|
333
|
+
throw OpenIapException.make(code: .developerError, message: "Missing required parameter: apple.sku")
|
|
325
334
|
}
|
|
326
335
|
let sku = appleOptions.sku
|
|
327
336
|
|
|
@@ -348,9 +357,12 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
348
357
|
latestTransaction: latest
|
|
349
358
|
)
|
|
350
359
|
return .first(mapped)
|
|
360
|
+
} catch let purchaseError as PurchaseError {
|
|
361
|
+
RnIapLog.failure("validateReceiptIOS", error: purchaseError)
|
|
362
|
+
throw OpenIapException.from(purchaseError)
|
|
351
363
|
} catch {
|
|
352
364
|
RnIapLog.failure("validateReceiptIOS", error: error)
|
|
353
|
-
throw
|
|
365
|
+
throw OpenIapException.make(code: .purchaseVerificationFailed, message: error.localizedDescription)
|
|
354
366
|
}
|
|
355
367
|
}
|
|
356
368
|
}
|
|
@@ -407,9 +419,13 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
407
419
|
errors: nitroErrors,
|
|
408
420
|
provider: PurchaseVerificationProvider(fromString: result.provider.rawValue) ?? .iapkit
|
|
409
421
|
)
|
|
422
|
+
} catch let purchaseError as PurchaseError {
|
|
423
|
+
// Convert PurchaseError to OpenIapException to preserve message through Nitro bridge
|
|
424
|
+
RnIapLog.failure("verifyPurchaseWithProvider", error: purchaseError)
|
|
425
|
+
throw OpenIapException.from(purchaseError)
|
|
410
426
|
} catch {
|
|
411
427
|
RnIapLog.failure("verifyPurchaseWithProvider", error: error)
|
|
412
|
-
throw
|
|
428
|
+
throw OpenIapException.make(code: .purchaseVerificationFailed, message: error.localizedDescription)
|
|
413
429
|
}
|
|
414
430
|
}
|
|
415
431
|
}
|
|
@@ -421,9 +437,12 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
421
437
|
let storefront = try await OpenIapModule.shared.getStorefrontIOS()
|
|
422
438
|
RnIapLog.result("getStorefront", storefront)
|
|
423
439
|
return storefront
|
|
440
|
+
} catch let purchaseError as PurchaseError {
|
|
441
|
+
RnIapLog.failure("getStorefront", error: purchaseError)
|
|
442
|
+
throw OpenIapException.from(purchaseError)
|
|
424
443
|
} catch {
|
|
425
444
|
RnIapLog.failure("getStorefront", error: error)
|
|
426
|
-
throw
|
|
445
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
427
446
|
}
|
|
428
447
|
}
|
|
429
448
|
}
|
|
@@ -484,9 +503,12 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
484
503
|
let payload = RnIapHelper.sanitizeDictionary(OpenIapSerialization.encode(product))
|
|
485
504
|
RnIapLog.result("getPromotedProductIOS", payload)
|
|
486
505
|
return RnIapHelper.convertProductDictionary(payload)
|
|
506
|
+
} catch let purchaseError as PurchaseError {
|
|
507
|
+
RnIapLog.failure("getPromotedProductIOS", error: purchaseError)
|
|
508
|
+
throw OpenIapException.from(purchaseError)
|
|
487
509
|
} catch {
|
|
488
510
|
RnIapLog.failure("getPromotedProductIOS", error: error)
|
|
489
|
-
throw
|
|
511
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
490
512
|
}
|
|
491
513
|
}
|
|
492
514
|
}
|
|
@@ -518,11 +540,11 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
518
540
|
} catch {
|
|
519
541
|
// Fallback with explicit error for simulator or unsupported cases
|
|
520
542
|
RnIapLog.failure("presentCodeRedemptionSheetIOS", error: error)
|
|
521
|
-
throw
|
|
543
|
+
throw OpenIapException.make(code: .featureNotSupported)
|
|
522
544
|
}
|
|
523
545
|
}
|
|
524
546
|
}
|
|
525
|
-
|
|
547
|
+
|
|
526
548
|
func clearTransactionIOS() throws -> Promise<Void> {
|
|
527
549
|
return Promise.async {
|
|
528
550
|
do {
|
|
@@ -590,11 +612,11 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
590
612
|
return Optional<NitroPurchase>.none
|
|
591
613
|
} catch {
|
|
592
614
|
RnIapLog.failure("currentEntitlementIOS", error: error)
|
|
593
|
-
throw
|
|
615
|
+
throw OpenIapException.make(code: .skuNotFound, productId: sku)
|
|
594
616
|
}
|
|
595
617
|
}
|
|
596
618
|
}
|
|
597
|
-
|
|
619
|
+
|
|
598
620
|
func latestTransactionIOS(sku: String) throws -> Promise<NitroPurchase?> {
|
|
599
621
|
return Promise.async {
|
|
600
622
|
try self.ensureConnection()
|
|
@@ -616,11 +638,11 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
616
638
|
return Optional<NitroPurchase>.none
|
|
617
639
|
} catch {
|
|
618
640
|
RnIapLog.failure("latestTransactionIOS", error: error)
|
|
619
|
-
throw
|
|
641
|
+
throw OpenIapException.make(code: .skuNotFound, productId: sku)
|
|
620
642
|
}
|
|
621
643
|
}
|
|
622
644
|
}
|
|
623
|
-
|
|
645
|
+
|
|
624
646
|
func getPendingTransactionsIOS() throws -> Promise<[NitroPurchase]> {
|
|
625
647
|
return Promise.async {
|
|
626
648
|
do {
|
|
@@ -654,13 +676,16 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
654
676
|
let ok = try await OpenIapModule.shared.syncIOS()
|
|
655
677
|
RnIapLog.result("syncIOS", ok)
|
|
656
678
|
return ok
|
|
679
|
+
} catch let purchaseError as PurchaseError {
|
|
680
|
+
RnIapLog.failure("syncIOS", error: purchaseError)
|
|
681
|
+
throw OpenIapException.from(purchaseError)
|
|
657
682
|
} catch {
|
|
658
683
|
RnIapLog.failure("syncIOS", error: error)
|
|
659
|
-
throw
|
|
684
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
660
685
|
}
|
|
661
686
|
}
|
|
662
687
|
}
|
|
663
|
-
|
|
688
|
+
|
|
664
689
|
func showManageSubscriptionsIOS() throws -> Promise<[NitroPurchase]> {
|
|
665
690
|
return Promise.async {
|
|
666
691
|
try self.ensureConnection()
|
|
@@ -678,9 +703,12 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
678
703
|
let payloads = RnIapHelper.sanitizeArray(OpenIapSerialization.purchases(purchases))
|
|
679
704
|
RnIapLog.result("showManageSubscriptionsIOS", payloads)
|
|
680
705
|
return payloads.map { RnIapHelper.convertPurchaseDictionary($0) }
|
|
706
|
+
} catch let purchaseError as PurchaseError {
|
|
707
|
+
RnIapLog.failure("showManageSubscriptionsIOS", error: purchaseError)
|
|
708
|
+
throw OpenIapException.from(purchaseError)
|
|
681
709
|
} catch {
|
|
682
710
|
RnIapLog.failure("showManageSubscriptionsIOS", error: error)
|
|
683
|
-
throw
|
|
711
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
684
712
|
}
|
|
685
713
|
}
|
|
686
714
|
}
|
|
@@ -693,13 +721,16 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
693
721
|
try await OpenIapModule.shared.deepLinkToSubscriptions(nil)
|
|
694
722
|
RnIapLog.result("deepLinkToSubscriptionsIOS", true)
|
|
695
723
|
return true
|
|
724
|
+
} catch let purchaseError as PurchaseError {
|
|
725
|
+
RnIapLog.failure("deepLinkToSubscriptionsIOS", error: purchaseError)
|
|
726
|
+
throw OpenIapException.from(purchaseError)
|
|
696
727
|
} catch {
|
|
697
728
|
RnIapLog.failure("deepLinkToSubscriptionsIOS", error: error)
|
|
698
|
-
throw
|
|
729
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
699
730
|
}
|
|
700
731
|
}
|
|
701
732
|
}
|
|
702
|
-
|
|
733
|
+
|
|
703
734
|
func isEligibleForIntroOfferIOS(groupID: String) throws -> Promise<Bool> {
|
|
704
735
|
return Promise.async {
|
|
705
736
|
RnIapLog.payload("isEligibleForIntroOfferIOS", ["groupID": groupID])
|
|
@@ -719,10 +750,10 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
719
750
|
return receipt
|
|
720
751
|
} catch let purchaseError as PurchaseError {
|
|
721
752
|
RnIapLog.failure("getReceiptDataIOS", error: purchaseError)
|
|
722
|
-
throw purchaseError
|
|
753
|
+
throw OpenIapException.from(purchaseError)
|
|
723
754
|
} catch {
|
|
724
755
|
RnIapLog.failure("getReceiptDataIOS", error: error)
|
|
725
|
-
throw
|
|
756
|
+
throw OpenIapException.make(code: .receiptFailed, message: error.localizedDescription)
|
|
726
757
|
}
|
|
727
758
|
}
|
|
728
759
|
}
|
|
@@ -737,10 +768,10 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
737
768
|
return receipt
|
|
738
769
|
} catch let purchaseError as PurchaseError {
|
|
739
770
|
RnIapLog.failure("getReceiptIOS", error: purchaseError)
|
|
740
|
-
throw purchaseError
|
|
771
|
+
throw OpenIapException.from(purchaseError)
|
|
741
772
|
} catch {
|
|
742
773
|
RnIapLog.failure("getReceiptIOS", error: error)
|
|
743
|
-
throw
|
|
774
|
+
throw OpenIapException.make(code: .receiptFailed, message: error.localizedDescription)
|
|
744
775
|
}
|
|
745
776
|
}
|
|
746
777
|
}
|
|
@@ -755,14 +786,14 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
755
786
|
return receipt
|
|
756
787
|
} catch let purchaseError as PurchaseError {
|
|
757
788
|
RnIapLog.failure("requestReceiptRefreshIOS", error: purchaseError)
|
|
758
|
-
throw purchaseError
|
|
789
|
+
throw OpenIapException.from(purchaseError)
|
|
759
790
|
} catch {
|
|
760
791
|
RnIapLog.failure("requestReceiptRefreshIOS", error: error)
|
|
761
|
-
throw
|
|
792
|
+
throw OpenIapException.make(code: .receiptFailed, message: error.localizedDescription)
|
|
762
793
|
}
|
|
763
794
|
}
|
|
764
795
|
}
|
|
765
|
-
|
|
796
|
+
|
|
766
797
|
func isTransactionVerifiedIOS(sku: String) throws -> Promise<Bool> {
|
|
767
798
|
return Promise.async {
|
|
768
799
|
try self.ensureConnection()
|
|
@@ -784,11 +815,11 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
784
815
|
return jws
|
|
785
816
|
} catch {
|
|
786
817
|
RnIapLog.failure("getTransactionJwsIOS", error: error)
|
|
787
|
-
throw
|
|
818
|
+
throw OpenIapException.make(code: .transactionValidationFailed, message: "Can't find transaction for sku \(sku)")
|
|
788
819
|
}
|
|
789
820
|
}
|
|
790
821
|
}
|
|
791
|
-
|
|
822
|
+
|
|
792
823
|
func beginRefundRequestIOS(sku: String) throws -> Promise<String?> {
|
|
793
824
|
return Promise.async {
|
|
794
825
|
do {
|
|
@@ -922,7 +953,7 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
922
953
|
|
|
923
954
|
private func ensureConnection() throws {
|
|
924
955
|
guard isInitialized else {
|
|
925
|
-
throw
|
|
956
|
+
throw OpenIapException.make(code: .initConnection, message: "Connection not initialized. Call initConnection() first.")
|
|
926
957
|
}
|
|
927
958
|
}
|
|
928
959
|
|
|
@@ -1024,7 +1055,7 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
1024
1055
|
|
|
1025
1056
|
func deepLinkToSubscriptionsAndroid(options: NitroDeepLinkOptionsAndroid) throws -> Promise<Void> {
|
|
1026
1057
|
return Promise.async {
|
|
1027
|
-
throw
|
|
1058
|
+
throw OpenIapException.make(code: .featureNotSupported)
|
|
1028
1059
|
}
|
|
1029
1060
|
}
|
|
1030
1061
|
|
|
@@ -1032,19 +1063,19 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
1032
1063
|
|
|
1033
1064
|
func checkAlternativeBillingAvailabilityAndroid() throws -> Promise<Bool> {
|
|
1034
1065
|
return Promise.async {
|
|
1035
|
-
throw
|
|
1066
|
+
throw OpenIapException.make(code: .featureNotSupported)
|
|
1036
1067
|
}
|
|
1037
1068
|
}
|
|
1038
1069
|
|
|
1039
1070
|
func showAlternativeBillingDialogAndroid() throws -> Promise<Bool> {
|
|
1040
1071
|
return Promise.async {
|
|
1041
|
-
throw
|
|
1072
|
+
throw OpenIapException.make(code: .featureNotSupported)
|
|
1042
1073
|
}
|
|
1043
1074
|
}
|
|
1044
1075
|
|
|
1045
1076
|
func createAlternativeBillingTokenAndroid(sku: String?) throws -> Promise<String?> {
|
|
1046
1077
|
return Promise.async {
|
|
1047
|
-
throw
|
|
1078
|
+
throw OpenIapException.make(code: .featureNotSupported)
|
|
1048
1079
|
}
|
|
1049
1080
|
}
|
|
1050
1081
|
|
|
@@ -1064,19 +1095,19 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
1064
1095
|
|
|
1065
1096
|
func isBillingProgramAvailableAndroid(program: BillingProgramAndroid) throws -> Promise<NitroBillingProgramAvailabilityResultAndroid> {
|
|
1066
1097
|
return Promise.async {
|
|
1067
|
-
throw
|
|
1098
|
+
throw OpenIapException.make(code: .featureNotSupported, message: "Billing Programs API is Android-only")
|
|
1068
1099
|
}
|
|
1069
1100
|
}
|
|
1070
1101
|
|
|
1071
1102
|
func createBillingProgramReportingDetailsAndroid(program: BillingProgramAndroid) throws -> Promise<NitroBillingProgramReportingDetailsAndroid> {
|
|
1072
1103
|
return Promise.async {
|
|
1073
|
-
throw
|
|
1104
|
+
throw OpenIapException.make(code: .featureNotSupported, message: "Billing Programs API is Android-only")
|
|
1074
1105
|
}
|
|
1075
1106
|
}
|
|
1076
1107
|
|
|
1077
1108
|
func launchExternalLinkAndroid(params: NitroLaunchExternalLinkParamsAndroid) throws -> Promise<Bool> {
|
|
1078
1109
|
return Promise.async {
|
|
1079
|
-
throw
|
|
1110
|
+
throw OpenIapException.make(code: .featureNotSupported, message: "Billing Programs API is Android-only")
|
|
1080
1111
|
}
|
|
1081
1112
|
}
|
|
1082
1113
|
|
|
@@ -1092,12 +1123,15 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
1092
1123
|
let canPresent = try await OpenIapModule.shared.canPresentExternalPurchaseNoticeIOS()
|
|
1093
1124
|
RnIapLog.result("canPresentExternalPurchaseNoticeIOS", canPresent)
|
|
1094
1125
|
return canPresent
|
|
1126
|
+
} catch let purchaseError as PurchaseError {
|
|
1127
|
+
RnIapLog.failure("canPresentExternalPurchaseNoticeIOS", error: purchaseError)
|
|
1128
|
+
throw OpenIapException.from(purchaseError)
|
|
1095
1129
|
} catch {
|
|
1096
1130
|
RnIapLog.failure("canPresentExternalPurchaseNoticeIOS", error: error)
|
|
1097
|
-
throw
|
|
1131
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
1098
1132
|
}
|
|
1099
1133
|
} else {
|
|
1100
|
-
let err =
|
|
1134
|
+
let err = OpenIapException.make(code: .featureNotSupported, message: "External purchase notice requires iOS 16.0 or later")
|
|
1101
1135
|
RnIapLog.failure("canPresentExternalPurchaseNoticeIOS", error: err)
|
|
1102
1136
|
throw err
|
|
1103
1137
|
}
|
|
@@ -1116,7 +1150,7 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
1116
1150
|
// Convert OpenIAP action to Nitro action via raw value
|
|
1117
1151
|
let actionString = result.result.rawValue
|
|
1118
1152
|
guard let nitroAction = ExternalPurchaseNoticeAction(fromString: actionString) else {
|
|
1119
|
-
throw
|
|
1153
|
+
throw OpenIapException.make(code: .serviceError, message: "Invalid action: \(actionString)")
|
|
1120
1154
|
}
|
|
1121
1155
|
|
|
1122
1156
|
let nitroResult = ExternalPurchaseNoticeResultIOS(
|
|
@@ -1125,12 +1159,15 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
1125
1159
|
)
|
|
1126
1160
|
RnIapLog.result("presentExternalPurchaseNoticeSheetIOS", result)
|
|
1127
1161
|
return nitroResult
|
|
1162
|
+
} catch let purchaseError as PurchaseError {
|
|
1163
|
+
RnIapLog.failure("presentExternalPurchaseNoticeSheetIOS", error: purchaseError)
|
|
1164
|
+
throw OpenIapException.from(purchaseError)
|
|
1128
1165
|
} catch {
|
|
1129
1166
|
RnIapLog.failure("presentExternalPurchaseNoticeSheetIOS", error: error)
|
|
1130
|
-
throw
|
|
1167
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
1131
1168
|
}
|
|
1132
1169
|
} else {
|
|
1133
|
-
let err =
|
|
1170
|
+
let err = OpenIapException.make(code: .featureNotSupported, message: "External purchase notice requires iOS 16.0 or later")
|
|
1134
1171
|
RnIapLog.failure("presentExternalPurchaseNoticeSheetIOS", error: err)
|
|
1135
1172
|
throw err
|
|
1136
1173
|
}
|
|
@@ -1151,12 +1188,15 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
1151
1188
|
)
|
|
1152
1189
|
RnIapLog.result("presentExternalPurchaseLinkIOS", result)
|
|
1153
1190
|
return nitroResult
|
|
1191
|
+
} catch let purchaseError as PurchaseError {
|
|
1192
|
+
RnIapLog.failure("presentExternalPurchaseLinkIOS", error: purchaseError)
|
|
1193
|
+
throw OpenIapException.from(purchaseError)
|
|
1154
1194
|
} catch {
|
|
1155
1195
|
RnIapLog.failure("presentExternalPurchaseLinkIOS", error: error)
|
|
1156
|
-
throw
|
|
1196
|
+
throw OpenIapException.make(code: .serviceError, message: error.localizedDescription)
|
|
1157
1197
|
}
|
|
1158
1198
|
} else {
|
|
1159
|
-
let err =
|
|
1199
|
+
let err = OpenIapException.make(code: .featureNotSupported, message: "External purchase link requires iOS 16.0 or later")
|
|
1160
1200
|
RnIapLog.failure("presentExternalPurchaseLinkIOS", error: err)
|
|
1161
1201
|
throw err
|
|
1162
1202
|
}
|
package/ios/RnIapHelper.swift
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import OpenIAP
|
|
3
3
|
|
|
4
|
+
/// Custom error that preserves error messages through Nitro bridge.
|
|
5
|
+
/// Similar to Android's OpenIapException, this wraps errors with JSON-serialized messages.
|
|
6
|
+
/// Uses NSError for better compatibility with Objective-C bridging in Nitro.
|
|
7
|
+
@available(iOS 15.0, *)
|
|
8
|
+
class OpenIapException: NSError {
|
|
9
|
+
static let domain = "com.margelo.nitro.rniap"
|
|
10
|
+
|
|
11
|
+
convenience init(_ json: String) {
|
|
12
|
+
self.init(domain: OpenIapException.domain, code: -1, userInfo: [NSLocalizedDescriptionKey: json])
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static func make(code: ErrorCode, message: String? = nil, productId: String? = nil) -> OpenIapException {
|
|
16
|
+
let errorMessage = message ?? code.rawValue
|
|
17
|
+
var dict: [String: Any] = [
|
|
18
|
+
"code": code.rawValue,
|
|
19
|
+
"message": errorMessage
|
|
20
|
+
]
|
|
21
|
+
if let productId = productId {
|
|
22
|
+
dict["productId"] = productId
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if let data = try? JSONSerialization.data(withJSONObject: dict),
|
|
26
|
+
let json = String(data: data, encoding: .utf8) {
|
|
27
|
+
return OpenIapException(json)
|
|
28
|
+
}
|
|
29
|
+
return OpenIapException("{\"code\":\"\(code.rawValue)\",\"message\":\"\(errorMessage)\"}")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static func from(_ error: PurchaseError) -> OpenIapException {
|
|
33
|
+
return make(code: error.code, message: error.message, productId: error.productId)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
4
37
|
@available(iOS 15.0, *)
|
|
5
38
|
enum RnIapHelper {
|
|
6
39
|
// MARK: - Sanitizers
|
|
@@ -272,13 +305,13 @@ enum RnIapHelper {
|
|
|
272
305
|
|
|
273
306
|
do {
|
|
274
307
|
guard let receipt = try await OpenIapModule.shared.getReceiptDataIOS(), !receipt.isEmpty else {
|
|
275
|
-
throw
|
|
308
|
+
throw OpenIapException.make(code: .receiptFailed)
|
|
276
309
|
}
|
|
277
310
|
return receipt
|
|
278
311
|
} catch let error as PurchaseError {
|
|
279
|
-
throw error
|
|
312
|
+
throw OpenIapException.from(error)
|
|
280
313
|
} catch {
|
|
281
|
-
throw
|
|
314
|
+
throw OpenIapException.make(code: .receiptFailed, message: error.localizedDescription)
|
|
282
315
|
}
|
|
283
316
|
}
|
|
284
317
|
|
package/package.json
CHANGED