@yuno-payments/yuno-sdk-react-native 1.0.17-rc.1 → 1.0.17-rc.11
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/android/src/main/java/com/yunosdkreactnative/YunoSdkModule.kt +294 -0
- package/ios/YunoSdk.m +25 -0
- package/ios/YunoSdk.swift +175 -0
- package/lib/commonjs/YunoPaymentMethods.js.map +1 -1
- package/lib/commonjs/YunoSdk.js +131 -4
- 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.map +1 -1
- package/lib/module/YunoSdk.js +131 -4
- 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 +5 -5
- package/src/YunoSdk.ts +163 -6
- package/src/core/types/HeadlessTypes.ts +110 -0
- package/src/core/types/OneTimeTokenInfo.ts +0 -1
- package/src/core/types/index.ts +17 -0
|
@@ -11,12 +11,26 @@ import com.yuno.sdk.YunoConfig
|
|
|
11
11
|
import com.yuno.sdk.YunoLanguage
|
|
12
12
|
import com.yuno.sdk.enrollment.*
|
|
13
13
|
import com.yuno.sdk.payments.*
|
|
14
|
+
import com.yuno.sdk.ApiClientPayment
|
|
15
|
+
import com.yuno.sdk.ApiClientPayment.Companion.generateToken
|
|
16
|
+
import com.yuno.sdk.ApiClientPayment.Companion.getThreeDSecureChallenge
|
|
17
|
+
import com.yuno.sdk.ApiClientEnroll
|
|
18
|
+
import com.yuno.sdk.ApiClientEnroll.Companion.continueEnrollment
|
|
19
|
+
import com.yuno.sdk.payments.TokenCollectedData
|
|
20
|
+
import com.yuno.sdk.enrollment.EnrollmentCollectedData
|
|
21
|
+
import com.yuno.sdk.ThreeDSecureChallengeResponse
|
|
14
22
|
import com.yuno.presentation.core.components.PaymentSelected
|
|
15
23
|
import com.yuno.presentation.core.card.CardFormType
|
|
16
24
|
import com.yuno.payments.features.payment.models.OneTimeTokenModel
|
|
17
25
|
import com.google.gson.Gson
|
|
18
26
|
import org.json.JSONObject
|
|
19
27
|
import org.json.JSONArray
|
|
28
|
+
import kotlinx.coroutines.CoroutineScope
|
|
29
|
+
import kotlinx.coroutines.Dispatchers
|
|
30
|
+
import kotlinx.coroutines.flow.launchIn
|
|
31
|
+
import kotlinx.coroutines.flow.onEach
|
|
32
|
+
import kotlinx.coroutines.flow.catch
|
|
33
|
+
import androidx.lifecycle.asFlow
|
|
20
34
|
|
|
21
35
|
/**
|
|
22
36
|
* Yuno SDK React Native Module for Android
|
|
@@ -821,4 +835,284 @@ class YunoSdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
821
835
|
}
|
|
822
836
|
return array
|
|
823
837
|
}
|
|
838
|
+
|
|
839
|
+
// ==================== HEADLESS PAYMENT FLOW ====================
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* Generate a one-time token (OTT) from collected payment data using the headless flow.
|
|
843
|
+
* This method mirrors the native SDK's generateToken() API.
|
|
844
|
+
*
|
|
845
|
+
* @param tokenCollectedData Map containing checkout_session, customer_session, and payment_method data
|
|
846
|
+
* @param checkoutSession The checkout session ID
|
|
847
|
+
* @param countryCode The country code for the payment
|
|
848
|
+
* @param promise Promise to resolve with token or error
|
|
849
|
+
*/
|
|
850
|
+
@ReactMethod
|
|
851
|
+
fun generateToken(
|
|
852
|
+
tokenCollectedData: ReadableMap,
|
|
853
|
+
checkoutSession: String,
|
|
854
|
+
countryCode: String,
|
|
855
|
+
promise: Promise
|
|
856
|
+
) {
|
|
857
|
+
try {
|
|
858
|
+
Log.d(TAG, "generateToken called with checkoutSession: $checkoutSession")
|
|
859
|
+
|
|
860
|
+
val activity = currentActivity
|
|
861
|
+
if (activity == null) {
|
|
862
|
+
promise.reject("ACTIVITY_UNAVAILABLE", "Current activity is null. Cannot generate token.")
|
|
863
|
+
return
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Convert ReadableMap to TokenCollectedData using Gson
|
|
867
|
+
val gson = Gson()
|
|
868
|
+
val jsonString = convertReadableMapToJson(tokenCollectedData)
|
|
869
|
+
Log.d(TAG, "JSON String: $jsonString")
|
|
870
|
+
val collectedData = gson.fromJson(jsonString, TokenCollectedData::class.java)
|
|
871
|
+
Log.d(TAG, "Parsed TokenCollectedData: checkoutSession=${collectedData.checkoutSession}, paymentMethod=${collectedData.paymentMethod}")
|
|
872
|
+
|
|
873
|
+
// Create API client
|
|
874
|
+
val apiClient = Yuno.apiClientPayment(
|
|
875
|
+
checkoutSession = checkoutSession,
|
|
876
|
+
countryCode = countryCode,
|
|
877
|
+
context = activity.applicationContext
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
// Generate token - pass activity for WebView context
|
|
881
|
+
apiClient.generateToken(collectedData, activity)
|
|
882
|
+
.asFlow()
|
|
883
|
+
.onEach { result ->
|
|
884
|
+
try {
|
|
885
|
+
when {
|
|
886
|
+
result.containsKey("token") && result["token"] != null -> {
|
|
887
|
+
val token = result["token"] as String
|
|
888
|
+
Log.d(TAG, "✅ Token generated successfully")
|
|
889
|
+
|
|
890
|
+
val response = Arguments.createMap().apply {
|
|
891
|
+
putString("token", token)
|
|
892
|
+
}
|
|
893
|
+
promise.resolve(response)
|
|
894
|
+
}
|
|
895
|
+
result.containsKey("error") && result["error"] != null -> {
|
|
896
|
+
val error = result["error"] as String
|
|
897
|
+
Log.e(TAG, "❌ Token generation failed: $error")
|
|
898
|
+
promise.reject("TOKEN_GENERATION_ERROR", error)
|
|
899
|
+
}
|
|
900
|
+
else -> {
|
|
901
|
+
Log.e(TAG, "❌ Unknown response from token generation")
|
|
902
|
+
promise.reject("TOKEN_GENERATION_ERROR", "Unknown error occurred")
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
} catch (e: Exception) {
|
|
906
|
+
Log.e(TAG, "❌ Error processing token generation result: ${e.message}")
|
|
907
|
+
promise.reject("TOKEN_GENERATION_ERROR", e.message)
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
.catch { e ->
|
|
911
|
+
Log.e(TAG, "❌ Flow error in token generation: ${e.message}")
|
|
912
|
+
promise.reject("TOKEN_GENERATION_ERROR", e.message ?: "Unknown error in token generation flow")
|
|
913
|
+
}
|
|
914
|
+
.launchIn(CoroutineScope(Dispatchers.Main))
|
|
915
|
+
} catch (e: Exception) {
|
|
916
|
+
Log.e(TAG, "❌ Error in generateToken: ${e.message}")
|
|
917
|
+
promise.reject("TOKEN_GENERATION_ERROR", e.message)
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Get the 3D Secure challenge URL for a checkout session.
|
|
923
|
+
* This method mirrors the native SDK's getThreeDSecureChallenge() API.
|
|
924
|
+
*
|
|
925
|
+
* @param checkoutSession The checkout session ID
|
|
926
|
+
* @param countryCode The country code for the payment
|
|
927
|
+
* @param promise Promise to resolve with URL or error
|
|
928
|
+
*/
|
|
929
|
+
@ReactMethod
|
|
930
|
+
fun getThreeDSecureChallenge(
|
|
931
|
+
checkoutSession: String,
|
|
932
|
+
countryCode: String,
|
|
933
|
+
promise: Promise
|
|
934
|
+
) {
|
|
935
|
+
try {
|
|
936
|
+
Log.d(TAG, "getThreeDSecureChallenge called with checkoutSession: $checkoutSession")
|
|
937
|
+
|
|
938
|
+
val activity = currentActivity
|
|
939
|
+
if (activity == null) {
|
|
940
|
+
promise.reject("ACTIVITY_UNAVAILABLE", "Current activity is null. Cannot get 3DS challenge.")
|
|
941
|
+
return
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Create API client
|
|
945
|
+
val apiClient = Yuno.apiClientPayment(
|
|
946
|
+
checkoutSession = checkoutSession,
|
|
947
|
+
countryCode = countryCode,
|
|
948
|
+
context = activity.applicationContext
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
// Get 3DS challenge - pass activity for WebView context
|
|
952
|
+
apiClient.getThreeDSecureChallenge(activity, checkoutSession)
|
|
953
|
+
.asFlow()
|
|
954
|
+
.onEach { result ->
|
|
955
|
+
try {
|
|
956
|
+
val response = Arguments.createMap().apply {
|
|
957
|
+
putString("type", result.type)
|
|
958
|
+
putString("data", result.data)
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (result.type == "URL") {
|
|
962
|
+
Log.d(TAG, "✅ 3DS Challenge URL retrieved successfully")
|
|
963
|
+
promise.resolve(response)
|
|
964
|
+
} else {
|
|
965
|
+
Log.e(TAG, "❌ 3DS Challenge failed: ${result.data}")
|
|
966
|
+
promise.reject("THREE_DS_ERROR", result.data)
|
|
967
|
+
}
|
|
968
|
+
} catch (e: Exception) {
|
|
969
|
+
Log.e(TAG, "❌ Error processing 3DS challenge result: ${e.message}")
|
|
970
|
+
promise.reject("THREE_DS_ERROR", e.message)
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
.catch { e ->
|
|
974
|
+
Log.e(TAG, "❌ Flow error in 3DS challenge: ${e.message}")
|
|
975
|
+
promise.reject("THREE_DS_ERROR", e.message ?: "Unknown error in 3DS challenge flow")
|
|
976
|
+
}
|
|
977
|
+
.launchIn(CoroutineScope(Dispatchers.Main))
|
|
978
|
+
} catch (e: Exception) {
|
|
979
|
+
Log.e(TAG, "❌ Error in getThreeDSecureChallenge: ${e.message}")
|
|
980
|
+
promise.reject("THREE_DS_ERROR", e.message)
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// ==================== HEADLESS ENROLLMENT FLOW ====================
|
|
985
|
+
|
|
986
|
+
@ReactMethod
|
|
987
|
+
fun continueEnrollment(
|
|
988
|
+
enrollmentCollectedData: ReadableMap,
|
|
989
|
+
customerSession: String,
|
|
990
|
+
countryCode: String,
|
|
991
|
+
promise: Promise
|
|
992
|
+
) {
|
|
993
|
+
try {
|
|
994
|
+
Log.d(TAG, "continueEnrollment called with customerSession: $customerSession")
|
|
995
|
+
|
|
996
|
+
val activity = currentActivity
|
|
997
|
+
if (activity == null) {
|
|
998
|
+
promise.reject("ACTIVITY_UNAVAILABLE", "Current activity is null. Cannot continue enrollment.")
|
|
999
|
+
return
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// Convert ReadableMap to JSON String
|
|
1003
|
+
val jsonString = convertReadableMapToJson(enrollmentCollectedData)
|
|
1004
|
+
Log.d(TAG, "📦 EnrollmentCollectedData JSON received: $jsonString")
|
|
1005
|
+
|
|
1006
|
+
// Convert JSON String to EnrollmentCollectedData using Gson
|
|
1007
|
+
val gson = Gson()
|
|
1008
|
+
val collectedData = gson.fromJson(jsonString, EnrollmentCollectedData::class.java)
|
|
1009
|
+
|
|
1010
|
+
// Create API client for enrollment
|
|
1011
|
+
val apiClient = Yuno.apiClientEnroll(
|
|
1012
|
+
customerSession = customerSession,
|
|
1013
|
+
countryCode = countryCode,
|
|
1014
|
+
context = activity
|
|
1015
|
+
)
|
|
1016
|
+
|
|
1017
|
+
// Continue enrollment
|
|
1018
|
+
apiClient.continueEnrollment(collectedData, activity)
|
|
1019
|
+
.asFlow()
|
|
1020
|
+
.onEach { result ->
|
|
1021
|
+
try {
|
|
1022
|
+
when {
|
|
1023
|
+
result.containsKey("vaulted_token") && result["vaulted_token"] != null -> {
|
|
1024
|
+
val vaultedToken = result["vaulted_token"] as String
|
|
1025
|
+
Log.d(TAG, "✅ Vaulted token created successfully")
|
|
1026
|
+
|
|
1027
|
+
val response = Arguments.createMap().apply {
|
|
1028
|
+
putString("vaultedToken", vaultedToken)
|
|
1029
|
+
}
|
|
1030
|
+
promise.resolve(response)
|
|
1031
|
+
}
|
|
1032
|
+
result.containsKey("error") && result["error"] != null -> {
|
|
1033
|
+
val error = result["error"] as String
|
|
1034
|
+
Log.e(TAG, "❌ Enrollment failed: $error")
|
|
1035
|
+
promise.reject("ENROLLMENT_ERROR", error)
|
|
1036
|
+
}
|
|
1037
|
+
else -> {
|
|
1038
|
+
Log.e(TAG, "❌ Unknown response from enrollment")
|
|
1039
|
+
promise.reject("ENROLLMENT_ERROR", "Unknown error occurred")
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
} catch (e: Exception) {
|
|
1043
|
+
Log.e(TAG, "❌ Error processing enrollment result: ${e.message}")
|
|
1044
|
+
promise.reject("ENROLLMENT_ERROR", e.message)
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
.catch { e ->
|
|
1048
|
+
Log.e(TAG, "❌ Flow error during enrollment: ${e.message}")
|
|
1049
|
+
promise.reject("ENROLLMENT_FLOW_ERROR", e.message)
|
|
1050
|
+
}
|
|
1051
|
+
.launchIn(CoroutineScope(Dispatchers.Main))
|
|
1052
|
+
} catch (e: Exception) {
|
|
1053
|
+
Log.e(TAG, "❌ Error in continueEnrollment: ${e.message}")
|
|
1054
|
+
promise.reject("ENROLLMENT_ERROR", e.message)
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* Helper function to convert ReadableMap to JSON string.
|
|
1060
|
+
*/
|
|
1061
|
+
private fun convertReadableMapToJson(readableMap: ReadableMap): String {
|
|
1062
|
+
val json = JSONObject()
|
|
1063
|
+
val iterator = readableMap.keySetIterator()
|
|
1064
|
+
|
|
1065
|
+
while (iterator.hasNextKey()) {
|
|
1066
|
+
val key = iterator.nextKey()
|
|
1067
|
+
val value = when (readableMap.getType(key)) {
|
|
1068
|
+
ReadableType.Boolean -> readableMap.getBoolean(key)
|
|
1069
|
+
ReadableType.Number -> readableMap.getDouble(key)
|
|
1070
|
+
ReadableType.String -> readableMap.getString(key)
|
|
1071
|
+
ReadableType.Map -> convertReadableMapToJsonObject(readableMap.getMap(key)!!)
|
|
1072
|
+
ReadableType.Array -> convertReadableArrayToJsonArray(readableMap.getArray(key)!!)
|
|
1073
|
+
ReadableType.Null -> JSONObject.NULL
|
|
1074
|
+
}
|
|
1075
|
+
json.put(key, value)
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
return json.toString()
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
private fun convertReadableMapToJsonObject(readableMap: ReadableMap): JSONObject {
|
|
1082
|
+
val json = JSONObject()
|
|
1083
|
+
val iterator = readableMap.keySetIterator()
|
|
1084
|
+
|
|
1085
|
+
while (iterator.hasNextKey()) {
|
|
1086
|
+
val key = iterator.nextKey()
|
|
1087
|
+
val value = when (readableMap.getType(key)) {
|
|
1088
|
+
ReadableType.Boolean -> readableMap.getBoolean(key)
|
|
1089
|
+
ReadableType.Number -> readableMap.getDouble(key)
|
|
1090
|
+
ReadableType.String -> readableMap.getString(key)
|
|
1091
|
+
ReadableType.Map -> convertReadableMapToJsonObject(readableMap.getMap(key)!!)
|
|
1092
|
+
ReadableType.Array -> convertReadableArrayToJsonArray(readableMap.getArray(key)!!)
|
|
1093
|
+
ReadableType.Null -> JSONObject.NULL
|
|
1094
|
+
}
|
|
1095
|
+
json.put(key, value)
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
return json
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
private fun convertReadableArrayToJsonArray(readableArray: ReadableArray): JSONArray {
|
|
1102
|
+
val json = JSONArray()
|
|
1103
|
+
|
|
1104
|
+
for (i in 0 until readableArray.size()) {
|
|
1105
|
+
val value = when (readableArray.getType(i)) {
|
|
1106
|
+
ReadableType.Boolean -> readableArray.getBoolean(i)
|
|
1107
|
+
ReadableType.Number -> readableArray.getDouble(i)
|
|
1108
|
+
ReadableType.String -> readableArray.getString(i)
|
|
1109
|
+
ReadableType.Map -> convertReadableMapToJsonObject(readableArray.getMap(i))
|
|
1110
|
+
ReadableType.Array -> convertReadableArrayToJsonArray(readableArray.getArray(i))
|
|
1111
|
+
ReadableType.Null -> JSONObject.NULL
|
|
1112
|
+
}
|
|
1113
|
+
json.put(value)
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
return json
|
|
1117
|
+
}
|
|
824
1118
|
}
|
package/ios/YunoSdk.m
CHANGED
|
@@ -82,5 +82,30 @@ RCT_EXTERN_METHOD(addListener:(NSString *)eventName)
|
|
|
82
82
|
|
|
83
83
|
RCT_EXTERN_METHOD(removeListeners:(double)count)
|
|
84
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
|
+
)
|
|
100
|
+
|
|
101
|
+
// Headless Enrollment Flow
|
|
102
|
+
RCT_EXTERN_METHOD(
|
|
103
|
+
continueEnrollment:(NSDictionary *)enrollmentCollectedData
|
|
104
|
+
customerSession:(NSString *)customerSession
|
|
105
|
+
countryCode:(NSString *)countryCode
|
|
106
|
+
resolver:(RCTPromiseResolveBlock)resolver
|
|
107
|
+
rejecter:(RCTPromiseRejectBlock)rejecter
|
|
108
|
+
)
|
|
109
|
+
|
|
85
110
|
@end
|
|
86
111
|
|
package/ios/YunoSdk.swift
CHANGED
|
@@ -559,3 +559,178 @@ extension YunoSdk: YunoEnrollmentDelegate {
|
|
|
559
559
|
}
|
|
560
560
|
}
|
|
561
561
|
|
|
562
|
+
// MARK: - Headless Payment Flow
|
|
563
|
+
extension YunoSdk {
|
|
564
|
+
/**
|
|
565
|
+
* Generate a one-time token (OTT) from collected payment data using the headless flow.
|
|
566
|
+
* This method mirrors the native SDK's generateToken() API.
|
|
567
|
+
*
|
|
568
|
+
* @param tokenCollectedData Dictionary containing checkout_session and payment_method data
|
|
569
|
+
* @param checkoutSession The checkout session ID
|
|
570
|
+
* @param countryCode The country code for the payment
|
|
571
|
+
* @param resolver Promise resolver
|
|
572
|
+
* @param rejecter Promise rejecter
|
|
573
|
+
*/
|
|
574
|
+
@objc
|
|
575
|
+
func generateToken(
|
|
576
|
+
_ tokenCollectedData: NSDictionary,
|
|
577
|
+
checkoutSession: String,
|
|
578
|
+
countryCode: String,
|
|
579
|
+
resolver: @escaping RCTPromiseResolveBlock,
|
|
580
|
+
rejecter: @escaping RCTPromiseRejectBlock
|
|
581
|
+
) {
|
|
582
|
+
Task { @MainActor [weak self] in
|
|
583
|
+
guard let self = self else { return }
|
|
584
|
+
|
|
585
|
+
do {
|
|
586
|
+
// Convert NSDictionary to JSON Data
|
|
587
|
+
guard JSONSerialization.isValidJSONObject(tokenCollectedData) else {
|
|
588
|
+
rejecter("INVALID_DATA", "Invalid token collected data", nil)
|
|
589
|
+
return
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
let jsonData = try JSONSerialization.data(withJSONObject: tokenCollectedData, options: [])
|
|
593
|
+
|
|
594
|
+
// Debug: Print the JSON string
|
|
595
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
596
|
+
print("🐛 iOS generateToken JSON: \(jsonString)")
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Decode to TokenCollectedData using the native SDK's Codable model
|
|
600
|
+
// Note: Don't use .convertFromSnakeCase because the SDK's models have custom decoders
|
|
601
|
+
// that already handle snake_case field names (checkout_session, payment_method, etc)
|
|
602
|
+
let decoder = JSONDecoder()
|
|
603
|
+
let collectedData = try decoder.decode(TokenCollectedData.self, from: jsonData)
|
|
604
|
+
|
|
605
|
+
// Create API client using the native SDK's factory method
|
|
606
|
+
let apiClient = Yuno.apiClientPayment(
|
|
607
|
+
countryCode: countryCode,
|
|
608
|
+
checkoutSession: checkoutSession
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
// Generate token using async/await (native SDK uses async throws)
|
|
612
|
+
let response = try await apiClient.generateToken(data: collectedData)
|
|
613
|
+
|
|
614
|
+
// The native SDK returns [String: Any] with token in "token" key
|
|
615
|
+
if let token = response["token"] as? String {
|
|
616
|
+
let responseDict: [String: Any] = ["token": token]
|
|
617
|
+
resolver(responseDict)
|
|
618
|
+
} else if let error = response["error"] as? String {
|
|
619
|
+
rejecter("TOKEN_GENERATION_ERROR", error, nil)
|
|
620
|
+
} else {
|
|
621
|
+
rejecter("TOKEN_GENERATION_ERROR", "No token in response", nil)
|
|
622
|
+
}
|
|
623
|
+
} catch {
|
|
624
|
+
rejecter("TOKEN_GENERATION_ERROR", "Failed to generate token: \(error.localizedDescription)", error)
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Get the 3D Secure challenge URL for a checkout session.
|
|
631
|
+
* This method mirrors the native SDK's getThreeDSecureChallenge() API.
|
|
632
|
+
*
|
|
633
|
+
* @param checkoutSession The checkout session ID
|
|
634
|
+
* @param countryCode The country code for the payment
|
|
635
|
+
* @param resolver Promise resolver
|
|
636
|
+
* @param rejecter Promise rejecter
|
|
637
|
+
*/
|
|
638
|
+
@objc
|
|
639
|
+
func getThreeDSecureChallenge(
|
|
640
|
+
_ checkoutSession: String,
|
|
641
|
+
countryCode: String,
|
|
642
|
+
resolver: @escaping RCTPromiseResolveBlock,
|
|
643
|
+
rejecter: @escaping RCTPromiseRejectBlock
|
|
644
|
+
) {
|
|
645
|
+
Task { @MainActor [weak self] in
|
|
646
|
+
guard let self = self else { return }
|
|
647
|
+
|
|
648
|
+
do {
|
|
649
|
+
// Create API client using the native SDK's factory method
|
|
650
|
+
let apiClient = Yuno.apiClientPayment(
|
|
651
|
+
countryCode: countryCode,
|
|
652
|
+
checkoutSession: checkoutSession
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
// Get 3DS challenge using async/await (native SDK uses async throws)
|
|
656
|
+
let response = try await apiClient.getThreeDSecureChallenge(checkoutSession: checkoutSession)
|
|
657
|
+
|
|
658
|
+
// The native SDK returns ThreeDSecureChallengeResponse with url property
|
|
659
|
+
let responseDict: [String: Any] = [
|
|
660
|
+
"type": "URL",
|
|
661
|
+
"data": response.url
|
|
662
|
+
]
|
|
663
|
+
|
|
664
|
+
resolver(responseDict)
|
|
665
|
+
} catch {
|
|
666
|
+
rejecter("THREE_DS_ERROR", "Failed to get 3DS challenge: \(error.localizedDescription)", error)
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// MARK: - Headless Enrollment Flow
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Continue enrollment to create a vaulted token from collected payment data.
|
|
675
|
+
* This method mirrors the native SDK's continueEnrollment() API.
|
|
676
|
+
*
|
|
677
|
+
* @param enrollmentCollectedData The enrollment data as NSDictionary
|
|
678
|
+
* @param customerSession The customer session ID
|
|
679
|
+
* @param countryCode The country code for the enrollment
|
|
680
|
+
* @param resolver Promise resolver
|
|
681
|
+
* @param rejecter Promise rejecter
|
|
682
|
+
*/
|
|
683
|
+
@objc
|
|
684
|
+
func continueEnrollment(
|
|
685
|
+
_ enrollmentCollectedData: NSDictionary,
|
|
686
|
+
customerSession: String,
|
|
687
|
+
countryCode: String,
|
|
688
|
+
resolver: @escaping RCTPromiseResolveBlock,
|
|
689
|
+
rejecter: @escaping RCTPromiseRejectBlock
|
|
690
|
+
) {
|
|
691
|
+
Task { @MainActor [weak self] in
|
|
692
|
+
guard let self = self else { return }
|
|
693
|
+
|
|
694
|
+
do {
|
|
695
|
+
// Convert NSDictionary to JSON Data
|
|
696
|
+
guard JSONSerialization.isValidJSONObject(enrollmentCollectedData) else {
|
|
697
|
+
rejecter("INVALID_DATA", "Invalid enrollment collected data", nil)
|
|
698
|
+
return
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
let jsonData = try JSONSerialization.data(withJSONObject: enrollmentCollectedData, options: [])
|
|
702
|
+
Log.d(TAG, "📦 EnrollmentCollectedData JSON sent to iOS SDK: \(String(data: jsonData, encoding: .utf8) ?? "Invalid JSON")")
|
|
703
|
+
|
|
704
|
+
// Decode to EnrollmentCollectedData using the native SDK's Codable model
|
|
705
|
+
let decoder = JSONDecoder()
|
|
706
|
+
let collectedData = try decoder.decode(EnrollmentCollectedData.self, from: jsonData)
|
|
707
|
+
|
|
708
|
+
// Create API client using the native SDK's factory method
|
|
709
|
+
let apiClient = Yuno.apiClientEnroll(
|
|
710
|
+
countryCode: countryCode,
|
|
711
|
+
customerSession: customerSession
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
// Continue enrollment using async/await (native SDK uses async throws)
|
|
715
|
+
let response = try await apiClient.continueEnrollment(data: collectedData)
|
|
716
|
+
|
|
717
|
+
// The native SDK returns [String: Any] with vaulted_token in "vaulted_token" key
|
|
718
|
+
if let vaultedToken = response["vaulted_token"] as? String {
|
|
719
|
+
let responseDict: [String: Any] = ["vaultedToken": vaultedToken]
|
|
720
|
+
resolver(responseDict)
|
|
721
|
+
} else if let error = response["error"] as? String {
|
|
722
|
+
rejecter("ENROLLMENT_ERROR", error, nil)
|
|
723
|
+
} else {
|
|
724
|
+
rejecter("ENROLLMENT_ERROR", "No vaulted token in response", nil)
|
|
725
|
+
}
|
|
726
|
+
} catch {
|
|
727
|
+
rejecter("ENROLLMENT_ERROR", "Failed to continue enrollment: \(error.localizedDescription)", error)
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Note: TokenCollectedData, CollectedData, CardData, and other related models
|
|
734
|
+
// are provided by the YunoSDK framework (imported at the top).
|
|
735
|
+
// We don't need to redefine them here.
|
|
736
|
+
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","LINKING_ERROR","Platform","select","ios","YunoSdkNative","NativeModules","YunoSdk","eventEmitter","NativeEventEmitter","NativeYunoPaymentMethodsView","requireNativeComponent","YunoPaymentMethods","checkoutSession","countryCode","onPaymentMethodSelected","onPaymentMethodError","style","testID","useEffect","console","warn","subscriptions","selectionListener","addListener","event","push","errorListener","forEach","sub","remove","createElement","exports"],"sourceRoot":"../../src","sources":["YunoPaymentMethods.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAOsB,SAAAD,wBAAAG,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAL,uBAAA,YAAAA,CAAAG,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;AAEtB;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AA2BA;;AAQA,MAAMkB,aAAa,GACjB,gHAAgH,GAChHC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,uBAAuB;EAAEZ,OAAO,EAAE;AAAG,CAAC,CAAC,GAC9D,sDAAsD,GACtD,+BAA+B,GAC/B,0CAA0C;;AAE5C;AACA,MAAMa,aAAa,GAAGC,0BAAa,CAACC,OAAO;;AAE3C;AACA,IAAIC,YAAuC,GAAG,IAAI;AAClD,IAAIH,aAAa,EAAE;EACjBG,YAAY,GAAG,IAAIC,+BAAkB,CAACJ,aAAa,CAAC;AACtD;;AAEA;AACA,MAAMK,4BAA4B,
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","LINKING_ERROR","Platform","select","ios","YunoSdkNative","NativeModules","YunoSdk","eventEmitter","NativeEventEmitter","NativeYunoPaymentMethodsView","requireNativeComponent","YunoPaymentMethods","checkoutSession","countryCode","onPaymentMethodSelected","onPaymentMethodError","style","testID","useEffect","console","warn","subscriptions","selectionListener","addListener","event","push","errorListener","forEach","sub","remove","createElement","exports"],"sourceRoot":"../../src","sources":["YunoPaymentMethods.tsx"],"mappings":"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","ignoreList":[]}
|