@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.
Files changed (66) hide show
  1. package/android/build.gradle +2 -2
  2. package/android/src/main/java/com/primerioreactnative/PrimerCardNumberEditTextManager.kt +83 -0
  3. package/android/src/main/java/com/primerioreactnative/PrimerHeadlessUniversalCheckout.kt +201 -0
  4. package/android/src/main/java/com/primerioreactnative/PrimerRN.kt +3 -3
  5. package/android/src/main/java/com/primerioreactnative/ReactNativePackage.kt +14 -3
  6. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerErrorRN.kt +2 -0
  7. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerSettingsRN.kt +3 -4
  8. package/android/src/main/java/com/primerioreactnative/huc/assets/AssetsManager.kt +56 -0
  9. package/android/src/main/java/com/primerioreactnative/huc/events/PrimerHeadlessUniversalCheckoutEvent.kt +11 -0
  10. package/android/src/main/java/com/primerioreactnative/huc/extensions/Map.kt +10 -0
  11. package/ios/{PrimerRN+PrimerDelegate.swift → Core/PrimerRN+PrimerDelegate.swift} +4 -3
  12. package/ios/{ReactNative.m → Core/PrimerRN.m} +1 -1
  13. package/ios/{PrimerRN.swift → Core/PrimerRN.swift} +26 -24
  14. package/ios/{ReactNative-Bridging-Header.h → Core/ReactNative-Bridging-Header.h} +2 -0
  15. package/ios/DataModels/PrimerErrorRN.swift +32 -2
  16. package/ios/DataModels/PrimerSettingsRN.swift +0 -6
  17. package/ios/Headless Universal Checkout/RNTPrimerCardNumberInputElementManager.m +23 -0
  18. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.m +30 -0
  19. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.swift +241 -0
  20. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.m +14 -0
  21. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.swift +117 -0
  22. package/lib/commonjs/Primer.js +2 -1
  23. package/lib/commonjs/Primer.js.map +1 -1
  24. package/lib/commonjs/PrimerInput.js +80 -0
  25. package/lib/commonjs/PrimerInput.js.map +1 -0
  26. package/lib/commonjs/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js +14 -0
  27. package/lib/commonjs/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js.map +1 -0
  28. package/lib/commonjs/headless_checkout/NativePrimerHeadlessUniversalCheckout.js +73 -0
  29. package/lib/commonjs/headless_checkout/NativePrimerHeadlessUniversalCheckout.js.map +1 -0
  30. package/lib/commonjs/headless_checkout/PrimerHeadlessUniversalCheckout.js +119 -0
  31. package/lib/commonjs/headless_checkout/PrimerHeadlessUniversalCheckout.js.map +1 -0
  32. package/lib/commonjs/headless_checkout/types.js +2 -0
  33. package/lib/commonjs/headless_checkout/types.js.map +1 -0
  34. package/lib/commonjs/index.js +28 -0
  35. package/lib/commonjs/index.js.map +1 -1
  36. package/lib/module/Primer.js +1 -1
  37. package/lib/module/Primer.js.map +1 -1
  38. package/lib/module/PrimerInput.js +46 -0
  39. package/lib/module/PrimerInput.js.map +1 -0
  40. package/lib/module/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js +5 -0
  41. package/lib/module/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js.map +1 -0
  42. package/lib/module/headless_checkout/NativePrimerHeadlessUniversalCheckout.js +64 -0
  43. package/lib/module/headless_checkout/NativePrimerHeadlessUniversalCheckout.js.map +1 -0
  44. package/lib/module/headless_checkout/PrimerHeadlessUniversalCheckout.js +104 -0
  45. package/lib/module/headless_checkout/PrimerHeadlessUniversalCheckout.js.map +1 -0
  46. package/lib/module/headless_checkout/types.js +2 -0
  47. package/lib/module/headless_checkout/types.js.map +1 -0
  48. package/lib/module/index.js +3 -1
  49. package/lib/module/index.js.map +1 -1
  50. package/lib/typescript/PrimerInput.d.ts +15 -0
  51. package/lib/typescript/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.d.ts +1 -0
  52. package/lib/typescript/headless_checkout/NativePrimerHeadlessUniversalCheckout.d.ts +10 -0
  53. package/lib/typescript/headless_checkout/PrimerHeadlessUniversalCheckout.d.ts +14 -0
  54. package/lib/typescript/headless_checkout/types.d.ts +16 -0
  55. package/lib/typescript/index.d.ts +3 -1
  56. package/lib/typescript/models/primer-settings.d.ts +1 -2
  57. package/package.json +1 -1
  58. package/primer-io-react-native.podspec +1 -1
  59. package/src/Primer.ts +2 -0
  60. package/src/PrimerInput.tsx +73 -0
  61. package/src/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.tsx +2 -0
  62. package/src/headless_checkout/NativePrimerHeadlessUniversalCheckout.ts +94 -0
  63. package/src/headless_checkout/PrimerHeadlessUniversalCheckout.ts +136 -0
  64. package/src/headless_checkout/types.ts +18 -0
  65. package/src/index.tsx +3 -1
  66. package/src/models/primer-settings.ts +1 -2
@@ -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.2.2"
131
- implementation 'io.primer:android:1.12.0'
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
- class PrimerRN(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
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(PrimerRN(reactContext))
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 emptyList()
21
+ return mutableListOf(
22
+ PrimerCardNumberEditTextManager(),
23
+ PrimerCardholderNameEditTextManager(),
24
+ PrimerExpiryEditTextManager(),
25
+ PrimerCvvEditTextManager(),
26
+ )
16
27
  }
17
28
  }
18
29
 
@@ -15,4 +15,6 @@ enum class ErrorTypeRN {
15
15
  InitFailed,
16
16
  CheckoutFlowFailed,
17
17
  TokenizationFailed,
18
+ AssetMissing,
19
+ AssetMismatch
18
20
  }
@@ -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 is3DSOnVaultingEnabled: Boolean? = null,
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 = is3DSOnVaultingEnabled ?: false,
103
+ is3DSOnVaultingEnabled = isThreeDsEnabled ?: false,
105
104
  debugOptions = PrimerDebugOptions(
106
- is3DSSanityCheckEnabled = !is3DSDevelopmentModeEnabled, // false on emulator
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
+ }
@@ -0,0 +1,10 @@
1
+ package com.primerioreactnative.huc.extensions
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+
5
+ internal fun Map<String, String>.toArgumentsMap() =
6
+ Arguments.createMap().apply {
7
+ entries.forEach {
8
+ putString(it.key, it.value)
9
+ }
10
+ }
@@ -22,12 +22,13 @@ extension PrimerRN: PrimerDelegate {
22
22
  resumeHandler: ResumeHandlerProtocol
23
23
  ) {
24
24
  do {
25
- let json = try encoder.encode(paymentMethodToken)
26
- let data = String(data: json, encoding: .utf8)!
27
- self.onTokenizeSuccessCallback?([data])
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)
@@ -1,7 +1,7 @@
1
1
  #import <React/RCTBridgeModule.h>
2
2
  #import <React/RCTViewManager.h>
3
3
 
4
- @interface RCT_EXTERN_MODULE(PrimerRN, RCTViewManager)
4
+ @interface RCT_EXTERN_MODULE(PrimerRN, NSObject)
5
5
 
6
6
  RCT_EXTERN_METHOD(configureSettings: (NSString *)request)
7
7
 
@@ -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(_ request: String) {
31
+ @objc func configureSettings(_ settingsStr: String) {
32
32
  do {
33
- let json = request.data(using: .utf8)!
34
- let settings = try JSONDecoder().decode(PrimerSettingsRN.self, from: json)
35
- self.settings = 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(_ request: String) {
41
+ @objc func configureTheme(_ themeStr: String) {
42
42
  do {
43
- let json = request.data(using: .utf8)!
44
- let themeRN = try JSONDecoder().decode(PrimerThemeRN.self, from: json)
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(_ request: String) {
51
+ @objc func configureIntent(_ intentStr: String) {
52
52
  do {
53
- let json = request.data(using: .utf8)!
54
- let intent = try JSONDecoder().decode(PrimerIntentRN.self, from: json)
55
- self.intent = 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(_ request: String) -> Void {
122
+ @objc func resume(_ resumeRequestStr: String) -> Void {
122
123
  DispatchQueue.main.async { [weak self] in
123
124
  do {
124
- let json = request.data(using: .utf8)!
125
- let request = try JSONDecoder().decode(PrimerResumeRequest.self, from: json)
125
+ let resumeRequestData = resumeRequestStr.data(using: .utf8)!
126
+ let resumeRequest = try JSONDecoder().decode(PrimerResumeRequest.self, from: resumeRequestData)
126
127
 
127
- if let error = request.error {
128
- self?.onResumeFlowCallback?(ErrorRN(message: error), nil)
129
- } else if let clientToken = request.token {
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(_ request: String) -> Void {
143
+ @objc func actionResume(_ resumeRequestStr: String) -> Void {
143
144
  DispatchQueue.main.async { [weak self] in
144
145
  do {
145
- let json = request.data(using: .utf8)!
146
- let request = try JSONDecoder().decode(PrimerResumeRequest.self, from: json)
146
+ let resumeRequestData = resumeRequestStr.data(using: .utf8)!
147
+ let resumeRequestRN = try JSONDecoder().decode(PrimerResumeRequest.self, from: resumeRequestData)
147
148
 
148
- if let error = request.error {
149
- self?.onActionResumeCallback?(ErrorRN(message: error), nil)
150
- } else if let clientToken = request.token {
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 ErrorRN: Error {
19
- let message: String
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
  }