react-native-hyperswitch-dev-sdk 0.4.3 → 0.4.5
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/HyperswitchSdkReactNative.podspec +4 -0
- package/android/build.gradle +0 -6
- package/android/src/main/java/com/hyperswitchsdkreactnative/modules/ReactNativeHyperswitchModule.kt +1 -5
- package/ios/Modules/ReactNative/HyperModule.h +25 -0
- package/ios/Modules/ReactNative/HyperModule.mm +169 -0
- package/ios/Modules/ReactNative/HyperswitchModule.swift +223 -165
- package/ios/Modules/ReactNative/NativeHyperswitchModule.mm +17 -12
- package/ios/Modules/ReactNative/NativeHyperswitchModuleImpl.swift +18 -7
- package/ios/Modules/ReactNative/NativePaymentElementModule.mm +122 -122
- package/ios/Views/Fabric/NativePaymentElementView.mm +8 -0
- package/ios/Views/Native/NativePaymentWidget.swift +125 -121
- package/ios/hyperswitchSDK/Core/HyperCVCWidget/CVCWidget.swift +9 -7
- package/ios/hyperswitchSDK/Core/HyperPaymentCardTextField/PaymentHandler.swift +1 -1
- package/ios/hyperswitchSDK/Core/HyperPaymentSheet/PaymentSheetView+UIKit.swift +79 -13
- package/ios/hyperswitchSDK/Core/HyperPaymentSheet/PaymentSheetView.swift +2 -2
- package/ios/hyperswitchSDK/Core/HyperPaymentWidget/PaymentWidget.swift +23 -19
- package/ios/hyperswitchSDK/Core/HyperSession/PaymentSession+UIKit.swift +14 -0
- package/ios/hyperswitchSDK/Core/NativeModule/HyperModuleImpl.swift +388 -0
- package/ios/hyperswitchSDK/Core/NativeModule/RNHeadlessManager.swift +58 -22
- package/ios/hyperswitchSDK/Core/NativeModule/RNViewManager.swift +84 -29
- package/ios/hyperswitchSDK/Core/NativeModule/hyper-Bridging-Header.h +2 -0
- package/ios/hyperswitchSDK/Public/RNBridgeFactory.h +28 -0
- package/ios/hyperswitchSDK/Public/RNBridgeFactory.mm +167 -0
- package/ios/hyperswitchSDK/Shared/PaymentSheet.swift +1 -1
- package/lib/commonjs/index.bundle.js +1 -1
- package/lib/commonjs/index.bundle.js.map +1 -1
- package/lib/module/index.bundle.js +1 -1
- package/lib/module/index.bundle.js.map +1 -1
- package/package.json +8 -4
- package/src/context/PaymentSession.ts +2 -0
- package/src/specs/NativeHyperswitchModule.ts +2 -2
- package/ios/Modules/ReactNative/HyperswitchSdkReactNative.h +0 -5
- package/ios/Modules/ReactNative/HyperswitchSdkReactNative.mm +0 -121
- package/ios/hyperswitchSDK/Core/NativeModule/HyperModule.m +0 -31
- package/ios/hyperswitchSDK/Core/NativeModule/HyperModule.swift +0 -343
|
@@ -14,7 +14,7 @@ public class CVCWidget: UIControl {
|
|
|
14
14
|
private var configurationDict: [String: Any]?
|
|
15
15
|
private var widgetReactTag: NSNumber?
|
|
16
16
|
internal var rootReactTag: NSNumber? { widgetReactTag }
|
|
17
|
-
private var rootView:
|
|
17
|
+
private var rootView: UIView?
|
|
18
18
|
private var cvcCallback: ((PaymentResult) -> Void)?
|
|
19
19
|
private var subscribedEventNames: [String]?
|
|
20
20
|
private let hyperswitch: Hyperswitch
|
|
@@ -112,12 +112,13 @@ public class CVCWidget: UIControl {
|
|
|
112
112
|
"paymentToken": paymentToken,
|
|
113
113
|
"paymentMethodId": paymentMethodId as Any,
|
|
114
114
|
]
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
// Bridge-based widget emit removed for payment-sheet-only build.
|
|
116
|
+
// RNViewManager.sharedInstance.bridge?.enqueueJSCall(
|
|
117
|
+
// "RCTDeviceEventEmitter",
|
|
118
|
+
// method: "emit",
|
|
119
|
+
// args: ["triggerWidgetAction", payload],
|
|
120
|
+
// completion: nil
|
|
121
|
+
// )
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
internal func dispatchPaymentEvent(type: String, payload: [String: Any]) {
|
|
@@ -134,3 +135,4 @@ public class CVCWidget: UIControl {
|
|
|
134
135
|
self.paymentEventListener = listener
|
|
135
136
|
}
|
|
136
137
|
}
|
|
138
|
+
|
|
@@ -27,7 +27,7 @@ public class PaymentHandler: NSObject {
|
|
|
27
27
|
self.completion = completion
|
|
28
28
|
|
|
29
29
|
RNViewManager.sharedInstance.responseHandler = self
|
|
30
|
-
|
|
30
|
+
HyperModuleImpl.shared.confirm(data: paymentParams.description())
|
|
31
31
|
}
|
|
32
32
|
public typealias PaymentHandlerActionPaymentIntentCompletionBlock = (
|
|
33
33
|
PaymentHandlerActionStatus, PaymentIntent?, NSError?
|
|
@@ -11,13 +11,13 @@ import React
|
|
|
11
11
|
/// Extension on the PaymentSheet class to handle the presentation of the payment sheet view.
|
|
12
12
|
internal extension PaymentSheet {
|
|
13
13
|
|
|
14
|
-
///
|
|
14
|
+
/// Present with a raw-string completion — the JS bundle's exit message is forwarded
|
|
15
|
+
/// unchanged to the caller. Used by the React Native TurboModule path.
|
|
15
16
|
private func presentWithRootView(
|
|
16
17
|
from presentingViewController: UIViewController,
|
|
17
|
-
rootView:
|
|
18
|
-
completion: @escaping (
|
|
18
|
+
rootView: UIView,
|
|
19
|
+
completion: @escaping (String) -> Void
|
|
19
20
|
) {
|
|
20
|
-
|
|
21
21
|
self.completion = completion
|
|
22
22
|
|
|
23
23
|
let paymentSheetViewController = HyperUIViewController()
|
|
@@ -25,25 +25,91 @@ internal extension PaymentSheet {
|
|
|
25
25
|
paymentSheetViewController.modalPresentationStyle = .overFullScreen
|
|
26
26
|
paymentSheetViewController.view = rootView
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// Register the sheet and VC directly with HyperModuleImpl so that
|
|
29
|
+
// exitPaymentsheet can call the completion without relying on
|
|
30
|
+
// bridge.uiManager.addUIBlock (which doesn't find Fabric-rendered views).
|
|
31
|
+
HyperModuleImpl.shared.registerPaymentSheet(self, vc: paymentSheetViewController)
|
|
32
|
+
|
|
29
33
|
presentingViewController.present(paymentSheetViewController, animated: false)
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
// MARK: - Public-facing helpers (PaymentResult overloads kept for SwiftUI / native SDK usage)
|
|
37
|
+
|
|
38
|
+
/// Present with a typed `PaymentResult` — the result is serialised to a JSON string
|
|
39
|
+
/// internally so the single `completion: ((String) -> Void)?` property is shared.
|
|
40
|
+
func present(
|
|
41
|
+
from presentingViewController: UIViewController,
|
|
42
|
+
completion: @escaping (PaymentResult) -> Void
|
|
43
|
+
) {
|
|
44
|
+
presentWithRootView(
|
|
45
|
+
from: presentingViewController,
|
|
46
|
+
rootView: getRootView(),
|
|
47
|
+
completion: { raw in completion(PaymentSheet.paymentResult(from: raw)) }
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Present with custom props and a typed `PaymentResult` completion.
|
|
52
|
+
func presentWithParams(
|
|
53
|
+
from presentingViewController: UIViewController,
|
|
54
|
+
props: [String: Any],
|
|
55
|
+
completion: @escaping (PaymentResult) -> Void
|
|
56
|
+
) {
|
|
57
|
+
presentWithRootView(
|
|
58
|
+
from: presentingViewController,
|
|
59
|
+
rootView: getRootViewWithParams(props: props),
|
|
60
|
+
completion: { raw in completion(PaymentSheet.paymentResult(from: raw)) }
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// MARK: - Internal raw-string overloads (used by HyperswitchModule / TurboModule path)
|
|
34
65
|
|
|
35
|
-
|
|
36
|
-
|
|
66
|
+
/// Present with default configuration and a raw-string completion.
|
|
67
|
+
func present(
|
|
68
|
+
from presentingViewController: UIViewController,
|
|
69
|
+
rawCompletion: @escaping (String) -> Void
|
|
70
|
+
) {
|
|
71
|
+
presentWithRootView(
|
|
72
|
+
from: presentingViewController,
|
|
73
|
+
rootView: getRootView(),
|
|
74
|
+
completion: rawCompletion
|
|
75
|
+
)
|
|
37
76
|
}
|
|
38
77
|
|
|
39
|
-
///
|
|
78
|
+
/// Present with custom props and a raw-string completion.
|
|
40
79
|
func presentWithParams(
|
|
41
80
|
from presentingViewController: UIViewController,
|
|
42
81
|
props: [String: Any],
|
|
43
|
-
|
|
82
|
+
rawCompletion: @escaping (String) -> Void
|
|
44
83
|
) {
|
|
84
|
+
presentWithRootView(
|
|
85
|
+
from: presentingViewController,
|
|
86
|
+
rootView: getRootViewWithParams(props: props),
|
|
87
|
+
completion: rawCompletion
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// MARK: - Shared result deserialiser
|
|
45
92
|
|
|
46
|
-
|
|
47
|
-
|
|
93
|
+
/// Convert the raw JSON string sent by the JS bundle into a typed `PaymentResult`.
|
|
94
|
+
/// Mirrors the logic in `HyperModuleImpl.paymentResult(from:)`.
|
|
95
|
+
private static func paymentResult(from raw: String) -> PaymentResult {
|
|
96
|
+
guard
|
|
97
|
+
let data = raw.data(using: .utf8),
|
|
98
|
+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
99
|
+
let status = dict["status"] as? String
|
|
100
|
+
else {
|
|
101
|
+
return .failed(error: NSError(domain: "UNKNOWN_ERROR", code: 0,
|
|
102
|
+
userInfo: ["message": "An error has occurred."]))
|
|
103
|
+
}
|
|
104
|
+
switch status {
|
|
105
|
+
case "success", "succeeded", "completed":
|
|
106
|
+
return .completed(data: status)
|
|
107
|
+
case "cancelled", "canceled":
|
|
108
|
+
return .canceled(data: status)
|
|
109
|
+
default:
|
|
110
|
+
let code = (dict["code"] as? String).flatMap { $0.isEmpty ? nil : $0 } ?? "UNKNOWN_ERROR"
|
|
111
|
+
let message = dict["message"] as? String ?? "An error has occurred."
|
|
112
|
+
return .failed(error: NSError(domain: code, code: 0, userInfo: ["message": message]))
|
|
113
|
+
}
|
|
48
114
|
}
|
|
49
115
|
}
|
|
@@ -13,7 +13,7 @@ import WebKit
|
|
|
13
13
|
internal extension PaymentSheet {
|
|
14
14
|
|
|
15
15
|
/// Method to get the root view for the payment sheet based on the configured properties.
|
|
16
|
-
func getRootView() ->
|
|
16
|
+
func getRootView() -> UIView {
|
|
17
17
|
|
|
18
18
|
let hyperswitchConfiguration = try? hyperswitchConfiguration?.toDictionary()
|
|
19
19
|
let paymentSessionConfiguration = try? paymentSessionConfiguration.toDictionary()
|
|
@@ -42,7 +42,7 @@ internal extension PaymentSheet {
|
|
|
42
42
|
|
|
43
43
|
/// Method to get the root view for the payment sheet with custom parameters.
|
|
44
44
|
/// - Note: Used by Flutter and React Native Wrappers to send separate props.
|
|
45
|
-
func getRootViewWithParams(props: [String: Any]) ->
|
|
45
|
+
func getRootViewWithParams(props: [String: Any]) -> UIView {
|
|
46
46
|
|
|
47
47
|
let hyperswitchConfiguration = try? hyperswitchConfiguration?.toDictionary()
|
|
48
48
|
let paymentSessionConfiguration = try? paymentSessionConfiguration.toDictionary()
|
|
@@ -16,7 +16,7 @@ public class PaymentWidget: UIControl {
|
|
|
16
16
|
private var configurationDict: [String: Any]?
|
|
17
17
|
private var widgetReactTag: NSNumber?
|
|
18
18
|
internal var rootReactTag: NSNumber? { widgetReactTag }
|
|
19
|
-
private var rootView:
|
|
19
|
+
private var rootView: UIView?
|
|
20
20
|
private var initCallback: ((PaymentResult) -> Void)?
|
|
21
21
|
private var shouldProceedWithPaymentCallback: ((PaymentRequestData, @escaping (Bool) -> Void) -> Void)?
|
|
22
22
|
private var cancellables = Set<AnyCancellable>()
|
|
@@ -124,12 +124,13 @@ public class PaymentWidget: UIControl {
|
|
|
124
124
|
.sink { [weak self] in
|
|
125
125
|
guard let self = self else { return }
|
|
126
126
|
let payload: [String: Any] = ["rootTag": self.widgetReactTag ?? -1]
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
127
|
+
// Bridge-based widget emit removed for payment-sheet-only build.
|
|
128
|
+
// RNViewManager.sharedInstance.bridge?.enqueueJSCall(
|
|
129
|
+
// "RCTDeviceEventEmitter",
|
|
130
|
+
// method: "emit",
|
|
131
|
+
// args: ["updateIntentInit", payload],
|
|
132
|
+
// completion: nil
|
|
133
|
+
// )
|
|
133
134
|
}
|
|
134
135
|
.store(in: &cancellables)
|
|
135
136
|
|
|
@@ -141,12 +142,13 @@ public class PaymentWidget: UIControl {
|
|
|
141
142
|
"rootTag": self.widgetReactTag ?? -1,
|
|
142
143
|
"sdkAuthorization": sdkAuthorization,
|
|
143
144
|
]
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
// Bridge-based widget emit removed for payment-sheet-only build.
|
|
146
|
+
// RNViewManager.sharedInstance.bridge?.enqueueJSCall(
|
|
147
|
+
// "RCTDeviceEventEmitter",
|
|
148
|
+
// method: "emit",
|
|
149
|
+
// args: ["updateIntentComplete", payload],
|
|
150
|
+
// completion: nil
|
|
151
|
+
// )
|
|
150
152
|
}
|
|
151
153
|
.store(in: &cancellables)
|
|
152
154
|
}
|
|
@@ -156,12 +158,13 @@ public class PaymentWidget: UIControl {
|
|
|
156
158
|
"rootTag": self.widgetReactTag ?? -1,
|
|
157
159
|
"actionType": "CONFIRM_PAYMENT_ACTION",
|
|
158
160
|
]
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
// Bridge-based widget emit removed for payment-sheet-only build.
|
|
162
|
+
// RNViewManager.sharedInstance.bridge?.enqueueJSCall(
|
|
163
|
+
// "RCTDeviceEventEmitter",
|
|
164
|
+
// method: "emit",
|
|
165
|
+
// args: ["triggerWidgetAction", payload],
|
|
166
|
+
// completion: nil
|
|
167
|
+
// )
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
internal func setPaymentEventListener(_ listener: PaymentEventListener?) {
|
|
@@ -210,3 +213,4 @@ public class PaymentWidget: UIControl {
|
|
|
210
213
|
}
|
|
211
214
|
}
|
|
212
215
|
}
|
|
216
|
+
|
|
@@ -73,6 +73,20 @@ extension PaymentSession {
|
|
|
73
73
|
paymentSheet.presentWithParams(from: viewController, props: params, completion: completion)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/// React Native / TurboModule overload — resolves with the raw JSON string from the JS
|
|
77
|
+
/// bundle so the result reaches the promise without any re-serialisation.
|
|
78
|
+
internal func presentPaymentSheetWithParams(
|
|
79
|
+
viewController: UIViewController,
|
|
80
|
+
params: [String: Any],
|
|
81
|
+
rawCompletion: @escaping (String) -> Void
|
|
82
|
+
) {
|
|
83
|
+
let paymentSheet = PaymentSheet(
|
|
84
|
+
paymentSessionConfiguration: paymentSessionConfiguration,
|
|
85
|
+
hyperswitchConfiguration: hyperswitchConfiguration ?? nil
|
|
86
|
+
)
|
|
87
|
+
paymentSheet.presentWithParams(from: viewController, props: params, rawCompletion: rawCompletion)
|
|
88
|
+
}
|
|
89
|
+
|
|
76
90
|
public func getCustomerSavedPaymentMethods(
|
|
77
91
|
_ func_: @escaping (PaymentSessionHandler) -> Void,
|
|
78
92
|
configuration: SavedPaymentMethodsConfiguration? = nil
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
//
|
|
2
|
+
// HyperModuleImpl.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Kuntimaddi Manideep on 22/07/26.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
import React
|
|
10
|
+
|
|
11
|
+
@objc(HyperModuleImpl)
|
|
12
|
+
public class HyperModuleImpl: NSObject {
|
|
13
|
+
|
|
14
|
+
@objc public static let shared = HyperModuleImpl()
|
|
15
|
+
|
|
16
|
+
/// The RN-registered event emitter (the ObjC++ `HyperModule` TurboModule) through
|
|
17
|
+
/// which JS-facing events are dispatched and the bridge / uiManager is reached.
|
|
18
|
+
@objc public weak var eventEmitter: RCTEventEmitter?
|
|
19
|
+
|
|
20
|
+
private let applePayPaymentHandler = ApplePayHandler()
|
|
21
|
+
private let expressCheckoutHandler = ExpressCheckoutLauncher()
|
|
22
|
+
private var presentCallback: RCTResponseSenderBlock? = nil
|
|
23
|
+
|
|
24
|
+
/// Direct reference to the currently-presented PaymentSheet.
|
|
25
|
+
/// Set by PaymentSheetView+UIKit when the sheet is presented; cleared in exitPaymentsheet.
|
|
26
|
+
/// Avoids relying on bridge.uiManager.addUIBlock (which doesn't find Fabric views).
|
|
27
|
+
private var activePaymentSheet: PaymentSheet?
|
|
28
|
+
private weak var activePaymentSheetVC: HyperUIViewController?
|
|
29
|
+
|
|
30
|
+
private override init() {
|
|
31
|
+
super.init()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MARK: - Payment sheet registration (called from PaymentSheetView+UIKit)
|
|
35
|
+
|
|
36
|
+
internal func registerPaymentSheet(_ sheet: PaymentSheet, vc: HyperUIViewController) {
|
|
37
|
+
activePaymentSheet = sheet
|
|
38
|
+
activePaymentSheetVC = vc
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Bridge-backed widget helpers removed for the new-arch/bridgeless build.
|
|
42
|
+
// private var bridge: RCTBridge? { eventEmitter?.bridge }
|
|
43
|
+
|
|
44
|
+
// MARK: - Events
|
|
45
|
+
|
|
46
|
+
@objc public func confirm(data: [String: Any]) {
|
|
47
|
+
eventEmitter?.sendEvent(withName: "confirm", body: data)
|
|
48
|
+
}
|
|
49
|
+
// MARK: WIP
|
|
50
|
+
// @objc public func confirmEC(data: [String: Any]) {
|
|
51
|
+
// eventEmitter?.sendEvent(withName: "confirmEC", body: data)
|
|
52
|
+
// }
|
|
53
|
+
|
|
54
|
+
// MARK: - Generic message passing
|
|
55
|
+
|
|
56
|
+
@objc public func sendMessageToNative(_ rnMessage: String) {}
|
|
57
|
+
|
|
58
|
+
// MARK: - Apple Pay
|
|
59
|
+
|
|
60
|
+
@objc public func launchApplePay(_ rnMessage: String, callback: @escaping RCTResponseSenderBlock) {
|
|
61
|
+
applePayPaymentHandler.startPayment(rnMessage: rnMessage, rnCallback: callback, presentCallback: self.presentCallback)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@objc public func startApplePay(_ rnMessage: String, callback: @escaping RCTResponseSenderBlock) {
|
|
65
|
+
callback([])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@objc public func presentApplePay(_ rnMessage: String, callback: @escaping RCTResponseSenderBlock) {
|
|
69
|
+
self.presentCallback = callback
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// MARK: - Widget (commented out — relies on RCTBridge/uiManager)
|
|
73
|
+
|
|
74
|
+
@objc public func launchWidgetPaymentSheet(_ requestObj: String, callback: @escaping RCTResponseSenderBlock) {
|
|
75
|
+
// let request = HyperModuleImpl.jsonObject(from: requestObj)
|
|
76
|
+
// expressCheckoutHandler.launchPaymentSheet(paymentResult: request, callBack: callback)
|
|
77
|
+
}
|
|
78
|
+
//
|
|
79
|
+
@objc public func onAddPaymentMethod(_ rnMessage: String) {
|
|
80
|
+
// PaymentMethodManagementWidget.onAddPaymentMethod?()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// MARK: - Payment sheet exits
|
|
84
|
+
|
|
85
|
+
@objc public func exitPaymentsheet(_ reactTag: NSNumber, result rnMessage: String, reset: Bool) {
|
|
86
|
+
// Consume and clear so they can't be called a second time.
|
|
87
|
+
let sheet = activePaymentSheet
|
|
88
|
+
let vc = activePaymentSheetVC
|
|
89
|
+
activePaymentSheet = nil
|
|
90
|
+
activePaymentSheetVC = nil
|
|
91
|
+
|
|
92
|
+
// Pass the raw JS result string directly — no PaymentResult conversion.
|
|
93
|
+
// PaymentSheet.completion is (String) -> Void, so this goes straight to
|
|
94
|
+
// the promise resolve in HyperswitchModule.presentPaymentSheet.
|
|
95
|
+
sheet?.completion?(rnMessage)
|
|
96
|
+
|
|
97
|
+
// Dismiss the VC, then release the rootView so the Fabric surface is torn down.
|
|
98
|
+
vc?.dismiss(animated: false) {
|
|
99
|
+
RNViewManager.sharedInstance.rootView?.removeFromSuperview()
|
|
100
|
+
RNViewManager.sharedInstance.rootView = nil
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@objc public func exitWidgetPaymentsheet(_ reactTag: NSNumber, result rnMessage: String, reset: Bool) {
|
|
105
|
+
// let result = paymentResult(from: rnMessage)
|
|
106
|
+
// withWidget(reactTag) { w in
|
|
107
|
+
// w.handleConfirmPaymentResponse(result)
|
|
108
|
+
// }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@objc public func exitPaymentMethodManagement(_ reactTag: NSNumber, result rnMessage: String, reset: Bool) {
|
|
112
|
+
exitSheet(rnMessage)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@objc public func exitCardForm(_ rnMessage: String) {
|
|
116
|
+
exitCardFormInternal(rnMessage)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// MARK: - Payment result / events (commented out — rely on RCTBridge/uiManager)
|
|
120
|
+
|
|
121
|
+
@objc public func notifyWidgetPaymentResult(_ rootTag: NSNumber, result rnMessage: String) {
|
|
122
|
+
// let result = paymentResult(from: rnMessage)
|
|
123
|
+
// guard case .failed = result else { return }
|
|
124
|
+
// withNativePaymentWidgetView(rootTag) { view in
|
|
125
|
+
// view.handleConfirmPaymentResponse(result)
|
|
126
|
+
// }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@objc public func onUpdateIntentEvent(_ rootTag: NSNumber, type: String, result: String) {
|
|
130
|
+
// withWidget(rootTag) { widget in
|
|
131
|
+
// widget.handleUpdateIntentEvent(type: type, result: result)
|
|
132
|
+
// }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@objc public func emitPaymentEvent(_ rootTag: NSNumber, eventType: String, payload: NSDictionary) {
|
|
136
|
+
// let map = (payload as? [String: Any]) ?? [:]
|
|
137
|
+
// resolveSubscribingTarget(rootTag) { target in
|
|
138
|
+
// if let widget = target as? PaymentWidget, widget.paymentEventListener != nil {
|
|
139
|
+
// widget.dispatchPaymentEvent(type: eventType, payload: map)
|
|
140
|
+
// } else if let cvc = target as? CVCWidget, cvc.paymentEventListener != nil {
|
|
141
|
+
// cvc.dispatchPaymentEvent(type: eventType, payload: map)
|
|
142
|
+
// } else if let sheet = target as? PaymentSheet, sheet.paymentEventListener != nil {
|
|
143
|
+
// sheet.dispatchPaymentEvent(type: eventType, payload: map)
|
|
144
|
+
// }
|
|
145
|
+
// }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@objc public func onPaymentConfirmButtonClick(_ rootTag: NSNumber, payload: String, callback: @escaping RCTResponseSenderBlock) {
|
|
149
|
+
// resolveSubscribingTarget(rootTag) { target in
|
|
150
|
+
// if let widget = target as? PaymentWidget {
|
|
151
|
+
// widget.handleShouldProceedWithPayment(payload: payload) { shouldProceed in
|
|
152
|
+
// callback([shouldProceed])
|
|
153
|
+
// }
|
|
154
|
+
// } else if let sheet = target as? PaymentSheet {
|
|
155
|
+
// sheet.handleShouldProceedWithPayment(payload: payload) { shouldProceed in
|
|
156
|
+
// callback([shouldProceed])
|
|
157
|
+
// }
|
|
158
|
+
// } else {
|
|
159
|
+
// callback([true])
|
|
160
|
+
// }
|
|
161
|
+
// }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// MARK: - 3DS / DDC iframe bridge
|
|
165
|
+
|
|
166
|
+
@objc public func openIframeBridge(_ url: String, timeoutMs: NSNumber, callback: @escaping RCTResponseSenderBlock) {
|
|
167
|
+
DispatchQueue.main.async {
|
|
168
|
+
let headlessWebView = HeadlessWebView(url: url, timeoutMs: timeoutMs, callback: callback)
|
|
169
|
+
headlessWebView.startFlow()
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// MARK: - Helpers
|
|
174
|
+
|
|
175
|
+
private static func jsonObject(from string: String) -> NSMutableDictionary {
|
|
176
|
+
guard
|
|
177
|
+
let data = string.data(using: .utf8),
|
|
178
|
+
let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [AnyHashable: Any]
|
|
179
|
+
else {
|
|
180
|
+
return NSMutableDictionary()
|
|
181
|
+
}
|
|
182
|
+
return NSMutableDictionary(dictionary: obj)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private func paymentResult(from rnMessage: String) -> PaymentResult {
|
|
186
|
+
guard let data = rnMessage.data(using: .utf8) else {
|
|
187
|
+
return .failed(
|
|
188
|
+
error: NSError(
|
|
189
|
+
domain: "UNKNOWN_ERROR",
|
|
190
|
+
code: 0,
|
|
191
|
+
userInfo: ["message": "An error has occurred."]
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
do {
|
|
197
|
+
guard let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] else {
|
|
198
|
+
return .failed(
|
|
199
|
+
error: NSError(
|
|
200
|
+
domain: "UNKNOWN_ERROR",
|
|
201
|
+
code: 0,
|
|
202
|
+
userInfo: ["message": "An error has occurred."]
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let status = jsonDictionary["status"]
|
|
208
|
+
|
|
209
|
+
if status == "success" || status == "succeeded" || status == "completed" {
|
|
210
|
+
return .completed(data: status ?? "success")
|
|
211
|
+
} else if status == "cancelled" || status == "canceled" {
|
|
212
|
+
return .canceled(data: status ?? "cancelled")
|
|
213
|
+
} else {
|
|
214
|
+
let error = NSError(
|
|
215
|
+
domain: (jsonDictionary["code"] ?? "") != "" ? jsonDictionary["code"]! : "UNKNOWN_ERROR",
|
|
216
|
+
code: 0,
|
|
217
|
+
userInfo: ["message": jsonDictionary["message"] ?? status ?? "An error has occurred."]
|
|
218
|
+
)
|
|
219
|
+
return .failed(error: error)
|
|
220
|
+
}
|
|
221
|
+
} catch {
|
|
222
|
+
return .failed(
|
|
223
|
+
error: NSError(
|
|
224
|
+
domain: "UNKNOWN_ERROR",
|
|
225
|
+
code: 0,
|
|
226
|
+
userInfo: ["message": "An error has occurred."]
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private func exitCardFormInternal(_ rnMessage: String) {
|
|
233
|
+
var response: String?
|
|
234
|
+
var error: NSError?
|
|
235
|
+
|
|
236
|
+
if let data = rnMessage.data(using: .utf8) {
|
|
237
|
+
do {
|
|
238
|
+
if let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {
|
|
239
|
+
let status = jsonDictionary["status"]
|
|
240
|
+
|
|
241
|
+
if status == "failed" || status == "requires_payment_method" {
|
|
242
|
+
error = NSError(
|
|
243
|
+
domain: (jsonDictionary["code"] ?? "") != "" ? jsonDictionary["code"]! : "UNKNOWN_ERROR",
|
|
244
|
+
code: 0,
|
|
245
|
+
userInfo: ["message": jsonDictionary["message"] ?? "An error has occurred."]
|
|
246
|
+
)
|
|
247
|
+
} else {
|
|
248
|
+
response = status
|
|
249
|
+
}
|
|
250
|
+
RNViewManager.sharedInstance.responseHandler?.didReceiveResponse(response: response, error: error)
|
|
251
|
+
} else {
|
|
252
|
+
RNViewManager.sharedInstance.responseHandler?.didReceiveResponse(
|
|
253
|
+
response: "failed",
|
|
254
|
+
error: NSError(domain: "UNKNOWN_ERROR", code: 0, userInfo: ["message": "An error has occurred."])
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
} catch {
|
|
258
|
+
RNViewManager.sharedInstance.responseHandler?.didReceiveResponse(
|
|
259
|
+
response: "failed",
|
|
260
|
+
error: NSError(domain: "UNKNOWN_ERROR", code: 0, userInfo: ["message": "An error has occurred."])
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
RNViewManager.sharedInstance.responseHandler?.didReceiveResponse(
|
|
265
|
+
response: "failed",
|
|
266
|
+
error: NSError(domain: "UNKNOWN_ERROR", code: 0, userInfo: ["message": "An error has occurred."])
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private func exitSheet(_ rnMessage: String) {
|
|
272
|
+
var response: String?
|
|
273
|
+
var error: NSError?
|
|
274
|
+
|
|
275
|
+
if let data = rnMessage.data(using: .utf8) {
|
|
276
|
+
do {
|
|
277
|
+
if let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {
|
|
278
|
+
let status = jsonDictionary["status"]
|
|
279
|
+
|
|
280
|
+
if status == "failed" || status == "requires_payment_method" {
|
|
281
|
+
error = NSError(
|
|
282
|
+
domain: (jsonDictionary["code"] ?? "") != "" ? jsonDictionary["code"]! : "UNKNOWN_ERROR",
|
|
283
|
+
code: 0,
|
|
284
|
+
userInfo: ["message": jsonDictionary["message"] ?? "An error has occurred."]
|
|
285
|
+
)
|
|
286
|
+
} else {
|
|
287
|
+
response = status
|
|
288
|
+
}
|
|
289
|
+
RNViewManager.sharedInstance.responseHandler?.didReceiveResponse(response: response, error: error)
|
|
290
|
+
} else {
|
|
291
|
+
RNViewManager.sharedInstance.responseHandler?.didReceiveResponse(
|
|
292
|
+
response: "failed",
|
|
293
|
+
error: NSError(domain: "UNKNOWN_ERROR", code: 0, userInfo: ["message": "An error has occurred."])
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
} catch {
|
|
297
|
+
RNViewManager.sharedInstance.responseHandler?.didReceiveResponse(
|
|
298
|
+
response: "failed",
|
|
299
|
+
error: NSError(domain: "UNKNOWN_ERROR", code: 0, userInfo: ["message": "An error has occurred."])
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
RNViewManager.sharedInstance.responseHandler?.didReceiveResponse(
|
|
304
|
+
response: "failed",
|
|
305
|
+
error: NSError(domain: "UNKNOWN_ERROR", code: 0, userInfo: ["message": "An error has occurred."])
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
DispatchQueue.main.async {
|
|
309
|
+
if let view = RNViewManager.sharedInstance.rootView {
|
|
310
|
+
let reactNativeVC: UIViewController? = view.reactViewController()
|
|
311
|
+
reactNativeVC?.dismiss(animated: false, completion: nil)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// MARK: - Bridge-backed view lookup helpers (commented out for new-arch/bridgeless build)
|
|
317
|
+
|
|
318
|
+
// private func withWidget(_ rootTag: NSNumber, _ block: @escaping (PaymentWidget) -> Void) {
|
|
319
|
+
// guard let bridge = self.bridge else { return }
|
|
320
|
+
// RCTGetUIManagerQueue().async {
|
|
321
|
+
// bridge.uiManager.addUIBlock { _, viewRegistry in
|
|
322
|
+
// guard let view = viewRegistry?[rootTag] else { return }
|
|
323
|
+
// var current: UIView? = view
|
|
324
|
+
// while let v = current {
|
|
325
|
+
// if let widget = v as? PaymentWidget {
|
|
326
|
+
// block(widget)
|
|
327
|
+
// return
|
|
328
|
+
// }
|
|
329
|
+
// current = v.superview
|
|
330
|
+
// }
|
|
331
|
+
// }
|
|
332
|
+
// }
|
|
333
|
+
// }
|
|
334
|
+
//
|
|
335
|
+
// private func withNativePaymentWidgetView(_ rootTag: NSNumber, _ block: @escaping (PaymentWidget) -> Void) {
|
|
336
|
+
// guard let bridge = self.bridge else { return }
|
|
337
|
+
// RCTGetUIManagerQueue().async {
|
|
338
|
+
// bridge.uiManager.addUIBlock { _, viewRegistry in
|
|
339
|
+
// guard let view = viewRegistry?[rootTag] else { return }
|
|
340
|
+
// var current: UIView? = view
|
|
341
|
+
// while let v = current {
|
|
342
|
+
// if let nativeWidget = v as? PaymentWidget {
|
|
343
|
+
// block(nativeWidget)
|
|
344
|
+
// return
|
|
345
|
+
// }
|
|
346
|
+
// current = v.superview
|
|
347
|
+
// }
|
|
348
|
+
// }
|
|
349
|
+
// }
|
|
350
|
+
// }
|
|
351
|
+
//
|
|
352
|
+
// private func resolveSubscribingTarget(_ rootTag: NSNumber, _ block: @escaping (AnyObject?) -> Void) {
|
|
353
|
+
// guard let bridge = self.bridge else {
|
|
354
|
+
// DispatchQueue.main.async { block(nil) }
|
|
355
|
+
// return
|
|
356
|
+
// }
|
|
357
|
+
// RCTGetUIManagerQueue().async {
|
|
358
|
+
// bridge.uiManager.addUIBlock { _, viewRegistry in
|
|
359
|
+
// guard let view = viewRegistry?[rootTag] else {
|
|
360
|
+
// DispatchQueue.main.async { block(nil) }
|
|
361
|
+
// return
|
|
362
|
+
// }
|
|
363
|
+
// var current: UIView? = view
|
|
364
|
+
// while let v = current {
|
|
365
|
+
// if v is PaymentWidget || v is CVCWidget {
|
|
366
|
+
// DispatchQueue.main.async { block(v) }
|
|
367
|
+
// return
|
|
368
|
+
// }
|
|
369
|
+
// current = v.superview
|
|
370
|
+
// }
|
|
371
|
+
// let sheet = (view.reactViewController() as? HyperUIViewController)?.paymentSheet
|
|
372
|
+
// DispatchQueue.main.async { block(sheet) }
|
|
373
|
+
// }
|
|
374
|
+
// }
|
|
375
|
+
// }
|
|
376
|
+
//
|
|
377
|
+
// private func withPaymentSheet(_ rootTag: NSNumber, _ block: @escaping (UIViewController?, PaymentSheet?) -> Void) {
|
|
378
|
+
// guard let bridge = self.bridge else { return }
|
|
379
|
+
// RCTGetUIManagerQueue().async {
|
|
380
|
+
// bridge.uiManager.addUIBlock { _, viewRegistry in
|
|
381
|
+
// let view = viewRegistry?[rootTag]
|
|
382
|
+
// let vc = view?.reactViewController() as? HyperUIViewController
|
|
383
|
+
// let sheet = vc?.paymentSheet
|
|
384
|
+
// DispatchQueue.main.async { block(vc, sheet) }
|
|
385
|
+
// }
|
|
386
|
+
// }
|
|
387
|
+
// }
|
|
388
|
+
}
|