@yuno-payments/yuno-sdk-react-native 1.0.16 → 1.0.17-rc.10
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/README.md +795 -262
- package/YunoSdk.podspec +48 -0
- package/android/src/main/java/com/yunosdkreactnative/YunoSdkModule.kt +341 -0
- package/ios/YunoPaymentMethodsViewManager.m +10 -0
- package/ios/YunoPaymentMethodsViewManager.swift +324 -0
- package/ios/YunoSdk.m +42 -5
- package/ios/YunoSdk.swift +303 -70
- package/lib/commonjs/YunoPaymentMethods.js +9 -9
- package/lib/commonjs/YunoPaymentMethods.js.map +1 -1
- package/lib/commonjs/YunoSdk.js +184 -25
- package/lib/commonjs/YunoSdk.js.map +1 -1
- package/lib/commonjs/core/types/HeadlessTypes.js +20 -0
- package/lib/commonjs/core/types/HeadlessTypes.js.map +1 -0
- package/lib/module/YunoPaymentMethods.js +9 -9
- package/lib/module/YunoPaymentMethods.js.map +1 -1
- package/lib/module/YunoSdk.js +184 -25
- package/lib/module/YunoSdk.js.map +1 -1
- package/lib/module/core/types/HeadlessTypes.js +16 -0
- package/lib/module/core/types/HeadlessTypes.js.map +1 -0
- package/package.json +1 -1
- package/src/YunoPaymentMethods.tsx +17 -15
- package/src/YunoSdk.ts +216 -39
- package/src/core/types/HeadlessTypes.ts +110 -0
- package/src/core/types/OneTimeTokenInfo.ts +0 -1
- package/src/core/types/index.ts +17 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
import SwiftUI
|
|
4
|
+
import React
|
|
5
|
+
import YunoSDK
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ViewManager for YunoPaymentMethodsView.
|
|
9
|
+
*
|
|
10
|
+
* This component embeds the native Yuno SDK payment methods list view
|
|
11
|
+
* into React Native, allowing developers to display payment methods in their app.
|
|
12
|
+
*
|
|
13
|
+
* Uses UIHostingController to bridge SwiftUI to UIKit for React Native.
|
|
14
|
+
*/
|
|
15
|
+
@objc(YunoPaymentMethodsViewManager)
|
|
16
|
+
class YunoPaymentMethodsViewManager: RCTViewManager {
|
|
17
|
+
|
|
18
|
+
override func view() -> UIView! {
|
|
19
|
+
let view = YunoPaymentMethodsView()
|
|
20
|
+
// Tell React Native this view handles its own scrolling
|
|
21
|
+
view.clipsToBounds = true
|
|
22
|
+
return view
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
override static func requiresMainQueueSetup() -> Bool {
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Custom UIView that wraps the Yuno payment methods SwiftUI view using UIHostingController.
|
|
32
|
+
*/
|
|
33
|
+
class YunoPaymentMethodsView: UIView, YunoPaymentFullDelegate {
|
|
34
|
+
|
|
35
|
+
private var hostingController: UIHostingController<AnyView>?
|
|
36
|
+
private var _checkoutSession: String?
|
|
37
|
+
private var _countryCode: String?
|
|
38
|
+
|
|
39
|
+
// YunoPaymentDelegate properties
|
|
40
|
+
var checkoutSession: String {
|
|
41
|
+
return _checkoutSession ?? ""
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
var countryCode: String {
|
|
45
|
+
return _countryCode ?? ""
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
var language: String? {
|
|
49
|
+
return nil // Use default language
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
var viewController: UIViewController? {
|
|
53
|
+
return self.findViewController()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override init(frame: CGRect) {
|
|
57
|
+
super.init(frame: frame)
|
|
58
|
+
setupView()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
required init?(coder: NSCoder) {
|
|
62
|
+
super.init(coder: coder)
|
|
63
|
+
setupView()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private func setupView() {
|
|
67
|
+
self.backgroundColor = .clear
|
|
68
|
+
self.isUserInteractionEnabled = true
|
|
69
|
+
self.clipsToBounds = true
|
|
70
|
+
|
|
71
|
+
print("[YunoPaymentMethods] 🏗️ setupView called")
|
|
72
|
+
// Don't load immediately - wait for checkoutSession and countryCode props
|
|
73
|
+
// loadPaymentMethodsView() will be called from updateCheckoutSessionIfReady()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
override func layoutSubviews() {
|
|
77
|
+
super.layoutSubviews()
|
|
78
|
+
print("[YunoPaymentMethods] 📐 layoutSubviews - frame: \(self.frame)")
|
|
79
|
+
// SwiftUI's Auto Layout constraints will handle the size
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
override func didMoveToWindow() {
|
|
84
|
+
super.didMoveToWindow()
|
|
85
|
+
if window != nil {
|
|
86
|
+
print("[YunoPaymentMethods] 🪟 View added to window, frame: \(self.frame)")
|
|
87
|
+
setNeedsLayout()
|
|
88
|
+
layoutIfNeeded()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Loads the payment methods view asynchronously from the Yuno SDK.
|
|
94
|
+
* Uses UIHostingController to convert SwiftUI View to UIView.
|
|
95
|
+
*
|
|
96
|
+
* The key is to use a separate async function with explicit return type
|
|
97
|
+
* to help Swift's type inference select the correct overload.
|
|
98
|
+
*/
|
|
99
|
+
private func loadPaymentMethodsView() {
|
|
100
|
+
print("[YunoPaymentMethods] 🔄 loadPaymentMethodsView called")
|
|
101
|
+
print("[YunoPaymentMethods] 📋 Current checkoutSession: \(self.checkoutSession)")
|
|
102
|
+
print("[YunoPaymentMethods] 🌍 Current countryCode: \(self.countryCode)")
|
|
103
|
+
|
|
104
|
+
Task { @MainActor in
|
|
105
|
+
// Get the SwiftUI view by calling the helper function
|
|
106
|
+
// that explicitly returns 'some View' type
|
|
107
|
+
print("[YunoPaymentMethods] 🎨 Getting SwiftUI view from Yuno SDK...")
|
|
108
|
+
let swiftUIView = await getYunoSwiftUIView()
|
|
109
|
+
print("[YunoPaymentMethods] ✅ SwiftUI view obtained")
|
|
110
|
+
|
|
111
|
+
// Wrap SwiftUI view in UIHostingController
|
|
112
|
+
let hostingController = UIHostingController(rootView: AnyView(swiftUIView))
|
|
113
|
+
hostingController.view.backgroundColor = .clear
|
|
114
|
+
self.hostingController = hostingController
|
|
115
|
+
|
|
116
|
+
guard let hostingView = hostingController.view else { return }
|
|
117
|
+
|
|
118
|
+
print("[YunoPaymentMethods] ➕ Adding SwiftUI hostingView")
|
|
119
|
+
|
|
120
|
+
// Add to view controller hierarchy for proper event handling
|
|
121
|
+
if let parentVC = self.findViewController() {
|
|
122
|
+
parentVC.addChild(hostingController)
|
|
123
|
+
hostingController.didMove(toParent: parentVC)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Add as subview and pin to edges
|
|
127
|
+
self.addSubview(hostingView)
|
|
128
|
+
hostingView.translatesAutoresizingMaskIntoConstraints = false
|
|
129
|
+
NSLayoutConstraint.activate([
|
|
130
|
+
hostingView.topAnchor.constraint(equalTo: self.topAnchor),
|
|
131
|
+
hostingView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
|
|
132
|
+
hostingView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
|
|
133
|
+
hostingView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
|
|
134
|
+
])
|
|
135
|
+
|
|
136
|
+
print("[YunoPaymentMethods] ✅ Payment methods view loaded")
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Helper function that explicitly returns SwiftUI View type.
|
|
142
|
+
* This helps Swift's type inference select the correct overload.
|
|
143
|
+
*/
|
|
144
|
+
@MainActor
|
|
145
|
+
private func getYunoSwiftUIView() async -> some View {
|
|
146
|
+
// By declaring the return type as 'some View', Swift knows to call
|
|
147
|
+
// the SwiftUI version of getPaymentMethodViewAsync
|
|
148
|
+
await Yuno.getPaymentMethodViewAsync(delegate: self)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Updates the checkout session.
|
|
153
|
+
*/
|
|
154
|
+
@objc func setCheckoutSession(_ checkoutSession: String) {
|
|
155
|
+
print("[YunoPaymentMethods] 📝 setCheckoutSession called with: \(checkoutSession)")
|
|
156
|
+
self._checkoutSession = checkoutSession
|
|
157
|
+
updateCheckoutSessionIfReady()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Updates the country code.
|
|
162
|
+
*/
|
|
163
|
+
@objc func setCountryCode(_ countryCode: String) {
|
|
164
|
+
print("[YunoPaymentMethods] 📝 setCountryCode called with: \(countryCode)")
|
|
165
|
+
self._countryCode = countryCode
|
|
166
|
+
updateCheckoutSessionIfReady()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Updates the checkout session in Yuno SDK if both checkoutSession and countryCode are available.
|
|
171
|
+
* Once both are set, loads the payment methods view.
|
|
172
|
+
*/
|
|
173
|
+
private func updateCheckoutSessionIfReady() {
|
|
174
|
+
guard let checkoutSession = _checkoutSession,
|
|
175
|
+
let countryCode = _countryCode,
|
|
176
|
+
!checkoutSession.isEmpty,
|
|
177
|
+
!countryCode.isEmpty else {
|
|
178
|
+
print("[YunoPaymentMethods] ⏳ Waiting for both checkoutSession and countryCode")
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check if view is already loaded
|
|
183
|
+
if hostingController != nil {
|
|
184
|
+
print("[YunoPaymentMethods] ✅ View already loaded, skipping reload")
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// The YunoPaymentDelegate protocol properties (checkoutSession, countryCode)
|
|
189
|
+
// are already implemented above and Yuno SDK will read them automatically
|
|
190
|
+
print("[YunoPaymentMethods] ✅✅ Checkout session ready: \(checkoutSession), country: \(countryCode)")
|
|
191
|
+
print("[YunoPaymentMethods] 🚀 Now loading payment methods view...")
|
|
192
|
+
|
|
193
|
+
// Now that we have both props, load the view
|
|
194
|
+
loadPaymentMethodsView()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// MARK: - YunoPaymentDelegate
|
|
198
|
+
|
|
199
|
+
func yunoCreatePayment(with token: String) {
|
|
200
|
+
print("[YunoPaymentMethods] yunoCreatePayment called with token: \(token)")
|
|
201
|
+
|
|
202
|
+
// Emit OTT token event to React Native
|
|
203
|
+
DispatchQueue.main.async {
|
|
204
|
+
if let bridge = RCTBridge.current() {
|
|
205
|
+
if let yunoModule = bridge.module(for: YunoSdk.self) as? YunoSdk {
|
|
206
|
+
yunoModule.sendOneTimeTokenEvent(token: token)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func yunoCreatePayment(with token: String, information: [String : Any]) {
|
|
213
|
+
print("[YunoPaymentMethods] yunoCreatePayment called with token and information")
|
|
214
|
+
|
|
215
|
+
// Emit OTT token event with full information to React Native
|
|
216
|
+
DispatchQueue.main.async {
|
|
217
|
+
if let bridge = RCTBridge.current() {
|
|
218
|
+
if let yunoModule = bridge.module(for: YunoSdk.self) as? YunoSdk {
|
|
219
|
+
yunoModule.sendOneTimeTokenEvent(token: token)
|
|
220
|
+
// The information parameter contains additional token details
|
|
221
|
+
// It's already sent via sendOneTimeTokenEvent which emits both events
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
func yunoPaymentResult(_ result: Yuno.Result) {
|
|
228
|
+
print("[YunoPaymentMethods] yunoPaymentResult: \(result)")
|
|
229
|
+
|
|
230
|
+
// Emit payment status event to React Native
|
|
231
|
+
let statusString: String
|
|
232
|
+
switch result {
|
|
233
|
+
case .succeeded:
|
|
234
|
+
statusString = "SUCCEEDED"
|
|
235
|
+
case .fail:
|
|
236
|
+
statusString = "FAILED"
|
|
237
|
+
case .reject:
|
|
238
|
+
statusString = "REJECTED"
|
|
239
|
+
case .processing:
|
|
240
|
+
statusString = "PROCESSING"
|
|
241
|
+
case .internalError:
|
|
242
|
+
statusString = "INTERNAL_ERROR"
|
|
243
|
+
case .userCancelled:
|
|
244
|
+
statusString = "CANCELLED_BY_USER"
|
|
245
|
+
@unknown default:
|
|
246
|
+
statusString = "UNKNOWN"
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
DispatchQueue.main.async {
|
|
250
|
+
if let bridge = RCTBridge.current() {
|
|
251
|
+
if let yunoModule = bridge.module(for: YunoSdk.self) as? YunoSdk {
|
|
252
|
+
yunoModule.sendPaymentStatusEvent(status: statusString)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// MARK: - YunoPaymentFullDelegate
|
|
259
|
+
|
|
260
|
+
func yunoDidSelect(paymentMethod: YunoSDK.PaymentMethodSelected) {
|
|
261
|
+
// Emit event to React Native
|
|
262
|
+
print("[YunoPaymentMethods] Payment method selected")
|
|
263
|
+
emitPaymentSelectedEvent(isSelected: true)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
func yunoDidUnenrollSuccessfully(_ success: Bool) {
|
|
267
|
+
print("[YunoPaymentMethods] yunoDidUnenrollSuccessfully: \(success)")
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
func yunoUpdatePaymentMethodsViewHeight(_ height: CGFloat) {
|
|
271
|
+
// Update view height if needed
|
|
272
|
+
print("[YunoPaymentMethods] yunoUpdatePaymentMethodsViewHeight: \(height)")
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// MARK: - Event Emission
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Emits a payment method selected event to React Native.
|
|
279
|
+
*/
|
|
280
|
+
private func emitPaymentSelectedEvent(isSelected: Bool) {
|
|
281
|
+
// Send event through YunoSdk module
|
|
282
|
+
DispatchQueue.main.async {
|
|
283
|
+
if let bridge = RCTBridge.current() {
|
|
284
|
+
if let yunoModule = bridge.module(for: YunoSdk.self) as? YunoSdk {
|
|
285
|
+
yunoModule.sendEvent(
|
|
286
|
+
withName: "onPaymentMethodSelected",
|
|
287
|
+
body: ["isSelected": isSelected]
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Emits an error event to React Native.
|
|
296
|
+
*/
|
|
297
|
+
private func emitErrorEvent(message: String) {
|
|
298
|
+
DispatchQueue.main.async {
|
|
299
|
+
if let bridge = RCTBridge.current() {
|
|
300
|
+
if let yunoModule = bridge.module(for: YunoSdk.self) as? YunoSdk {
|
|
301
|
+
yunoModule.sendEvent(
|
|
302
|
+
withName: "onPaymentMethodError",
|
|
303
|
+
body: ["message": message]
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// MARK: - UIView extension to find view controller
|
|
312
|
+
|
|
313
|
+
extension UIView {
|
|
314
|
+
func findViewController() -> UIViewController? {
|
|
315
|
+
if let nextResponder = self.next as? UIViewController {
|
|
316
|
+
return nextResponder
|
|
317
|
+
} else if let nextResponder = self.next as? UIView {
|
|
318
|
+
return nextResponder.findViewController()
|
|
319
|
+
} else {
|
|
320
|
+
return nil
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
package/ios/YunoSdk.m
CHANGED
|
@@ -33,7 +33,9 @@ RCT_EXTERN_METHOD(
|
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
RCT_EXTERN_METHOD(
|
|
36
|
-
continuePayment:(
|
|
36
|
+
continuePayment:(NSString *)checkoutSession
|
|
37
|
+
countryCode:(NSString *)countryCode
|
|
38
|
+
showPaymentStatus:(BOOL)showPaymentStatus
|
|
37
39
|
resolver:(RCTPromiseResolveBlock)resolver
|
|
38
40
|
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
39
41
|
)
|
|
@@ -56,10 +58,45 @@ RCT_EXTERN_METHOD(
|
|
|
56
58
|
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
57
59
|
)
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
RCT_EXTERN_METHOD(
|
|
62
|
+
getLastOneTimeToken:(RCTPromiseResolveBlock)resolver
|
|
63
|
+
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
RCT_EXTERN_METHOD(
|
|
67
|
+
getLastOneTimeTokenInfo:(RCTPromiseResolveBlock)resolver
|
|
68
|
+
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
RCT_EXTERN_METHOD(
|
|
72
|
+
clearLastOneTimeToken:(RCTPromiseResolveBlock)resolver
|
|
73
|
+
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
RCT_EXTERN_METHOD(
|
|
77
|
+
clearLastPaymentStatus:(RCTPromiseResolveBlock)resolver
|
|
78
|
+
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
RCT_EXTERN_METHOD(addListener:(NSString *)eventName)
|
|
82
|
+
|
|
83
|
+
RCT_EXTERN_METHOD(removeListeners:(double)count)
|
|
84
|
+
|
|
85
|
+
// Headless Payment Flow
|
|
86
|
+
RCT_EXTERN_METHOD(
|
|
87
|
+
generateToken:(NSDictionary *)tokenCollectedData
|
|
88
|
+
checkoutSession:(NSString *)checkoutSession
|
|
89
|
+
countryCode:(NSString *)countryCode
|
|
90
|
+
resolver:(RCTPromiseResolveBlock)resolver
|
|
91
|
+
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
RCT_EXTERN_METHOD(
|
|
95
|
+
getThreeDSecureChallenge:(NSString *)checkoutSession
|
|
96
|
+
countryCode:(NSString *)countryCode
|
|
97
|
+
resolver:(RCTPromiseResolveBlock)resolver
|
|
98
|
+
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
99
|
+
)
|
|
63
100
|
|
|
64
101
|
@end
|
|
65
102
|
|