@primer-io/react-native 2.26.3 → 2.28.0-beta.1

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.
Files changed (81) hide show
  1. package/android/.project +6 -0
  2. package/android/.settings/org.eclipse.buildship.core.prefs +5 -5
  3. package/android/build.gradle +1 -3
  4. package/android/src/main/AndroidManifest.xml +1 -4
  5. package/android/src/main/java/com/primerioreactnative/PrimerRN.kt +3 -3
  6. package/android/src/main/java/com/primerioreactnative/PrimerRNEventListener.kt +4 -3
  7. package/android/src/main/java/com/primerioreactnative/PrimerRNHeadlessUniversalCheckout.kt +2 -2
  8. package/android/src/main/java/com/primerioreactnative/PrimerRNHeadlessUniversalCheckoutListener.kt +46 -9
  9. package/android/src/main/java/com/primerioreactnative/ReactNativePackage.kt +4 -0
  10. package/android/src/main/java/com/primerioreactnative/components/manager/ach/AchMandateManager.kt +70 -0
  11. package/android/src/main/java/com/primerioreactnative/components/manager/ach/PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.kt +283 -0
  12. package/android/src/main/java/com/primerioreactnative/components/manager/banks/PrimerRNHeadlessUniversalCheckoutBanksComponent.kt +7 -37
  13. package/android/src/main/java/com/primerioreactnative/components/manager/klarna/PrimerRNHeadlessUniversalCheckoutKlarnaComponent.kt +5 -31
  14. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerCheckoutAdditionalInfoRN.kt +17 -4
  15. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerClientSessionRN.kt +9 -0
  16. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerSettingsRN.kt +53 -36
  17. package/android/src/main/java/com/primerioreactnative/datamodels/ach/AchUserDetailsCollectableDataRN.kt +22 -0
  18. package/android/src/main/java/com/primerioreactnative/datamodels/ach/AchUserDetailsStepRN.kt +22 -0
  19. package/android/src/main/java/com/primerioreactnative/datamodels/extensions/ach/AchUserDetailsCollectableData.kt +11 -0
  20. package/android/src/main/java/com/primerioreactnative/datamodels/extensions/ach/AchUserDetailsStep.kt +14 -0
  21. package/android/src/main/java/com/primerioreactnative/extensions/JSONExtensions.kt +41 -0
  22. package/android/src/main/java/com/primerioreactnative/extensions/PrimerCheckoutAdditionalInfo.kt +4 -0
  23. package/android/src/main/java/com/primerioreactnative/extensions/PrimerOrder.kt +5 -1
  24. package/android/src/main/java/com/primerioreactnative/extensions/PrimerPaymentMethodOptionsRN.kt +48 -15
  25. package/ios/Sources/DataModels/Ach Component/RNTAchUserDetailsCollectableData.swift +54 -0
  26. package/ios/Sources/DataModels/Ach Component/RNTAchUserDetailsStep.swift +40 -0
  27. package/ios/Sources/DataModels/PrimerCheckoutAdditionalInfoRN.swift +61 -0
  28. package/ios/Sources/DataModels/PrimerSettings+Extensions.swift +58 -4
  29. package/ios/Sources/Headless Universal Checkout/Managers/Ach/PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.m +28 -0
  30. package/ios/Sources/Headless Universal Checkout/Managers/Ach/PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.swift +233 -0
  31. package/ios/Sources/Headless Universal Checkout/Managers/Ach/RNTAchMandateManager.m +16 -0
  32. package/ios/Sources/Headless Universal Checkout/Managers/Ach/RNTAchMandateManager.swift +53 -0
  33. package/ios/Sources/Headless Universal Checkout/Managers/Klarna/PrimerRNHeadlessUniversalCheckoutKlarnaComponent.swift +2 -2
  34. package/ios/Sources/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.swift +55 -7
  35. package/ios/Sources/version.swift +1 -1
  36. package/lib/commonjs/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.js +106 -0
  37. package/lib/commonjs/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.js.map +1 -0
  38. package/lib/commonjs/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.js +38 -0
  39. package/lib/commonjs/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.js.map +1 -0
  40. package/lib/commonjs/index.js +70 -0
  41. package/lib/commonjs/index.js.map +1 -1
  42. package/lib/commonjs/models/ach/AchCollectableData.js +6 -0
  43. package/lib/commonjs/models/ach/AchCollectableData.js.map +1 -0
  44. package/lib/commonjs/models/ach/AchSteps.js +6 -0
  45. package/lib/commonjs/models/ach/AchSteps.js.map +1 -0
  46. package/lib/module/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.js +99 -0
  47. package/lib/module/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.js.map +1 -0
  48. package/lib/module/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.js +31 -0
  49. package/lib/module/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.js.map +1 -0
  50. package/lib/module/index.js +8 -0
  51. package/lib/module/index.js.map +1 -1
  52. package/lib/module/models/ach/AchCollectableData.js +2 -0
  53. package/lib/module/models/ach/AchCollectableData.js.map +1 -0
  54. package/lib/module/models/ach/AchSteps.js +2 -0
  55. package/lib/module/models/ach/AchSteps.js.map +1 -0
  56. package/lib/typescript/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.d.ts +53 -0
  57. package/lib/typescript/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.d.ts.map +1 -0
  58. package/lib/typescript/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.d.ts +12 -0
  59. package/lib/typescript/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.d.ts.map +1 -0
  60. package/lib/typescript/index.d.ts +8 -0
  61. package/lib/typescript/index.d.ts.map +1 -1
  62. package/lib/typescript/models/PrimerCheckoutAdditionalInfo.d.ts +10 -0
  63. package/lib/typescript/models/PrimerCheckoutAdditionalInfo.d.ts.map +1 -1
  64. package/lib/typescript/models/PrimerClientSession.d.ts +7 -0
  65. package/lib/typescript/models/PrimerClientSession.d.ts.map +1 -1
  66. package/lib/typescript/models/PrimerSettings.d.ts +40 -1
  67. package/lib/typescript/models/PrimerSettings.d.ts.map +1 -1
  68. package/lib/typescript/models/ach/AchCollectableData.d.ts +25 -0
  69. package/lib/typescript/models/ach/AchCollectableData.d.ts.map +1 -0
  70. package/lib/typescript/models/ach/AchSteps.d.ts +28 -0
  71. package/lib/typescript/models/ach/AchSteps.d.ts.map +1 -0
  72. package/package.json +1 -1
  73. package/primer-io-react-native.podspec +1 -1
  74. package/src/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.ts +158 -0
  75. package/src/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.ts +32 -0
  76. package/src/index.tsx +8 -0
  77. package/src/models/PrimerCheckoutAdditionalInfo.ts +19 -5
  78. package/src/models/PrimerClientSession.ts +9 -1
  79. package/src/models/PrimerSettings.ts +46 -1
  80. package/src/models/ach/AchCollectableData.ts +29 -0
  81. package/src/models/ach/AchSteps.ts +29 -0
@@ -0,0 +1,40 @@
1
+ //
2
+ // RNTAchUserDetailsStep.swift
3
+ // Pods
4
+ //
5
+ // Created by Flaviu Dunca on 12.06.2024.
6
+ //
7
+
8
+ import Foundation
9
+ import PrimerSDK
10
+
11
+ protocol AchUserDetailsStepRN {
12
+ var stepName: String { get }
13
+ }
14
+
15
+ class UserDetailsCollectedRN: AchUserDetailsStepRN, Encodable {
16
+ let stepName: String = "userDetailsCollected"
17
+ }
18
+
19
+ class UserDetailsRetrievedRN: AchUserDetailsStepRN, Encodable {
20
+ let stepName: String = "userDetailsRetrieved"
21
+ let firstName: String
22
+ let lastName: String
23
+ let emailAddress: String
24
+
25
+ init(firstName: String, lastName: String, emailAddress: String) {
26
+ self.firstName = firstName
27
+ self.lastName = lastName
28
+ self.emailAddress = emailAddress
29
+ }
30
+ }
31
+
32
+ extension ACHUserDetailsStep {
33
+ func toUserDetailsCollectedRN() -> UserDetailsCollectedRN {
34
+ return UserDetailsCollectedRN()
35
+ }
36
+
37
+ func toUserDetailsRetrievedRN(firstName: String, lastName: String, emailAddress: String) -> UserDetailsRetrievedRN {
38
+ return UserDetailsRetrievedRN(firstName: firstName, lastName: lastName, emailAddress: emailAddress)
39
+ }
40
+ }
@@ -0,0 +1,61 @@
1
+ //
2
+ // PrimerCheckoutAdditionalInfoRN.swift
3
+ // primer-io-react-native
4
+ //
5
+ // Created by Flaviu Dunca on 17.06.2024.
6
+ //
7
+
8
+ import Foundation
9
+ import PrimerSDK
10
+
11
+
12
+ protocol PrimerCheckoutAdditionalInfoRN: Encodable {
13
+ var additionalInfoName: String { get }
14
+ }
15
+
16
+ class MultibancoCheckoutAdditionalInfoRN: PrimerCheckoutAdditionalInfoRN {
17
+ let expiresAt: String?
18
+ let reference: String?
19
+ let entity: String?
20
+ let additionalInfoName: String = "MultibancoCheckoutAdditionalInfo"
21
+
22
+ init(expiresAt: String?, reference: String?, entity: String?) {
23
+ self.expiresAt = expiresAt
24
+ self.reference = reference
25
+ self.entity = entity
26
+ }
27
+ }
28
+
29
+ protocol PrimerCheckoutQRCodeInfoRN: PrimerCheckoutAdditionalInfoRN {}
30
+
31
+ class PromptPayCheckoutAdditionalInfoRN: PrimerCheckoutQRCodeInfoRN {
32
+ let expiresAt: String
33
+ let qrCodeUrl: String?
34
+ let qrCodeBase64: String?
35
+ var additionalInfoName: String = "PromptPayCheckoutAdditionalInfo"
36
+
37
+ init(expiresAt: String, qrCodeUrl: String?, qrCodeBase64: String?) {
38
+ self.expiresAt = expiresAt
39
+ self.qrCodeUrl = qrCodeUrl
40
+ self.qrCodeBase64 = qrCodeBase64
41
+ }
42
+ }
43
+
44
+ protocol PrimerCheckoutVoucherInfoRN: PrimerCheckoutAdditionalInfoRN {}
45
+
46
+ class XenditCheckoutVoucherAdditionalInfoRN: PrimerCheckoutVoucherInfoRN {
47
+ let expiresAt: String
48
+ let couponCode: String
49
+ let retailerName: String?
50
+ var additionalInfoName: String = "XenditCheckoutVoucherAdditionalInfo"
51
+
52
+ init(expiresAt: String, couponCode: String, retailerName: String?) {
53
+ self.expiresAt = expiresAt
54
+ self.couponCode = couponCode
55
+ self.retailerName = retailerName
56
+ }
57
+ }
58
+
59
+ class AchAdditionalInfoDisplayMandateRN: PrimerCheckoutAdditionalInfoRN {
60
+ var additionalInfoName: String = "DisplayStripeAchMandateAdditionalInfo"
61
+ }
@@ -34,20 +34,51 @@ extension PrimerSettings {
34
34
 
35
35
  var applePayOptions: PrimerApplePayOptions?
36
36
  if let rnApplePayOptions = ((settingsJson["paymentMethodOptions"] as? [String: Any])?["applePayOptions"] as? [String: Any]),
37
- let rnApplePayMerchantIdentifier = rnApplePayOptions["merchantIdentifier"] as? String,
38
- let rnApplePayMerchantName = rnApplePayOptions["merchantName"] as? String
37
+ let rnApplePayMerchantIdentifier = rnApplePayOptions["merchantIdentifier"] as? String
39
38
  {
39
+ let rnApplePayMerchantName = rnApplePayOptions["merchantName"] as? String
40
40
  let rnApplePayIsCaptureBillingAddressEnabled = (rnApplePayOptions["isCaptureBillingAddressEnabled"] as? Bool) ?? false
41
41
  let rnApplePayShowApplePayForUnsupportedDevice = (rnApplePayOptions["showApplePayForUnsupportedDevice"] as? Bool) ?? true
42
42
  let rnApplePayCheckProvidedNetworks = (rnApplePayOptions["checkProvidedNetworks"] as? Bool) ?? true
43
43
 
44
+ var shippingOptions: PrimerApplePayOptions.ShippingOptions? = nil
45
+ if let rnShippingOptions = rnApplePayOptions["shippingOptions"] as? [String: Any] {
46
+ let isCaptureShippingAddressEnabled = rnShippingOptions["isCaptureShippingAddressEnabled"] as? Bool ?? false
47
+ let requireShippingMethod = rnShippingOptions["requireShippingMethod"] as? Bool ?? false
48
+
49
+ var additionalShippingContactFields: [PrimerApplePayOptions.ShippingOptions.AdditionalShippingContactField]? = nil
50
+ if let additionalFieldsStrings = rnShippingOptions["additionalShippingContactFields"] as? [String] {
51
+ additionalShippingContactFields = additionalFieldsStrings.compactMap { fieldStr in
52
+ switch fieldStr {
53
+ case "name":
54
+ return .name
55
+ case "emailAddress":
56
+ return .emailAddress
57
+ case "phoneNumber":
58
+ return .phoneNumber
59
+ default:
60
+ return nil // Ignore unknown values or handle accordingly
61
+ }
62
+ }
63
+ }
64
+
65
+ shippingOptions = PrimerApplePayOptions.ShippingOptions(
66
+ isCaptureShippingAddressEnabled: isCaptureShippingAddressEnabled,
67
+ additionalShippingContactFields: additionalShippingContactFields,
68
+ requireShippingMethod: requireShippingMethod
69
+ )
70
+ }
71
+
44
72
  applePayOptions = PrimerApplePayOptions(
45
73
  merchantIdentifier: rnApplePayMerchantIdentifier,
46
74
  merchantName: rnApplePayMerchantName,
47
75
  isCaptureBillingAddressEnabled: rnApplePayIsCaptureBillingAddressEnabled,
48
76
  showApplePayForUnsupportedDevice: rnApplePayShowApplePayForUnsupportedDevice,
49
- checkProvidedNetworks: rnApplePayCheckProvidedNetworks)
77
+ checkProvidedNetworks: rnApplePayCheckProvidedNetworks,
78
+ shippingOptions: shippingOptions
79
+ )
50
80
  }
81
+
51
82
 
52
83
  var klarnaOptions: PrimerKlarnaOptions?
53
84
  if let rnKlarnaRecurringPaymentDescription = ((settingsJson["paymentMethodOptions"] as? [String: Any])?["klarnaOptions"] as? [String: Any])?["recurringPaymentDescription"] as? String {
@@ -85,12 +116,35 @@ extension PrimerSettings {
85
116
  if let rnThreeDsAppRequestorUrlStr = (((settingsJson["paymentMethodOptions"] as? [String: Any])?["threeDsOptions"] as? [String: Any])?["iOS"] as? [String: Any])?["threeDsAppRequestorUrl"] as? String {
86
117
  threeDsOptions = PrimerThreeDsOptions(threeDsAppRequestorUrl: rnThreeDsAppRequestorUrlStr)
87
118
  }
119
+
120
+ var stripeOptions: PrimerStripeOptions?
121
+ if let stripeOptionsMap = ((settingsJson["paymentMethodOptions"] as? [String: Any])?["stripeOptions"] as? [String: Any]) {
122
+ if let publishableKey = stripeOptionsMap["publishableKey"] as? String {
123
+ var mandateData: PrimerStripeOptions.MandateData?
124
+ if let mandateDataMap = stripeOptionsMap["mandateData"] as? [String: String] {
125
+ if let fullMandateText = mandateDataMap["fullMandateText"] {
126
+ mandateData = .fullMandate(text: fullMandateText)
127
+ } else if let merchantName = mandateDataMap["merchantName"] {
128
+ mandateData = .templateMandate(merchantName: merchantName)
129
+ } else {
130
+ PrimerLogging.shared.logger.warn(message: "Found mandate data but no resource key or merchant name - check your stripe config")
131
+ }
132
+ } else {
133
+ PrimerLogging.shared.logger.warn(message: "Found stripe options but no mandate data - check your stripe config")
134
+ }
135
+ stripeOptions = .init(
136
+ publishableKey: publishableKey,
137
+ mandateData: mandateData
138
+ )
139
+ }
140
+ }
88
141
 
89
142
  let paymentMethodOptions = PrimerPaymentMethodOptions(
90
143
  urlScheme: rnUrlScheme,
91
144
  applePayOptions: applePayOptions,
92
145
  klarnaOptions: klarnaOptions,
93
- threeDsOptions: threeDsOptions)
146
+ threeDsOptions: threeDsOptions,
147
+ stripeOptions: stripeOptions)
94
148
 
95
149
  self.init(
96
150
  paymentHandling: paymentHandling,
@@ -0,0 +1,28 @@
1
+ //
2
+ // PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.m
3
+ // primer-io-react-native
4
+ //
5
+ // Created by Flaviu Dunca on 14.06.2024.
6
+ //
7
+
8
+ #import <Foundation/Foundation.h>
9
+ #import <React/RCTBridgeModule.h>
10
+ #import <React/RCTEventEmitter.h>
11
+
12
+ @interface RCT_EXTERN_MODULE(RNTPrimerHeadlessUniversalCheckoutStripeAchUserDetailsComponent, RCTEventEmitter)
13
+
14
+ RCT_EXTERN_METHOD(configure:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
15
+
16
+ RCT_EXTERN_METHOD(onSetFirstName:(NSString *)firstName resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
17
+
18
+ RCT_EXTERN_METHOD(onSetLastName:(NSString *)lastName resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
19
+
20
+ RCT_EXTERN_METHOD(onSetEmailAddress:(NSString *)emailAddress resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
21
+
22
+ RCT_EXTERN_METHOD(start:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
23
+
24
+ RCT_EXTERN_METHOD(submit:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
25
+
26
+ RCT_EXTERN_METHOD(cleanUp:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
27
+
28
+ @end
@@ -0,0 +1,233 @@
1
+ //
2
+ // PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.swift
3
+ // Pods
4
+ //
5
+ // Created by Flaviu Dunca on 12.06.2024.
6
+ //
7
+
8
+ import Foundation
9
+ import UIKit
10
+ import PrimerSDK
11
+
12
+ @objc(RNTPrimerHeadlessUniversalCheckoutStripeAchUserDetailsComponent)
13
+ class RNTPrimerHeadlessUniversalCheckoutStripeAchUserDetailsComponent: RCTEventEmitter {
14
+
15
+ #if canImport(PrimerStripeSDK)
16
+ private var achManager: PrimerHeadlessUniversalCheckout.AchManager = PrimerHeadlessUniversalCheckout.AchManager()
17
+ #endif
18
+ var stripeAchUserDetailsComponent: (any StripeAchUserDetailsComponent)?
19
+ var clientToken: String?
20
+
21
+ override class func requiresMainQueueSetup() -> Bool {
22
+ return true
23
+ }
24
+
25
+ override init() {
26
+ super.init()
27
+ }
28
+
29
+ override func supportedEvents() -> [String] {
30
+ return PrimerHeadlessUniversalCheckoutComponentEvent.allCases.compactMap({ $0.stringValue })
31
+ }
32
+
33
+ // MARK: - API
34
+
35
+ @objc
36
+ public func configure(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
37
+ print("configure")
38
+
39
+ do {
40
+ #if canImport(PrimerStripeSDK)
41
+ guard let stripeAchUserDetailsComponent: any StripeAchUserDetailsComponent = try? achManager.provide(paymentMethodType: "STRIPE_ACH") else {
42
+ let err = RNTNativeError(
43
+ errorId: "native-ios",
44
+ errorDescription: "Failed to find asset of STRIPE_ACH for this session",
45
+ recoverySuggestion: nil)
46
+ throw err
47
+ }
48
+ self.stripeAchUserDetailsComponent = stripeAchUserDetailsComponent
49
+ RNTAchMandateManager.mandateDelegate = achManager.mandateDelegate
50
+ stripeAchUserDetailsComponent.stepDelegate = self
51
+ stripeAchUserDetailsComponent.errorDelegate = self
52
+ stripeAchUserDetailsComponent.validationDelegate = self
53
+ #else
54
+ let err = RNTNativeError(
55
+ errorId: "native-ios",
56
+ errorDescription: "PrimerStripeSDK missing",
57
+ recoverySuggestion: "Check if PrimerStripeSDK is included in your Podfile")
58
+
59
+ throw err
60
+ #endif
61
+
62
+ resolver(nil)
63
+ } catch {
64
+ rejecter(error.rnError["errorId"]!, error.rnError["description"], error)
65
+ }
66
+ }
67
+
68
+ @objc
69
+ public func cleanUp(
70
+ _ resolver: RCTPromiseResolveBlock,
71
+ rejecter: RCTPromiseRejectBlock
72
+ ) {
73
+ RNTAchMandateManager.mandateDelegate = nil
74
+ stripeAchUserDetailsComponent = nil
75
+ resolver(nil)
76
+ }
77
+
78
+ @objc
79
+ public func start(
80
+ _ resolver: RCTPromiseResolveBlock,
81
+ rejecter: RCTPromiseRejectBlock
82
+ ) {
83
+ print("Start Stripe ACH process")
84
+
85
+ guard let stripeAchUserDetailsComponent = self.stripeAchUserDetailsComponent else {
86
+ rejecter("UNINITIALIZED_ERROR", "Stripe ACH user details component is uninitialized", nil)
87
+ return
88
+ }
89
+
90
+ stripeAchUserDetailsComponent.start()
91
+ resolver(nil)
92
+ }
93
+
94
+ @objc
95
+ public func submit(
96
+ _ resolver: RCTPromiseResolveBlock,
97
+ rejecter: RCTPromiseRejectBlock
98
+ ) {
99
+ guard let stripeAchUserDetailsComponent = self.stripeAchUserDetailsComponent else {
100
+ rejecter("UNINITIALIZED_ERROR", "Stripe ACH user details component is uninitialized", nil)
101
+ return
102
+ }
103
+
104
+ stripeAchUserDetailsComponent.submit()
105
+ resolver(nil)
106
+ }
107
+
108
+ @objc
109
+ public func onSetFirstName(
110
+ _ firstName: String,
111
+ resolver: RCTPromiseResolveBlock,
112
+ rejecter: RCTPromiseRejectBlock
113
+ ) {
114
+ guard let stripeAchUserDetailsComponent = self.stripeAchUserDetailsComponent else {
115
+ rejecter("UNINITIALIZED_ERROR", "Stripe ACH user details component is uninitialized", nil)
116
+ return
117
+ }
118
+
119
+ DispatchQueue.main.async {
120
+ stripeAchUserDetailsComponent.updateCollectedData(collectableData: ACHUserDetailsCollectableData.firstName(firstName))
121
+ }
122
+
123
+ resolver(nil)
124
+ }
125
+
126
+ @objc
127
+ public func onSetLastName(
128
+ _ lastName: String,
129
+ resolver: RCTPromiseResolveBlock,
130
+ rejecter: RCTPromiseRejectBlock
131
+ ) {
132
+ guard let stripeAchUserDetailsComponent = self.stripeAchUserDetailsComponent else {
133
+ rejecter("UNINITIALIZED_ERROR", "Stripe ACH user details component is uninitialized", nil)
134
+ return
135
+ }
136
+
137
+ DispatchQueue.main.async {
138
+ stripeAchUserDetailsComponent.updateCollectedData(collectableData: ACHUserDetailsCollectableData.lastName(lastName))
139
+ }
140
+
141
+ resolver(nil)
142
+ }
143
+
144
+ @objc
145
+ public func onSetEmailAddress(
146
+ _ emailAddress: String,
147
+ resolver: RCTPromiseResolveBlock,
148
+ rejecter: RCTPromiseRejectBlock
149
+ ) {
150
+ guard let stripeAchUserDetailsComponent = self.stripeAchUserDetailsComponent else {
151
+ rejecter("UNINITIALIZED_ERROR", "Stripe ACH user details component is uninitialized", nil)
152
+ return
153
+ }
154
+
155
+ DispatchQueue.main.async {
156
+ stripeAchUserDetailsComponent.updateCollectedData(collectableData: ACHUserDetailsCollectableData.emailAddress(emailAddress))
157
+ }
158
+
159
+ resolver(nil)
160
+ }
161
+ }
162
+
163
+ extension RNTPrimerHeadlessUniversalCheckoutStripeAchUserDetailsComponent: PrimerHeadlessSteppableDelegate {
164
+ func didReceiveStep(step: PrimerSDK.PrimerHeadlessStep) {
165
+ guard let step = step as? ACHUserDetailsStep else { return }
166
+
167
+ switch step {
168
+ case .retrievedUserDetails(let details):
169
+ let json = try? step.toUserDetailsRetrievedRN(firstName: details.firstName, lastName: details.lastName, emailAddress: details.emailAddress).toJsonObject()
170
+
171
+ sendEvent(
172
+ withName: PrimerHeadlessUniversalCheckoutComponentEvent.onStep.stringValue,
173
+ body: json
174
+ )
175
+
176
+ case .didCollectUserDetails:
177
+ let json = try? step.toUserDetailsCollectedRN().toJsonObject()
178
+
179
+ sendEvent(
180
+ withName: PrimerHeadlessUniversalCheckoutComponentEvent.onStep.stringValue,
181
+ body: json
182
+ )
183
+
184
+ default:
185
+ break
186
+ }
187
+ }
188
+ }
189
+
190
+ extension RNTPrimerHeadlessUniversalCheckoutStripeAchUserDetailsComponent: PrimerHeadlessValidatableDelegate {
191
+ func didUpdate(validationStatus: PrimerSDK.PrimerValidationStatus, for data: PrimerSDK.PrimerCollectableData?) {
192
+ guard let data = data as? ACHUserDetailsCollectableData else { return }
193
+
194
+ var errorJsonObject: Any?
195
+ let eventName: String
196
+
197
+ switch validationStatus {
198
+ case .valid:
199
+ eventName = PrimerHeadlessUniversalCheckoutComponentEvent.onValid.stringValue
200
+ case .validating:
201
+ eventName = PrimerHeadlessUniversalCheckoutComponentEvent.onValidating.stringValue
202
+ case .invalid(let errors):
203
+ errorJsonObject = try? ["errors": errors.map { $0.toPrimerValidationErrorRN() }].toJsonObject()
204
+ eventName = PrimerHeadlessUniversalCheckoutComponentEvent.onInvalid.stringValue
205
+ case .error(let error):
206
+ errorJsonObject = try? ["errors": [error.toPrimerErrorRN()]].toJsonObject()
207
+ eventName = PrimerHeadlessUniversalCheckoutComponentEvent.onValidationError.stringValue
208
+ }
209
+
210
+ var dataJsonObject: Any?
211
+ switch data {
212
+ case .firstName(let value):
213
+ dataJsonObject = try? ["data": data.toFirstNameRN(value: value)].toJsonObject()
214
+ case .lastName(let value):
215
+ dataJsonObject = try? ["data": data.toLastNameRN(value: value)].toJsonObject()
216
+ case .emailAddress(let value):
217
+ dataJsonObject = try? ["data": data.toEmailAddressRN(value: value)].toJsonObject()
218
+ }
219
+ let allData = ((dataJsonObject as? [String: Any]) ?? [:]).merging((errorJsonObject as? [String: Any]) ?? [:]) { (_, new) in new }
220
+ sendEvent(withName: eventName, body: allData)
221
+ }
222
+ }
223
+
224
+ extension RNTPrimerHeadlessUniversalCheckoutStripeAchUserDetailsComponent: PrimerHeadlessErrorableDelegate {
225
+ func didReceiveError(error: PrimerSDK.PrimerError) {
226
+ let rnError = error.toPrimerErrorRN()
227
+ let serializedData = try? ["errors": [rnError]].toJsonObject()
228
+
229
+ self.sendEvent(
230
+ withName: PrimerHeadlessUniversalCheckoutComponentEvent.onError.stringValue,
231
+ body: serializedData)
232
+ }
233
+ }
@@ -0,0 +1,16 @@
1
+ //
2
+ // RNTAchMandateManager.m
3
+ // primer-io-react-native
4
+ //
5
+ // Created by Flaviu Dunca on 18.06.2024.
6
+ //
7
+
8
+ #import <React/RCTBridgeModule.h>
9
+
10
+ @interface RCT_EXTERN_MODULE(RNTAchMandateManager, NSObject)
11
+
12
+ RCT_EXTERN_METHOD(acceptMandate:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
13
+
14
+ RCT_EXTERN_METHOD(declineMandate:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
15
+
16
+ @end
@@ -0,0 +1,53 @@
1
+ //
2
+ // RNTAchMandateManager.swift
3
+ // primer-io-react-native
4
+ //
5
+ // Created by Flaviu Dunca on 18.06.2024.
6
+ //
7
+
8
+ import Foundation
9
+ import React
10
+ import PrimerSDK
11
+
12
+ @objc(RNTAchMandateManager)
13
+ class RNTAchMandateManager: NSObject, RCTBridgeModule {
14
+ private let errorMessage = "Mandate delegate is not defined"
15
+
16
+ static func moduleName() -> String! {
17
+ return "RNTAchMandateManager"
18
+ }
19
+
20
+ func methodQueue() -> DispatchQueue {
21
+ return DispatchQueue.main
22
+ }
23
+
24
+ @objc static func requiresMainQueueSetup() -> Bool {
25
+ return true
26
+ }
27
+
28
+ static var mandateDelegate: ACHMandateDelegate? = nil
29
+
30
+ @objc
31
+ func acceptMandate(
32
+ _ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock
33
+ ) {
34
+ if let delegate = RNTAchMandateManager.mandateDelegate {
35
+ delegate.acceptMandate()
36
+ resolver(nil)
37
+ } else {
38
+ rejecter("native-ios", errorMessage, nil)
39
+ }
40
+ }
41
+
42
+ @objc
43
+ func declineMandate(
44
+ _ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock
45
+ ) {
46
+ if let delegate = RNTAchMandateManager.mandateDelegate {
47
+ delegate.declineMandate()
48
+ resolver(nil)
49
+ } else {
50
+ rejecter("native-ios", errorMessage, nil)
51
+ }
52
+ }
53
+ }
@@ -241,7 +241,7 @@ extension RNTPrimerHeadlessUniversalCheckoutKlarnaComponent: PrimerHeadlessValid
241
241
  eventName = PrimerHeadlessUniversalCheckoutComponentEvent.onValidating.stringValue
242
242
  case .invalid(let errors):
243
243
  let rnErrors = errors.map { $0.toPrimerValidationErrorRN() }
244
- let serializedData = try? ["errors": rnErrors].toJsonObject()
244
+ let serializedData = try? ["errors": rnErrors].toJsonObject() // TODO TWS iOS: include 'data'
245
245
 
246
246
  self.sendEvent(
247
247
  withName: PrimerHeadlessUniversalCheckoutComponentEvent.onInvalid.stringValue,
@@ -249,7 +249,7 @@ extension RNTPrimerHeadlessUniversalCheckoutKlarnaComponent: PrimerHeadlessValid
249
249
  return
250
250
  case .error(let error):
251
251
  let rnError = error.toPrimerErrorRN()
252
- let serializedData = try? ["errors": [rnError]].toJsonObject()
252
+ let serializedData = try? ["errors": [rnError]].toJsonObject() // TODO TWS iOS: include 'data'
253
253
 
254
254
  self.sendEvent(
255
255
  withName: PrimerHeadlessUniversalCheckoutComponentEvent.onValidationError.stringValue,
@@ -7,6 +7,7 @@
7
7
 
8
8
  import Foundation
9
9
  import PrimerSDK
10
+ import React
10
11
 
11
12
  @objc
12
13
  enum PrimerHeadlessUniversalCheckoutEvents: Int, CaseIterable {
@@ -75,8 +76,6 @@ class RNTPrimerHeadlessUniversalCheckout: RCTEventEmitter {
75
76
 
76
77
  override init() {
77
78
  super.init()
78
- PrimerHeadlessUniversalCheckout.current.delegate = self
79
- PrimerHeadlessUniversalCheckout.current.uiDelegate = self
80
79
  }
81
80
 
82
81
  override func supportedEvents() -> [String]! {
@@ -91,6 +90,9 @@ class RNTPrimerHeadlessUniversalCheckout: RCTEventEmitter {
91
90
  resolver: @escaping RCTPromiseResolveBlock,
92
91
  rejecter: @escaping RCTPromiseRejectBlock)
93
92
  {
93
+ PrimerHeadlessUniversalCheckout.current.delegate = self
94
+ PrimerHeadlessUniversalCheckout.current.uiDelegate = self
95
+
94
96
  do {
95
97
  var tmpSettings: PrimerSettings?
96
98
  if let settingsStr = settingsStr {
@@ -346,11 +348,57 @@ extension RNTPrimerHeadlessUniversalCheckout: PrimerHeadlessUniversalCheckoutDel
346
348
  DispatchQueue.main.async {
347
349
  if self.implementedRNCallbacks?.isOnCheckoutAdditionalInfoImplemented == true {
348
350
  do {
349
- let checkoutAdditionalInfoJson = try additionalInfo?.toJsonObject()
350
- self.sendEvent(
351
- withName: rnCallbackName,
352
- body: checkoutAdditionalInfoJson)
353
-
351
+ switch additionalInfo {
352
+ case let info as ACHBankAccountCollectorAdditionalInfo:
353
+ // Handled internally, nothing for merchant to handle
354
+ let keyWindow = UIApplication
355
+ .shared
356
+ .connectedScenes
357
+ .flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
358
+ .last { $0.isKeyWindow }
359
+ DispatchQueue.main.async {
360
+ if let rootViewController = keyWindow?.rootViewController {
361
+ rootViewController.present(info.collectorViewController, animated: true, completion: nil)
362
+ } else {
363
+ let checkoutData = PrimerCheckoutData(payment: nil, additionalInfo: additionalInfo)
364
+ self.handleRNBridgeError(
365
+ RNTNativeError(
366
+ errorId: "native-ios",
367
+ errorDescription: "Failed to retrieve root view controller, cannot present Stripe view controller",
368
+ recoverySuggestion: nil
369
+ ),
370
+ checkoutData: checkoutData,
371
+ stopOnDebug: true
372
+ )
373
+ }
374
+ }
375
+ return
376
+ case _ as ACHMandateAdditionalInfo:
377
+ self.sendEvent(
378
+ withName: rnCallbackName,
379
+ body: try AchAdditionalInfoDisplayMandateRN().toJsonObject()
380
+ )
381
+ case let info as MultibancoCheckoutAdditionalInfo:
382
+ self.sendEvent(
383
+ withName: rnCallbackName,
384
+ body: try MultibancoCheckoutAdditionalInfoRN(expiresAt: info.expiresAt, reference: info.reference, entity: info.entity).toJsonObject()
385
+ )
386
+ case let info as PromptPayCheckoutAdditionalInfo:
387
+ self.sendEvent(
388
+ withName: rnCallbackName,
389
+ body: try PromptPayCheckoutAdditionalInfoRN(expiresAt: info.expiresAt, qrCodeUrl: info.qrCodeUrl, qrCodeBase64: info.qrCodeBase64).toJsonObject()
390
+ )
391
+ case let info as XenditCheckoutVoucherAdditionalInfo:
392
+ self.sendEvent(
393
+ withName: rnCallbackName,
394
+ body: try XenditCheckoutVoucherAdditionalInfoRN(expiresAt: info.expiresAt, couponCode: info.couponCode, retailerName: info.retailerName).toJsonObject()
395
+ )
396
+ default:
397
+ self.sendEvent(
398
+ withName: rnCallbackName,
399
+ body: try additionalInfo?.toJsonObject()
400
+ )
401
+ }
354
402
  } catch {
355
403
  let checkoutData = PrimerCheckoutData(payment: nil, additionalInfo: additionalInfo)
356
404
  self.handleRNBridgeError(error, checkoutData: checkoutData, stopOnDebug: true)
@@ -1,2 +1,2 @@
1
1
  // swiftlint:disable:next identifier_name
2
- public let PrimerReactNativeSDKVersion = "2.26.3"
2
+ public let PrimerReactNativeSDKVersion = "2.27.0"