@primer-io/react-native 2.0.1 → 2.1.0

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 (92) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/primerioreactnative/PrimerCardNumberEditTextManager.kt +83 -0
  3. package/android/src/main/java/com/primerioreactnative/PrimerRN.kt +2 -2
  4. package/android/src/main/java/com/primerioreactnative/PrimerRNHeadlessUniversalCheckout.kt +244 -0
  5. package/android/src/main/java/com/primerioreactnative/PrimerRNHeadlessUniversalCheckoutListener.kt +275 -0
  6. package/android/src/main/java/com/primerioreactnative/ReactNativePackage.kt +14 -3
  7. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerErrorRN.kt +3 -0
  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 +15 -0
  10. package/android/src/main/java/com/primerioreactnative/utils/PrimerHeadlessUniversalCheckoutImplementedRNCallbacks.kt +31 -0
  11. package/ios/Core/ReactNative-Bridging-Header.h +14 -0
  12. package/ios/DataModels/ImplementedRNCallbacks.swift +34 -9
  13. package/ios/DataModels/PrimerSettingsRN.swift +15 -16
  14. package/ios/Headless Universal Checkout/RNTPrimerCardNumberInputElementManager.m +23 -0
  15. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.m +62 -0
  16. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.swift +493 -0
  17. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.m +14 -0
  18. package/ios/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.swift +117 -0
  19. package/ios/RNTPrimer.m +2 -0
  20. package/ios/RNTPrimer.swift +56 -51
  21. package/ios/ReactNative-Bridging-Header.h +1 -0
  22. package/lib/commonjs/Primer.js +6 -0
  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/RNPrimer.js +11 -0
  27. package/lib/commonjs/RNPrimer.js.map +1 -1
  28. package/lib/commonjs/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js +14 -0
  29. package/lib/commonjs/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js.map +1 -0
  30. package/lib/commonjs/headless_checkout/PrimerHeadlessUniversalCheckout.js +286 -0
  31. package/lib/commonjs/headless_checkout/PrimerHeadlessUniversalCheckout.js.map +1 -0
  32. package/lib/commonjs/headless_checkout/RNPrimerHeadlessUniversalCheckout.js +197 -0
  33. package/lib/commonjs/headless_checkout/RNPrimerHeadlessUniversalCheckout.js.map +1 -0
  34. package/lib/commonjs/{models/Primer.js → headless_checkout/types.js} +1 -1
  35. package/lib/commonjs/{models/Primer.js.map → headless_checkout/types.js.map} +0 -0
  36. package/lib/commonjs/index.js +13 -5
  37. package/lib/commonjs/index.js.map +1 -1
  38. package/lib/commonjs/models/PrimerInterfaces.js +6 -0
  39. package/lib/{module/models/Primer.js.map → commonjs/models/PrimerInterfaces.js.map} +0 -0
  40. package/lib/module/Primer.js +7 -2
  41. package/lib/module/Primer.js.map +1 -1
  42. package/lib/module/PrimerInput.js +46 -0
  43. package/lib/module/PrimerInput.js.map +1 -0
  44. package/lib/module/RNPrimer.js +11 -0
  45. package/lib/module/RNPrimer.js.map +1 -1
  46. package/lib/module/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js +5 -0
  47. package/lib/module/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.js.map +1 -0
  48. package/lib/module/headless_checkout/PrimerHeadlessUniversalCheckout.js +273 -0
  49. package/lib/module/headless_checkout/PrimerHeadlessUniversalCheckout.js.map +1 -0
  50. package/lib/module/headless_checkout/RNPrimerHeadlessUniversalCheckout.js +188 -0
  51. package/lib/module/headless_checkout/RNPrimerHeadlessUniversalCheckout.js.map +1 -0
  52. package/lib/module/headless_checkout/types.js +2 -0
  53. package/lib/module/headless_checkout/types.js.map +1 -0
  54. package/lib/module/index.js +2 -1
  55. package/lib/module/index.js.map +1 -1
  56. package/lib/module/models/PrimerInterfaces.js +2 -0
  57. package/lib/module/models/PrimerInterfaces.js.map +1 -0
  58. package/lib/typescript/MerchantExample/App.d.ts +11 -0
  59. package/lib/typescript/src/Primer.d.ts +2 -0
  60. package/lib/typescript/src/PrimerInput.d.ts +15 -0
  61. package/lib/typescript/{RNPrimer.d.ts → src/RNPrimer.d.ts} +1 -0
  62. package/lib/typescript/src/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.d.ts +1 -0
  63. package/lib/typescript/src/headless_checkout/PrimerHeadlessUniversalCheckout.d.ts +11 -0
  64. package/lib/typescript/src/headless_checkout/RNPrimerHeadlessUniversalCheckout.d.ts +22 -0
  65. package/lib/typescript/src/headless_checkout/types.d.ts +22 -0
  66. package/lib/typescript/{index.d.ts → src/index.d.ts} +2 -1
  67. package/lib/typescript/{models → src/models}/PrimerCheckoutData.d.ts +0 -0
  68. package/lib/typescript/{models → src/models}/PrimerCheckoutPaymentMethodData.d.ts +0 -0
  69. package/lib/typescript/{models → src/models}/PrimerClientSession.d.ts +0 -0
  70. package/lib/typescript/{models → src/models}/PrimerCountryCode.d.ts +0 -0
  71. package/lib/typescript/{models → src/models}/PrimerCurrencyCode.d.ts +0 -0
  72. package/lib/typescript/{models → src/models}/PrimerError.d.ts +0 -0
  73. package/lib/typescript/{models/Primer.d.ts → src/models/PrimerInterfaces.d.ts} +2 -1
  74. package/lib/typescript/{models → src/models}/PrimerPaymentMethodTokenData.d.ts +0 -0
  75. package/lib/typescript/{models → src/models}/PrimerSessionIntent.d.ts +0 -0
  76. package/lib/typescript/{models → src/models}/PrimerSettings.d.ts +6 -2
  77. package/lib/typescript/{models → src/models}/PrimerTheme.d.ts +0 -0
  78. package/lib/typescript/{utils → src/utils}/index.d.ts +0 -0
  79. package/package.json +1 -1
  80. package/primer-io-react-native.podspec +1 -1
  81. package/src/Primer.ts +6 -1
  82. package/src/PrimerInput.tsx +73 -0
  83. package/src/RNPrimer.ts +13 -0
  84. package/src/RNTPrimerHeadlessUniversalCheckoutCardFormUIManager.tsx +2 -0
  85. package/src/headless_checkout/PrimerHeadlessUniversalCheckout.ts +327 -0
  86. package/src/headless_checkout/RNPrimerHeadlessUniversalCheckout.ts +252 -0
  87. package/src/headless_checkout/types.ts +32 -0
  88. package/src/index.tsx +4 -2
  89. package/src/models/{Primer.ts → PrimerInterfaces.ts} +2 -1
  90. package/src/models/PrimerSettings.ts +17 -2
  91. package/lib/module/models/Primer.js +0 -2
  92. package/lib/typescript/Primer.d.ts +0 -2
@@ -137,5 +137,5 @@ dependencies {
137
137
  api 'com.facebook.react:react-native:+'
138
138
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
139
139
  implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
140
- implementation 'io.primer:android:2.0.1'
140
+ implementation 'io.primer:android:2.1.0'
141
141
  }
@@ -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
+ }
@@ -16,9 +16,9 @@ import kotlinx.serialization.encodeToString
16
16
  import kotlinx.serialization.json.Json
17
17
  import org.json.JSONObject
18
18
 
19
- class PrimerRN(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
19
+ class PrimerRN(private val reactContext: ReactApplicationContext, private val json: Json) :
20
+ ReactContextBaseJavaModule(reactContext) {
20
21
  private var mListener = PrimerRNEventListener()
21
- private val json = Json { ignoreUnknownKeys = true }
22
22
 
23
23
  init {
24
24
  mListener.sendEvent = { eventName, paramsJson -> sendEvent(eventName, paramsJson) }
@@ -0,0 +1,244 @@
1
+ package com.primerioreactnative
2
+
3
+ import android.util.Log
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.*
8
+ import com.primerioreactnative.huc.assets.AssetsManager
9
+ import com.primerioreactnative.huc.assets.AssetsManager.drawableToBitmap
10
+ import com.primerioreactnative.huc.assets.AssetsManager.getFile
11
+ import com.primerioreactnative.huc.events.PrimerHeadlessUniversalCheckoutEvent
12
+ import com.primerioreactnative.utils.PrimerHeadlessUniversalCheckoutImplementedRNCallbacks
13
+ import com.primerioreactnative.utils.convertJsonToMap
14
+ import com.primerioreactnative.utils.errorTo
15
+ import io.primer.android.ExperimentalPrimerApi
16
+ import io.primer.android.components.PrimerHeadlessUniversalCheckout
17
+ import io.primer.android.components.ui.assets.ImageType
18
+ import io.primer.android.data.configuration.models.PaymentMethodType
19
+ import io.primer.android.data.configuration.models.PrimerPaymentMethodType
20
+ import kotlinx.serialization.decodeFromString
21
+ import kotlinx.serialization.encodeToString
22
+ import kotlinx.serialization.json.Json
23
+ import org.json.JSONObject
24
+
25
+ @ExperimentalPrimerApi
26
+ class PrimerRNHeadlessUniversalCheckout(
27
+ private val reactContext: ReactApplicationContext,
28
+ private val json: Json,
29
+ ) : ReactContextBaseJavaModule(reactContext) {
30
+
31
+ private val listener = PrimerRNHeadlessUniversalCheckoutListener()
32
+
33
+ init {
34
+ listener.sendEvent = { eventName, paramsJson -> sendEvent(eventName, paramsJson) }
35
+ listener.sendError = { paramsJson -> onError(paramsJson) }
36
+ listener.sendErrorWithCheckoutData =
37
+ { paramsJson, checkoutData -> onError(paramsJson, checkoutData) }
38
+ }
39
+
40
+ override fun getName(): String {
41
+ return "PrimerHeadlessUniversalCheckout"
42
+ }
43
+
44
+ @ReactMethod
45
+ fun startWithClientToken(
46
+ clientToken: String,
47
+ settingsStr: String?,
48
+ errorCallback: Callback,
49
+ successCallback: Callback
50
+ ) {
51
+ listener.successCallback = successCallback
52
+ try {
53
+ val settings =
54
+ if (settingsStr.isNullOrBlank()) PrimerSettingsRN() else json.decodeFromString(
55
+ settingsStr
56
+ )
57
+ PrimerHeadlessUniversalCheckout.current.start(
58
+ reactContext,
59
+ clientToken,
60
+ settings.toPrimerSettings(),
61
+ listener
62
+ )
63
+ } catch (e: Exception) {
64
+ errorCallback.invoke(
65
+ json.encodeToString(
66
+ ErrorTypeRN.NativeBridgeFailed errorTo
67
+ "failed to initialise PrimerHeadlessUniversalCheckout SDK, error: $e",
68
+ )
69
+ )
70
+ }
71
+ }
72
+
73
+ @ReactMethod
74
+ fun showPaymentMethod(paymentMethodTypeStr: String, promise: Promise) {
75
+ val primerPaymentMethodType = PrimerPaymentMethodType.safeValueOf(paymentMethodTypeStr)
76
+ if (primerPaymentMethodType == PaymentMethodType.UNKNOWN) {
77
+ val exception =
78
+ ErrorTypeRN.NativeBridgeFailed errorTo "Payment method type $paymentMethodTypeStr is not valid."
79
+ onError(exception)
80
+ promise.reject(exception.errorId, exception.description)
81
+ } else {
82
+ PrimerHeadlessUniversalCheckout.current.showPaymentMethod(
83
+ reactContext,
84
+ primerPaymentMethodType
85
+ )
86
+ promise.resolve(null)
87
+ }
88
+ }
89
+
90
+ @ReactMethod
91
+ fun disposePrimerHeadlessUniversalCheckout() {
92
+ PrimerHeadlessUniversalCheckout.current.cleanup()
93
+ listener.removeCallbacksAndHandlers()
94
+ }
95
+
96
+ @ReactMethod
97
+ fun getAssetForPaymentMethodType(
98
+ paymentMethodType: String,
99
+ assetType: String,
100
+ errorCallback: Callback,
101
+ successCallback: Callback
102
+ ) {
103
+
104
+ val primerPaymentMethodType = PrimerPaymentMethodType.safeValueOf(paymentMethodType)
105
+ val type = ImageType.values().find { it.name.equals(assetType, ignoreCase = true) }
106
+ when {
107
+ primerPaymentMethodType == PaymentMethodType.UNKNOWN -> {
108
+ errorCallback.invoke(
109
+ json.encodeToString(
110
+ ErrorTypeRN.InvalidPaymentMethodType errorTo
111
+ "Asset for $paymentMethodType does not exist, make sure you don't have any typos."
112
+ )
113
+ )
114
+ }
115
+ type == null -> {
116
+ errorCallback.invoke(
117
+ json.encodeToString(
118
+ ErrorTypeRN.AssetMismatch errorTo
119
+ "You have provided assetType=$assetType, but variable assetType can be 'LOGO' or 'ICON'."
120
+ )
121
+ )
122
+ }
123
+ else -> {
124
+ PrimerHeadlessUniversalCheckout.getAsset(primerPaymentMethodType, type)?.let { resourceId ->
125
+ val file = getFile(reactContext, paymentMethodType)
126
+ AssetsManager.saveBitmapToFile(
127
+ file,
128
+ drawableToBitmap(ContextCompat.getDrawable(reactContext, resourceId)!!),
129
+ )
130
+ successCallback.invoke("file://${file.absolutePath}")
131
+ } ?: run {
132
+ errorCallback.invoke(
133
+ json.encodeToString(
134
+ ErrorTypeRN.AssetMissing errorTo
135
+ "Failed to find $assetType for $paymentMethodType"
136
+ )
137
+ )
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ // region tokenization handlers
144
+ @ReactMethod
145
+ fun handleTokenizationNewClientToken(newClientToken: String, promise: Promise) {
146
+ listener.handleTokenizationNewClientToken(newClientToken)
147
+ promise.resolve(null)
148
+ }
149
+
150
+ @ReactMethod
151
+ fun handleTokenizationSuccess(promise: Promise) {
152
+ listener.handleTokenizationSuccess(promise)
153
+ }
154
+
155
+ @ReactMethod
156
+ fun handleTokenizationFailure(errorMessage: String?, promise: Promise) {
157
+ listener.handleTokenizationFailure(errorMessage.orEmpty(), promise)
158
+ }
159
+ // endregion
160
+
161
+ // region resume handlers
162
+ @ReactMethod
163
+ fun handleResumeNewClientToken(newClientToken: String, promise: Promise) {
164
+ listener.handleResumeNewClientToken(newClientToken)
165
+ promise.resolve(null)
166
+ }
167
+
168
+ @ReactMethod
169
+ fun handleResumeSuccess(promise: Promise) {
170
+ listener.handleResumeSuccess(promise)
171
+ }
172
+
173
+ @ReactMethod
174
+ fun handleResumeFailure(errorMessage: String?, promise: Promise) {
175
+ listener.handleResumeFailure(errorMessage.orEmpty(), promise)
176
+ }
177
+ // endregion
178
+
179
+ // region payment handlers
180
+ @ReactMethod
181
+ fun handlePaymentCreationContinue(promise: Promise) {
182
+ listener.handlePaymentCreationContinue()
183
+ promise.resolve(null)
184
+ }
185
+
186
+ @ReactMethod
187
+ fun handlePaymentCreationAbort(errorMessage: String?, promise: Promise) {
188
+ listener.handlePaymentCreationAbort(errorMessage.orEmpty())
189
+ promise.resolve(null)
190
+ }
191
+ // endregion
192
+
193
+ @ReactMethod
194
+ fun setImplementedRNCallbacks(implementedRNCallbacksStr: String, promise: Promise) {
195
+ try {
196
+ Log.d(TAG, "implementedRNCallbacks: $implementedRNCallbacksStr")
197
+ val implementedRNCallbacks =
198
+ json.decodeFromString<PrimerHeadlessUniversalCheckoutImplementedRNCallbacks>(
199
+ implementedRNCallbacksStr
200
+ )
201
+ listener.setImplementedCallbacks(implementedRNCallbacks)
202
+ promise.resolve(null)
203
+ } catch (e: Exception) {
204
+ val exception =
205
+ ErrorTypeRN.NativeBridgeFailed errorTo "Implemented callbacks $implementedRNCallbacksStr is not valid."
206
+ onError(exception)
207
+ promise.reject(exception.errorId, exception.description, e)
208
+ }
209
+ }
210
+
211
+ private fun onError(exception: PrimerErrorRN, checkoutDataRN: PrimerCheckoutDataRN? = null) {
212
+ val params = Arguments.createMap()
213
+ val errorJson = JSONObject(Json.encodeToString(exception))
214
+ val errorData = prepareData(errorJson)
215
+ params.putMap(Keys.ERROR, errorData)
216
+ checkoutDataRN?.let {
217
+ val checkoutDataJson = JSONObject(Json.encodeToString(it))
218
+ val checkoutData = prepareData(checkoutDataJson)
219
+ params.putMap(Keys.CHECKOUT_DATA, checkoutData)
220
+ }
221
+ sendEvent(PrimerHeadlessUniversalCheckoutEvent.ON_ERROR.eventName, params)
222
+ }
223
+
224
+ private fun sendEvent(name: String, params: WritableMap) {
225
+ reactApplicationContext.getJSModule(
226
+ DeviceEventManagerModule.RCTDeviceEventEmitter::class.java
227
+ ).emit(name, params)
228
+ }
229
+
230
+ private fun sendEvent(name: String, data: JSONObject?) {
231
+ val params = prepareData(data)
232
+ reactApplicationContext.getJSModule(
233
+ DeviceEventManagerModule.RCTDeviceEventEmitter::class.java
234
+ ).emit(name, params)
235
+ }
236
+
237
+ private fun prepareData(data: JSONObject?): WritableMap {
238
+ return data?.let { convertJsonToMap(data) } ?: Arguments.createMap()
239
+ }
240
+
241
+ private companion object {
242
+ const val TAG = "PrimerHUC"
243
+ }
244
+ }
@@ -0,0 +1,275 @@
1
+ package com.primerioreactnative
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.Callback
5
+ import com.facebook.react.bridge.Promise
6
+ import com.primerioreactnative.datamodels.*
7
+ import com.primerioreactnative.extensions.toPrimerCheckoutDataRN
8
+ import com.primerioreactnative.extensions.toPrimerClientSessionRN
9
+ import com.primerioreactnative.extensions.toPrimerPaymentMethodDataRN
10
+ import com.primerioreactnative.huc.events.PrimerHeadlessUniversalCheckoutEvent
11
+ import com.primerioreactnative.utils.PrimerHeadlessUniversalCheckoutImplementedRNCallbacks
12
+ import com.primerioreactnative.utils.errorTo
13
+ import io.primer.android.ExperimentalPrimerApi
14
+ import io.primer.android.completion.PrimerPaymentCreationDecisionHandler
15
+ import io.primer.android.completion.PrimerResumeDecisionHandler
16
+ import io.primer.android.components.PrimerHeadlessUniversalCheckoutListener
17
+ import io.primer.android.components.domain.core.models.PrimerHeadlessUniversalCheckoutPaymentMethod
18
+ import io.primer.android.data.configuration.models.PrimerPaymentMethodType
19
+ import io.primer.android.domain.PrimerCheckoutData
20
+ import io.primer.android.domain.action.models.PrimerClientSession
21
+ import io.primer.android.domain.error.models.PrimerError
22
+ import io.primer.android.domain.tokenization.models.PrimerPaymentMethodData
23
+ import io.primer.android.domain.tokenization.models.PrimerPaymentMethodTokenData
24
+ import kotlinx.serialization.encodeToString
25
+ import kotlinx.serialization.json.Json
26
+ import org.json.JSONObject
27
+
28
+ @OptIn(ExperimentalPrimerApi::class)
29
+ class PrimerRNHeadlessUniversalCheckoutListener : PrimerHeadlessUniversalCheckoutListener {
30
+ private var paymentCreationDecisionHandler: ((errorMessage: String?) -> Unit)? = null
31
+ private var tokenizeSuccessDecisionHandler: ((resumeToken: String?, errorMessage: String?) -> Unit)? =
32
+ null
33
+ private var resumeSuccessDecisionHandler: ((resumeToken: String?, errorMessage: String?) -> Unit)? =
34
+ null
35
+ private var implementedRNCallbacks: PrimerHeadlessUniversalCheckoutImplementedRNCallbacks? = null
36
+
37
+ var sendEvent: ((eventName: String, paramsJson: JSONObject?) -> Unit)? = null
38
+
39
+ var sendError: ((error: PrimerErrorRN) -> Unit)? = null
40
+ var sendErrorWithCheckoutData: ((error: PrimerErrorRN, checkoutData: PrimerCheckoutDataRN?) -> Unit)? =
41
+ null
42
+
43
+ var successCallback: Callback? = null
44
+
45
+ override fun onAvailablePaymentMethodsLoaded(paymentMethods: List<PrimerHeadlessUniversalCheckoutPaymentMethod>) {
46
+ sendEvent?.invoke(
47
+ PrimerHeadlessUniversalCheckoutEvent.ON_HUC_AVAILABLE_PAYMENT_METHODS_LOADED.eventName,
48
+ JSONObject().apply {
49
+ put("paymentMethodTypes", paymentMethods.map { it.paymentMethodType.name })
50
+ }
51
+ )
52
+ successCallback?.invoke(
53
+ Arguments.fromList(paymentMethods.map { it.paymentMethodType.name })
54
+ )
55
+ }
56
+
57
+ override fun onPreparationStarted(paymentMethodType: PrimerPaymentMethodType) {
58
+ sendEvent?.invoke(
59
+ PrimerHeadlessUniversalCheckoutEvent.ON_HUC_PREPARE_START.eventName,
60
+ JSONObject(Json.encodeToString(PrimerPaymentMethodDataRN(paymentMethodType)))
61
+ )
62
+ }
63
+
64
+ override fun onPaymentMethodShowed(paymentMethodType: PrimerPaymentMethodType) {
65
+ sendEvent?.invoke(
66
+ PrimerHeadlessUniversalCheckoutEvent.ON_HUC_PAYMENT_METHOD_SHOW.eventName,
67
+ JSONObject(Json.encodeToString(PrimerPaymentMethodDataRN(paymentMethodType)))
68
+ )
69
+ }
70
+
71
+ override fun onTokenizationStarted(paymentMethodType: PrimerPaymentMethodType) {
72
+ sendEvent?.invoke(
73
+ PrimerHeadlessUniversalCheckoutEvent.ON_HUC_TOKENIZE_START.eventName,
74
+ JSONObject(Json.encodeToString(PrimerPaymentMethodDataRN(paymentMethodType)))
75
+ )
76
+ }
77
+
78
+ override fun onCheckoutCompleted(checkoutData: PrimerCheckoutData) {
79
+ if (implementedRNCallbacks?.isOnCheckoutCompleteImplemented == true) {
80
+ sendEvent?.invoke(
81
+ PrimerHeadlessUniversalCheckoutEvent.ON_CHECKOUT_COMPLETE.eventName,
82
+ JSONObject(Json.encodeToString(checkoutData.toPrimerCheckoutDataRN()))
83
+ )
84
+ } else {
85
+ sendError?.invoke(
86
+ ErrorTypeRN.NativeBridgeFailed
87
+ errorTo "Callback [onCheckoutComplete] should be implemented."
88
+ )
89
+ }
90
+ }
91
+
92
+ override fun onBeforePaymentCreated(
93
+ paymentMethodData: PrimerPaymentMethodData,
94
+ createPaymentHandler: PrimerPaymentCreationDecisionHandler
95
+ ) {
96
+ if (implementedRNCallbacks?.isOnBeforePaymentCreateImplemented == true) {
97
+ paymentCreationDecisionHandler = { errorMessage ->
98
+ when {
99
+ errorMessage != null -> createPaymentHandler.abortPaymentCreation(errorMessage.ifBlank { null })
100
+ else -> createPaymentHandler.continuePaymentCreation()
101
+ }
102
+ }
103
+ sendEvent?.invoke(
104
+ PrimerHeadlessUniversalCheckoutEvent.ON_BEFORE_PAYMENT_CREATE.eventName,
105
+ JSONObject(Json.encodeToString(paymentMethodData.toPrimerPaymentMethodDataRN()))
106
+ )
107
+ } else {
108
+ super.onBeforePaymentCreated(paymentMethodData, createPaymentHandler)
109
+ }
110
+ }
111
+
112
+ override fun onBeforeClientSessionUpdated() {
113
+ if (implementedRNCallbacks?.isOnBeforeClientSessionUpdateImplemented == true) {
114
+ sendEvent?.invoke(
115
+ PrimerHeadlessUniversalCheckoutEvent.ON_BEFORE_CLIENT_SESSION_UPDATE.eventName,
116
+ null
117
+ )
118
+ } else {
119
+ super.onBeforeClientSessionUpdated()
120
+ }
121
+ }
122
+
123
+ override fun onClientSessionUpdated(clientSession: PrimerClientSession) {
124
+ if (implementedRNCallbacks?.isOnClientSessionUpdateImplemented == true) {
125
+ sendEvent?.invoke(
126
+ PrimerHeadlessUniversalCheckoutEvent.ON_CLIENT_SESSION_UPDATE.eventName,
127
+ JSONObject(Json.encodeToString(clientSession.toPrimerClientSessionRN()))
128
+ )
129
+ } else {
130
+ super.onClientSessionUpdated(clientSession)
131
+ }
132
+ }
133
+
134
+ override fun onTokenizeSuccess(
135
+ paymentMethodTokenData: PrimerPaymentMethodTokenData,
136
+ decisionHandler: PrimerResumeDecisionHandler
137
+ ) {
138
+ if (implementedRNCallbacks?.isOnTokenizeSuccessImplemented == true) {
139
+ val token = PrimerPaymentInstrumentTokenRN.fromPaymentMethodToken(paymentMethodTokenData)
140
+ val request = JSONObject(Json.encodeToString(token))
141
+ tokenizeSuccessDecisionHandler = { newClientToken, err ->
142
+ when {
143
+ err != null -> decisionHandler.handleFailure(err.ifBlank { null })
144
+ newClientToken != null -> decisionHandler.continueWithNewClientToken(newClientToken)
145
+ else -> decisionHandler.handleSuccess()
146
+ }
147
+ }
148
+ sendEvent?.invoke(PrimerHeadlessUniversalCheckoutEvent.ON_TOKENIZE_SUCCESS.eventName, request)
149
+ } else {
150
+ sendError?.invoke(
151
+ ErrorTypeRN.NativeBridgeFailed
152
+ errorTo "Callback [onTokenizeSuccess] should be implemented."
153
+ )
154
+ }
155
+ }
156
+
157
+ override fun onResumeSuccess(
158
+ resumeToken: String,
159
+ decisionHandler: PrimerResumeDecisionHandler
160
+ ) {
161
+ if (implementedRNCallbacks?.isOnResumeSuccessImplemented == true) {
162
+ resumeSuccessDecisionHandler = { newClientToken, err ->
163
+ when {
164
+ err != null -> decisionHandler.handleFailure(err.ifBlank { null })
165
+ newClientToken != null -> decisionHandler.continueWithNewClientToken(newClientToken)
166
+ else -> decisionHandler.handleSuccess()
167
+ }
168
+ }
169
+
170
+ val resumeToken = mapOf(Keys.RESUME_TOKEN to resumeToken)
171
+ sendEvent?.invoke(
172
+ PrimerHeadlessUniversalCheckoutEvent.ON_RESUME_SUCCESS.eventName,
173
+ JSONObject(Json.encodeToString(resumeToken))
174
+ )
175
+ } else {
176
+ sendError?.invoke(
177
+ ErrorTypeRN.NativeBridgeFailed
178
+ errorTo "Callback [onResumeSuccess] should be implemented."
179
+ )
180
+ }
181
+ }
182
+
183
+ override fun onFailed(
184
+ error: PrimerError,
185
+ checkoutData: PrimerCheckoutData?,
186
+ ) {
187
+ if (implementedRNCallbacks?.isOnErrorImplemented == true) {
188
+ sendErrorWithCheckoutData?.invoke(
189
+ PrimerErrorRN(
190
+ error.errorId,
191
+ error.description,
192
+ error.recoverySuggestion
193
+ ), checkoutData?.toPrimerCheckoutDataRN()
194
+ )
195
+ } else {
196
+ super.onFailed(error, checkoutData)
197
+ }
198
+ }
199
+
200
+ override fun onFailed(error: PrimerError) {
201
+ if (implementedRNCallbacks?.isOnErrorImplemented == true) {
202
+ sendError?.invoke(PrimerErrorRN(error.errorId, error.description, error.recoverySuggestion))
203
+ } else {
204
+ super.onFailed(error)
205
+ }
206
+ }
207
+
208
+ // region tokenization handlers
209
+ fun handleTokenizationNewClientToken(newClientToken: String) {
210
+ tokenizeSuccessDecisionHandler?.invoke(newClientToken, null)
211
+ tokenizeSuccessDecisionHandler = null
212
+ }
213
+
214
+ fun handleTokenizationSuccess(promise: Promise) {
215
+ val error =
216
+ ErrorTypeRN.NativeBridgeFailed errorTo "PrimerTokenizationHandler's " +
217
+ "handleSuccess function is not available on HUC."
218
+ promise.reject(error.errorId, error.description)
219
+ }
220
+
221
+ fun handleTokenizationFailure(errorMessage: String, promise: Promise) {
222
+ val error =
223
+ ErrorTypeRN.NativeBridgeFailed errorTo "PrimerTokenizationHandler's " +
224
+ "handleFailure function is not available on HUC."
225
+ promise.reject(error.errorId, error.description)
226
+ }
227
+ // endregion
228
+
229
+ // region resume handlers
230
+ fun handleResumeNewClientToken(newClientToken: String) {
231
+ resumeSuccessDecisionHandler?.invoke(newClientToken, null)
232
+ resumeSuccessDecisionHandler = null
233
+ }
234
+
235
+ fun handleResumeSuccess(promise: Promise) {
236
+ val error =
237
+ ErrorTypeRN.NativeBridgeFailed errorTo "PrimerResumeHandler's " +
238
+ "handleSuccess function is not available on HUC."
239
+ promise.reject(error.errorId, error.description)
240
+ }
241
+
242
+ fun handleResumeFailure(errorMessage: String, promise: Promise) {
243
+ val error =
244
+ ErrorTypeRN.NativeBridgeFailed errorTo "PrimerResumeHandler's " +
245
+ "handleFailure function is not available on HUC."
246
+ promise.reject(error.errorId, error.description)
247
+ }
248
+ // endregion
249
+
250
+ // region payment create handlers
251
+ fun handlePaymentCreationContinue() {
252
+ paymentCreationDecisionHandler?.invoke(null)
253
+ paymentCreationDecisionHandler = null
254
+ }
255
+
256
+ fun handlePaymentCreationAbort(errorMessage: String) {
257
+ paymentCreationDecisionHandler?.invoke(errorMessage)
258
+ paymentCreationDecisionHandler = null
259
+ }
260
+ // endregion
261
+
262
+ fun setImplementedCallbacks(
263
+ implementedRNCallbacks:
264
+ PrimerHeadlessUniversalCheckoutImplementedRNCallbacks
265
+ ) {
266
+ this.implementedRNCallbacks = implementedRNCallbacks
267
+ }
268
+
269
+ fun removeCallbacksAndHandlers() {
270
+ paymentCreationDecisionHandler = null
271
+ tokenizeSuccessDecisionHandler = null
272
+ resumeSuccessDecisionHandler = null
273
+ implementedRNCallbacks = null
274
+ }
275
+ }
@@ -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, json),
16
+ PrimerRNHeadlessUniversalCheckout(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
 
@@ -12,4 +12,7 @@ data class PrimerErrorRN(
12
12
  @Serializable
13
13
  enum class ErrorTypeRN(val errorId: String) {
14
14
  NativeBridgeFailed("native-bridge"),
15
+ AssetMissing("missing-asset"),
16
+ AssetMismatch("mismatch-asset"),
17
+ InvalidPaymentMethodType("invalid-payment-method-type")
15
18
  }