judokit-react-native 3.4.7 → 3.5.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.
package/JudoPay.tsx CHANGED
@@ -13,7 +13,10 @@ export {
13
13
  JudoTransactionType,
14
14
  JudoTransactionMode,
15
15
  JudoPaymentMethod,
16
- JudoCardNetwork
16
+ JudoCardNetwork,
17
+ ChallengeRequestIndicator,
18
+ JudoThreeDSButtonType,
19
+ ScaExemption
17
20
  } from './types/JudoTypes'
18
21
 
19
22
  export type {
@@ -25,7 +28,13 @@ export type {
25
28
  JudoTheme,
26
29
  JudoResponse,
27
30
  JudoConfiguration,
28
- JudoAuthorization
31
+ JudoAuthorization,
32
+ NetworkTimeout,
33
+ JudoThreeDSButtonCustomization,
34
+ JudoThreeDSLabelCustomization,
35
+ JudoThreeDSTextBoxCustomization,
36
+ JudoThreeDSToolbarCustomization,
37
+ JudoThreeDSUIConfiguration
29
38
  } from './types/JudoTypes'
30
39
 
31
40
  export {
@@ -58,7 +67,6 @@ export type { JudoPBBAConfiguration } from './types/JudoPBBATypes'
58
67
  export { JudoPBBAButton }
59
68
 
60
69
  class JudoPay {
61
-
62
70
  //------------------------------------------------------------------
63
71
  // Private properties
64
72
  //------------------------------------------------------------------
@@ -114,7 +122,9 @@ class JudoPay {
114
122
  *
115
123
  * @returns an asynchronous boolean value that indicates if ApplePay is available.
116
124
  */
117
- public isApplePayAvailableWithConfiguration(configuration: JudoConfiguration): Promise<boolean> {
125
+ public isApplePayAvailableWithConfiguration(
126
+ configuration: JudoConfiguration
127
+ ): Promise<boolean> {
118
128
  const params = this.generateJudoParameters(configuration)
119
129
  return NativeModules.RNJudo.isApplePayAvailableWithConfiguration(params)
120
130
  }
@@ -139,6 +149,18 @@ class JudoPay {
139
149
  return NativeModules.RNJudo.invokeTransaction(params)
140
150
  }
141
151
 
152
+ public async fetchTransactionDetails(
153
+ receiptId: string
154
+ ): Promise<JudoResponse> {
155
+ const params = {
156
+ authorization: this.generateAuthorizationParameters(),
157
+ sandboxed: this.isSandboxed,
158
+ receiptId
159
+ }
160
+
161
+ return NativeModules.RNJudo.fetchTransactionDetails(params)
162
+ }
163
+
142
164
  /**
143
165
  * A method for completing a payment/pre-auth transaction using a saved card token.
144
166
  *
@@ -155,8 +177,8 @@ class JudoPay {
155
177
  mode: JudoTransactionMode,
156
178
  configuration: JudoConfiguration,
157
179
  cardToken: string,
158
- securityCode: string,
159
- cardholderName: string,
180
+ securityCode: string | undefined | null,
181
+ cardholderName: string | undefined | null,
160
182
  cardScheme: string
161
183
  ): Promise<JudoResponse> {
162
184
  const params = this.generateTransactionModeParameters(
@@ -295,7 +317,10 @@ class JudoPay {
295
317
  }
296
318
  }
297
319
 
298
- private readonly generateAuthorizationParameters = (): Record<string, any> => {
320
+ private readonly generateAuthorizationParameters = (): Record<
321
+ string,
322
+ any
323
+ > => {
299
324
  if (this.authorization.secret) {
300
325
  return {
301
326
  token: this.authorization.token,
package/RNJudopay.podspec CHANGED
@@ -15,7 +15,7 @@ Pod::Spec.new do |s|
15
15
  s.source_files = "ios/Classes/**/*.{h,m}"
16
16
  s.requires_arc = true
17
17
  s.dependency "React"
18
- s.dependency "JudoKit-iOS", "3.1.6"
18
+ s.dependency "JudoKit-iOS", "3.1.10"
19
19
 
20
20
  s.test_spec 'RNJudoTests' do |test_spec|
21
21
  test_spec.source_files = 'ios/RNJudoTests/**/*.{h,m}'
@@ -9,7 +9,7 @@ import groovy.json.JsonSlurper
9
9
 
10
10
  def DEFAULT_COMPILE_SDK_VERSION = 31
11
11
  def DEFAULT_BUILD_TOOLS_VERSION = "30.0.3"
12
- def DEFAULT_MIN_SDK_VERSION = 19
12
+ def DEFAULT_MIN_SDK_VERSION = 21
13
13
  def DEFAULT_TARGET_SDK_VERSION = 31
14
14
 
15
15
  def safeExtGet(prop, fallback) {
@@ -101,7 +101,7 @@ dependencies {
101
101
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
102
102
  implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
103
103
 
104
- implementation 'com.judopay:judokit-android:3.0.10'
104
+ implementation 'com.judopay:judokit-android:3.0.13'
105
105
 
106
106
  //JUnit 5
107
107
  testImplementation "org.junit.jupiter:junit-jupiter-api:$junit5_version"
@@ -54,15 +54,15 @@ internal val ReadableMap.cardholderName: String?
54
54
  internal val ReadableMap.cardType: CardNetwork
55
55
  get() {
56
56
  val cardScheme = if (hasKey("cardScheme")) {
57
- getString("cardScheme") ?: ""
57
+ getString("cardScheme")?.lowercase() ?: ""
58
58
  } else {
59
59
  ""
60
60
  }
61
61
 
62
- return when (cardScheme.lowercase()) {
63
- CARD_SCHEME_VISA -> CardNetwork.VISA
64
- CARD_SCHEME_MASTERCARD -> CardNetwork.MASTERCARD
65
- CARD_SCHEME_AMEX -> CardNetwork.AMEX
62
+ return when {
63
+ cardScheme.contains(CARD_SCHEME_VISA) -> CardNetwork.VISA
64
+ cardScheme.contains(CARD_SCHEME_MASTERCARD) -> CardNetwork.MASTERCARD
65
+ cardScheme.contains(CARD_SCHEME_AMEX) -> CardNetwork.AMEX
66
66
  else -> CardNetwork.OTHER
67
67
  }
68
68
  }
@@ -70,6 +70,9 @@ internal val ReadableMap.cardType: CardNetwork
70
70
  internal val ReadableMap.judoId: String?
71
71
  get() = configuration?.getString("judoId")
72
72
 
73
+ internal val ReadableMap.receiptId: String?
74
+ get() = getString("receiptId")
75
+
73
76
  internal val ReadableMap.token: String?
74
77
  get() = authorization?.getString("token")
75
78
 
@@ -100,6 +103,9 @@ internal val ReadableMap.paymentReference: String?
100
103
  internal val ReadableMap.isInitialRecurringPayment: Boolean?
101
104
  get() = configuration?.getBoolean("isInitialRecurringPayment")
102
105
 
106
+ internal val ReadableMap.isDelayedAuthorisation: Boolean?
107
+ get() = configuration?.getBoolean("isDelayedAuthorisation")
108
+
103
109
  internal val ReadableMap.networkTimeout: ReadableMap?
104
110
  get() {
105
111
  if (configuration.hasKey("networkTimeout")) {
@@ -170,6 +176,15 @@ internal val ReadableMap.cardAddressCountryCode: Int?
170
176
  return null
171
177
  }
172
178
 
179
+ internal val ReadableMap.cardAddressState: String?
180
+ get() {
181
+ if (cardAddress.hasKey("state")) {
182
+ return cardAddress?.getString("state")
183
+ }
184
+
185
+ return null
186
+ }
187
+
173
188
  internal val ReadableMap.networkConnectTimeout: Long?
174
189
  get() = networkTimeout?.getDouble("connectTimeout")?.toLong()
175
190
 
@@ -267,6 +282,182 @@ internal val ReadableMap.uiConfiguration: ReadableMap?
267
282
  return null
268
283
  }
269
284
 
285
+ internal val ReadableMap.threeDSUIConfiguration: ReadableMap?
286
+ get() {
287
+ if (uiConfiguration.hasKey("threeDSUIConfiguration")) {
288
+ return uiConfiguration?.getMap("threeDSUIConfiguration")
289
+ }
290
+ return null
291
+ }
292
+
293
+ internal val ReadableMap.threeDSUITextBoxCustomization: ReadableMap?
294
+ get() {
295
+ if (threeDSUIConfiguration.hasKey("textBoxCustomization")) {
296
+ return threeDSUIConfiguration?.getMap("textBoxCustomization")
297
+ }
298
+ return null
299
+ }
300
+
301
+ internal val ReadableMap.threeDSUIButtonCustomizations: ReadableMap?
302
+ get() {
303
+ if (threeDSUIConfiguration.hasKey("buttonCustomizations")) {
304
+ return threeDSUIConfiguration?.getMap("buttonCustomizations")
305
+ }
306
+ return null
307
+ }
308
+
309
+ internal val ReadableMap.threeDSUISubmitButtonCustomization: ReadableMap?
310
+ get() {
311
+ if (threeDSUIButtonCustomizations.hasKey("SUBMIT")) {
312
+ return threeDSUIButtonCustomizations?.getMap("SUBMIT")
313
+ }
314
+ return null
315
+ }
316
+
317
+ internal val ReadableMap.threeDSUIContinueButtonCustomization: ReadableMap?
318
+ get() {
319
+ if (threeDSUIButtonCustomizations.hasKey("CONTINUE")) {
320
+ return threeDSUIButtonCustomizations?.getMap("CONTINUE")
321
+ }
322
+ return null
323
+ }
324
+
325
+ internal val ReadableMap.threeDSUINextButtonCustomization: ReadableMap?
326
+ get() {
327
+ if (threeDSUIButtonCustomizations.hasKey("NEXT")) {
328
+ return threeDSUIButtonCustomizations?.getMap("NEXT")
329
+ }
330
+ return null
331
+ }
332
+
333
+ internal val ReadableMap.threeDSUICancelButtonCustomization: ReadableMap?
334
+ get() {
335
+ if (threeDSUIButtonCustomizations.hasKey("CANCEL")) {
336
+ return threeDSUIButtonCustomizations?.getMap("CANCEL")
337
+ }
338
+ return null
339
+ }
340
+
341
+ internal val ReadableMap.threeDSUIResendButtonCustomization: ReadableMap?
342
+ get() {
343
+ if (threeDSUIButtonCustomizations.hasKey("RESEND")) {
344
+ return threeDSUIButtonCustomizations?.getMap("RESEND")
345
+ }
346
+ return null
347
+ }
348
+
349
+ internal val ReadableMap.threeDSUIToolbarCustomization: ReadableMap?
350
+ get() {
351
+ if (threeDSUIConfiguration.hasKey("toolbarCustomization")) {
352
+ return threeDSUIConfiguration?.getMap("toolbarCustomization")
353
+ }
354
+ return null
355
+ }
356
+
357
+ internal val ReadableMap.threeDSUILabelCustomization: ReadableMap?
358
+ get() {
359
+ if (threeDSUIConfiguration.hasKey("labelCustomization")) {
360
+ return threeDSUIConfiguration?.getMap("labelCustomization")
361
+ }
362
+ return null
363
+ }
364
+
365
+ internal val ReadableMap.textFontName: String?
366
+ get() {
367
+ if (hasKey("textFontName")) {
368
+ return getString("textFontName")
369
+ }
370
+ return null
371
+ }
372
+
373
+ internal val ReadableMap.textColor: String?
374
+ get() {
375
+ if (hasKey("textColor")) {
376
+ return getString("textColor")
377
+ }
378
+ return null
379
+ }
380
+
381
+ internal val ReadableMap.backgroundColor: String?
382
+ get() {
383
+ if (hasKey("backgroundColor")) {
384
+ return getString("backgroundColor")
385
+ }
386
+ return null
387
+ }
388
+
389
+ internal val ReadableMap.headerText: String?
390
+ get() {
391
+ if (hasKey("headerText")) {
392
+ return getString("headerText")
393
+ }
394
+ return null
395
+ }
396
+
397
+ internal val ReadableMap.buttonText: String?
398
+ get() {
399
+ if (hasKey("buttonText")) {
400
+ return getString("buttonText")
401
+ }
402
+ return null
403
+ }
404
+
405
+ internal val ReadableMap.headingTextFontName: String?
406
+ get() {
407
+ if (hasKey("headingTextFontName")) {
408
+ return getString("headingTextFontName")
409
+ }
410
+ return null
411
+ }
412
+
413
+ internal val ReadableMap.headingTextColor: String?
414
+ get() {
415
+ if (hasKey("headingTextColor")) {
416
+ return getString("headingTextColor")
417
+ }
418
+ return null
419
+ }
420
+
421
+ internal val ReadableMap.borderColor: String?
422
+ get() {
423
+ if (hasKey("borderColor")) {
424
+ return getString("borderColor")
425
+ }
426
+ return null
427
+ }
428
+
429
+ internal val ReadableMap.textFontSize: Int?
430
+ get() {
431
+ if (hasKey("textFontSize")) {
432
+ return getInt("textFontSize")
433
+ }
434
+ return null
435
+ }
436
+
437
+ internal val ReadableMap.cornerRadius: Int?
438
+ get() {
439
+ if (hasKey("cornerRadius")) {
440
+ return getInt("cornerRadius")
441
+ }
442
+ return null
443
+ }
444
+
445
+ internal val ReadableMap.headingTextFontSize: Int?
446
+ get() {
447
+ if (hasKey("headingTextFontSize")) {
448
+ return getInt("headingTextFontSize")
449
+ }
450
+ return null
451
+ }
452
+
453
+ internal val ReadableMap.borderWidth: Int?
454
+ get() {
455
+ if (hasKey("borderWidth")) {
456
+ return getInt("borderWidth")
457
+ }
458
+ return null
459
+ }
460
+
270
461
  internal val ReadableMap.isAVSEnabled: Boolean?
271
462
  get() = uiConfiguration?.getBoolean("isAVSEnabled")
272
463
 
@@ -5,6 +5,11 @@ import android.os.Bundle
5
5
  import com.facebook.react.bridge.Arguments
6
6
  import com.facebook.react.bridge.ReadableMap
7
7
  import com.facebook.react.bridge.WritableMap
8
+ import com.judopay.judo3ds2.customization.ButtonCustomization
9
+ import com.judopay.judo3ds2.customization.LabelCustomization
10
+ import com.judopay.judo3ds2.customization.TextBoxCustomization
11
+ import com.judopay.judo3ds2.customization.ToolbarCustomization
12
+ import com.judopay.judo3ds2.customization.UiCustomization
8
13
  import com.judopay.judokit.android.Judo
9
14
  import com.judopay.judokit.android.api.model.Authorization
10
15
  import com.judopay.judokit.android.api.model.BasicAuthorization
@@ -87,6 +92,7 @@ internal fun getMappedResult(result: JudoResult?): WritableMap {
87
92
  cardDetailsMap.putString("cardCountry", result?.cardDetails?.country)
88
93
  cardDetailsMap.putString("cardFunding", result?.cardDetails?.funding)
89
94
  cardDetailsMap.putString("cardScheme", result?.cardDetails?.scheme)
95
+ cardDetailsMap.putString("cardHolderName", result?.cardDetails?.cardHolderName)
90
96
 
91
97
  map.putMap("cardDetails", cardDetailsMap)
92
98
 
@@ -99,6 +105,27 @@ internal fun getMappedResult(result: JudoResult?): WritableMap {
99
105
  return map
100
106
  }
101
107
 
108
+ internal fun getJudoConfigurationForApiService(options: ReadableMap): Judo {
109
+ val authorization = getAuthorization(options)
110
+
111
+ val amount = Amount.Builder()
112
+ .setAmount("0.00")
113
+ .setCurrency(Currency.GBP)
114
+ .build()
115
+
116
+ val reference = Reference.Builder()
117
+ .setConsumerReference("reference")
118
+ .build()
119
+
120
+ return Judo.Builder(PaymentWidgetType.CARD_PAYMENT)
121
+ .setIsSandboxed(options.isSandboxed)
122
+ .setJudoId("000000")
123
+ .setAuthorization(authorization)
124
+ .setAmount(amount)
125
+ .setReference(reference)
126
+ .build()
127
+ }
128
+
102
129
  internal fun getJudoConfiguration(type: PaymentWidgetType, options: ReadableMap): Judo {
103
130
  val authorization = getAuthorization(options)
104
131
  val amount = getAmount(options)
@@ -127,6 +154,7 @@ internal fun getJudoConfiguration(type: PaymentWidgetType, options: ReadableMap)
127
154
  .setGooglePayConfiguration(googlePayConfiguration)
128
155
  .setPBBAConfiguration(pbbaConfiguration)
129
156
  .setInitialRecurringPayment(options.isInitialRecurringPayment)
157
+ .setDelayedAuthorisation(options.isDelayedAuthorisation)
130
158
  .setNetworkTimeout(timeouts)
131
159
  .setChallengeRequestIndicator(challengeRequestIndicator)
132
160
  .setScaExemption(scaExemption)
@@ -148,6 +176,7 @@ internal fun getAddress(options: ReadableMap): Address? {
148
176
  .setTown(options.cardAddressTown)
149
177
  .setPostCode(options.cardAddressPostCode)
150
178
  .setCountryCode(options.cardAddressCountryCode)
179
+ .setState(options.cardAddressState)
151
180
  .build()
152
181
  }
153
182
  return null
@@ -330,12 +359,110 @@ internal fun getUIConfiguration(options: ReadableMap): UiConfiguration? {
330
359
  .setShouldPaymentButtonDisplayAmount(options.shouldPaymentButtonDisplayAmount)
331
360
  .setShouldPaymentMethodsVerifySecurityCode(options.shouldPaymentMethodsVerifySecurityCode)
332
361
  .setShouldAskForBillingInformation(options.shouldAskForBillingInformation)
362
+ .setThreeDSUiCustomization(getThreeDSUiCustomization(options))
333
363
  .build()
334
364
  } else {
335
365
  null
336
366
  }
337
367
  }
338
368
 
369
+ internal fun getThreeDSUiCustomization(options: ReadableMap): UiCustomization? {
370
+ return if (options.threeDSUIConfiguration != null) {
371
+
372
+ val uiCustomization = UiCustomization()
373
+
374
+ options.threeDSUIToolbarCustomization?.let {
375
+ uiCustomization.setToolbarCustomization(ToolbarCustomization().apply {
376
+ setTextFontName(it.textFontName)
377
+ setTextColor(it.textColor)
378
+ setTextFontSize(it.textFontSize)
379
+ setBackgroundColor(it.backgroundColor)
380
+ setHeaderText(it.headerText)
381
+ setButtonText(it.buttonText)
382
+ })
383
+ }
384
+
385
+ options.threeDSUILabelCustomization?.let {
386
+ uiCustomization.setLabelCustomization(LabelCustomization().apply {
387
+ setTextFontName(it.textFontName)
388
+ setTextColor(it.textColor)
389
+ setTextFontSize(it.textFontSize)
390
+ setHeadingTextFontName(it.headingTextFontName)
391
+ setHeadingTextColor(it.headingTextColor)
392
+ setHeadingTextFontSize(it.headingTextFontSize)
393
+ })
394
+ }
395
+
396
+ options.threeDSUITextBoxCustomization?.let {
397
+ uiCustomization.setTextBoxCustomization(TextBoxCustomization().apply {
398
+ setTextFontName(it.textFontName)
399
+ setTextColor(it.textColor)
400
+ setTextFontSize(it.textFontSize)
401
+ setBorderWidth(it.borderWidth)
402
+ setBorderColor(it.borderColor)
403
+ setCornerRadius(it.cornerRadius)
404
+ })
405
+ }
406
+
407
+ if (options.threeDSUIButtonCustomizations != null) {
408
+
409
+ options.threeDSUISubmitButtonCustomization?.let {
410
+ uiCustomization.setButtonCustomization(ButtonCustomization().apply {
411
+ setTextFontName(it.textFontName)
412
+ setTextColor(it.textColor)
413
+ setTextFontSize(it.textFontSize)
414
+ setBackgroundColor(it.backgroundColor)
415
+ setCornerRadius(it.cornerRadius)
416
+ }, UiCustomization.ButtonType.SUBMIT)
417
+ }
418
+
419
+ options.threeDSUINextButtonCustomization?.let {
420
+ uiCustomization.setButtonCustomization(ButtonCustomization().apply {
421
+ setTextFontName(it.textFontName)
422
+ setTextColor(it.textColor)
423
+ setTextFontSize(it.textFontSize)
424
+ setBackgroundColor(it.backgroundColor)
425
+ setCornerRadius(it.cornerRadius)
426
+ }, UiCustomization.ButtonType.NEXT)
427
+ }
428
+
429
+ options.threeDSUIContinueButtonCustomization?.let {
430
+ uiCustomization.setButtonCustomization(ButtonCustomization().apply {
431
+ setTextFontName(it.textFontName)
432
+ setTextColor(it.textColor)
433
+ setTextFontSize(it.textFontSize)
434
+ setBackgroundColor(it.backgroundColor)
435
+ setCornerRadius(it.cornerRadius)
436
+ }, UiCustomization.ButtonType.CONTINUE)
437
+ }
438
+
439
+ options.threeDSUICancelButtonCustomization?.let {
440
+ uiCustomization.setButtonCustomization(ButtonCustomization().apply {
441
+ setTextFontName(it.textFontName)
442
+ setTextColor(it.textColor)
443
+ setTextFontSize(it.textFontSize)
444
+ setBackgroundColor(it.backgroundColor)
445
+ setCornerRadius(it.cornerRadius)
446
+ }, UiCustomization.ButtonType.CANCEL)
447
+ }
448
+
449
+ options.threeDSUIResendButtonCustomization?.let {
450
+ uiCustomization.setButtonCustomization(ButtonCustomization().apply {
451
+ setTextFontName(it.textFontName)
452
+ setTextColor(it.textColor)
453
+ setTextFontSize(it.textFontSize)
454
+ setBackgroundColor(it.backgroundColor)
455
+ setCornerRadius(it.cornerRadius)
456
+ }, UiCustomization.ButtonType.RESEND)
457
+ }
458
+ }
459
+
460
+ uiCustomization
461
+ } else {
462
+ null
463
+ }
464
+ }
465
+
339
466
  internal fun getPrimaryAccountDetails(options: ReadableMap): PrimaryAccountDetails? {
340
467
  return if (options.primaryAccountDetails != null) {
341
468
  PrimaryAccountDetails.Builder()
@@ -9,6 +9,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
9
9
  import com.facebook.react.bridge.*
10
10
  import com.judopay.judokit.android.Judo
11
11
  import com.judopay.judokit.android.api.JudoApiService
12
+ import com.judopay.judokit.android.api.error.toJudoError
12
13
  import com.judopay.judokit.android.api.factory.JudoApiServiceFactory
13
14
  import com.judopay.judokit.android.api.model.response.JudoApiCallResult
14
15
  import com.judopay.judokit.android.api.model.response.Receipt
@@ -31,12 +32,14 @@ import retrofit2.Response
31
32
 
32
33
  const val JUDO_PAYMENT_WIDGET_REQUEST_CODE = 65520
33
34
  const val JUDO_PROMISE_REJECTION_CODE = "JUDO_ERROR"
35
+ const val REQUEST_FAILED_MESSAGE = "The request was unsuccessful."
34
36
 
35
37
  class JudoReactNativeModule internal constructor(val context: ReactApplicationContext) :
36
38
  ReactContextBaseJavaModule(context), CardTransactionManagerResultListener {
37
39
 
38
40
  private val listener = JudoReactNativeActivityEventListener()
39
41
  internal var transactionPromise: Promise? = null
42
+ private var isSubscribedToCardTransactionResults = false
40
43
 
41
44
  /**
42
45
  * A broadcast receiver to catch Pay by Bank app /order/bank/sale response event.
@@ -107,11 +110,12 @@ class JudoReactNativeModule internal constructor(val context: ReactApplicationCo
107
110
  @ReactMethod
108
111
  fun performTokenTransaction(options: ReadableMap, promise: Promise) {
109
112
  try {
113
+ ensureIsSubscribedToCardTransactionResults()
114
+
110
115
  val activity = context.currentActivity as FragmentActivity
111
116
  val manager = CardTransactionManager.getInstance(activity)
112
117
 
113
118
  val judo = getTokenTransactionConfiguration(options)
114
-
115
119
  val cardToken = options.cardToken
116
120
 
117
121
  if (cardToken == null) {
@@ -119,6 +123,8 @@ class JudoReactNativeModule internal constructor(val context: ReactApplicationCo
119
123
  return
120
124
  }
121
125
 
126
+ manager.configureWith(judo)
127
+
122
128
  val details = TransactionDetails.Builder()
123
129
  .setCardHolderName(options.cardholderName)
124
130
  .setSecurityNumber(options.securityCode)
@@ -147,18 +153,84 @@ class JudoReactNativeModule internal constructor(val context: ReactApplicationCo
147
153
  }
148
154
  }
149
155
 
156
+ @ReactMethod
157
+ fun fetchTransactionDetails(options: ReadableMap, promise: Promise) {
158
+ try {
159
+ val judo = getJudoConfigurationForApiService(options)
160
+ val receiptId = options.receiptId ?: ""
161
+
162
+ val service = JudoApiServiceFactory.createApiService(context, judo)
163
+
164
+ val fetchTransactionDetailsCallback = object : Callback<JudoApiCallResult<Receipt>> {
165
+ override fun onResponse(
166
+ call: Call<JudoApiCallResult<Receipt>>,
167
+ response: Response<JudoApiCallResult<Receipt>>
168
+ ) {
169
+ when (val result = response.body()) {
170
+ is JudoApiCallResult.Success -> {
171
+ val receipt = result.data
172
+ if (receipt != null) {
173
+ val judoResult = receipt.toJudoResult()
174
+ promise.resolve(getMappedResult(judoResult))
175
+ } else {
176
+ promise.reject(JUDO_PROMISE_REJECTION_CODE, REQUEST_FAILED_MESSAGE)
177
+ }
178
+ }
179
+ is JudoApiCallResult.Failure -> {
180
+ val message = result.error?.message ?: REQUEST_FAILED_MESSAGE
181
+ promise.reject(JUDO_PROMISE_REJECTION_CODE, message)
182
+ }
183
+ else -> {
184
+ promise.reject(JUDO_PROMISE_REJECTION_CODE, REQUEST_FAILED_MESSAGE)
185
+ }
186
+ }
187
+ }
188
+
189
+ override fun onFailure(
190
+ call: Call<JudoApiCallResult<Receipt>>,
191
+ throwable: Throwable
192
+ ) {
193
+ promise.reject(JUDO_PROMISE_REJECTION_CODE, throwable.localizedMessage, throwable)
194
+ }
195
+ }
196
+
197
+ service
198
+ .fetchTransactionWithReceiptId(receiptId)
199
+ .enqueue(fetchTransactionDetailsCallback)
200
+
201
+ } catch (error: Exception) {
202
+ promise.reject(JUDO_PROMISE_REJECTION_CODE, error.localizedMessage, error)
203
+ }
204
+ }
205
+
206
+ private fun ensureIsSubscribedToCardTransactionResults() {
207
+ if (isSubscribedToCardTransactionResults) {
208
+ return
209
+ }
210
+
211
+ val activity = context.currentActivity
212
+ activity?.let {
213
+ isSubscribedToCardTransactionResults = true
214
+ CardTransactionManager.getInstance(activity as FragmentActivity).registerResultListener(this)
215
+ }
216
+ }
217
+
218
+ private fun unsubscribeFromCardTransactionResults() {
219
+ val activity = context.currentActivity
220
+ activity?.let {
221
+ isSubscribedToCardTransactionResults = false
222
+ CardTransactionManager.getInstance(activity as FragmentActivity).unRegisterResultListener(this)
223
+ }
224
+ }
225
+
150
226
  override fun initialize() {
151
227
  super.initialize()
152
-
153
- val activity = context.currentActivity as FragmentActivity
154
- CardTransactionManager.getInstance(activity).registerResultListener(this)
228
+ ensureIsSubscribedToCardTransactionResults()
155
229
  }
156
230
 
157
231
  override fun invalidate() {
158
232
  super.invalidate()
159
-
160
- val activity = context.currentActivity as FragmentActivity
161
- CardTransactionManager.getInstance(activity).unRegisterResultListener(this)
233
+ unsubscribeFromCardTransactionResults()
162
234
  }
163
235
 
164
236
  override fun onCardTransactionResult(result: JudoPaymentResult) {