@primer-io/react-native 2.0.1 → 2.2.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.
Files changed (92) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/primerioreactnative/PrimerCardNumberEditTextManager.kt +83 -0
  3. package/android/src/main/java/com/primerioreactnative/PrimerRN.kt +2 -2
  4. package/android/src/main/java/com/primerioreactnative/PrimerRNHeadlessUniversalCheckout.kt +244 -0
  5. package/android/src/main/java/com/primerioreactnative/PrimerRNHeadlessUniversalCheckoutListener.kt +276 -0
  6. package/android/src/main/java/com/primerioreactnative/ReactNativePackage.kt +14 -3
  7. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerErrorRN.kt +3 -0
  8. package/android/src/main/java/com/primerioreactnative/huc/assets/AssetsManager.kt +56 -0
  9. package/android/src/main/java/com/primerioreactnative/huc/events/PrimerHeadlessUniversalCheckoutEvent.kt +15 -0
  10. package/android/src/main/java/com/primerioreactnative/utils/PrimerHeadlessUniversalCheckoutImplementedRNCallbacks.kt +31 -0
  11. package/ios/Core/ReactNative-Bridging-Header.h +14 -0
  12. package/ios/DataModels/ImplementedRNCallbacks.swift +34 -9
  13. package/ios/DataModels/PrimerSettingsRN.swift +15 -16
  14. package/ios/Headless Universal Checkout/RNTPrimerCardNumberInputElementManager.m +23 -0
  15. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.m +62 -0
  16. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.swift +493 -0
  17. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.m +14 -0
  18. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.swift +117 -0
  19. package/ios/RNTPrimer.m +2 -0
  20. package/ios/RNTPrimer.swift +56 -51
  21. package/ios/ReactNative-Bridging-Header.h +1 -0
  22. package/lib/commonjs/Primer.js +6 -0
  23. package/lib/commonjs/Primer.js.map +1 -1
  24. package/lib/commonjs/PrimerInput.js +80 -0
  25. package/lib/commonjs/PrimerInput.js.map +1 -0
  26. package/lib/commonjs/RNPrimer.js +11 -0
  27. package/lib/commonjs/RNPrimer.js.map +1 -1
  28. package/lib/commonjs/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js +14 -0
  29. package/lib/commonjs/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js.map +1 -0
  30. package/lib/commonjs/headless_checkout/PrimerHeadlessUniversalCheckout.js +286 -0
  31. package/lib/commonjs/headless_checkout/PrimerHeadlessUniversalCheckout.js.map +1 -0
  32. package/lib/commonjs/headless_checkout/RNPrimerHeadlessUniversalCheckout.js +197 -0
  33. package/lib/commonjs/headless_checkout/RNPrimerHeadlessUniversalCheckout.js.map +1 -0
  34. package/lib/commonjs/{models/Primer.js → headless_checkout/types.js} +1 -1
  35. package/lib/commonjs/{models/Primer.js.map → headless_checkout/types.js.map} +0 -0
  36. package/lib/commonjs/index.js +13 -5
  37. package/lib/commonjs/index.js.map +1 -1
  38. package/lib/commonjs/models/PrimerInterfaces.js +6 -0
  39. package/lib/{module/models/Primer.js.map → commonjs/models/PrimerInterfaces.js.map} +0 -0
  40. package/lib/module/Primer.js +7 -2
  41. package/lib/module/Primer.js.map +1 -1
  42. package/lib/module/PrimerInput.js +46 -0
  43. package/lib/module/PrimerInput.js.map +1 -0
  44. package/lib/module/RNPrimer.js +11 -0
  45. package/lib/module/RNPrimer.js.map +1 -1
  46. package/lib/module/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js +5 -0
  47. package/lib/module/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js.map +1 -0
  48. package/lib/module/headless_checkout/PrimerHeadlessUniversalCheckout.js +273 -0
  49. package/lib/module/headless_checkout/PrimerHeadlessUniversalCheckout.js.map +1 -0
  50. package/lib/module/headless_checkout/RNPrimerHeadlessUniversalCheckout.js +188 -0
  51. package/lib/module/headless_checkout/RNPrimerHeadlessUniversalCheckout.js.map +1 -0
  52. package/lib/module/headless_checkout/types.js +2 -0
  53. package/lib/module/headless_checkout/types.js.map +1 -0
  54. package/lib/module/index.js +2 -1
  55. package/lib/module/index.js.map +1 -1
  56. package/lib/module/models/PrimerInterfaces.js +2 -0
  57. package/lib/module/models/PrimerInterfaces.js.map +1 -0
  58. package/lib/typescript/MerchantExample/App.d.ts +11 -0
  59. package/lib/typescript/src/Primer.d.ts +2 -0
  60. package/lib/typescript/src/PrimerInput.d.ts +15 -0
  61. package/lib/typescript/{RNPrimer.d.ts → src/RNPrimer.d.ts} +1 -0
  62. package/lib/typescript/src/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.d.ts +1 -0
  63. package/lib/typescript/src/headless_checkout/PrimerHeadlessUniversalCheckout.d.ts +11 -0
  64. package/lib/typescript/src/headless_checkout/RNPrimerHeadlessUniversalCheckout.d.ts +22 -0
  65. package/lib/typescript/src/headless_checkout/types.d.ts +22 -0
  66. package/lib/typescript/{index.d.ts → src/index.d.ts} +2 -1
  67. package/lib/typescript/{models → src/models}/PrimerCheckoutData.d.ts +0 -0
  68. package/lib/typescript/{models → src/models}/PrimerCheckoutPaymentMethodData.d.ts +0 -0
  69. package/lib/typescript/{models → src/models}/PrimerClientSession.d.ts +0 -0
  70. package/lib/typescript/{models → src/models}/PrimerCountryCode.d.ts +0 -0
  71. package/lib/typescript/{models → src/models}/PrimerCurrencyCode.d.ts +0 -0
  72. package/lib/typescript/{models → src/models}/PrimerError.d.ts +0 -0
  73. package/lib/typescript/{models/Primer.d.ts → src/models/PrimerInterfaces.d.ts} +2 -1
  74. package/lib/typescript/{models → src/models}/PrimerPaymentMethodTokenData.d.ts +0 -0
  75. package/lib/typescript/{models → src/models}/PrimerSessionIntent.d.ts +0 -0
  76. package/lib/typescript/{models → src/models}/PrimerSettings.d.ts +6 -2
  77. package/lib/typescript/{models → src/models}/PrimerTheme.d.ts +0 -0
  78. package/lib/typescript/{utils → src/utils}/index.d.ts +0 -0
  79. package/package.json +1 -1
  80. package/primer-io-react-native.podspec +1 -1
  81. package/src/Primer.ts +6 -1
  82. package/src/PrimerInput.tsx +73 -0
  83. package/src/RNPrimer.ts +13 -0
  84. package/src/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.tsx +2 -0
  85. package/src/headless_checkout/PrimerHeadlessUniversalCheckout.ts +327 -0
  86. package/src/headless_checkout/RNPrimerHeadlessUniversalCheckout.ts +252 -0
  87. package/src/headless_checkout/types.ts +32 -0
  88. package/src/index.tsx +4 -2
  89. package/src/models/{Primer.ts → PrimerInterfaces.ts} +2 -1
  90. package/src/models/PrimerSettings.ts +17 -2
  91. package/lib/module/models/Primer.js +0 -2
  92. package/lib/typescript/Primer.d.ts +0 -2
@@ -0,0 +1,493 @@
1
+ //
2
+ // RNTPrimerHeadlessUniversalCheckout.swift
3
+ // primer-io-react-native
4
+ //
5
+ // Created by Evangelos on 8/3/22.
6
+ //
7
+
8
+ import Foundation
9
+ import PrimerSDK
10
+
11
+ @objc
12
+ enum PrimerHeadlessUniversalCheckoutEvents: Int, CaseIterable {
13
+
14
+ case onHUCTokenizeStart = 0
15
+ case onHUCPrepareStart
16
+ case onHUCAvailablePaymentMethodsLoaded
17
+ case onHUCPaymentMethodShow
18
+ case onTokenizeSuccess
19
+ case onResumeSuccess
20
+ case onBeforePaymentCreate
21
+ case onBeforeClientSessionUpdate
22
+ case onClientSessionUpdate
23
+ case onCheckoutComplete
24
+ case onError
25
+
26
+ var stringValue: String {
27
+ switch self {
28
+ case .onHUCPrepareStart:
29
+ return "onHUCPrepareStart"
30
+ case .onHUCTokenizeStart:
31
+ return "onHUCTokenizeStart"
32
+ case .onHUCPaymentMethodShow:
33
+ return "onHUCPaymentMethodShow"
34
+ case .onTokenizeSuccess:
35
+ return "onTokenizeSuccess"
36
+ case .onResumeSuccess:
37
+ return "onResumeSuccess"
38
+ case .onBeforePaymentCreate:
39
+ return "onBeforePaymentCreate"
40
+ case .onBeforeClientSessionUpdate:
41
+ return "onBeforeClientSessionUpdate"
42
+ case .onClientSessionUpdate:
43
+ return "onClientSessionUpdate"
44
+ case .onCheckoutComplete:
45
+ return "onCheckoutComplete"
46
+ case .onHUCAvailablePaymentMethodsLoaded:
47
+ return "onHUCAvailablePaymentMethodsLoaded"
48
+ case .onError:
49
+ return "onError"
50
+ }
51
+ }
52
+ }
53
+
54
+ @objc(PrimerHeadlessUniversalCheckout)
55
+ class RNTPrimerHeadlessUniversalCheckout: RCTEventEmitter {
56
+
57
+ private var primerWillCreatePaymentWithDataDecisionHandler: ((_ errorMessage: String?) -> Void)?
58
+ private var primerDidTokenizePaymentMethodDecisionHandler: ((_ resumeToken: String?, _ errorMessage: String?) -> Void)?
59
+ private var primerDidResumeWithDecisionHandler: ((_ resumeToken: String?, _ errorMessage: String?) -> Void)?
60
+ private var primerDidFailWithErrorDecisionHandler: ((_ errorMessage: String) -> Void)?
61
+ private var implementedRNCallbacks: ImplementedRNCallbacks?
62
+
63
+ override class func requiresMainQueueSetup() -> Bool {
64
+ return true
65
+ }
66
+
67
+ override init() {
68
+ super.init()
69
+ PrimerHeadlessUniversalCheckout.current.delegate = self
70
+ }
71
+
72
+ override func supportedEvents() -> [String]! {
73
+ return PrimerHeadlessUniversalCheckoutEvents.allCases.compactMap({ $0.stringValue })
74
+ }
75
+
76
+ // MARK: - API
77
+
78
+ @objc
79
+ public func startWithClientToken(_ clientToken: String,
80
+ settingsStr: String?,
81
+ errorCallback: @escaping RCTResponseSenderBlock,
82
+ successCallback: @escaping RCTResponseSenderBlock)
83
+ {
84
+ var settings: PrimerSettings?
85
+ if let settingsStr = settingsStr {
86
+ do {
87
+ settings = try PrimerSettings.initialize(with: settingsStr)
88
+ } catch {
89
+ errorCallback([error.rnError])
90
+ return
91
+ }
92
+ }
93
+
94
+ PrimerHeadlessUniversalCheckout.current.start(withClientToken: clientToken, settings: settings, delegate: self) { paymentMethodTypes, err in
95
+ if let err = err {
96
+ errorCallback([err.rnError])
97
+ } else if let paymentMethodTypes = paymentMethodTypes {
98
+ successCallback([paymentMethodTypes.compactMap({$0.rawValue})])
99
+ }
100
+ }
101
+ }
102
+
103
+ @objc
104
+ public func getAssetForPaymentMethodType(_ paymentMethodTypeStr: String,
105
+ assetType assetTypeStr: String,
106
+ errorCallback: @escaping RCTResponseSenderBlock,
107
+ successCallback: @escaping RCTResponseSenderBlock)
108
+ {
109
+ do {
110
+ try validate(assetStr: paymentMethodTypeStr, assetTypeStr: assetTypeStr)
111
+ let paymentMethodType = PrimerPaymentMethodType(rawValue: paymentMethodTypeStr)
112
+
113
+ guard let image = PrimerHeadlessUniversalCheckout.getAsset(for: paymentMethodType, assetType: PrimerAsset.ImageType(rawValue: assetTypeStr)!) else {
114
+ let err = NativeError(errorId: "missing-asset", errorDescription: "Failed to find \(assetTypeStr) for \(paymentMethodTypeStr)", recoverySuggestion: nil)
115
+ errorCallback([err.rnError])
116
+ return
117
+ }
118
+
119
+
120
+ let imageURL = try self.tempStoreImage(image: image, name: paymentMethodTypeStr)
121
+ successCallback([imageURL.absoluteString])
122
+ } catch {
123
+ errorCallback([error.rnError])
124
+ }
125
+ }
126
+
127
+ @objc
128
+ public func getAssetForCardNetwork(_ cardNetworkStr: String,
129
+ assetType assetTypeStr: String,
130
+ errorCallback: @escaping RCTResponseSenderBlock,
131
+ successCallback: @escaping RCTResponseSenderBlock)
132
+ {
133
+ do {
134
+ try validate(assetStr: cardNetworkStr, assetTypeStr: assetTypeStr)
135
+ guard let cardNetwork = CardNetwork(rawValue: cardNetworkStr) else {
136
+ let err = NativeError(errorId: "invalid-card-network", errorDescription: "Card network for \(cardNetworkStr) does not exist, make sure you don't have any typos.", recoverySuggestion: nil)
137
+ errorCallback([err.rnError])
138
+ return
139
+ }
140
+
141
+ guard let image = PrimerHeadlessUniversalCheckout.getAsset(for: cardNetwork, assetType: PrimerAsset.ImageType(rawValue: assetTypeStr)!) else {
142
+ let err = NativeError(errorId: "missing-asset", errorDescription: "Failed to find \(assetTypeStr) for \(cardNetworkStr)", recoverySuggestion: nil)
143
+ errorCallback([err.rnError])
144
+ return
145
+ }
146
+
147
+
148
+ let imageURL = try self.tempStoreImage(image: image, name: cardNetworkStr)
149
+ successCallback([imageURL.absoluteString])
150
+ } catch {
151
+ errorCallback([error.rnError])
152
+ }
153
+ }
154
+
155
+ @objc
156
+ public func showPaymentMethod(_ paymentMethodTypeStr: String,
157
+ resolver: RCTPromiseResolveBlock,
158
+ rejecter: RCTPromiseRejectBlock)
159
+ {
160
+ let paymentMethodType = PrimerPaymentMethodType(rawValue: paymentMethodTypeStr)
161
+ PrimerHeadlessUniversalCheckout.current.showPaymentMethod(paymentMethodType)
162
+ resolver(nil)
163
+ }
164
+
165
+ @objc
166
+ public func disposePrimerHeadlessUniversalCheckout() {
167
+
168
+ }
169
+
170
+ // MARK: - HELPERS
171
+
172
+ private func validate(assetStr: String, assetTypeStr: String) throws {
173
+ var errors: [NativeError] = []
174
+
175
+ if PrimerPaymentMethodType(rawValue: assetStr) != .other(rawValue: assetStr) {
176
+ // ...
177
+ } else if CardNetwork(rawValue: assetStr) != nil {
178
+ // ...
179
+ } else {
180
+ let err = NativeError(errorId: "invalid-payment-method-type", errorDescription: "Asset for \(assetStr) does not exist, make sure you don't have any typos.", recoverySuggestion: nil)
181
+ errors.append(err)
182
+ }
183
+
184
+
185
+ if PrimerAsset.ImageType(rawValue: assetTypeStr) == nil {
186
+ let err = NativeError(errorId: "mismatch", errorDescription: "You have provided assetType=\(assetTypeStr), which is not valid", recoverySuggestion: "Use one of the following values: \(PrimerAsset.ImageType.allCases.compactMap({ $0.rawValue }))")
187
+ errors.append(err)
188
+ }
189
+
190
+ if errors.isEmpty {
191
+ return
192
+ } else if errors.count == 1 {
193
+ throw errors.first!
194
+ } else {
195
+ let errorDescription: String = errors.compactMap({ $0.errorDescription }).joined(separator: "/n")
196
+ let recoverySuggestion = errors.compactMap({ $0.recoverySuggestion }).joined(separator: "/n")
197
+ let err = NativeError(errorId: "underlying-errors", errorDescription: errorDescription, recoverySuggestion: recoverySuggestion)
198
+ throw err
199
+ }
200
+ }
201
+
202
+ private func tempStoreImage(image: UIImage, name: String) throws -> URL {
203
+ guard let imageURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("\(name).png") else {
204
+ let err = NativeError(errorId: "error", errorDescription: "Failed to create URL for asset", recoverySuggestion: nil)
205
+ throw err
206
+ }
207
+
208
+ guard let pngData = image.pngData() else {
209
+ let err = NativeError(errorId: "error", errorDescription: "Failed to get image's PNG data", recoverySuggestion: nil)
210
+ throw err
211
+ }
212
+
213
+ try pngData.write(to: imageURL)
214
+ return imageURL
215
+ }
216
+
217
+ // MARK: - DECISION HANDLERS
218
+
219
+ // MARK: Tokenization
220
+
221
+ @objc
222
+ public func handleTokenizationNewClientToken(_ newClientToken: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
223
+ DispatchQueue.main.async {
224
+ self.primerDidTokenizePaymentMethodDecisionHandler?(newClientToken, nil)
225
+ self.primerDidTokenizePaymentMethodDecisionHandler = nil
226
+ resolver(nil)
227
+ }
228
+ }
229
+
230
+ @objc
231
+ public func handleTokenizationSuccess(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
232
+ DispatchQueue.main.async {
233
+ self.primerDidTokenizePaymentMethodDecisionHandler?(nil, nil)
234
+ self.primerDidTokenizePaymentMethodDecisionHandler = nil
235
+ let err = NSError(domain: "native-bridge", code: 0, userInfo: [NSLocalizedDescriptionKey: "PrimerTokenizationHandler's handleSuccess function is not available on HUC."])
236
+ rejecter(err.rnError["errorId"]!, err.rnError["description"], err)
237
+ }
238
+ }
239
+
240
+ @objc
241
+ public func handleTokenizationFailure(_ errorMessage: String?, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
242
+ DispatchQueue.main.async {
243
+ self.primerDidTokenizePaymentMethodDecisionHandler?(nil, errorMessage ?? "")
244
+ self.primerDidTokenizePaymentMethodDecisionHandler = nil
245
+ let err = NSError(domain: "native-bridge", code: 0, userInfo: [NSLocalizedDescriptionKey: "PrimerTokenizationHandler's handleFailure function is not available on HUC."])
246
+ rejecter(err.rnError["errorId"]!, err.rnError["description"], err)
247
+ }
248
+ }
249
+
250
+ // MARK: Resume Payment
251
+
252
+ @objc
253
+ public func handleResumeWithNewClientToken(_ newClientToken: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
254
+ DispatchQueue.main.async {
255
+ self.primerDidResumeWithDecisionHandler?(newClientToken, nil)
256
+ self.primerDidResumeWithDecisionHandler = nil
257
+ resolver(nil)
258
+ }
259
+ }
260
+
261
+ @objc
262
+ public func handleResumeSuccess(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
263
+ DispatchQueue.main.async {
264
+ let err = NSError(domain: "native-bridge", code: 0, userInfo: [NSLocalizedDescriptionKey: "PrimerResumeHandler's handleSuccess function is not available on HUC."])
265
+ rejecter(err.rnError["errorId"]!, err.rnError["description"], err)
266
+ }
267
+ }
268
+
269
+ @objc
270
+ public func handleResumeFailure(_ errorMessage: String?, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
271
+ DispatchQueue.main.async {
272
+ let err = NSError(domain: "native-bridge", code: 0, userInfo: [NSLocalizedDescriptionKey: "PrimerResumeHandler's handleFailure function is not available on HUC."])
273
+ rejecter(err.rnError["errorId"]!, err.rnError["description"], err)
274
+ }
275
+ }
276
+
277
+ // MARK: Payment Creation
278
+
279
+ @objc
280
+ public func handlePaymentCreationAbort(_ errorMessage: String?, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
281
+ DispatchQueue.main.async {
282
+ self.primerWillCreatePaymentWithDataDecisionHandler?(errorMessage ?? "")
283
+ self.primerWillCreatePaymentWithDataDecisionHandler = nil
284
+ resolver(nil)
285
+ }
286
+ }
287
+
288
+ @objc
289
+ public func handlePaymentCreationContinue(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
290
+ DispatchQueue.main.async {
291
+ self.primerWillCreatePaymentWithDataDecisionHandler?(nil)
292
+ self.primerWillCreatePaymentWithDataDecisionHandler = nil
293
+ resolver(nil)
294
+ }
295
+ }
296
+
297
+ // MARK: Helpers
298
+
299
+ private func detectImplemetedCallbacks() {
300
+ sendEvent(withName: PrimerEvents.detectImplementedRNCallbacks.stringValue, body: nil)
301
+ }
302
+
303
+ @objc
304
+ public func setImplementedRNCallbacks(_ implementedRNCallbacksStr: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
305
+ DispatchQueue.main.async {
306
+ do {
307
+ guard let implementedRNCallbacksData = implementedRNCallbacksStr.data(using: .utf8) else {
308
+ let err = NSError(domain: "native-bridge", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to convert string to data"])
309
+ throw err
310
+ }
311
+ self.implementedRNCallbacks = try JSONDecoder().decode(ImplementedRNCallbacks.self, from: implementedRNCallbacksData)
312
+ resolver(nil)
313
+ } catch {
314
+ self.handleRNBridgeError(error, checkoutData: nil, stopOnDebug: false)
315
+ rejecter(error.rnError["errorId"]!, error.rnError["description"], error)
316
+ }
317
+ }
318
+ }
319
+
320
+ private func handleRNBridgeError(_ error: Error, checkoutData: PrimerCheckoutData?, stopOnDebug: Bool) {
321
+ DispatchQueue.main.async {
322
+ if stopOnDebug {
323
+ assertionFailure(error.localizedDescription)
324
+ }
325
+
326
+ var body: [String: Any] = ["error": error.rnError]
327
+ if let checkoutData = checkoutData,
328
+ let data = try? JSONEncoder().encode(checkoutData),
329
+ let json = try? JSONSerialization.jsonObject(with: data){
330
+ body["checkoutData"] = json
331
+ }
332
+
333
+ self.sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onError.stringValue, body: body)
334
+ }
335
+ }
336
+ }
337
+
338
+ // MARK: - EVENTS
339
+
340
+ extension RNTPrimerHeadlessUniversalCheckout: PrimerHeadlessUniversalCheckoutDelegate {
341
+
342
+ func primerHeadlessUniversalCheckoutPreparationDidStart(for paymentMethodType: String) {
343
+ sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onHUCPrepareStart.stringValue, body: ["paymentMethodType": paymentMethodType])
344
+ }
345
+
346
+ func primerHeadlessUniversalCheckoutTokenizationDidStart(for paymentMethodType: String) {
347
+ sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onHUCTokenizeStart.stringValue, body: ["paymentMethodType": paymentMethodType])
348
+ }
349
+
350
+ func primerHeadlessUniversalCheckoutDidLoadAvailablePaymentMethods(_ paymentMethodTypes: [String]) {
351
+ sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onHUCAvailablePaymentMethodsLoaded.stringValue, body: ["paymentMethodTypes": paymentMethodTypes])
352
+ }
353
+
354
+ func primerHeadlessUniversalCheckoutPaymentMethodDidShow(for paymentMethodType: String) {
355
+ sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onHUCPaymentMethodShow.stringValue, body: ["paymentMethodType": paymentMethodType])
356
+ }
357
+
358
+ func primerHeadlessUniversalCheckoutDidTokenizePaymentMethod(_ paymentMethodTokenData: PrimerPaymentMethodTokenData, decisionHandler: @escaping (PrimerResumeDecision) -> Void) {
359
+ if self.implementedRNCallbacks?.isOnTokenizeSuccessImplemented == true {
360
+ self.primerDidTokenizePaymentMethodDecisionHandler = { (newClientToken, errorMessage) in
361
+ DispatchQueue.main.async {
362
+ if let errorMessage = errorMessage {
363
+ decisionHandler(.fail(withErrorMessage: errorMessage.isEmpty ? nil : errorMessage))
364
+ } else if let newClientToken = newClientToken {
365
+ decisionHandler(.continueWithNewClientToken(newClientToken))
366
+ } else {
367
+ decisionHandler(.succeed())
368
+ }
369
+ }
370
+ }
371
+
372
+ do {
373
+ let data = try JSONEncoder().encode(paymentMethodTokenData)
374
+ let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
375
+ DispatchQueue.main.async {
376
+ self.sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onTokenizeSuccess.stringValue, body: json)
377
+ }
378
+ } catch {
379
+ self.handleRNBridgeError(error, checkoutData: nil, stopOnDebug: true)
380
+ }
381
+ } else {
382
+ // RN dev hasn't opted in on listening the tokenization callback.
383
+ // Throw an error if it's the manual flow, ignore if it's the
384
+ // auto flow.
385
+ }
386
+ }
387
+
388
+ func primerHeadlessUniversalCheckoutDidResumeWith(_ resumeToken: String, decisionHandler: @escaping (PrimerResumeDecision) -> Void) {
389
+ if self.implementedRNCallbacks?.isOnResumeSuccessImplemented == true {
390
+ self.primerDidResumeWithDecisionHandler = { (resumeToken, errorMessage) in
391
+ DispatchQueue.main.async {
392
+ if let errorMessage = errorMessage {
393
+ decisionHandler(.fail(withErrorMessage: errorMessage.isEmpty ? nil : errorMessage))
394
+ } else if let resumeToken = resumeToken {
395
+ decisionHandler(.continueWithNewClientToken(resumeToken))
396
+ } else {
397
+ decisionHandler(.succeed())
398
+ }
399
+ }
400
+ }
401
+
402
+ DispatchQueue.main.async {
403
+ self.sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onResumeSuccess.stringValue, body: ["resumeToken": resumeToken])
404
+ }
405
+ } else {
406
+ // RN dev hasn't opted in on listening the tokenization callback.
407
+ // Throw an error if it's the manual flow.
408
+ }
409
+ }
410
+
411
+ func primerHeadlessUniversalCheckoutDidFail(withError err: Error) {
412
+ if self.implementedRNCallbacks?.isOnErrorImplemented == true {
413
+ // Send the error message to the RN bridge.
414
+ self.handleRNBridgeError(err, checkoutData: nil, stopOnDebug: false)
415
+
416
+ } else {
417
+ // RN dev hasn't opted in on listening SDK dismiss.
418
+ // Ignore!
419
+ }
420
+ }
421
+
422
+ func primerHeadlessUniversalCheckoutDidCompleteCheckoutWithData(_ data: PrimerCheckoutData) {
423
+ DispatchQueue.main.async {
424
+ if self.implementedRNCallbacks?.isOnCheckoutCompleteImplemented == true {
425
+ do {
426
+ let checkoutData = try JSONEncoder().encode(data)
427
+ let checkoutJson = try JSONSerialization.jsonObject(with: checkoutData, options: .allowFragments)
428
+ self.sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onCheckoutComplete.stringValue, body: checkoutJson)
429
+ } catch {
430
+ self.handleRNBridgeError(error, checkoutData: data, stopOnDebug: true)
431
+ }
432
+ } else {
433
+ let err = NSError(domain: "native-bridge", code: 1, userInfo: [NSLocalizedDescriptionKey: "Callback [onCheckoutComplete] should be implemented."])
434
+ self.handleRNBridgeError(err, checkoutData: data, stopOnDebug: false)
435
+ }
436
+ }
437
+ }
438
+
439
+ func primerHeadlessUniversalCheckoutClientSessionWillUpdate() {
440
+ if self.implementedRNCallbacks?.isOnBeforeClientSessionUpdateImplemented == true {
441
+ DispatchQueue.main.async {
442
+ self.sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onBeforeClientSessionUpdate.stringValue, body: nil)
443
+ }
444
+ } else {
445
+ // RN Dev hasn't implemented this callback, ignore.
446
+ }
447
+ }
448
+
449
+ func primerHeadlessUniversalCheckoutClientSessionDidUpdate(_ clientSession: PrimerClientSession) {
450
+ if self.implementedRNCallbacks?.isOnClientSessionUpdateImplemented == true {
451
+ do {
452
+ let data = try JSONEncoder().encode(clientSession)
453
+ let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
454
+ DispatchQueue.main.async {
455
+ self.sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onClientSessionUpdate.stringValue, body: json)
456
+ }
457
+ } catch {
458
+ self.handleRNBridgeError(error, checkoutData: nil, stopOnDebug: true)
459
+ }
460
+ } else {
461
+ // RN Dev hasn't implemented this callback, ignore.
462
+ }
463
+ }
464
+
465
+ func primerHeadlessUniversalCheckoutWillCreatePaymentWithData(_ data: PrimerCheckoutPaymentMethodData, decisionHandler: @escaping (PrimerPaymentCreationDecision) -> Void) {
466
+ if self.implementedRNCallbacks?.isOnBeforePaymentCreateImplemented == true {
467
+ self.primerWillCreatePaymentWithDataDecisionHandler = { errorMessage in
468
+ DispatchQueue.main.async {
469
+ if let errorMessage = errorMessage {
470
+ decisionHandler(.abortPaymentCreation(withErrorMessage: errorMessage.isEmpty ? nil : errorMessage))
471
+ } else {
472
+ decisionHandler(.continuePaymentCreation())
473
+ }
474
+ }
475
+ }
476
+
477
+ DispatchQueue.main.async {
478
+ do {
479
+ let checkoutPaymentmethodData = try JSONEncoder().encode(data)
480
+ let checkoutPaymentmethodJson = try JSONSerialization.jsonObject(with: checkoutPaymentmethodData, options: .allowFragments)
481
+ self.sendEvent(withName: PrimerHeadlessUniversalCheckoutEvents.onBeforePaymentCreate.stringValue, body: checkoutPaymentmethodJson)
482
+ } catch {
483
+ self.handleRNBridgeError(error, checkoutData: nil, stopOnDebug: true)
484
+ }
485
+ }
486
+ } else {
487
+ // RN Dev hasn't implemented this callback, send a decision to continue the flow.
488
+ DispatchQueue.main.async {
489
+ decisionHandler(.continuePaymentCreation())
490
+ }
491
+ }
492
+ }
493
+ }
@@ -0,0 +1,14 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTViewManager.h>
3
+ #import <React/RCTUIManager.h>
4
+ #import <React/RCTView.h>
5
+
6
+ @interface RCT_EXTERN_MODULE(PrimerHeadlessUniversalCheckoutCardFormUIManager, NSObject)
7
+
8
+ RCT_EXTERN_METHOD(setInputElements:(NSString *)inputElements errorCallback: (RCTResponseSenderBlock)errorCallback successCallback: (RCTResponseSenderBlock)successCallback)
9
+
10
+ RCT_EXTERN_METHOD(tokenize)
11
+
12
+ RCT_EXTERN_METHOD(addInput:(NSNumber* __nonnull)tag)
13
+
14
+ @end
@@ -0,0 +1,117 @@
1
+ //
2
+ // RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.swift
3
+ // primer-io-react-native
4
+ //
5
+ // Created by Evangelos on 4/3/22.
6
+ //
7
+
8
+ import Foundation
9
+ import PrimerSDK
10
+
11
+ @objc(PrimerHeadlessUniversalCheckoutCardFormUIManager)
12
+ class RNTPrimerHeadlessUniversalCheckoutCardFormUIManager: RCTViewManager {
13
+
14
+ @objc
15
+ override static func requiresMainQueueSetup() -> Bool {
16
+ return true
17
+ }
18
+
19
+ private lazy var cardFormUIManager: PrimerHeadlessUniversalCheckout.CardFormUIManager = {
20
+ return try! PrimerHeadlessUniversalCheckout.CardFormUIManager()
21
+ }()
22
+ private var successCallback: RCTResponseSenderBlock? {
23
+ didSet {
24
+
25
+ }
26
+ }
27
+
28
+ deinit {
29
+ print("🧨 deinit: \(self) \(Unmanaged.passUnretained(self).toOpaque())")
30
+ }
31
+
32
+ override init() {
33
+ super.init()
34
+ do {
35
+ self.cardFormUIManager = try PrimerHeadlessUniversalCheckout.CardFormUIManager()
36
+ self.cardFormUIManager.cardFormUIManagerDelegate = self
37
+ } catch {
38
+
39
+ }
40
+
41
+ print("⭐ init: \(self) \(Unmanaged.passUnretained(self).toOpaque())")
42
+ }
43
+
44
+ @objc
45
+ override func constantsToExport() -> [AnyHashable : Any]! {
46
+ return ["message": "Hello from native code"]
47
+ }
48
+
49
+ func cardFormUIManager(_ cardFormUIManager: PrimerHeadlessUniversalCheckout.CardFormUIManager, isCardFormValid: Bool) {
50
+
51
+ }
52
+
53
+ @objc
54
+ func setInputElements(_ inputElements: String, errorCallback: RCTResponseSenderBlock, successCallback: @escaping RCTResponseSenderBlock) {
55
+ print("RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.setInputElements\n\(inputElements)")
56
+ print("⭐ \(self) \(Unmanaged.passUnretained(self).toOpaque())")
57
+ self.successCallback = successCallback
58
+ self.successCallback?(["what??"])
59
+ }
60
+
61
+ @objc
62
+ func setInputElementsWithTags(_ tags: [NSNumber]) {
63
+ RCTUnsafeExecuteOnMainQueueSync {
64
+ guard let rctUIManager = self.bridge.module(for: RCTUIManager.self) as? RCTUIManager else {
65
+ return
66
+ }
67
+
68
+ for tag in tags {
69
+ if let view = rctUIManager.view(forReactTag: tag) {
70
+ if let inputElement = view as? PrimerInputElement {
71
+ self.cardFormUIManager.inputElements = [inputElement]
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ @objc
79
+ func addInput(_ tag: NSNumber) {
80
+ RCTUnsafeExecuteOnMainQueueSync {
81
+ guard let rctUIManager = self.bridge.module(for: RCTUIManager.self) as? RCTUIManager else {
82
+ return
83
+ }
84
+
85
+ if let view = rctUIManager.view(forReactTag: tag) {
86
+ if let inputElement = view as? PrimerInputElement {
87
+ self.cardFormUIManager.inputElements = [inputElement]
88
+ }
89
+ }
90
+
91
+ }
92
+ }
93
+
94
+ @objc
95
+ func tokenize() {
96
+ print("RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.tokenize")
97
+ }
98
+
99
+ }
100
+
101
+ extension RNTPrimerHeadlessUniversalCheckoutCardFormUIManager: PrimerInputElementDelegate {
102
+
103
+ func inputElementDidFocus(_ sender: PrimerInputElement) {
104
+
105
+ }
106
+
107
+ func inputElementDidBlur(_ sender: PrimerInputElement) {
108
+
109
+ }
110
+
111
+ }
112
+
113
+ extension RNTPrimerHeadlessUniversalCheckoutCardFormUIManager: PrimerCardFormDelegate {
114
+
115
+
116
+
117
+ }
package/ios/RNTPrimer.m CHANGED
@@ -22,6 +22,8 @@ RCT_EXTERN_METHOD(showPaymentMethod:(NSString *)paymentMethodStr intent:(NSStrin
22
22
 
23
23
  RCT_EXTERN_METHOD(dismiss)
24
24
 
25
+ RCT_EXTERN_METHOD(dispose)
26
+
25
27
  // MARK: - HELPERS
26
28
 
27
29
  RCT_EXTERN_METHOD(setImplementedRNCallbacks:(NSString *)implementedRNCallbacksStr resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)rejecter)