@primer-io/react-native 2.26.3 → 2.27.1

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 (77) hide show
  1. package/android/.project +6 -0
  2. package/android/.settings/org.eclipse.buildship.core.prefs +5 -5
  3. package/android/build.gradle +1 -3
  4. package/android/src/main/AndroidManifest.xml +1 -4
  5. package/android/src/main/java/com/primerioreactnative/PrimerRN.kt +3 -3
  6. package/android/src/main/java/com/primerioreactnative/PrimerRNEventListener.kt +4 -3
  7. package/android/src/main/java/com/primerioreactnative/PrimerRNHeadlessUniversalCheckout.kt +2 -2
  8. package/android/src/main/java/com/primerioreactnative/PrimerRNHeadlessUniversalCheckoutListener.kt +46 -9
  9. package/android/src/main/java/com/primerioreactnative/ReactNativePackage.kt +4 -0
  10. package/android/src/main/java/com/primerioreactnative/components/manager/ach/AchMandateManager.kt +70 -0
  11. package/android/src/main/java/com/primerioreactnative/components/manager/ach/PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.kt +283 -0
  12. package/android/src/main/java/com/primerioreactnative/components/manager/banks/PrimerRNHeadlessUniversalCheckoutBanksComponent.kt +7 -37
  13. package/android/src/main/java/com/primerioreactnative/components/manager/klarna/PrimerRNHeadlessUniversalCheckoutKlarnaComponent.kt +5 -31
  14. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerCheckoutAdditionalInfoRN.kt +17 -4
  15. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerErrorRN.kt +1 -1
  16. package/android/src/main/java/com/primerioreactnative/datamodels/PrimerSettingsRN.kt +44 -37
  17. package/android/src/main/java/com/primerioreactnative/datamodels/ach/AchUserDetailsCollectableDataRN.kt +22 -0
  18. package/android/src/main/java/com/primerioreactnative/datamodels/ach/AchUserDetailsStepRN.kt +22 -0
  19. package/android/src/main/java/com/primerioreactnative/datamodels/extensions/ach/AchUserDetailsCollectableData.kt +11 -0
  20. package/android/src/main/java/com/primerioreactnative/datamodels/extensions/ach/AchUserDetailsStep.kt +14 -0
  21. package/android/src/main/java/com/primerioreactnative/extensions/JSONExtensions.kt +41 -0
  22. package/android/src/main/java/com/primerioreactnative/extensions/PrimerCheckoutAdditionalInfo.kt +4 -0
  23. package/android/src/main/java/com/primerioreactnative/extensions/PrimerPaymentMethodOptionsRN.kt +42 -15
  24. package/ios/Sources/DataModels/Ach Component/RNTAchUserDetailsCollectableData.swift +54 -0
  25. package/ios/Sources/DataModels/Ach Component/RNTAchUserDetailsStep.swift +40 -0
  26. package/ios/Sources/DataModels/PrimerCheckoutAdditionalInfoRN.swift +61 -0
  27. package/ios/Sources/DataModels/PrimerSettings+Extensions.swift +24 -1
  28. package/ios/Sources/Headless Universal Checkout/Managers/Ach/PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.m +28 -0
  29. package/ios/Sources/Headless Universal Checkout/Managers/Ach/PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.swift +233 -0
  30. package/ios/Sources/Headless Universal Checkout/Managers/Ach/RNTAchMandateManager.m +16 -0
  31. package/ios/Sources/Headless Universal Checkout/Managers/Ach/RNTAchMandateManager.swift +53 -0
  32. package/ios/Sources/Headless Universal Checkout/Managers/Klarna/PrimerRNHeadlessUniversalCheckoutKlarnaComponent.swift +2 -2
  33. package/ios/Sources/Headless Universal Checkout/RNTPrimerHeadlessUniversalCheckout.swift +55 -7
  34. package/ios/Sources/version.swift +1 -1
  35. package/lib/commonjs/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.js +106 -0
  36. package/lib/commonjs/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.js.map +1 -0
  37. package/lib/commonjs/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.js +38 -0
  38. package/lib/commonjs/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.js.map +1 -0
  39. package/lib/commonjs/index.js +70 -0
  40. package/lib/commonjs/index.js.map +1 -1
  41. package/lib/commonjs/models/ach/AchCollectableData.js +6 -0
  42. package/lib/commonjs/models/ach/AchCollectableData.js.map +1 -0
  43. package/lib/commonjs/models/ach/AchSteps.js +6 -0
  44. package/lib/commonjs/models/ach/AchSteps.js.map +1 -0
  45. package/lib/module/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.js +99 -0
  46. package/lib/module/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.js.map +1 -0
  47. package/lib/module/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.js +31 -0
  48. package/lib/module/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.js.map +1 -0
  49. package/lib/module/index.js +8 -0
  50. package/lib/module/index.js.map +1 -1
  51. package/lib/module/models/ach/AchCollectableData.js +2 -0
  52. package/lib/module/models/ach/AchCollectableData.js.map +1 -0
  53. package/lib/module/models/ach/AchSteps.js +2 -0
  54. package/lib/module/models/ach/AchSteps.js.map +1 -0
  55. package/lib/typescript/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.d.ts +53 -0
  56. package/lib/typescript/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.d.ts.map +1 -0
  57. package/lib/typescript/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.d.ts +12 -0
  58. package/lib/typescript/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.d.ts.map +1 -0
  59. package/lib/typescript/index.d.ts +8 -0
  60. package/lib/typescript/index.d.ts.map +1 -1
  61. package/lib/typescript/models/PrimerCheckoutAdditionalInfo.d.ts +10 -0
  62. package/lib/typescript/models/PrimerCheckoutAdditionalInfo.d.ts.map +1 -1
  63. package/lib/typescript/models/PrimerSettings.d.ts +14 -0
  64. package/lib/typescript/models/PrimerSettings.d.ts.map +1 -1
  65. package/lib/typescript/models/ach/AchCollectableData.d.ts +25 -0
  66. package/lib/typescript/models/ach/AchCollectableData.d.ts.map +1 -0
  67. package/lib/typescript/models/ach/AchSteps.d.ts +28 -0
  68. package/lib/typescript/models/ach/AchSteps.d.ts.map +1 -0
  69. package/package.json +1 -1
  70. package/primer-io-react-native.podspec +1 -1
  71. package/src/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchManager.ts +158 -0
  72. package/src/HeadlessUniversalCheckout/Managers/PaymentMethodManagers/AchMandateManager.ts +32 -0
  73. package/src/index.tsx +8 -0
  74. package/src/models/PrimerCheckoutAdditionalInfo.ts +19 -5
  75. package/src/models/PrimerSettings.ts +19 -0
  76. package/src/models/ach/AchCollectableData.ts +29 -0
  77. package/src/models/ach/AchSteps.ts +29 -0
package/android/.project CHANGED
@@ -5,6 +5,11 @@
5
5
  <projects>
6
6
  </projects>
7
7
  <buildSpec>
8
+ <buildCommand>
9
+ <name>org.eclipse.jdt.core.javabuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
8
13
  <buildCommand>
9
14
  <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
10
15
  <arguments>
@@ -12,6 +17,7 @@
12
17
  </buildCommand>
13
18
  </buildSpec>
14
19
  <natures>
20
+ <nature>org.eclipse.jdt.core.javanature</nature>
15
21
  <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
16
22
  </natures>
17
23
  <filteredResources>
@@ -1,11 +1,11 @@
1
- arguments=
2
- auto.sync=false
1
+ arguments=--init-script /var/folders/wt/mqwpc4jx2kjgqf88mzm5qvx40000gn/T/d146c9752a26f79b52047fb6dc6ed385d064e120494f96f08ca63a317c41f94c.gradle --init-script /var/folders/wt/mqwpc4jx2kjgqf88mzm5qvx40000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
2
+ auto.sync=true
3
3
  build.scans.enabled=false
4
- connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(7.4.2))
5
- connection.project.dir=
4
+ connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
5
+ connection.project.dir=../../example/android
6
6
  eclipse.preferences.version=1
7
7
  gradle.user.home=
8
- java.home=/Library/Java/JavaVirtualMachines/jdk-11.0.14.jdk/Contents/Home
8
+ java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home
9
9
  jvm.arguments=
10
10
  offline.mode=false
11
11
  override.workspace.settings=true
@@ -40,7 +40,6 @@ android {
40
40
  group 'io.primer'
41
41
  namespace "com.primerioreactnative"
42
42
  compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
43
- buildToolsVersion getExtOrDefault('buildToolsVersion')
44
43
  defaultConfig {
45
44
  minSdkVersion 21
46
45
  targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
@@ -155,8 +154,7 @@ dependencies {
155
154
  api 'com.facebook.react:react-native:+'
156
155
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
157
156
  implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1"
158
- implementation 'io.primer:android:2.28.1'
159
-
157
+ implementation 'io.primer:android:2.31.0'
160
158
  testImplementation 'io.mockk:mockk:1.13.11'
161
159
  testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
162
160
  testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2'
@@ -1,4 +1 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.primerioreactnative">
3
-
4
- </manifest>
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"/>
@@ -14,9 +14,9 @@ import kotlinx.serialization.encodeToString
14
14
  import kotlinx.serialization.json.Json
15
15
  import org.json.JSONObject
16
16
 
17
- class PrimerRN(reactContext: ReactApplicationContext, private val json: Json) :
17
+ class PrimerRN(private val reactContext: ReactApplicationContext, private val json: Json) :
18
18
  ReactContextBaseJavaModule(reactContext) {
19
- private var mListener = PrimerRNEventListener()
19
+ private val mListener = PrimerRNEventListener()
20
20
 
21
21
  init {
22
22
  mListener.sendEvent = { eventName, paramsJson -> sendEvent(eventName, paramsJson) }
@@ -36,7 +36,7 @@ class PrimerRN(reactContext: ReactApplicationContext, private val json: Json) :
36
36
  if (settingsStr.isBlank()) PrimerSettingsRN() else json.decodeFromString(
37
37
  settingsStr
38
38
  )
39
- startSdk(settings.toPrimerSettings())
39
+ startSdk(settings.toPrimerSettings(reactContext))
40
40
  promise.resolve(null)
41
41
  } catch (e: Exception) {
42
42
  Log.e("PrimerRN", "configure settings error: $e")
@@ -5,6 +5,7 @@ import com.primerioreactnative.extensions.toCheckoutAdditionalInfoRN
5
5
  import com.primerioreactnative.extensions.toPrimerCheckoutDataRN
6
6
  import com.primerioreactnative.extensions.toPrimerClientSessionRN
7
7
  import com.primerioreactnative.extensions.toPrimerPaymentMethodDataRN
8
+ import com.primerioreactnative.extensions.removeType
8
9
  import com.primerioreactnative.utils.PrimerImplementedRNCallbacks
9
10
  import com.primerioreactnative.utils.errorTo
10
11
  import io.primer.android.PrimerCheckoutListener
@@ -45,7 +46,7 @@ class PrimerRNEventListener : PrimerCheckoutListener {
45
46
  PrimerEvents.ON_CHECKOUT_COMPLETE.eventName,
46
47
  JSONObject(Json.encodeToString(checkoutData.toPrimerCheckoutDataRN())).apply {
47
48
  val additionalInfoJson = optJSONObject(Keys.ADDITIONAL_INFO)
48
- additionalInfoJson?.remove("type")
49
+ additionalInfoJson?.removeType()
49
50
  putOpt(Keys.ADDITIONAL_INFO, additionalInfoJson)
50
51
  }
51
52
  )
@@ -158,7 +159,7 @@ class PrimerRNEventListener : PrimerCheckoutListener {
158
159
  sendEvent?.invoke(
159
160
  PrimerEvents.ON_RESUME_PENDING.eventName,
160
161
  JSONObject(Json.encodeToString(additionalInfo.toCheckoutAdditionalInfoRN())).apply {
161
- remove("type")
162
+ removeType()
162
163
  }
163
164
  )
164
165
  }
@@ -176,7 +177,7 @@ class PrimerRNEventListener : PrimerCheckoutListener {
176
177
  sendEvent?.invoke(
177
178
  PrimerEvents.ON_CHECKOUT_RECEIVED_ADDITIONAL_INFO.eventName,
178
179
  JSONObject(Json.encodeToString(additionalInfo.toCheckoutAdditionalInfoRN())).apply {
179
- remove("type")
180
+ removeType()
180
181
  }
181
182
  )
182
183
  }
@@ -21,7 +21,7 @@ class PrimerRNHeadlessUniversalCheckout(
21
21
  private val json: Json,
22
22
  ) : ReactContextBaseJavaModule(reactContext) {
23
23
 
24
- private val listener = PrimerRNHeadlessUniversalCheckoutListener()
24
+ private val listener = PrimerRNHeadlessUniversalCheckoutListener(reactContext)
25
25
 
26
26
  init {
27
27
  listener.sendEvent = { eventName, paramsJson -> sendEvent(eventName, paramsJson) }
@@ -49,7 +49,7 @@ class PrimerRNHeadlessUniversalCheckout(
49
49
  PrimerHeadlessUniversalCheckout.current.start(
50
50
  reactContext,
51
51
  clientToken,
52
- settings.toPrimerSettings(),
52
+ settings.toPrimerSettings(reactContext),
53
53
  listener,
54
54
  listener
55
55
  )
@@ -1,14 +1,18 @@
1
1
  package com.primerioreactnative
2
2
 
3
3
  import com.facebook.react.bridge.Promise
4
+ import com.facebook.react.bridge.ReactContext
4
5
  import com.primerioreactnative.components.datamodels.core.PrimerRNAvailablePaymentMethods
5
6
  import com.primerioreactnative.components.datamodels.core.toPrimerRNHeadlessUniversalCheckoutPaymentMethod
6
7
  import com.primerioreactnative.components.events.PrimerHeadlessUniversalCheckoutEvent
8
+ import com.primerioreactnative.components.manager.ach.PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent
9
+ import com.primerioreactnative.components.manager.ach.PrimerRNAchMandateManager
7
10
  import com.primerioreactnative.datamodels.*
8
11
  import com.primerioreactnative.extensions.toCheckoutAdditionalInfoRN
9
12
  import com.primerioreactnative.extensions.toPrimerCheckoutDataRN
10
13
  import com.primerioreactnative.extensions.toPrimerClientSessionRN
11
14
  import com.primerioreactnative.extensions.toPrimerPaymentMethodDataRN
15
+ import com.primerioreactnative.extensions.removeType
12
16
  import com.primerioreactnative.utils.PrimerHeadlessUniversalCheckoutImplementedRNCallbacks
13
17
  import com.primerioreactnative.utils.errorTo
14
18
  import com.primerioreactnative.utils.toWritableMap
@@ -23,13 +27,18 @@ import io.primer.android.domain.error.models.PrimerError
23
27
  import io.primer.android.domain.payments.additionalInfo.MultibancoCheckoutAdditionalInfo
24
28
  import io.primer.android.domain.payments.additionalInfo.PrimerCheckoutAdditionalInfo
25
29
  import io.primer.android.domain.payments.additionalInfo.PromptPayCheckoutAdditionalInfo
30
+ import io.primer.android.domain.payments.additionalInfo.AchAdditionalInfo
26
31
  import io.primer.android.domain.tokenization.models.PrimerPaymentMethodData
27
32
  import io.primer.android.domain.tokenization.models.PrimerPaymentMethodTokenData
33
+ import io.primer.android.Primer
28
34
  import kotlinx.serialization.encodeToString
29
35
  import kotlinx.serialization.json.Json
30
36
  import org.json.JSONObject
37
+ import androidx.activity.ComponentActivity
31
38
 
32
- class PrimerRNHeadlessUniversalCheckoutListener : PrimerHeadlessUniversalCheckoutListener,
39
+ class PrimerRNHeadlessUniversalCheckoutListener(
40
+ private val reactContext: ReactContext
41
+ ) : PrimerHeadlessUniversalCheckoutListener,
33
42
  PrimerHeadlessUniversalCheckoutUiListener {
34
43
  private var paymentCreationDecisionHandler: ((errorMessage: String?) -> Unit)? = null
35
44
  private var tokenizeSuccessDecisionHandler: ((resumeToken: String?, errorMessage: String?) -> Unit)? =
@@ -113,7 +122,7 @@ class PrimerRNHeadlessUniversalCheckoutListener : PrimerHeadlessUniversalCheckou
113
122
  PrimerHeadlessUniversalCheckoutEvent.ON_CHECKOUT_COMPLETE.eventName,
114
123
  JSONObject(Json.encodeToString(checkoutData.toPrimerCheckoutDataRN())).apply {
115
124
  val additionalInfoJson = optJSONObject(Keys.ADDITIONAL_INFO)
116
- additionalInfoJson?.remove("type")
125
+ additionalInfoJson?.removeType()
117
126
  putOpt(Keys.ADDITIONAL_INFO, additionalInfoJson)
118
127
  }
119
128
  )
@@ -225,7 +234,7 @@ class PrimerRNHeadlessUniversalCheckoutListener : PrimerHeadlessUniversalCheckou
225
234
  sendEvent?.invoke(
226
235
  PrimerHeadlessUniversalCheckoutEvent.ON_CHECKOUT_PENDING.eventName,
227
236
  JSONObject(Json.encodeToString(additionalInfo.toCheckoutAdditionalInfoRN())).apply {
228
- remove("type")
237
+ removeType()
229
238
  }
230
239
  )
231
240
  }
@@ -240,13 +249,37 @@ class PrimerRNHeadlessUniversalCheckoutListener : PrimerHeadlessUniversalCheckou
240
249
 
241
250
  override fun onCheckoutAdditionalInfoReceived(additionalInfo: PrimerCheckoutAdditionalInfo) {
242
251
  if (implementedRNCallbacks?.isOnCheckoutAdditionalInfoImplemented == true) {
243
- if (additionalInfo is PromptPayCheckoutAdditionalInfo) {
244
- sendEvent?.invoke(
245
- PrimerHeadlessUniversalCheckoutEvent.ON_CHECKOUT_ADDITIONAL_INFO.eventName,
246
- JSONObject(Json.encodeToString(additionalInfo.toCheckoutAdditionalInfoRN())).apply {
247
- remove("type")
252
+ when (additionalInfo) {
253
+ is PromptPayCheckoutAdditionalInfo -> {
254
+ sendEvent?.invoke(
255
+ PrimerHeadlessUniversalCheckoutEvent.ON_CHECKOUT_ADDITIONAL_INFO.eventName,
256
+ JSONObject(json.encodeToString(additionalInfo.toCheckoutAdditionalInfoRN())).apply {
257
+ removeType()
258
+ }
259
+ )
260
+ }
261
+ is AchAdditionalInfo.ProvideActivityResultRegistry -> {
262
+ val activityResultRegistry = (reactContext.currentActivity as? ComponentActivity)?.activityResultRegistry
263
+ if (activityResultRegistry == null) {
264
+ sendError?.invoke(
265
+ ErrorTypeRN.NativeBridgeFailed
266
+ errorTo PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent.UNSUPPORTED_ACTIVITY_ERROR
267
+ )
268
+ } else {
269
+ additionalInfo.provide(activityResultRegistry)
248
270
  }
249
- )
271
+ }
272
+
273
+ is AchAdditionalInfo.DisplayMandate -> {
274
+ PrimerRNAchMandateManager.acceptMandate = additionalInfo.onAcceptMandate
275
+ PrimerRNAchMandateManager.declineMandate = additionalInfo.onDeclineMandate
276
+ sendEvent?.invoke(
277
+ PrimerHeadlessUniversalCheckoutEvent.ON_CHECKOUT_ADDITIONAL_INFO.eventName,
278
+ JSONObject(json.encodeToString(additionalInfo.toCheckoutAdditionalInfoRN())).apply {
279
+ removeType()
280
+ }
281
+ )
282
+ }
250
283
  }
251
284
  } else {
252
285
  sendError?.invoke(
@@ -331,4 +364,8 @@ class PrimerRNHeadlessUniversalCheckoutListener : PrimerHeadlessUniversalCheckou
331
364
  resumeSuccessDecisionHandler = null
332
365
  implementedRNCallbacks = null
333
366
  }
367
+
368
+ private companion object {
369
+ val json = Json { encodeDefaults = true }
370
+ }
334
371
  }
@@ -11,6 +11,8 @@ import com.primerioreactnative.components.manager.vault.PrimerRNHeadlessUniversa
11
11
  import com.primerioreactnative.components.manager.redirect.PrimerRNHeadlessUniversalCheckoutBanksComponent
12
12
  import com.primerioreactnative.components.manager.klarna.PrimerRNHeadlessUniversalCheckoutKlarnaComponent
13
13
  import com.primerioreactnative.components.manager.klarna.PrimerKlarnaPaymentViewManager
14
+ import com.primerioreactnative.components.manager.ach.PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent
15
+ import com.primerioreactnative.components.manager.ach.PrimerRNAchMandateManager
14
16
  import kotlinx.serialization.json.Json
15
17
 
16
18
  class ReactNativePackage : ReactPackage {
@@ -26,6 +28,8 @@ class ReactNativePackage : ReactPackage {
26
28
  PrimerRNHeadlessUniversalCheckoutAssetManager(reactContext),
27
29
  PrimerRNHeadlessUniversalCheckoutVaultManager(reactContext, json),
28
30
  PrimerRNHeadlessUniversalCheckoutKlarnaComponent(reactContext),
31
+ PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent(reactContext),
32
+ PrimerRNAchMandateManager(reactContext),
29
33
  //region Component with redirect components
30
34
  PrimerRNHeadlessUniversalCheckoutBanksComponent(reactContext)
31
35
  //endregion
@@ -0,0 +1,70 @@
1
+ package com.primerioreactnative.components.manager.ach
2
+
3
+ import androidx.lifecycle.LifecycleOwner
4
+ import androidx.lifecycle.lifecycleScope
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
8
+ import com.facebook.react.bridge.ReactMethod
9
+ import com.primerioreactnative.datamodels.ErrorTypeRN
10
+ import com.primerioreactnative.utils.errorTo
11
+ import kotlinx.coroutines.launch
12
+
13
+ class PrimerRNAchMandateManager(
14
+ private val reactContext: ReactApplicationContext,
15
+ ) : ReactContextBaseJavaModule(reactContext) {
16
+ override fun getName() = "RNTAchMandateManager"
17
+
18
+ @ReactMethod
19
+ fun acceptMandate(promise: Promise) {
20
+ executeMandateAction(promise, PrimerRNAchMandateManager.Companion.acceptMandate, UNITIALIZED_ACCEPT_MANDATE)
21
+ }
22
+
23
+ @ReactMethod
24
+ fun declineMandate(promise: Promise) {
25
+ executeMandateAction(promise, PrimerRNAchMandateManager.Companion.declineMandate, UNITIALIZED_DECLINE_MANDATE)
26
+ }
27
+
28
+ private fun executeMandateAction(promise: Promise, action: (suspend () -> Unit)?, error: String) {
29
+ val lifecycleScope = getLifecycleScopeOrNull()
30
+
31
+ if (lifecycleScope == null) {
32
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo UNSUPPORTED_ACTIVITY_ERROR
33
+ promise.reject(exception.errorId, exception.description)
34
+ } else {
35
+ lifecycleScope.launch {
36
+ if (action == null) {
37
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo error
38
+ promise.reject(exception.errorId, exception.description)
39
+ } else {
40
+ action()
41
+ promise.resolve(null)
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ private fun getLifecycleScopeOrNull() =
48
+ (reactContext.currentActivity as? LifecycleOwner)?.lifecycleScope
49
+
50
+ companion object {
51
+ var acceptMandate: (suspend () -> Unit)? = null
52
+ var declineMandate: (suspend () -> Unit)? = null
53
+
54
+ val UNSUPPORTED_ACTIVITY_ERROR =
55
+ """
56
+ Unsupported activity type.
57
+ Make sure your root activity extends LifecycleOwner.
58
+ """.trimIndent()
59
+
60
+ val UNITIALIZED_ACCEPT_MANDATE =
61
+ """
62
+ Unitialized 'acceptMandate' property.
63
+ """.trimIndent()
64
+
65
+ val UNITIALIZED_DECLINE_MANDATE =
66
+ """
67
+ Unitialized 'declineMandate' property.
68
+ """.trimIndent()
69
+ }
70
+ }
@@ -0,0 +1,283 @@
1
+ package com.primerioreactnative.components.manager.ach
2
+
3
+ import androidx.activity.ComponentActivity
4
+ import androidx.lifecycle.LifecycleOwner
5
+ import androidx.lifecycle.ViewModelStoreOwner
6
+ import androidx.lifecycle.lifecycleScope
7
+ import com.facebook.react.bridge.Arguments
8
+ import com.facebook.react.bridge.Promise
9
+ import com.facebook.react.bridge.ReactApplicationContext
10
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
11
+ import com.facebook.react.bridge.ReactMethod
12
+ import com.facebook.react.bridge.WritableArray
13
+ import com.facebook.react.bridge.WritableMap
14
+ import com.facebook.react.modules.core.DeviceEventManagerModule
15
+ import com.primerioreactnative.PrimerRNViewModelStoreOwner
16
+ import com.primerioreactnative.components.events.PrimerHeadlessUniversalCheckoutComponentEvent
17
+ import com.primerioreactnative.datamodels.ErrorTypeRN
18
+ import com.primerioreactnative.datamodels.PrimerValidationErrorRN
19
+ import com.primerioreactnative.datamodels.extensions.ach.toUserDetailsCollectedRN
20
+ import com.primerioreactnative.datamodels.extensions.ach.toEmailAddressRN
21
+ import com.primerioreactnative.datamodels.extensions.ach.toFirstNameRN
22
+ import com.primerioreactnative.datamodels.extensions.ach.toLastNameRN
23
+ import com.primerioreactnative.datamodels.extensions.ach.toUserDetailsRetrievedRN
24
+ import com.primerioreactnative.utils.convertJsonToArray
25
+ import com.primerioreactnative.utils.convertJsonToMap
26
+ import com.primerioreactnative.utils.errorTo
27
+ import io.primer.android.components.manager.ach.PrimerHeadlessUniversalCheckoutAchManager
28
+ import io.primer.android.components.manager.core.composable.PrimerValidationStatus
29
+ import io.primer.android.components.presentation.paymentMethods.nativeUi.stripe.ach.StripeAchUserDetailsComponent
30
+ import io.primer.android.components.presentation.paymentMethods.nativeUi.stripe.ach.composable.AchUserDetailsCollectableData
31
+ import io.primer.android.components.presentation.paymentMethods.nativeUi.stripe.ach.composable.AchUserDetailsStep
32
+ import io.primer.android.domain.error.models.PrimerError
33
+ import com.primerioreactnative.utils.toWritableArray
34
+ import com.primerioreactnative.utils.toWritableMap
35
+ import com.primerioreactnative.extensions.putErrors
36
+ import com.primerioreactnative.extensions.putValidationErrors
37
+ import kotlinx.coroutines.Job
38
+ import kotlinx.coroutines.coroutineScope
39
+ import kotlinx.coroutines.flow.collectLatest
40
+ import kotlinx.coroutines.launch
41
+ import kotlinx.serialization.encodeToString
42
+ import kotlinx.serialization.json.Json
43
+ import org.json.JSONArray
44
+ import org.json.JSONObject
45
+ import android.util.Log
46
+
47
+ class PrimerRNHeadlessUniversalCheckoutStripeAchUserDetailsComponent(
48
+ private val reactContext: ReactApplicationContext,
49
+ ) : ReactContextBaseJavaModule(reactContext) {
50
+ override fun getName() = "RNTPrimerHeadlessUniversalCheckoutStripeAchUserDetailsComponent"
51
+
52
+ private var job: Job? = null
53
+ private var viewModelStoreOwner: ViewModelStoreOwner? = null
54
+ private var component: StripeAchUserDetailsComponent? = null
55
+
56
+ @ReactMethod
57
+ fun configure(promise: Promise) {
58
+ val currentViewModelStoreOwner =
59
+ reactContext.currentActivity as? ViewModelStoreOwner
60
+ ?: run { PrimerRNViewModelStoreOwner() }
61
+
62
+ val activity = reactContext.currentActivity as? ComponentActivity
63
+
64
+ if (activity == null) {
65
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo UNSUPPORTED_ACTIVITY_ERROR
66
+ promise.reject(exception.errorId, exception.description)
67
+ return
68
+ }
69
+
70
+ viewModelStoreOwner = currentViewModelStoreOwner
71
+
72
+ runCatching {
73
+ PrimerHeadlessUniversalCheckoutAchManager(currentViewModelStoreOwner)
74
+ .provide<StripeAchUserDetailsComponent>("STRIPE_ACH")
75
+ }
76
+ .onSuccess { component = it }
77
+ .onFailure {
78
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo "$INITIALIZATION_ERROR: ${it.message}"
79
+ promise.reject(exception.errorId, exception.description)
80
+ return
81
+ }
82
+
83
+ val lifecycleScope = (activity as LifecycleOwner).lifecycleScope
84
+
85
+ job =
86
+ lifecycleScope.launch {
87
+ coroutineScope {
88
+ launch { configureStepListener() }
89
+
90
+ launch { configureValidationListener() }
91
+
92
+ launch { configureErrorListener() }
93
+ promise.resolve(null)
94
+ }
95
+ }
96
+ }
97
+
98
+ @ReactMethod
99
+ fun start(promise: Promise) {
100
+ if (component == null) {
101
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo UNINITIALIZED_ERROR
102
+ promise.reject(exception.errorId, exception.description)
103
+ } else {
104
+ component?.start()
105
+ promise.resolve(null)
106
+ }
107
+ }
108
+
109
+ @ReactMethod
110
+ fun submit(promise: Promise) {
111
+ if (component == null) {
112
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo UNINITIALIZED_ERROR
113
+ promise.reject(exception.errorId, exception.description)
114
+ } else {
115
+ component?.submit()
116
+ promise.resolve(null)
117
+ }
118
+ }
119
+
120
+ @ReactMethod
121
+ fun onSetFirstName(firstName: String, promise: Promise) {
122
+ if (component == null) {
123
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo UNINITIALIZED_ERROR
124
+ promise.reject(exception.errorId, exception.description)
125
+ } else {
126
+ component?.updateCollectedData(AchUserDetailsCollectableData.FirstName(firstName))
127
+ promise.resolve(null)
128
+ }
129
+ }
130
+
131
+ @ReactMethod
132
+ fun onSetLastName(lastName: String, promise: Promise) {
133
+ if (component == null) {
134
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo UNINITIALIZED_ERROR
135
+ promise.reject(exception.errorId, exception.description)
136
+ } else {
137
+ component?.updateCollectedData(AchUserDetailsCollectableData.LastName(lastName))
138
+ promise.resolve(null)
139
+ }
140
+ }
141
+
142
+ @ReactMethod
143
+ fun onSetEmailAddress(emailAddress: String, promise: Promise) {
144
+ if (component == null) {
145
+ val exception = ErrorTypeRN.NativeBridgeFailed errorTo UNINITIALIZED_ERROR
146
+ promise.reject(exception.errorId, exception.description)
147
+ } else {
148
+ component?.updateCollectedData(AchUserDetailsCollectableData.EmailAddress(emailAddress))
149
+ promise.resolve(null)
150
+ }
151
+ }
152
+
153
+ private suspend fun configureErrorListener() {
154
+ component?.componentError?.collectLatest { error ->
155
+ sendEvent(
156
+ PrimerHeadlessUniversalCheckoutComponentEvent.ON_ERROR.eventName,
157
+ JSONObject().apply { putErrors(error) }
158
+ )
159
+ }
160
+ }
161
+
162
+ private suspend fun configureStepListener() {
163
+ component?.componentStep?.collectLatest { step ->
164
+ when (step) {
165
+ is AchUserDetailsStep.UserDetailsRetrieved -> {
166
+ sendEvent(
167
+ PrimerHeadlessUniversalCheckoutComponentEvent.ON_STEP.eventName,
168
+ JSONObject(json.encodeToString(step.toUserDetailsRetrievedRN()))
169
+ )
170
+ }
171
+ is AchUserDetailsStep.UserDetailsCollected -> {
172
+ sendEvent(
173
+ PrimerHeadlessUniversalCheckoutComponentEvent.ON_STEP.eventName,
174
+ JSONObject(json.encodeToString(step.toUserDetailsCollectedRN()))
175
+ )
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ private suspend fun configureValidationListener() {
182
+ component?.componentValidationStatus?.collectLatest { validationStatus ->
183
+ when (validationStatus) {
184
+ is PrimerValidationStatus.Validating -> {
185
+ sendEvent(
186
+ PrimerHeadlessUniversalCheckoutComponentEvent.ON_VALIDATING.eventName,
187
+ JSONObject().apply {
188
+ putData(validationStatus.collectableData as AchUserDetailsCollectableData)
189
+ }
190
+ )
191
+ }
192
+ is PrimerValidationStatus.Invalid -> {
193
+ sendEvent(
194
+ PrimerHeadlessUniversalCheckoutComponentEvent.ON_IN_VALID.eventName,
195
+ JSONObject().apply {
196
+ putData(validationStatus.collectableData as AchUserDetailsCollectableData)
197
+ putValidationErrors(validationStatus.validationErrors)
198
+ }
199
+ )
200
+ }
201
+ is PrimerValidationStatus.Valid -> {
202
+ sendEvent(
203
+ PrimerHeadlessUniversalCheckoutComponentEvent.ON_VALID.eventName,
204
+ JSONObject().apply {
205
+ putData(validationStatus.collectableData as AchUserDetailsCollectableData)
206
+ }
207
+ )
208
+ }
209
+ is PrimerValidationStatus.Error -> {
210
+ sendEvent(
211
+ PrimerHeadlessUniversalCheckoutComponentEvent.ON_VALIDATION_ERROR.eventName,
212
+ JSONObject().apply {
213
+ putData(validationStatus.collectableData as AchUserDetailsCollectableData)
214
+ putErrors(validationStatus.error)
215
+ }
216
+ )
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ private fun JSONObject.putData(collectableData: AchUserDetailsCollectableData) {
223
+ put(
224
+ "data",
225
+ JSONObject(
226
+ json.encodeToString(
227
+ when (collectableData) {
228
+ is AchUserDetailsCollectableData.FirstName ->
229
+ collectableData.toFirstNameRN()
230
+ is AchUserDetailsCollectableData.LastName -> collectableData.toLastNameRN()
231
+ is AchUserDetailsCollectableData.EmailAddress ->
232
+ collectableData.toEmailAddressRN()
233
+ }
234
+ )
235
+ )
236
+ )
237
+ }
238
+
239
+ @ReactMethod fun addListener(eventName: String?) = Unit
240
+
241
+ @ReactMethod fun removeListeners(count: Int?) = Unit
242
+
243
+ private fun sendEvent(name: String, data: JSONObject?) {
244
+ reactApplicationContext
245
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
246
+ .emit(name, data.toWritableMap())
247
+ }
248
+
249
+ private fun sendEvent(name: String, data: JSONArray?) {
250
+ reactApplicationContext
251
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
252
+ .emit(name, data.toWritableArray())
253
+ }
254
+
255
+ @ReactMethod
256
+ fun cleanUp(promise: Promise) {
257
+ job?.cancel()
258
+ job = null
259
+ component = null
260
+ viewModelStoreOwner?.viewModelStore?.clear()
261
+ promise.resolve(null)
262
+ }
263
+
264
+ companion object {
265
+ const val INITIALIZATION_ERROR = """
266
+ An error occurred during component initialization.
267
+ """
268
+
269
+ const val UNINITIALIZED_ERROR =
270
+ """
271
+ The component has not been initialized.
272
+ Make sure you have initialized the `component` first.
273
+ """
274
+
275
+ const val UNSUPPORTED_ACTIVITY_ERROR =
276
+ """
277
+ Unsupported activity type.
278
+ Make sure your root activity extends ComponentActivity.
279
+ """
280
+
281
+ val json = Json { encodeDefaults = true }
282
+ }
283
+ }