@primer-io/react-native 0.12.4 → 0.13.0-beta.2
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/build.gradle +2 -2
- package/android/src/main/java/com/primerioreactnative/PrimerCardNumberEditTextManager.kt +83 -0
- package/android/src/main/java/com/primerioreactnative/PrimerHeadlessUniversalCheckout.kt +201 -0
- package/android/src/main/java/com/primerioreactnative/PrimerRN.kt +3 -3
- package/android/src/main/java/com/primerioreactnative/ReactNativePackage.kt +14 -3
- package/android/src/main/java/com/primerioreactnative/datamodels/PrimerErrorRN.kt +2 -0
- package/android/src/main/java/com/primerioreactnative/datamodels/PrimerSettingsRN.kt +3 -4
- package/android/src/main/java/com/primerioreactnative/huc/assets/AssetsManager.kt +56 -0
- package/android/src/main/java/com/primerioreactnative/huc/events/PrimerHeadlessUniversalCheckoutEvent.kt +11 -0
- package/android/src/main/java/com/primerioreactnative/huc/extensions/Map.kt +10 -0
- package/ios/{PrimerRN+PrimerDelegate.swift → Core/PrimerRN+PrimerDelegate.swift} +4 -3
- package/ios/{ReactNative.m → Core/PrimerRN.m} +1 -1
- package/ios/{PrimerRN.swift → Core/PrimerRN.swift} +26 -24
- package/ios/{ReactNative-Bridging-Header.h → Core/ReactNative-Bridging-Header.h} +2 -0
- package/ios/DataModels/PrimerErrorRN.swift +32 -2
- package/ios/DataModels/PrimerSettingsRN.swift +0 -6
- package/ios/Headless Universal Checkout/RNTPrimerCardNumberInputElementManager.m +23 -0
- package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.m +30 -0
- package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.swift +241 -0
- package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.m +14 -0
- package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.swift +117 -0
- package/lib/commonjs/Primer.js +2 -1
- package/lib/commonjs/Primer.js.map +1 -1
- package/lib/commonjs/PrimerInput.js +80 -0
- package/lib/commonjs/PrimerInput.js.map +1 -0
- package/lib/commonjs/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js +14 -0
- package/lib/commonjs/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js.map +1 -0
- package/lib/commonjs/headless_checkout/NativePrimerHeadlessUniversalCheckout.js +73 -0
- package/lib/commonjs/headless_checkout/NativePrimerHeadlessUniversalCheckout.js.map +1 -0
- package/lib/commonjs/headless_checkout/PrimerHeadlessUniversalCheckout.js +119 -0
- package/lib/commonjs/headless_checkout/PrimerHeadlessUniversalCheckout.js.map +1 -0
- package/lib/commonjs/headless_checkout/types.js +2 -0
- package/lib/commonjs/headless_checkout/types.js.map +1 -0
- package/lib/commonjs/index.js +28 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/Primer.js +1 -1
- package/lib/module/Primer.js.map +1 -1
- package/lib/module/PrimerInput.js +46 -0
- package/lib/module/PrimerInput.js.map +1 -0
- package/lib/module/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js +5 -0
- package/lib/module/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js.map +1 -0
- package/lib/module/headless_checkout/NativePrimerHeadlessUniversalCheckout.js +64 -0
- package/lib/module/headless_checkout/NativePrimerHeadlessUniversalCheckout.js.map +1 -0
- package/lib/module/headless_checkout/PrimerHeadlessUniversalCheckout.js +104 -0
- package/lib/module/headless_checkout/PrimerHeadlessUniversalCheckout.js.map +1 -0
- package/lib/module/headless_checkout/types.js +2 -0
- package/lib/module/headless_checkout/types.js.map +1 -0
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/PrimerInput.d.ts +15 -0
- package/lib/typescript/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.d.ts +1 -0
- package/lib/typescript/headless_checkout/NativePrimerHeadlessUniversalCheckout.d.ts +10 -0
- package/lib/typescript/headless_checkout/PrimerHeadlessUniversalCheckout.d.ts +14 -0
- package/lib/typescript/headless_checkout/types.d.ts +16 -0
- package/lib/typescript/index.d.ts +3 -1
- package/lib/typescript/models/primer-settings.d.ts +1 -2
- package/package.json +1 -1
- package/primer-io-react-native.podspec +1 -1
- package/src/Primer.ts +2 -0
- package/src/PrimerInput.tsx +73 -0
- package/src/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.tsx +2 -0
- package/src/headless_checkout/NativePrimerHeadlessUniversalCheckout.ts +94 -0
- package/src/headless_checkout/PrimerHeadlessUniversalCheckout.ts +136 -0
- package/src/headless_checkout/types.ts +18 -0
- package/src/index.tsx +3 -1
- package/src/models/primer-settings.ts +1 -2
package/android/build.gradle
CHANGED
|
@@ -127,6 +127,6 @@ dependencies {
|
|
|
127
127
|
// noinspection GradleDynamicVersion
|
|
128
128
|
api 'com.facebook.react:react-native:+'
|
|
129
129
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
130
|
-
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.
|
|
131
|
-
implementation 'io.primer:android:1.12.
|
|
130
|
+
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1"
|
|
131
|
+
implementation 'io.primer:android:1.12.1'
|
|
132
132
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
package com.primerioreactnative
|
|
2
|
+
|
|
3
|
+
import android.content.res.ColorStateList
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.graphics.drawable.ColorDrawable
|
|
6
|
+
import android.graphics.drawable.GradientDrawable
|
|
7
|
+
import android.view.ViewGroup
|
|
8
|
+
import android.widget.EditText
|
|
9
|
+
import android.widget.LinearLayout
|
|
10
|
+
import android.widget.TextView
|
|
11
|
+
import com.facebook.drawee.backends.pipeline.Fresco
|
|
12
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
14
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
15
|
+
import com.facebook.react.uimanager.ViewProps
|
|
16
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
17
|
+
import com.facebook.react.views.image.ReactImageView
|
|
18
|
+
import io.primer.android.components.manager.PrimerCardManager
|
|
19
|
+
import io.primer.android.components.manager.PrimerUniversalCheckoutCardManagerInterface
|
|
20
|
+
import io.primer.android.components.ui.widgets.*
|
|
21
|
+
|
|
22
|
+
val listOfTextInputs = mutableListOf<PrimerEditText>()
|
|
23
|
+
|
|
24
|
+
class PrimerCardNumberEditTextManager : SimpleViewManager<PrimerCardNumberEditText>() {
|
|
25
|
+
|
|
26
|
+
override fun getName(): String {
|
|
27
|
+
return REACT_CLASS
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
override fun createViewInstance(reactContext: ThemedReactContext): PrimerCardNumberEditText {
|
|
31
|
+
val editText = PrimerCardNumberEditText(reactContext)
|
|
32
|
+
return editText
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
companion object {
|
|
36
|
+
const val REACT_CLASS = "PrimerCardNumberEditText"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class PrimerCardholderNameEditTextManager : SimpleViewManager<PrimerCardholderNameEditText>() {
|
|
41
|
+
|
|
42
|
+
override fun getName(): String {
|
|
43
|
+
return REACT_CLASS
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override fun createViewInstance(reactContext: ThemedReactContext): PrimerCardholderNameEditText {
|
|
47
|
+
return PrimerCardholderNameEditText(reactContext)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
companion object {
|
|
51
|
+
const val REACT_CLASS = "PrimerCardholderNameEditText"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class PrimerExpiryEditTextManager : SimpleViewManager<PrimerExpiryEditText>() {
|
|
56
|
+
|
|
57
|
+
override fun getName(): String {
|
|
58
|
+
return REACT_CLASS
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override fun createViewInstance(reactContext: ThemedReactContext): PrimerExpiryEditText {
|
|
62
|
+
return PrimerExpiryEditText(reactContext)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
companion object {
|
|
66
|
+
const val REACT_CLASS = "PrimerExpiryEditText"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class PrimerCvvEditTextManager : SimpleViewManager<PrimerCvvEditText>() {
|
|
71
|
+
|
|
72
|
+
override fun getName(): String {
|
|
73
|
+
return REACT_CLASS
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
override fun createViewInstance(reactContext: ThemedReactContext): PrimerCvvEditText {
|
|
77
|
+
return PrimerCvvEditText(reactContext)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
companion object {
|
|
81
|
+
const val REACT_CLASS = "PrimerCvvEditText"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
package com.primerioreactnative
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.Nullable
|
|
4
|
+
import androidx.core.content.ContextCompat
|
|
5
|
+
import com.facebook.react.bridge.*
|
|
6
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
7
|
+
import com.primerioreactnative.datamodels.ErrorTypeRN
|
|
8
|
+
import com.primerioreactnative.datamodels.PrimerErrorRN
|
|
9
|
+
import com.primerioreactnative.datamodels.PrimerPaymentInstrumentTokenRN
|
|
10
|
+
import com.primerioreactnative.datamodels.PrimerSettingsRN
|
|
11
|
+
import com.primerioreactnative.huc.assets.AssetsManager
|
|
12
|
+
import com.primerioreactnative.huc.assets.AssetsManager.drawableToBitmap
|
|
13
|
+
import com.primerioreactnative.huc.assets.AssetsManager.getFile
|
|
14
|
+
import com.primerioreactnative.huc.events.PrimerHeadlessUniversalCheckoutEvent
|
|
15
|
+
import com.primerioreactnative.huc.extensions.toArgumentsMap
|
|
16
|
+
import io.primer.android.completion.ResumeHandler
|
|
17
|
+
import io.primer.android.components.PrimerHeadlessUniversalCheckout
|
|
18
|
+
import io.primer.android.components.PrimerHeadlessUniversalCheckoutListener
|
|
19
|
+
import io.primer.android.components.domain.core.models.PrimerHeadlessUniversalCheckoutPaymentMethod
|
|
20
|
+
import io.primer.android.components.ui.assets.ImageType
|
|
21
|
+
import io.primer.android.model.dto.*
|
|
22
|
+
import kotlinx.serialization.decodeFromString
|
|
23
|
+
import kotlinx.serialization.encodeToString
|
|
24
|
+
import kotlinx.serialization.json.Json
|
|
25
|
+
|
|
26
|
+
class PrimerHeadlessUniversalCheckout(
|
|
27
|
+
private val reactContext: ReactApplicationContext,
|
|
28
|
+
private val json: Json,
|
|
29
|
+
) : ReactContextBaseJavaModule(reactContext), PrimerHeadlessUniversalCheckoutListener {
|
|
30
|
+
|
|
31
|
+
override fun getName(): String {
|
|
32
|
+
return "PrimerHeadlessUniversalCheckout"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private var resumeHandler: ResumeHandler? = null
|
|
36
|
+
private var successCallback: Callback? = null
|
|
37
|
+
|
|
38
|
+
@ReactMethod
|
|
39
|
+
fun startWithClientToken(
|
|
40
|
+
clientToken: String,
|
|
41
|
+
settingsStr: String?,
|
|
42
|
+
errorCallback: Callback,
|
|
43
|
+
successCallback: Callback
|
|
44
|
+
) {
|
|
45
|
+
try {
|
|
46
|
+
val settings = json.decodeFromString<PrimerSettingsRN>(settingsStr.orEmpty())
|
|
47
|
+
this.successCallback = successCallback
|
|
48
|
+
PrimerHeadlessUniversalCheckout.current.start(
|
|
49
|
+
reactContext,
|
|
50
|
+
clientToken,
|
|
51
|
+
PrimerConfig(settings = settings.format()),
|
|
52
|
+
this
|
|
53
|
+
)
|
|
54
|
+
} catch (e: Exception) {
|
|
55
|
+
errorCallback.invoke(
|
|
56
|
+
json.encodeToString(
|
|
57
|
+
PrimerErrorRN(
|
|
58
|
+
ErrorTypeRN.InitFailed,
|
|
59
|
+
"failed to initialise PrimerHeadlessUniversalCheckout SDK, error: $e",
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@ReactMethod
|
|
67
|
+
fun resumeWithClientToken(resumeToken: String) {
|
|
68
|
+
this.resumeHandler?.handleNewClientToken(resumeToken)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@ReactMethod
|
|
72
|
+
fun showPaymentMethod(paymentMethodType: String) {
|
|
73
|
+
val primerPaymentMethodType = PrimerPaymentMethodType.safeValueOf(paymentMethodType)
|
|
74
|
+
PrimerHeadlessUniversalCheckout.current.showPaymentMethod(
|
|
75
|
+
reactContext,
|
|
76
|
+
primerPaymentMethodType
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@ReactMethod
|
|
81
|
+
fun disposeHeadlessCheckout() {
|
|
82
|
+
PrimerHeadlessUniversalCheckout.current.cleanup()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@ReactMethod
|
|
86
|
+
fun getAssetForPaymentMethodType(
|
|
87
|
+
paymentMethodType: String,
|
|
88
|
+
assetType: String,
|
|
89
|
+
errorCallback: Callback,
|
|
90
|
+
successCallback: Callback
|
|
91
|
+
) {
|
|
92
|
+
|
|
93
|
+
val primerPaymentMethodType = PrimerPaymentMethodType.safeValueOf(paymentMethodType)
|
|
94
|
+
val type = ImageType.values().find { it.name.equals(assetType, ignoreCase = true) }
|
|
95
|
+
when {
|
|
96
|
+
primerPaymentMethodType == PaymentMethodType.UNKNOWN -> {
|
|
97
|
+
errorCallback.invoke(
|
|
98
|
+
json.encodeToString(
|
|
99
|
+
PrimerErrorRN(
|
|
100
|
+
ErrorTypeRN.AssetMissing,
|
|
101
|
+
"Asset for $paymentMethodType does not exist, make sure you don't have any typos."
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
type == null -> {
|
|
107
|
+
errorCallback.invoke(
|
|
108
|
+
json.encodeToString(
|
|
109
|
+
PrimerErrorRN(
|
|
110
|
+
ErrorTypeRN.AssetMismatch,
|
|
111
|
+
"You have provided assetType=$assetType, but variable assetType can be 'LOGO' or 'ICON'."
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
else -> {
|
|
117
|
+
PrimerHeadlessUniversalCheckout.getAsset(primerPaymentMethodType, type)?.let { resourceId ->
|
|
118
|
+
val file = getFile(reactContext, paymentMethodType)
|
|
119
|
+
AssetsManager.saveBitmapToFile(
|
|
120
|
+
file,
|
|
121
|
+
drawableToBitmap(ContextCompat.getDrawable(reactContext, resourceId)!!),
|
|
122
|
+
)
|
|
123
|
+
successCallback.invoke("file://${file.absolutePath}")
|
|
124
|
+
} ?: run {
|
|
125
|
+
errorCallback.invoke(
|
|
126
|
+
json.encodeToString(
|
|
127
|
+
PrimerErrorRN(
|
|
128
|
+
ErrorTypeRN.AssetMissing,
|
|
129
|
+
"Failed to find $paymentMethodType for $assetType"
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
override fun onClientSessionSetupSuccessfully(paymentMethods: List<PrimerHeadlessUniversalCheckoutPaymentMethod>) {
|
|
139
|
+
sendEvent(PrimerHeadlessUniversalCheckoutEvent.CLIENT_SESSION_SETUP_SUCCESS)
|
|
140
|
+
successCallback?.invoke(
|
|
141
|
+
Arguments.fromList(paymentMethods.map { it.paymentMethodType.name })
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
override fun onTokenizationPreparation() {
|
|
146
|
+
sendEvent(PrimerHeadlessUniversalCheckoutEvent.TOKENIZATION_PREPARATION_STARTED)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
override fun onTokenizationStarted() {
|
|
150
|
+
sendEvent(PrimerHeadlessUniversalCheckoutEvent.TOKENIZATION_STARTED)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
override fun onPaymentMethodShowed() {
|
|
154
|
+
sendEvent(PrimerHeadlessUniversalCheckoutEvent.PAYMENT_METH0D_SHOWED)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
override fun onTokenizationSuccess(
|
|
158
|
+
paymentMethodToken: PaymentMethodToken,
|
|
159
|
+
resumeHandler: ResumeHandler
|
|
160
|
+
) {
|
|
161
|
+
this.resumeHandler = resumeHandler
|
|
162
|
+
|
|
163
|
+
sendEvent(
|
|
164
|
+
PrimerHeadlessUniversalCheckoutEvent.TOKENIZATION_SUCCESS,
|
|
165
|
+
mapOf(
|
|
166
|
+
"paymentMethodToken" to json.encodeToString(
|
|
167
|
+
PrimerPaymentInstrumentTokenRN.fromPaymentMethodToken(
|
|
168
|
+
paymentMethodToken
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
).toArgumentsMap()
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
override fun onResumeSuccess(resumeToken: String, resumeHandler: ResumeHandler) {
|
|
176
|
+
this.resumeHandler = resumeHandler
|
|
177
|
+
|
|
178
|
+
sendEvent(
|
|
179
|
+
PrimerHeadlessUniversalCheckoutEvent.RESUME,
|
|
180
|
+
mapOf("resumeToken" to resumeToken).toArgumentsMap()
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
override fun onError(error: APIError) {
|
|
186
|
+
sendEvent(
|
|
187
|
+
PrimerHeadlessUniversalCheckoutEvent.ERROR,
|
|
188
|
+
mapOf("error" to error.toString()).toArgumentsMap()
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
private fun sendEvent(
|
|
194
|
+
event: PrimerHeadlessUniversalCheckoutEvent,
|
|
195
|
+
@Nullable params: WritableMap? = null
|
|
196
|
+
) {
|
|
197
|
+
reactContext
|
|
198
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
199
|
+
.emit(event.eventName, params)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -14,8 +14,8 @@ import kotlinx.serialization.encodeToString
|
|
|
14
14
|
import kotlinx.serialization.json.Json
|
|
15
15
|
import kotlin.Error
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
class PrimerRN(reactContext: ReactApplicationContext) :
|
|
18
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
19
19
|
|
|
20
20
|
private var settings: PrimerSettingsRN = PrimerSettingsRN()
|
|
21
21
|
private var theme: PrimerThemeRN = PrimerThemeRN()
|
|
@@ -26,7 +26,7 @@ class PrimerRN(reactContext: ReactApplicationContext) : ReactContextBaseJavaModu
|
|
|
26
26
|
private var sdkWasInitialised = false
|
|
27
27
|
private var haltExecution = false
|
|
28
28
|
|
|
29
|
-
private val json = Json{ ignoreUnknownKeys = true }
|
|
29
|
+
private val json = Json { ignoreUnknownKeys = true }
|
|
30
30
|
|
|
31
31
|
override fun getName(): String = "PrimerRN"
|
|
32
32
|
|
|
@@ -4,15 +4,26 @@ import com.facebook.react.ReactPackage
|
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
6
|
import com.facebook.react.uimanager.ViewManager
|
|
7
|
-
|
|
7
|
+
import kotlinx.serialization.json.Json
|
|
8
8
|
|
|
9
9
|
class ReactNativePackage : ReactPackage {
|
|
10
|
+
|
|
11
|
+
private val json = Json { ignoreUnknownKeys = true }
|
|
12
|
+
|
|
10
13
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
11
|
-
return listOf(
|
|
14
|
+
return listOf(
|
|
15
|
+
PrimerRN(reactContext),
|
|
16
|
+
PrimerHeadlessUniversalCheckout(reactContext, json)
|
|
17
|
+
)
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
15
|
-
return
|
|
21
|
+
return mutableListOf(
|
|
22
|
+
PrimerCardNumberEditTextManager(),
|
|
23
|
+
PrimerCardholderNameEditTextManager(),
|
|
24
|
+
PrimerExpiryEditTextManager(),
|
|
25
|
+
PrimerCvvEditTextManager(),
|
|
26
|
+
)
|
|
16
27
|
}
|
|
17
28
|
}
|
|
18
29
|
|
|
@@ -92,18 +92,17 @@ data class OptionsRN(
|
|
|
92
92
|
val isResultScreenEnabled: Boolean? = null,
|
|
93
93
|
val isLoadingScreenEnabled: Boolean? = null,
|
|
94
94
|
val isFullScreenEnabled: Boolean? = null,
|
|
95
|
-
val
|
|
95
|
+
val isThreeDsEnabled: Boolean? = null,
|
|
96
96
|
val locale: String? = null,
|
|
97
97
|
val android: AndroidOptionsRN? = null,
|
|
98
|
-
val is3DSDevelopmentModeEnabled: Boolean = true
|
|
99
98
|
) {
|
|
100
99
|
fun format(): Options {
|
|
101
100
|
return Options(
|
|
102
101
|
showUI = isLoadingScreenEnabled ?: true,
|
|
103
102
|
redirectScheme = android?.redirectScheme,
|
|
104
|
-
is3DSOnVaultingEnabled =
|
|
103
|
+
is3DSOnVaultingEnabled = isThreeDsEnabled ?: false,
|
|
105
104
|
debugOptions = PrimerDebugOptions(
|
|
106
|
-
is3DSSanityCheckEnabled =
|
|
105
|
+
is3DSSanityCheckEnabled = true, // false on emulator
|
|
107
106
|
),
|
|
108
107
|
)
|
|
109
108
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
package com.primerioreactnative.huc.assets
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.Canvas
|
|
5
|
+
import android.graphics.drawable.BitmapDrawable
|
|
6
|
+
import android.graphics.drawable.Drawable
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.io.FileOutputStream
|
|
10
|
+
import java.util.*
|
|
11
|
+
|
|
12
|
+
internal object AssetsManager {
|
|
13
|
+
|
|
14
|
+
private const val ASSETS_DIRECTORY = "primer-sdk"
|
|
15
|
+
private const val COMPRESS_QUALITY = 100
|
|
16
|
+
|
|
17
|
+
fun drawableToBitmap(drawable: Drawable): Bitmap {
|
|
18
|
+
if (drawable is BitmapDrawable) {
|
|
19
|
+
return drawable.bitmap
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
val bitmap = Bitmap.createBitmap(
|
|
23
|
+
drawable.intrinsicWidth,
|
|
24
|
+
drawable.intrinsicHeight,
|
|
25
|
+
Bitmap.Config.ARGB_8888
|
|
26
|
+
)
|
|
27
|
+
val canvas = Canvas(bitmap)
|
|
28
|
+
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
|
29
|
+
drawable.draw(canvas)
|
|
30
|
+
return bitmap
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fun saveBitmapToFile(
|
|
34
|
+
file: File,
|
|
35
|
+
bitmap: Bitmap,
|
|
36
|
+
format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG,
|
|
37
|
+
) {
|
|
38
|
+
|
|
39
|
+
FileOutputStream(file).use {
|
|
40
|
+
bitmap.compress(format, COMPRESS_QUALITY, it)
|
|
41
|
+
it.close()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fun getFile(context: ReactApplicationContext, path: String): File {
|
|
46
|
+
val directory = File(context.filesDir, path)
|
|
47
|
+
if (!directory.exists()) directory.mkdirs()
|
|
48
|
+
val file = File(
|
|
49
|
+
context.filesDir,
|
|
50
|
+
ASSETS_DIRECTORY + File.pathSeparator + path.toLowerCase(Locale.ROOT)
|
|
51
|
+
)
|
|
52
|
+
if (!file.exists()) file.createNewFile()
|
|
53
|
+
return file
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package com.primerioreactnative.huc.events
|
|
2
|
+
|
|
3
|
+
internal enum class PrimerHeadlessUniversalCheckoutEvent(val eventName: String) {
|
|
4
|
+
CLIENT_SESSION_SETUP_SUCCESS("clientSessionDidSetUpSuccessfully"),
|
|
5
|
+
TOKENIZATION_PREPARATION_STARTED("preparationStarted"),
|
|
6
|
+
PAYMENT_METH0D_SHOWED("paymentMethodPresented"),
|
|
7
|
+
TOKENIZATION_STARTED("tokenizationStarted"),
|
|
8
|
+
TOKENIZATION_SUCCESS("tokenizationSucceeded"),
|
|
9
|
+
RESUME("resume"),
|
|
10
|
+
ERROR("error");
|
|
11
|
+
}
|
|
@@ -22,12 +22,13 @@ extension PrimerRN: PrimerDelegate {
|
|
|
22
22
|
resumeHandler: ResumeHandlerProtocol
|
|
23
23
|
) {
|
|
24
24
|
do {
|
|
25
|
-
let
|
|
26
|
-
let
|
|
27
|
-
self.onTokenizeSuccessCallback?([
|
|
25
|
+
let paymentMethodTokenData = try encoder.encode(paymentMethodToken)
|
|
26
|
+
let paymentMethodTokenStr = String(data: paymentMethodTokenData, encoding: .utf8)!
|
|
27
|
+
self.onTokenizeSuccessCallback?([paymentMethodTokenStr])
|
|
28
28
|
} catch {
|
|
29
29
|
checkoutFailed(with: ErrorTypeRN.ParseJsonFailed)
|
|
30
30
|
}
|
|
31
|
+
|
|
31
32
|
onResumeFlowCallback = { error, clientToken in
|
|
32
33
|
if let error = error {
|
|
33
34
|
resumeHandler.handle(error: error)
|
|
@@ -28,31 +28,31 @@ class PrimerRN: NSObject {
|
|
|
28
28
|
private var sdkWasInitialised = false
|
|
29
29
|
internal var haltExecution = false
|
|
30
30
|
|
|
31
|
-
@objc func configureSettings(_
|
|
31
|
+
@objc func configureSettings(_ settingsStr: String) {
|
|
32
32
|
do {
|
|
33
|
-
let
|
|
34
|
-
let
|
|
35
|
-
self.settings =
|
|
33
|
+
let settingsData = settingsStr.data(using: .utf8)!
|
|
34
|
+
let settingsRN = try JSONDecoder().decode(PrimerSettingsRN.self, from: settingsData)
|
|
35
|
+
self.settings = settingsRN
|
|
36
36
|
} catch {
|
|
37
37
|
checkoutFailed(with: ErrorTypeRN.ParseJsonFailed)
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
@objc func configureTheme(_
|
|
41
|
+
@objc func configureTheme(_ themeStr: String) {
|
|
42
42
|
do {
|
|
43
|
-
let
|
|
44
|
-
let themeRN = try JSONDecoder().decode(PrimerThemeRN.self, from:
|
|
43
|
+
let themeData = themeStr.data(using: .utf8)!
|
|
44
|
+
let themeRN = try JSONDecoder().decode(PrimerThemeRN.self, from: themeData)
|
|
45
45
|
self.theme = themeRN
|
|
46
46
|
} catch {
|
|
47
47
|
checkoutFailed(with: ErrorTypeRN.ParseJsonFailed)
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
@objc func configureIntent(_
|
|
51
|
+
@objc func configureIntent(_ intentStr: String) {
|
|
52
52
|
do {
|
|
53
|
-
let
|
|
54
|
-
let
|
|
55
|
-
self.intent =
|
|
53
|
+
let intentData = intentStr.data(using: .utf8)!
|
|
54
|
+
let intentRN = try JSONDecoder().decode(PrimerIntentRN.self, from: intentData)
|
|
55
|
+
self.intent = intentRN
|
|
56
56
|
} catch {
|
|
57
57
|
checkoutFailed(with: ErrorTypeRN.ParseJsonFailed)
|
|
58
58
|
}
|
|
@@ -112,26 +112,27 @@ class PrimerRN: NSObject {
|
|
|
112
112
|
Primer.shared.showUniversalCheckout(on: viewController, clientToken: token)
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
+
|
|
115
116
|
} catch {
|
|
116
117
|
self?.checkoutFailed(with: error)
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
@objc func resume(_
|
|
122
|
+
@objc func resume(_ resumeRequestStr: String) -> Void {
|
|
122
123
|
DispatchQueue.main.async { [weak self] in
|
|
123
124
|
do {
|
|
124
|
-
let
|
|
125
|
-
let
|
|
125
|
+
let resumeRequestData = resumeRequestStr.data(using: .utf8)!
|
|
126
|
+
let resumeRequest = try JSONDecoder().decode(PrimerResumeRequest.self, from: resumeRequestData)
|
|
126
127
|
|
|
127
|
-
if let
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
if let errorDescription = resumeRequest.error {
|
|
129
|
+
let errorRN = NativeError(errorId: "server-error", errorDescription: errorDescription, recoverySuggestion: nil)
|
|
130
|
+
self?.onResumeFlowCallback?(errorRN, nil)
|
|
131
|
+
} else if let clientToken = resumeRequest.token {
|
|
130
132
|
self?.onResumeFlowCallback?(nil, clientToken)
|
|
131
133
|
} else {
|
|
132
134
|
self?.onResumeFlowCallback?(nil, nil)
|
|
133
135
|
}
|
|
134
|
-
|
|
135
136
|
self?.onResumeFlowCallback = nil
|
|
136
137
|
} catch {
|
|
137
138
|
self?.checkoutFailed(with: error)
|
|
@@ -139,15 +140,16 @@ class PrimerRN: NSObject {
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
@objc func actionResume(_
|
|
143
|
+
@objc func actionResume(_ resumeRequestStr: String) -> Void {
|
|
143
144
|
DispatchQueue.main.async { [weak self] in
|
|
144
145
|
do {
|
|
145
|
-
let
|
|
146
|
-
let
|
|
146
|
+
let resumeRequestData = resumeRequestStr.data(using: .utf8)!
|
|
147
|
+
let resumeRequestRN = try JSONDecoder().decode(PrimerResumeRequest.self, from: resumeRequestData)
|
|
147
148
|
|
|
148
|
-
if let
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
if let errorDescription = resumeRequestRN.error {
|
|
150
|
+
let errorRN = NativeError(errorId: "server-error", errorDescription: errorDescription, recoverySuggestion: nil)
|
|
151
|
+
self?.onActionResumeCallback?(errorRN, nil)
|
|
152
|
+
} else if let clientToken = resumeRequestRN.token {
|
|
151
153
|
self?.onActionResumeCallback?(nil, clientToken)
|
|
152
154
|
} else {
|
|
153
155
|
self?.onActionResumeCallback?(nil, self?.clientToken)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#import <React/RCTBridgeModule.h>
|
|
2
2
|
#import <React/RCTViewManager.h>
|
|
3
|
+
#import <React/RCTUIManager.h>
|
|
4
|
+
#import <React/RCTView.h>
|
|
3
5
|
// for view controller implementation https://stackoverflow.com/questions/45741903/react-native-ios-how-can-i-navigate-to-a-non-react-native-view-native-ios-vie/46007680#46007680
|
|
4
6
|
#import "React/RCTBridge.h"
|
|
5
7
|
#import "React/RCTEventDispatcher.h"
|
|
@@ -15,6 +15,36 @@ enum ErrorTypeRN: String, Error, Encodable {
|
|
|
15
15
|
case generic
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
struct
|
|
19
|
-
let
|
|
18
|
+
struct NativeError: CustomNSError, LocalizedError {
|
|
19
|
+
let errorId: String
|
|
20
|
+
let errorDescription: String?
|
|
21
|
+
let recoverySuggestion: String?
|
|
22
|
+
|
|
23
|
+
var rnError: [String: String] {
|
|
24
|
+
var json: [String: String] = [
|
|
25
|
+
"errorId": errorId,
|
|
26
|
+
"description": errorDescription ?? "Something went wrong"
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
if let recoverySuggestion = recoverySuggestion {
|
|
30
|
+
json["recoverySuggestion"] = recoverySuggestion
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return json
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
internal extension Error {
|
|
38
|
+
var rnError: [String: String] {
|
|
39
|
+
var json: [String: String] = [
|
|
40
|
+
"errorId": (self as NSError).domain,
|
|
41
|
+
"description": localizedDescription
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
if let recoverySuggestion = (self as NSError).localizedRecoverySuggestion {
|
|
45
|
+
json["recoverySuggestion"] = recoverySuggestion
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return json
|
|
49
|
+
}
|
|
20
50
|
}
|
|
@@ -20,8 +20,6 @@ extension PrimerSettingsRN {
|
|
|
20
20
|
postalCode: billingAddress.postalCode
|
|
21
21
|
)
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
let debugOptions = PrimerDebugOptions(is3DSSanityCheckEnabled: !(options?.is3DSDevelopmentModeEnabled ?? false))
|
|
25
23
|
|
|
26
24
|
return PrimerSettings(
|
|
27
25
|
merchantIdentifier: options?.ios?.merchantIdentifier,
|
|
@@ -37,9 +35,7 @@ extension PrimerSettingsRN {
|
|
|
37
35
|
businessDetails: business?.primerFormat,
|
|
38
36
|
orderItems: order?.itemsFormatted ?? [],
|
|
39
37
|
isInitialLoadingHidden: !(options?.isLoadingScreenEnabled ?? true),
|
|
40
|
-
is3DSOnVaultingEnabled: options?.is3DSOnVaultingEnabled ?? false,
|
|
41
38
|
orderId: order?.id,
|
|
42
|
-
debugOptions: debugOptions,
|
|
43
39
|
customer: Customer(
|
|
44
40
|
firstName: customer?.firstName,
|
|
45
41
|
lastName: customer?.lastName,
|
|
@@ -149,8 +145,6 @@ fileprivate struct OptionsRN: Decodable {
|
|
|
149
145
|
let isResultScreenEnabled: Bool?
|
|
150
146
|
let isLoadingScreenEnabled: Bool?
|
|
151
147
|
let isFullScreenEnabled: Bool?
|
|
152
|
-
let is3DSOnVaultingEnabled: Bool?
|
|
153
|
-
let is3DSDevelopmentModeEnabled: Bool?
|
|
154
148
|
let locale: String?
|
|
155
149
|
let ios: IosOptionsRN?
|
|
156
150
|
}
|