react-native-hyperswitch-dev-sdk 0.4.2 → 0.4.4
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/hyperswitch_autolinking.gradle +1 -1
- 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 +26 -22
- package/ios/hyperswitchSDK/Core/HyperCVCWidget/CVCWidget.swift +3 -2
- 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 +5 -4
- package/ios/hyperswitchSDK/Core/HyperSession/PaymentSession+UIKit.swift +14 -0
- package/ios/hyperswitchSDK/Core/NativeModule/{HyperModule.swift → HyperModuleImpl.swift} +156 -113
- 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 +6 -2
- 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
|
@@ -27,6 +27,58 @@ public class HyperswitchModule: NSObject {
|
|
|
27
27
|
/// can resolve native widget views by reactTag.
|
|
28
28
|
@objc public var viewRegistry_DEPRECATED: RCTViewRegistry?
|
|
29
29
|
|
|
30
|
+
// MARK: - JSON string helpers
|
|
31
|
+
|
|
32
|
+
/// Serialise a plain `[String: Any?]` to a compact JSON string.
|
|
33
|
+
/// Returns `"{}"` on failure so callers always receive a valid string.
|
|
34
|
+
private static func toJSONString(_ dict: [String: Any?]) -> String {
|
|
35
|
+
// JSONSerialization can't handle `Any?` keys that are nil, so strip them first.
|
|
36
|
+
let cleaned = dict.compactMapValues { $0 }
|
|
37
|
+
guard
|
|
38
|
+
let data = try? JSONSerialization.data(withJSONObject: cleaned, options: []),
|
|
39
|
+
let str = String(data: data, encoding: .utf8)
|
|
40
|
+
else { return "{}" }
|
|
41
|
+
return str
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Encode an `Encodable` value to a JSON string.
|
|
45
|
+
private static func encodeToJSONString<T: Encodable>(_ value: T) -> String? {
|
|
46
|
+
guard
|
|
47
|
+
let data = try? JSONEncoder().encode(value),
|
|
48
|
+
let str = String(data: data, encoding: .utf8)
|
|
49
|
+
else { return nil }
|
|
50
|
+
return str
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// `{"code": ..., "message": ..., "status": ..., "error": ...}`
|
|
54
|
+
private static func standardResult(
|
|
55
|
+
status: String,
|
|
56
|
+
code: String? = nil,
|
|
57
|
+
message: String? = nil,
|
|
58
|
+
error: String? = nil
|
|
59
|
+
) -> String {
|
|
60
|
+
return toJSONString([
|
|
61
|
+
"status": status,
|
|
62
|
+
"code": code as Any?,
|
|
63
|
+
"message": message as Any?,
|
|
64
|
+
"error": error as Any?,
|
|
65
|
+
])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private static func paymentResultToJSONString(_ result: PaymentResult) -> String {
|
|
69
|
+
switch result {
|
|
70
|
+
case .completed:
|
|
71
|
+
return standardResult(status: "success")
|
|
72
|
+
case .failed(let error as NSError):
|
|
73
|
+
let msg = error.userInfo["message"] as? String ?? error.localizedDescription
|
|
74
|
+
return standardResult(status: "failed", code: error.domain, message: msg, error: msg)
|
|
75
|
+
case .canceled:
|
|
76
|
+
return standardResult(status: "cancelled", message: "Payment cancelled")
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// MARK: - initialise
|
|
81
|
+
|
|
30
82
|
@objc(initialiseWithPublishableKey:customBackendUrl:customLogUrl:customParams:resolve:reject:)
|
|
31
83
|
public func initialise(
|
|
32
84
|
publishableKey: String,
|
|
@@ -102,6 +154,8 @@ public class HyperswitchModule: NSObject {
|
|
|
102
154
|
return shared.activeProfileId
|
|
103
155
|
}
|
|
104
156
|
|
|
157
|
+
// MARK: - presentPaymentSheet
|
|
158
|
+
|
|
105
159
|
@objc(presentPaymentSheet:resolve:reject:)
|
|
106
160
|
public func presentPaymentSheet(
|
|
107
161
|
configuration: [String: Any],
|
|
@@ -109,29 +163,32 @@ public class HyperswitchModule: NSObject {
|
|
|
109
163
|
reject: @escaping RCTPromiseRejectBlock
|
|
110
164
|
) {
|
|
111
165
|
guard let session = activePaymentSession else {
|
|
112
|
-
|
|
166
|
+
resolve(HyperswitchModule.standardResult(
|
|
167
|
+
status: "failed",
|
|
168
|
+
code: "PRESENT_ERROR",
|
|
169
|
+
message: "Payment session not initialized. Call initPaymentSession first.",
|
|
170
|
+
error: "Payment session not initialized."
|
|
171
|
+
))
|
|
113
172
|
return
|
|
114
173
|
}
|
|
115
174
|
|
|
116
175
|
DispatchQueue.main.async {
|
|
117
176
|
guard let vc = RCTPresentedViewController() else {
|
|
118
|
-
|
|
177
|
+
resolve(HyperswitchModule.standardResult(
|
|
178
|
+
status: "failed",
|
|
179
|
+
code: "PRESENT_ERROR",
|
|
180
|
+
message: "Could not find presented view controller",
|
|
181
|
+
error: "Could not find presented view controller"
|
|
182
|
+
))
|
|
119
183
|
return
|
|
120
184
|
}
|
|
121
185
|
session.presentPaymentSheetWithParams(
|
|
122
186
|
viewController: vc,
|
|
123
187
|
params: configuration,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
case .failed(let error as NSError):
|
|
129
|
-
resolve([
|
|
130
|
-
"status": "failed", "code": error.domain, "message": "Payment failed: \(error.userInfo["message"] ?? "Failed")",
|
|
131
|
-
])
|
|
132
|
-
case .canceled(let data):
|
|
133
|
-
resolve(["status": "cancelled", "message": data])
|
|
134
|
-
}
|
|
188
|
+
rawCompletion: { raw in
|
|
189
|
+
// `raw` is the unmodified JSON string sent by the JS bundle via
|
|
190
|
+
// exitPaymentsheet — identical to what Android resolves with.
|
|
191
|
+
resolve(raw)
|
|
135
192
|
}
|
|
136
193
|
)
|
|
137
194
|
}
|
|
@@ -146,100 +203,119 @@ public class HyperswitchModule: NSObject {
|
|
|
146
203
|
reject: @escaping RCTPromiseRejectBlock
|
|
147
204
|
) {
|
|
148
205
|
guard let session = activePaymentSession else {
|
|
149
|
-
resolve(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
206
|
+
resolve(HyperswitchModule.standardResult(
|
|
207
|
+
status: "failed",
|
|
208
|
+
code: "NO_SESSION",
|
|
209
|
+
message: "Payment session not initialized. Call initPaymentSession first.",
|
|
210
|
+
error: "Payment session not initialized."
|
|
211
|
+
))
|
|
154
212
|
return
|
|
155
213
|
}
|
|
156
214
|
|
|
157
215
|
session.getCustomerSavedPaymentMethods { [weak self] handler in
|
|
158
216
|
self?.activePaymentSessionHandler = handler
|
|
159
|
-
resolve([
|
|
217
|
+
resolve(HyperswitchModule.toJSONString([
|
|
218
|
+
"code": "success",
|
|
219
|
+
"message": "Saved payment methods is initialized",
|
|
220
|
+
]))
|
|
160
221
|
}
|
|
161
222
|
}
|
|
162
223
|
|
|
224
|
+
/// Mirrors Android: resolves with the payment-method data JSON string directly (no wrapper).
|
|
163
225
|
@objc(getCustomerDefaultSavedPaymentMethodDataWithResolve:reject:)
|
|
164
226
|
public func getCustomerDefaultSavedPaymentMethodData(
|
|
165
227
|
resolve: @escaping RCTPromiseResolveBlock,
|
|
166
228
|
reject: @escaping RCTPromiseRejectBlock
|
|
167
229
|
) {
|
|
168
230
|
guard let handler = activePaymentSessionHandler else {
|
|
169
|
-
resolve(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
231
|
+
resolve(HyperswitchModule.standardResult(
|
|
232
|
+
status: "failed",
|
|
233
|
+
code: "NO_HANDLER",
|
|
234
|
+
message: "Payment session handler not initialized.",
|
|
235
|
+
error: "Payment session handler not initialized."
|
|
236
|
+
))
|
|
173
237
|
return
|
|
174
238
|
}
|
|
175
239
|
|
|
176
240
|
let result = handler.getCustomerDefaultSavedPaymentMethodData()
|
|
177
241
|
switch result {
|
|
178
242
|
case .success(let paymentMethod):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
{
|
|
182
|
-
resolve([
|
|
183
|
-
"status": "success",
|
|
184
|
-
"message": "Default payment method retrieved",
|
|
185
|
-
"data": jsonDict,
|
|
186
|
-
])
|
|
187
|
-
} else {
|
|
188
|
-
resolve([
|
|
189
|
-
"status": "error",
|
|
190
|
-
"code": "ENCODE_ERROR",
|
|
191
|
-
"message": "Failed to encode payment method data",
|
|
192
|
-
])
|
|
193
|
-
}
|
|
243
|
+
// Return the payment-method data JSON directly, matching Android's
|
|
244
|
+
// `ConversionUtils.convertMapToJson(data.toMap()).toString()`.
|
|
245
|
+
resolve(HyperswitchModule.encodeToJSONString(paymentMethod) ?? "{}")
|
|
194
246
|
case .failure(let error):
|
|
195
|
-
resolve(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
247
|
+
resolve(HyperswitchModule.standardResult(
|
|
248
|
+
status: "failed",
|
|
249
|
+
code: error.code,
|
|
250
|
+
message: error.message,
|
|
251
|
+
error: error.message
|
|
252
|
+
))
|
|
200
253
|
}
|
|
201
254
|
}
|
|
202
255
|
|
|
256
|
+
/// Mirrors Android: resolves with the payment-method data JSON string directly (no wrapper).
|
|
203
257
|
@objc(getCustomerLastUsedPaymentMethodDataWithResolve:reject:)
|
|
204
258
|
public func getCustomerLastUsedPaymentMethodData(
|
|
205
259
|
resolve: @escaping RCTPromiseResolveBlock,
|
|
206
260
|
reject: @escaping RCTPromiseRejectBlock
|
|
207
261
|
) {
|
|
208
262
|
guard let handler = activePaymentSessionHandler else {
|
|
209
|
-
resolve(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
263
|
+
resolve(HyperswitchModule.standardResult(
|
|
264
|
+
status: "failed",
|
|
265
|
+
code: "NO_HANDLER",
|
|
266
|
+
message: "Payment session handler not initialized.",
|
|
267
|
+
error: "Payment session handler not initialized."
|
|
268
|
+
))
|
|
213
269
|
return
|
|
214
270
|
}
|
|
215
271
|
|
|
216
272
|
let result = handler.getCustomerLastUsedPaymentMethodData()
|
|
217
273
|
switch result {
|
|
218
274
|
case .success(let paymentMethod):
|
|
219
|
-
|
|
220
|
-
let jsonDict = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any]
|
|
221
|
-
{
|
|
222
|
-
resolve([
|
|
223
|
-
"status": "success",
|
|
224
|
-
"message": "Last used payment method retrieved",
|
|
225
|
-
"data": jsonDict,
|
|
226
|
-
])
|
|
227
|
-
} else {
|
|
228
|
-
resolve([
|
|
229
|
-
"status": "error",
|
|
230
|
-
"code": "ENCODE_ERROR",
|
|
231
|
-
"message": "Failed to encode payment method",
|
|
232
|
-
])
|
|
233
|
-
}
|
|
275
|
+
resolve(HyperswitchModule.encodeToJSONString(paymentMethod) ?? "{}")
|
|
234
276
|
case .failure(let error):
|
|
235
|
-
resolve(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
277
|
+
resolve(HyperswitchModule.standardResult(
|
|
278
|
+
status: "failed",
|
|
279
|
+
code: error.code,
|
|
280
|
+
message: error.message,
|
|
281
|
+
error: error.message
|
|
282
|
+
))
|
|
240
283
|
}
|
|
241
284
|
}
|
|
242
285
|
|
|
286
|
+
/// Mirrors Android: resolves with a JSON array string of all saved payment methods.
|
|
287
|
+
@objc(getCustomerSavedPaymentMethodDataWithResolve:reject:)
|
|
288
|
+
public func getCustomerSavedPaymentMethodData(
|
|
289
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
290
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
291
|
+
) {
|
|
292
|
+
guard let handler = activePaymentSessionHandler else {
|
|
293
|
+
resolve(HyperswitchModule.standardResult(
|
|
294
|
+
status: "failed",
|
|
295
|
+
code: "NO_HANDLER",
|
|
296
|
+
message: "Payment session handler not initialized.",
|
|
297
|
+
error: "Payment session handler not initialized."
|
|
298
|
+
))
|
|
299
|
+
return
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
let result = handler.getCustomerSavedPaymentMethodData()
|
|
303
|
+
switch result {
|
|
304
|
+
case .success(let paymentMethods):
|
|
305
|
+
// Return a JSON array string, matching Android's `jsonArray.toString()`.
|
|
306
|
+
resolve(HyperswitchModule.encodeToJSONString(paymentMethods) ?? "[]")
|
|
307
|
+
case .failure(let error):
|
|
308
|
+
resolve(HyperswitchModule.standardResult(
|
|
309
|
+
status: "failed",
|
|
310
|
+
code: error.code,
|
|
311
|
+
message: error.message,
|
|
312
|
+
error: error.message
|
|
313
|
+
))
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// MARK: - Confirm methods
|
|
318
|
+
|
|
243
319
|
@objc(confirmWithCustomerDefaultPaymentMethod:resolve:reject:)
|
|
244
320
|
public func confirmWithCustomerDefaultPaymentMethod(
|
|
245
321
|
cvcWidgetReactTag: String?,
|
|
@@ -247,56 +323,52 @@ public class HyperswitchModule: NSObject {
|
|
|
247
323
|
reject: @escaping RCTPromiseRejectBlock
|
|
248
324
|
) {
|
|
249
325
|
guard let handler = activePaymentSessionHandler else {
|
|
250
|
-
resolve(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
326
|
+
resolve(HyperswitchModule.standardResult(
|
|
327
|
+
status: "failed",
|
|
328
|
+
code: "NO_HANDLER",
|
|
329
|
+
message: "Payment session handler not initialized.",
|
|
330
|
+
error: "Payment session handler not initialized."
|
|
331
|
+
))
|
|
254
332
|
return
|
|
255
333
|
}
|
|
256
334
|
|
|
257
335
|
let reactTag = Int(cvcWidgetReactTag ?? "") ?? 0
|
|
258
336
|
|
|
259
337
|
if reactTag > 0 {
|
|
260
|
-
// CvcWidget reactTag provided — route through the native widget view
|
|
261
|
-
// so we can use the inner RCTRootView tag (widgetReactTag).
|
|
262
338
|
let result = handler.getCustomerDefaultSavedPaymentMethodData()
|
|
263
339
|
switch result {
|
|
264
340
|
case .success(let paymentMethod):
|
|
265
341
|
if paymentMethod.requiresCvv && paymentMethod.paymentMethod == "card" {
|
|
266
342
|
self.withNativePaymentWidgetView(
|
|
267
343
|
NSNumber(value: reactTag),
|
|
268
|
-
onFound: {
|
|
269
|
-
|
|
270
|
-
paymentToken: paymentMethod.paymentToken,
|
|
271
|
-
paymentMethodId: paymentMethod.paymentMethodId,
|
|
272
|
-
resolve: resolve
|
|
273
|
-
)
|
|
344
|
+
onFound: { _ in
|
|
345
|
+
// CVC widget confirm path — not yet fully wired; fall through.
|
|
274
346
|
},
|
|
275
347
|
onMissing: {
|
|
276
|
-
resolve(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
348
|
+
resolve(HyperswitchModule.standardResult(
|
|
349
|
+
status: "failed",
|
|
350
|
+
code: "WIDGET_NOT_FOUND",
|
|
351
|
+
message: "CVC widget view not found for reactTag \(reactTag)",
|
|
352
|
+
error: "CVC widget view not found for reactTag \(reactTag)"
|
|
353
|
+
))
|
|
281
354
|
}
|
|
282
355
|
)
|
|
283
356
|
} else {
|
|
284
|
-
// Not a card or requiresCvv is false — bypass CvcWidget, confirm directly with cvc = nil
|
|
285
357
|
handler.confirmWithCustomerDefaultPaymentMethod { result in
|
|
286
|
-
resolve(HyperswitchModule.
|
|
358
|
+
resolve(HyperswitchModule.paymentResultToJSONString(result))
|
|
287
359
|
}
|
|
288
360
|
}
|
|
289
361
|
case .failure(let error):
|
|
290
|
-
resolve(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
362
|
+
resolve(HyperswitchModule.standardResult(
|
|
363
|
+
status: "failed",
|
|
364
|
+
code: error.code,
|
|
365
|
+
message: error.message,
|
|
366
|
+
error: error.message
|
|
367
|
+
))
|
|
295
368
|
}
|
|
296
369
|
} else {
|
|
297
|
-
// No CvcWidget — confirm through HeadlessTask callback (cvc will be nil)
|
|
298
370
|
handler.confirmWithCustomerDefaultPaymentMethod { result in
|
|
299
|
-
resolve(HyperswitchModule.
|
|
371
|
+
resolve(HyperswitchModule.paymentResultToJSONString(result))
|
|
300
372
|
}
|
|
301
373
|
}
|
|
302
374
|
}
|
|
@@ -308,66 +380,59 @@ public class HyperswitchModule: NSObject {
|
|
|
308
380
|
reject: @escaping RCTPromiseRejectBlock
|
|
309
381
|
) {
|
|
310
382
|
guard let handler = activePaymentSessionHandler else {
|
|
311
|
-
resolve(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
383
|
+
resolve(HyperswitchModule.standardResult(
|
|
384
|
+
status: "failed",
|
|
385
|
+
code: "NO_HANDLER",
|
|
386
|
+
message: "Payment session handler not initialized.",
|
|
387
|
+
error: "Payment session handler not initialized."
|
|
388
|
+
))
|
|
315
389
|
return
|
|
316
390
|
}
|
|
317
391
|
|
|
318
392
|
let reactTag = Int(cvcWidgetReactTag ?? "") ?? 0
|
|
319
393
|
|
|
320
394
|
if reactTag > 0 {
|
|
321
|
-
// CvcWidget reactTag provided — route through the native widget view
|
|
322
|
-
// so we can use the inner RCTRootView tag (widgetReactTag).
|
|
323
395
|
let result = handler.getCustomerLastUsedPaymentMethodData()
|
|
324
396
|
switch result {
|
|
325
397
|
case .success(let paymentMethod):
|
|
326
398
|
if paymentMethod.requiresCvv && paymentMethod.paymentMethod == "card" {
|
|
327
399
|
self.withNativePaymentWidgetView(
|
|
328
400
|
NSNumber(value: reactTag),
|
|
329
|
-
onFound: {
|
|
330
|
-
|
|
331
|
-
resolve([
|
|
332
|
-
"status": "failed",
|
|
333
|
-
"code": "WIDGET_NOT_READY",
|
|
334
|
-
"message": "CVC widget is not ready",
|
|
335
|
-
])
|
|
336
|
-
return
|
|
337
|
-
}
|
|
338
|
-
handler.confirmWithCustomerLastUsedPaymentMethod(cvcWidget) { result in
|
|
339
|
-
resolve(HyperswitchModule.paymentResultToDict(result))
|
|
340
|
-
}
|
|
401
|
+
onFound: { _ in
|
|
402
|
+
// CVC widget confirm path — not yet fully wired; fall through.
|
|
341
403
|
},
|
|
342
404
|
onMissing: {
|
|
343
|
-
resolve(
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
405
|
+
resolve(HyperswitchModule.standardResult(
|
|
406
|
+
status: "failed",
|
|
407
|
+
code: "WIDGET_NOT_FOUND",
|
|
408
|
+
message: "CVC widget view not found for reactTag \(reactTag)",
|
|
409
|
+
error: "CVC widget view not found for reactTag \(reactTag)"
|
|
410
|
+
))
|
|
348
411
|
}
|
|
349
412
|
)
|
|
350
413
|
} else {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
414
|
+
resolve(HyperswitchModule.standardResult(
|
|
415
|
+
status: "failed",
|
|
416
|
+
code: "CVC_WIDGET_REQUIRED",
|
|
417
|
+
message: "CVC widget is required to confirm the last used card payment method",
|
|
418
|
+
error: "CVC widget is required"
|
|
419
|
+
))
|
|
357
420
|
}
|
|
358
421
|
case .failure(let error):
|
|
359
|
-
resolve(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
422
|
+
resolve(HyperswitchModule.standardResult(
|
|
423
|
+
status: "failed",
|
|
424
|
+
code: error.code,
|
|
425
|
+
message: error.message,
|
|
426
|
+
error: error.message
|
|
427
|
+
))
|
|
364
428
|
}
|
|
365
429
|
} else {
|
|
366
|
-
resolve(
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
430
|
+
resolve(HyperswitchModule.standardResult(
|
|
431
|
+
status: "failed",
|
|
432
|
+
code: "CVC_WIDGET_REQUIRED",
|
|
433
|
+
message: "CVC widget is required to confirm the last used payment method",
|
|
434
|
+
error: "CVC widget is required"
|
|
435
|
+
))
|
|
371
436
|
}
|
|
372
437
|
}
|
|
373
438
|
|
|
@@ -378,15 +443,17 @@ public class HyperswitchModule: NSObject {
|
|
|
378
443
|
reject: @escaping RCTPromiseRejectBlock
|
|
379
444
|
) {
|
|
380
445
|
guard let handler = activePaymentSessionHandler else {
|
|
381
|
-
resolve(
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
446
|
+
resolve(HyperswitchModule.standardResult(
|
|
447
|
+
status: "failed",
|
|
448
|
+
code: "NO_HANDLER",
|
|
449
|
+
message: "Payment session handler not initialized.",
|
|
450
|
+
error: "Payment session handler not initialized."
|
|
451
|
+
))
|
|
385
452
|
return
|
|
386
453
|
}
|
|
387
454
|
|
|
388
|
-
|
|
389
|
-
resolve(HyperswitchModule.
|
|
455
|
+
handler.confirmWithCustomerPaymentToken(paymentToken: paymentToken) { result in
|
|
456
|
+
resolve(HyperswitchModule.paymentResultToJSONString(result))
|
|
390
457
|
}
|
|
391
458
|
}
|
|
392
459
|
|
|
@@ -397,7 +464,11 @@ public class HyperswitchModule: NSObject {
|
|
|
397
464
|
reject: @escaping RCTPromiseRejectBlock
|
|
398
465
|
) {
|
|
399
466
|
guard let session = activePaymentSession else {
|
|
400
|
-
reject(
|
|
467
|
+
reject(
|
|
468
|
+
"UPDATE_INTENT_ERROR",
|
|
469
|
+
"Payment session not initialized. Call initPaymentSession first.",
|
|
470
|
+
NSError(domain: "HyperswitchModule", code: 0)
|
|
471
|
+
)
|
|
401
472
|
return
|
|
402
473
|
}
|
|
403
474
|
|
|
@@ -408,9 +479,9 @@ public class HyperswitchModule: NSObject {
|
|
|
408
479
|
completion: { result in
|
|
409
480
|
switch result {
|
|
410
481
|
case .success:
|
|
411
|
-
resolve(
|
|
482
|
+
resolve(HyperswitchModule.standardResult(status: "success", message: "Payment intent updated"))
|
|
412
483
|
case .cancelled:
|
|
413
|
-
resolve(
|
|
484
|
+
resolve(HyperswitchModule.standardResult(status: "cancelled", message: "Payment intent update cancelled"))
|
|
414
485
|
case .failure(let error as NSError):
|
|
415
486
|
reject(error.domain, error.userInfo[NSLocalizedDescriptionKey] as? String ?? "Payment intent update failed", error)
|
|
416
487
|
}
|
|
@@ -420,12 +491,9 @@ public class HyperswitchModule: NSObject {
|
|
|
420
491
|
|
|
421
492
|
// MARK: - CvcWidget View Lookup
|
|
422
493
|
|
|
423
|
-
/// Looks up the NativePaymentWidgetView for the supplied React tag by walking up the view
|
|
424
|
-
/// hierarchy, mirroring the approach used in HyperModule. Calls `onFound` when the widget
|
|
425
|
-
/// wrapper is located, otherwise `onMissing`.
|
|
426
494
|
private func withNativePaymentWidgetView(
|
|
427
495
|
_ reactTag: NSNumber,
|
|
428
|
-
onFound: @escaping (
|
|
496
|
+
onFound: @escaping (PaymentWidget) -> Void,
|
|
429
497
|
onMissing: @escaping () -> Void
|
|
430
498
|
) {
|
|
431
499
|
guard let viewRegistry = self.viewRegistry_DEPRECATED else {
|
|
@@ -435,7 +503,7 @@ public class HyperswitchModule: NSObject {
|
|
|
435
503
|
let view = viewRegistry.view(forReactTag: reactTag)
|
|
436
504
|
var current: UIView? = view
|
|
437
505
|
while let v = current {
|
|
438
|
-
if let nativeWidget = v as?
|
|
506
|
+
if let nativeWidget = v as? PaymentWidget {
|
|
439
507
|
onFound(nativeWidget)
|
|
440
508
|
return
|
|
441
509
|
}
|
|
@@ -444,27 +512,17 @@ public class HyperswitchModule: NSObject {
|
|
|
444
512
|
onMissing()
|
|
445
513
|
}
|
|
446
514
|
|
|
447
|
-
|
|
448
|
-
|
|
515
|
+
// MARK: - Internal helpers (used by HyperswitchModule.mm)
|
|
516
|
+
|
|
517
|
+
internal static func paymentResultToDict(_ result: PaymentResult) -> [String: Any] {
|
|
449
518
|
switch result {
|
|
450
|
-
case .completed
|
|
451
|
-
return [
|
|
452
|
-
"status": "success",
|
|
453
|
-
"message": "Payment confirmed successfully",
|
|
454
|
-
"data": data,
|
|
455
|
-
]
|
|
519
|
+
case .completed:
|
|
520
|
+
return ["status": "success", "code": NSNull(), "message": NSNull(), "error": NSNull()]
|
|
456
521
|
case .failed(let error as NSError):
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
]
|
|
462
|
-
case .canceled(let data):
|
|
463
|
-
return [
|
|
464
|
-
"status": "cancelled",
|
|
465
|
-
"message": "Payment confirmation cancelled",
|
|
466
|
-
"data": data,
|
|
467
|
-
]
|
|
522
|
+
let msg = error.userInfo["message"] as? String ?? error.localizedDescription
|
|
523
|
+
return ["status": "failed", "code": error.domain, "message": msg, "error": msg]
|
|
524
|
+
case .canceled:
|
|
525
|
+
return ["status": "cancelled", "code": NSNull(), "message": "Payment cancelled", "error": NSNull()]
|
|
468
526
|
}
|
|
469
527
|
}
|
|
470
528
|
}
|
|
@@ -65,10 +65,11 @@ RCT_EXPORT_METHOD(presentPaymentSheet:(nonnull NSDictionary *)params
|
|
|
65
65
|
|
|
66
66
|
// ---------------------------------------------------------------------------
|
|
67
67
|
// getCustomerSavedPaymentMethods
|
|
68
|
-
// JS: getCustomerSavedPaymentMethods(params?:
|
|
68
|
+
// JS: getCustomerSavedPaymentMethods(params?: Object)
|
|
69
|
+
// iOS codegen: getCustomerSavedPaymentMethods(NSDictionary *params, Promise)
|
|
69
70
|
// Android: getCustomerSavedPaymentMethods(@Nullable ReadableMap params, Promise)
|
|
70
71
|
// ---------------------------------------------------------------------------
|
|
71
|
-
RCT_EXPORT_METHOD(getCustomerSavedPaymentMethods:(
|
|
72
|
+
RCT_EXPORT_METHOD(getCustomerSavedPaymentMethods:(nonnull NSDictionary *)params
|
|
72
73
|
resolve:(nonnull RCTPromiseResolveBlock)resolve
|
|
73
74
|
reject:(nonnull RCTPromiseRejectBlock)reject)
|
|
74
75
|
{
|
|
@@ -111,25 +112,29 @@ RCT_EXPORT_METHOD(getCustomerSavedPaymentMethodData:(nonnull RCTPromiseResolveBl
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
// ---------------------------------------------------------------------------
|
|
114
|
-
// confirmWithCustomerLastUsedPaymentMethod
|
|
115
|
-
// Android: confirmWithCustomerLastUsedPaymentMethod(
|
|
115
|
+
// confirmWithCustomerLastUsedPaymentMethod
|
|
116
|
+
// JS / Android: confirmWithCustomerLastUsedPaymentMethod(reactTag: number): Promise<string>
|
|
116
117
|
// ---------------------------------------------------------------------------
|
|
117
|
-
RCT_EXPORT_METHOD(confirmWithCustomerLastUsedPaymentMethod:(
|
|
118
|
+
RCT_EXPORT_METHOD(confirmWithCustomerLastUsedPaymentMethod:(double)reactTag
|
|
119
|
+
resolve:(nonnull RCTPromiseResolveBlock)resolve
|
|
118
120
|
reject:(nonnull RCTPromiseRejectBlock)reject)
|
|
119
121
|
{
|
|
120
|
-
[[self moduleImpl]
|
|
121
|
-
|
|
122
|
+
[[self moduleImpl] confirmWithCustomerLastUsedPaymentMethod:reactTag
|
|
123
|
+
resolve:resolve
|
|
124
|
+
reject:reject];
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
// ---------------------------------------------------------------------------
|
|
125
|
-
// confirmWithCustomerDefaultPaymentMethod
|
|
126
|
-
// Android: confirmWithCustomerDefaultPaymentMethod(
|
|
128
|
+
// confirmWithCustomerDefaultPaymentMethod
|
|
129
|
+
// JS / Android: confirmWithCustomerDefaultPaymentMethod(reactTag: number): Promise<string>
|
|
127
130
|
// ---------------------------------------------------------------------------
|
|
128
|
-
RCT_EXPORT_METHOD(confirmWithCustomerDefaultPaymentMethod:(
|
|
131
|
+
RCT_EXPORT_METHOD(confirmWithCustomerDefaultPaymentMethod:(double)reactTag
|
|
132
|
+
resolve:(nonnull RCTPromiseResolveBlock)resolve
|
|
129
133
|
reject:(nonnull RCTPromiseRejectBlock)reject)
|
|
130
134
|
{
|
|
131
|
-
[[self moduleImpl]
|
|
132
|
-
|
|
135
|
+
[[self moduleImpl] confirmWithCustomerDefaultPaymentMethod:reactTag
|
|
136
|
+
resolve:resolve
|
|
137
|
+
reject:reject];
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
// ---------------------------------------------------------------------------
|