@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
package/YunoSdk.podspec
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
5
|
+
|
|
6
|
+
Pod::Spec.new do |s|
|
|
7
|
+
s.name = "YunoSdk"
|
|
8
|
+
s.version = package["version"]
|
|
9
|
+
s.summary = package["description"]
|
|
10
|
+
s.homepage = package["homepage"]
|
|
11
|
+
s.license = package["license"]
|
|
12
|
+
s.authors = package["author"]
|
|
13
|
+
|
|
14
|
+
s.platforms = { :ios => "14.0" }
|
|
15
|
+
s.source = { :git => "https://github.com/yuno-payments/yuno-sdk-react-native.git", :tag => "#{s.version}" }
|
|
16
|
+
|
|
17
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
18
|
+
|
|
19
|
+
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
|
|
20
|
+
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
|
21
|
+
if respond_to?(:install_modules_dependencies, true)
|
|
22
|
+
install_modules_dependencies(s)
|
|
23
|
+
else
|
|
24
|
+
s.dependency "React-Core"
|
|
25
|
+
|
|
26
|
+
# Don't install the dependencies when we run `pod install` in the old architecture.
|
|
27
|
+
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
|
28
|
+
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
|
29
|
+
s.pod_target_xcconfig = {
|
|
30
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
31
|
+
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
32
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
|
33
|
+
}
|
|
34
|
+
s.dependency "React-Codegen"
|
|
35
|
+
s.dependency "RCT-Folly"
|
|
36
|
+
s.dependency "RCTRequired"
|
|
37
|
+
s.dependency "RCTTypeSafety"
|
|
38
|
+
s.dependency "ReactCommon/turbomodule/core"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Yuno iOS SDK
|
|
43
|
+
s.dependency "YunoSDK", "2.7.1"
|
|
44
|
+
|
|
45
|
+
s.swift_version = '5.0'
|
|
46
|
+
s.static_framework = true
|
|
47
|
+
end
|
|
48
|
+
|
|
@@ -2,6 +2,7 @@ package com.yunosdkreactnative
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.util.Log
|
|
5
6
|
import androidx.activity.ComponentActivity
|
|
6
7
|
import com.facebook.react.bridge.*
|
|
7
8
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
@@ -10,12 +11,26 @@ import com.yuno.sdk.YunoConfig
|
|
|
10
11
|
import com.yuno.sdk.YunoLanguage
|
|
11
12
|
import com.yuno.sdk.enrollment.*
|
|
12
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
|
|
13
22
|
import com.yuno.presentation.core.components.PaymentSelected
|
|
14
23
|
import com.yuno.presentation.core.card.CardFormType
|
|
15
24
|
import com.yuno.payments.features.payment.models.OneTimeTokenModel
|
|
16
25
|
import com.google.gson.Gson
|
|
17
26
|
import org.json.JSONObject
|
|
18
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
|
|
19
34
|
|
|
20
35
|
/**
|
|
21
36
|
* Yuno SDK React Native Module for Android
|
|
@@ -51,6 +66,14 @@ class YunoSdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
51
66
|
@Volatile
|
|
52
67
|
private var lastOneTimeTokenInfo: OneTimeTokenModel? = null
|
|
53
68
|
|
|
69
|
+
// Store the last payment status to prevent stale status from previous flows
|
|
70
|
+
@Volatile
|
|
71
|
+
private var lastPaymentStatus: String? = null
|
|
72
|
+
|
|
73
|
+
// Flag to track if we're starting a fresh payment flow
|
|
74
|
+
@Volatile
|
|
75
|
+
private var isPaymentFlowCleared: Boolean = false
|
|
76
|
+
|
|
54
77
|
// Gson instance for JSON serialization
|
|
55
78
|
private val gson = Gson()
|
|
56
79
|
|
|
@@ -612,6 +635,24 @@ class YunoSdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
612
635
|
}
|
|
613
636
|
}
|
|
614
637
|
|
|
638
|
+
/**
|
|
639
|
+
* Clears the last stored payment status.
|
|
640
|
+
* This should be called at the start of each new payment flow
|
|
641
|
+
* to prevent stale status from previous flows.
|
|
642
|
+
*/
|
|
643
|
+
@ReactMethod
|
|
644
|
+
fun clearLastPaymentStatus(promise: Promise) {
|
|
645
|
+
try {
|
|
646
|
+
// Don't set to null - keep the last status to compare against stale events
|
|
647
|
+
// Just mark that we're starting a fresh flow
|
|
648
|
+
isPaymentFlowCleared = true
|
|
649
|
+
Log.d(TAG, "💫 Payment status cleared - fresh flow starting (last status was: $lastPaymentStatus)")
|
|
650
|
+
promise.resolve(true)
|
|
651
|
+
} catch (e: Exception) {
|
|
652
|
+
promise.reject("CLEAR_STATUS_ERROR", "Error clearing last payment status: ${e.message}", e)
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
615
656
|
// Lifecycle Methods
|
|
616
657
|
override fun onHostResume() {}
|
|
617
658
|
override fun onHostPause() {}
|
|
@@ -651,6 +692,26 @@ class YunoSdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
651
692
|
|
|
652
693
|
internal fun sendPaymentStatusEvent(status: String?) {
|
|
653
694
|
val convertedStatus = convertPaymentStatus(status)
|
|
695
|
+
|
|
696
|
+
// If we just cleared the status and this is the same status as before,
|
|
697
|
+
// it's a stale event from the native SDK - ignore it
|
|
698
|
+
if (isPaymentFlowCleared && convertedStatus == lastPaymentStatus) {
|
|
699
|
+
Log.d(TAG, "🚫 Ignoring stale payment status: $convertedStatus (from previous flow)")
|
|
700
|
+
isPaymentFlowCleared = false
|
|
701
|
+
lastPaymentStatus = null // Clear it now that we've ignored the stale event
|
|
702
|
+
return
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Reset the cleared flag after processing first real new event
|
|
706
|
+
if (isPaymentFlowCleared) {
|
|
707
|
+
Log.d(TAG, "✅ First new status after clear: $convertedStatus (previous was: $lastPaymentStatus)")
|
|
708
|
+
isPaymentFlowCleared = false
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Store the current status for future comparison
|
|
712
|
+
lastPaymentStatus = convertedStatus
|
|
713
|
+
|
|
714
|
+
Log.d(TAG, "✅ Emitting payment status: $convertedStatus")
|
|
654
715
|
val params = Arguments.createMap().apply {
|
|
655
716
|
putString("status", convertedStatus)
|
|
656
717
|
}
|
|
@@ -774,4 +835,284 @@ class YunoSdkModule(private val reactContext: ReactApplicationContext) :
|
|
|
774
835
|
}
|
|
775
836
|
return array
|
|
776
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
|
+
}
|
|
777
1118
|
}
|