judokit-react-native 3.4.8 → 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.8"
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}'
@@ -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.11'
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")) {
@@ -276,6 +282,182 @@ internal val ReadableMap.uiConfiguration: ReadableMap?
276
282
  return null
277
283
  }
278
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
+
279
461
  internal val ReadableMap.isAVSEnabled: Boolean?
280
462
  get() = uiConfiguration?.getBoolean("isAVSEnabled")
281
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)
@@ -331,12 +359,110 @@ internal fun getUIConfiguration(options: ReadableMap): UiConfiguration? {
331
359
  .setShouldPaymentButtonDisplayAmount(options.shouldPaymentButtonDisplayAmount)
332
360
  .setShouldPaymentMethodsVerifySecurityCode(options.shouldPaymentMethodsVerifySecurityCode)
333
361
  .setShouldAskForBillingInformation(options.shouldAskForBillingInformation)
362
+ .setThreeDSUiCustomization(getThreeDSUiCustomization(options))
334
363
  .build()
335
364
  } else {
336
365
  null
337
366
  }
338
367
  }
339
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
+
340
466
  internal fun getPrimaryAccountDetails(options: ReadableMap): PrimaryAccountDetails? {
341
467
  return if (options.primaryAccountDetails != null) {
342
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,6 +32,7 @@ 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 {
@@ -114,7 +116,6 @@ class JudoReactNativeModule internal constructor(val context: ReactApplicationCo
114
116
  val manager = CardTransactionManager.getInstance(activity)
115
117
 
116
118
  val judo = getTokenTransactionConfiguration(options)
117
-
118
119
  val cardToken = options.cardToken
119
120
 
120
121
  if (cardToken == null) {
@@ -122,6 +123,8 @@ class JudoReactNativeModule internal constructor(val context: ReactApplicationCo
122
123
  return
123
124
  }
124
125
 
126
+ manager.configureWith(judo)
127
+
125
128
  val details = TransactionDetails.Builder()
126
129
  .setCardHolderName(options.cardholderName)
127
130
  .setSecurityNumber(options.securityCode)
@@ -150,6 +153,56 @@ class JudoReactNativeModule internal constructor(val context: ReactApplicationCo
150
153
  }
151
154
  }
152
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
+
153
206
  private fun ensureIsSubscribedToCardTransactionResults() {
154
207
  if (isSubscribedToCardTransactionResults) {
155
208
  return
@@ -128,13 +128,19 @@ RCT_REMAP_METHOD(fetchTransactionDetails,
128
128
  properties:(NSDictionary *)properties
129
129
  fetchTransactionDetailsWithResolver:(RCTPromiseResolveBlock)resolve
130
130
  rejecter:(RCTPromiseRejectBlock)reject) {
131
+ @try {
132
+ self.apiService = [RNWrappers apiServiceFromProperties:properties];
133
+ self.completionBlock = [self completionBlockWithResolve:resolve andReject:reject];
131
134
 
132
- self.apiService = [RNWrappers apiServiceFromProperties:properties];
133
- self.completionBlock = [self completionBlockWithResolve:resolve andReject:reject];
134
-
135
- NSString *receiptId = [RNWrappers receiptIdFromProperties:properties];
135
+ NSString *receiptId = [RNWrappers receiptIdFromProperties:properties];
136
136
 
137
- [self.apiService fetchTransactionWithReceiptId:receiptId completion:self.completionBlock];
137
+ [self.apiService fetchTransactionWithReceiptId:receiptId completion:self.completionBlock];
138
+ } @catch (NSException *exception) {
139
+ NSError *error = [[NSError alloc] initWithDomain:RNJudoErrorDomain
140
+ code:0
141
+ userInfo:exception.userInfo];
142
+ reject(kJudoPromiseRejectionCode, exception.reason, error);
143
+ }
138
144
  }
139
145
 
140
146
  //----------------------------------------------
@@ -204,7 +210,17 @@ RCT_REMAP_METHOD(fetchTransactionDetails,
204
210
  message = description;
205
211
  }
206
212
 
207
- reject(kJudoPromiseRejectionCode, message, error);
213
+ // TODO: RCTJSErrorFromCodeMessageAndNSError expects an NSError instane in userInfo[NSUnderlyingErrorKey]
214
+ // which in case of a 3DS SDK error is a NSString ('JP3DSSDKRuntimeException') - so it crashes
215
+ // ! this should be fixed in JudoKit-iOS, and the folowing workaround removed ASAP
216
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
217
+ [userInfo removeObjectForKey:NSUnderlyingErrorKey];
218
+ NSError *myError = [[NSError alloc] initWithDomain:error.domain
219
+ code:error.code
220
+ userInfo:[NSDictionary dictionaryWithDictionary:userInfo]];
221
+
222
+
223
+ reject(kJudoPromiseRejectionCode, message, myError);
208
224
  } else {
209
225
  NSDictionary *mappedResponse = [RNWrappers dictionaryFromResponse:response];
210
226
  resolve(mappedResponse);
@@ -28,6 +28,7 @@
28
28
  #import "RNTypes.h"
29
29
  #import "NSDictionary+JudoConvert.h"
30
30
  #import "UIColor+RNAdditions.h"
31
+ #import <Judo3DS2_iOS/Judo3DS2_iOS.h>
31
32
 
32
33
  static NSString *const kCardSchemeVISA = @"visa";
33
34
  static NSString *const kCardSchemeMasterCard = @"mastercard";
@@ -129,6 +130,7 @@ static NSString *const kCardSchemeAMEX = @"amex";
129
130
 
130
131
  NSString *judoId = [configurationDict stringForKey:@"judoId"];
131
132
  NSNumber *isInitialRecurringPayment = [configurationDict optionalBoolForKey:@"isInitialRecurringPayment"];
133
+ NSNumber *isDelayedAuthorisation = [configurationDict optionalBoolForKey:@"isDelayedAuthorisation"];
132
134
 
133
135
  JPAmount *amount = [RNWrappers amountFromConfiguration:configurationDict];
134
136
  JPReference *reference = [RNWrappers referenceFromConfiguration:configurationDict];
@@ -137,7 +139,14 @@ static NSString *const kCardSchemeAMEX = @"amex";
137
139
  amount:amount
138
140
  reference:reference];
139
141
 
140
- configuration.isInitialRecurringPayment = isInitialRecurringPayment;
142
+ if (isInitialRecurringPayment) {
143
+ configuration.isInitialRecurringPayment = isInitialRecurringPayment.boolValue;
144
+ }
145
+
146
+ if (isDelayedAuthorisation) {
147
+ configuration.isDelayedAuthorisation = isDelayedAuthorisation.boolValue;
148
+ }
149
+
141
150
  configuration.uiConfiguration = [RNWrappers uiConfigurationFromConfiguration:configurationDict];
142
151
  configuration.supportedCardNetworks = [RNWrappers cardNetworksFromConfiguration:configurationDict];
143
152
  configuration.primaryAccountDetails = [RNWrappers accountDetailsFromConfiguration:configurationDict];
@@ -246,15 +255,15 @@ static NSString *const kCardSchemeAMEX = @"amex";
246
255
  + (JPCardNetworkType)cardTypeFromProperties:(NSDictionary *)properties {
247
256
  NSString *cardScheme = [properties optionalStringForKey:@"cardScheme"].lowercaseString;
248
257
 
249
- if ([kCardSchemeVISA isEqualToString:cardScheme]) {
258
+ if ([cardScheme containsString:kCardSchemeVISA]) {
250
259
  return JPCardNetworkTypeVisa;
251
260
  }
252
261
 
253
- if ([kCardSchemeMasterCard isEqualToString:cardScheme]) {
262
+ if ([cardScheme containsString:kCardSchemeMasterCard]) {
254
263
  return JPCardNetworkTypeMasterCard;
255
264
  }
256
265
 
257
- if ([kCardSchemeAMEX isEqualToString:cardScheme]) {
266
+ if ([cardScheme containsString:kCardSchemeAMEX]) {
258
267
  return JPCardNetworkTypeAMEX;
259
268
  }
260
269
 
@@ -342,26 +351,334 @@ static NSString *const kCardSchemeAMEX = @"amex";
342
351
  return uiConfiguration;
343
352
  }
344
353
 
345
- NSNumber *isAVSEnabled = [dictionary boolForKey:@"isAVSEnabled"];
346
- NSNumber *shouldDisplayAmount = [dictionary boolForKey:@"shouldPaymentMethodsDisplayAmount"];
347
- NSNumber *isPayButtonAmountVisible = [dictionary boolForKey:@"shouldPaymentButtonDisplayAmount"];
348
- NSNumber *isSecureCodeCheckEnabled = [dictionary boolForKey:@"shouldPaymentMethodsVerifySecurityCode"];
354
+ NSNumber *isAVSEnabled = [dictionary optionalBoolForKey:@"isAVSEnabled"];
355
+ NSNumber *shouldDisplayAmount = [dictionary optionalBoolForKey:@"shouldPaymentMethodsDisplayAmount"];
356
+ NSNumber *isPayButtonAmountVisible = [dictionary optionalBoolForKey:@"shouldPaymentButtonDisplayAmount"];
357
+ NSNumber *isSecureCodeCheckEnabled = [dictionary optionalBoolForKey:@"shouldPaymentMethodsVerifySecurityCode"];
349
358
  NSNumber *shouldAskForBillingInformation = [dictionary optionalBoolForKey:@"shouldAskForBillingInformation"];
350
359
 
351
- uiConfiguration.isAVSEnabled = isAVSEnabled.boolValue;
352
- uiConfiguration.shouldPaymentMethodsDisplayAmount = shouldDisplayAmount.boolValue;
353
- uiConfiguration.shouldPaymentButtonDisplayAmount = isPayButtonAmountVisible.boolValue;
354
- uiConfiguration.shouldPaymentMethodsVerifySecurityCode = isSecureCodeCheckEnabled.boolValue;
360
+ if (isAVSEnabled) {
361
+ uiConfiguration.isAVSEnabled = isAVSEnabled.boolValue;
362
+ }
363
+
364
+ if (shouldDisplayAmount) {
365
+ uiConfiguration.shouldPaymentMethodsDisplayAmount = shouldDisplayAmount.boolValue;
366
+ }
367
+
368
+ if (isPayButtonAmountVisible) {
369
+ uiConfiguration.shouldPaymentButtonDisplayAmount = isPayButtonAmountVisible.boolValue;
370
+ }
371
+
372
+ if (isSecureCodeCheckEnabled) {
373
+ uiConfiguration.shouldPaymentMethodsVerifySecurityCode = isSecureCodeCheckEnabled.boolValue;
374
+ }
355
375
 
356
376
  if (shouldAskForBillingInformation) {
357
377
  uiConfiguration.shouldAskForBillingInformation = shouldAskForBillingInformation.boolValue;
358
378
  }
359
379
 
360
380
  uiConfiguration.theme = [self themeFromUIConfiguration:dictionary];
381
+ uiConfiguration.threeDSUICustomization = [self threeDSUICustomization:dictionary];
361
382
 
362
383
  return uiConfiguration;
363
384
  }
364
385
 
386
+ + (JP3DSUICustomization *)threeDSUICustomization:(NSDictionary *)uiCustomization {
387
+ NSDictionary *dictionary = [uiCustomization optionalDictionaryForKey:@"threeDSUIConfiguration"];
388
+
389
+ if (!dictionary) {
390
+ return nil;
391
+ }
392
+
393
+ NSDictionary *buttonCustomizations = [dictionary optionalDictionaryForKey:@"buttonCustomizations"];
394
+ NSDictionary *toolbarCustomizations = [dictionary optionalDictionaryForKey:@"toolbarCustomization"];
395
+ NSDictionary *labelCustomizations = [dictionary optionalDictionaryForKey:@"labelCustomization"];
396
+ NSDictionary *textBoxCustomizations = [dictionary optionalDictionaryForKey:@"textBoxCustomization"];
397
+
398
+ JP3DSUICustomization *customization = [JP3DSUICustomization new];
399
+
400
+ if (buttonCustomizations) {
401
+ NSDictionary *submitButtonCustomization = [buttonCustomizations optionalDictionaryForKey:@"SUBMIT"];
402
+ NSDictionary *continueButtonCustomization = [buttonCustomizations optionalDictionaryForKey:@"CONTINUE"];
403
+ NSDictionary *nextButtonCustomization = [buttonCustomizations optionalDictionaryForKey:@"NEXT"];
404
+ NSDictionary *cancelButtonCustomization = [buttonCustomizations optionalDictionaryForKey:@"CANCEL"];
405
+ NSDictionary *resendButtonCustomization = [buttonCustomizations optionalDictionaryForKey:@"RESEND"];
406
+
407
+ if (submitButtonCustomization) {
408
+ JP3DSButtonCustomization *submitCustomization = [JP3DSButtonCustomization new];
409
+
410
+ NSString *textFontName = [submitButtonCustomization optionalStringForKey:@"textFontName"];
411
+ NSString *textColor = [submitButtonCustomization optionalStringForKey:@"textColor"];
412
+ NSString *backgroundColor = [submitButtonCustomization optionalStringForKey:@"backgroundColor"];
413
+ NSNumber *cornerRadius = [submitButtonCustomization optionalNumberForKey:@"cornerRadius"];
414
+ NSNumber *textFontSize = [submitButtonCustomization optionalNumberForKey:@"textFontSize"];
415
+
416
+ if (textFontName) {
417
+ [submitCustomization setTextFontName:textFontName];
418
+ }
419
+
420
+ if (textColor) {
421
+ [submitCustomization setTextColor:textColor];
422
+ }
423
+
424
+ if (backgroundColor) {
425
+ [submitCustomization setBackgroundColor:backgroundColor];
426
+ }
427
+
428
+ if (cornerRadius) {
429
+ [submitCustomization setCornerRadius:cornerRadius.integerValue];
430
+ }
431
+
432
+ if (textFontSize) {
433
+ [submitCustomization setTextFontSize:textFontSize.integerValue];
434
+ }
435
+
436
+ [customization setButtonCustomization:submitCustomization ofType:JP3DSButtonTypeSubmit];
437
+ }
438
+
439
+ if (continueButtonCustomization) {
440
+ JP3DSButtonCustomization *continueCustomization = [JP3DSButtonCustomization new];
441
+
442
+ NSString *textFontName = [continueButtonCustomization optionalStringForKey:@"textFontName"];
443
+ NSString *textColor = [continueButtonCustomization optionalStringForKey:@"textColor"];
444
+ NSString *backgroundColor = [continueButtonCustomization optionalStringForKey:@"backgroundColor"];
445
+ NSNumber *cornerRadius = [continueButtonCustomization optionalNumberForKey:@"cornerRadius"];
446
+ NSNumber *textFontSize = [continueButtonCustomization optionalNumberForKey:@"textFontSize"];
447
+
448
+ if (textFontName) {
449
+ [continueCustomization setTextFontName:textFontName];
450
+ }
451
+
452
+ if (textColor) {
453
+ [continueCustomization setTextColor:textColor];
454
+ }
455
+
456
+ if (backgroundColor) {
457
+ [continueCustomization setBackgroundColor:backgroundColor];
458
+ }
459
+
460
+ if (cornerRadius) {
461
+ [continueCustomization setCornerRadius:cornerRadius.integerValue];
462
+ }
463
+
464
+ if (textFontSize) {
465
+ [continueCustomization setTextFontSize:textFontSize.integerValue];
466
+ }
467
+
468
+ [customization setButtonCustomization:continueCustomization ofType:JP3DSButtonTypeContinue];
469
+ }
470
+
471
+ if (nextButtonCustomization) {
472
+ JP3DSButtonCustomization *nextCustomization = [JP3DSButtonCustomization new];
473
+
474
+ NSString *textFontName = [nextButtonCustomization optionalStringForKey:@"textFontName"];
475
+ NSString *textColor = [nextButtonCustomization optionalStringForKey:@"textColor"];
476
+ NSString *backgroundColor = [nextButtonCustomization optionalStringForKey:@"backgroundColor"];
477
+ NSNumber *cornerRadius = [nextButtonCustomization optionalNumberForKey:@"cornerRadius"];
478
+ NSNumber *textFontSize = [nextButtonCustomization optionalNumberForKey:@"textFontSize"];
479
+
480
+ if (textFontName) {
481
+ [nextCustomization setTextFontName:textFontName];
482
+ }
483
+
484
+ if (textColor) {
485
+ [nextCustomization setTextColor:textColor];
486
+ }
487
+
488
+ if (backgroundColor) {
489
+ [nextCustomization setBackgroundColor:backgroundColor];
490
+ }
491
+
492
+ if (cornerRadius) {
493
+ [nextCustomization setCornerRadius:cornerRadius.integerValue];
494
+ }
495
+
496
+ if (textFontSize) {
497
+ [nextCustomization setTextFontSize:textFontSize.integerValue];
498
+ }
499
+
500
+ [customization setButtonCustomization:nextCustomization ofType:JP3DSButtonTypeNext];
501
+ }
502
+
503
+ if (cancelButtonCustomization) {
504
+ JP3DSButtonCustomization *cancelCustomization = [JP3DSButtonCustomization new];
505
+
506
+ NSString *textFontName = [cancelButtonCustomization optionalStringForKey:@"textFontName"];
507
+ NSString *textColor = [cancelButtonCustomization optionalStringForKey:@"textColor"];
508
+ NSString *backgroundColor = [cancelButtonCustomization optionalStringForKey:@"backgroundColor"];
509
+ NSNumber *cornerRadius = [cancelButtonCustomization optionalNumberForKey:@"cornerRadius"];
510
+ NSNumber *textFontSize = [cancelButtonCustomization optionalNumberForKey:@"textFontSize"];
511
+
512
+ if (textFontName) {
513
+ [cancelCustomization setTextFontName:textFontName];
514
+ }
515
+
516
+ if (textColor) {
517
+ [cancelCustomization setTextColor:textColor];
518
+ }
519
+
520
+ if (backgroundColor) {
521
+ [cancelCustomization setBackgroundColor:backgroundColor];
522
+ }
523
+
524
+ if (cornerRadius) {
525
+ [cancelCustomization setCornerRadius:cornerRadius.integerValue];
526
+ }
527
+
528
+ if (textFontSize) {
529
+ [cancelCustomization setTextFontSize:textFontSize.integerValue];
530
+ }
531
+
532
+ [customization setButtonCustomization:cancelCustomization ofType:JP3DSButtonTypeCancel];
533
+ }
534
+
535
+ if (resendButtonCustomization) {
536
+ JP3DSButtonCustomization *resendCustomization = [JP3DSButtonCustomization new];
537
+
538
+ NSString *textFontName = [resendButtonCustomization optionalStringForKey:@"textFontName"];
539
+ NSString *textColor = [resendButtonCustomization optionalStringForKey:@"textColor"];
540
+ NSString *backgroundColor = [resendButtonCustomization optionalStringForKey:@"backgroundColor"];
541
+ NSNumber *cornerRadius = [resendButtonCustomization optionalNumberForKey:@"cornerRadius"];
542
+ NSNumber *textFontSize = [resendButtonCustomization optionalNumberForKey:@"textFontSize"];
543
+
544
+ if (textFontName) {
545
+ [resendCustomization setTextFontName:textFontName];
546
+ }
547
+
548
+ if (textColor) {
549
+ [resendCustomization setTextColor:textColor];
550
+ }
551
+
552
+ if (backgroundColor) {
553
+ [resendCustomization setBackgroundColor:backgroundColor];
554
+ }
555
+
556
+ if (cornerRadius) {
557
+ [resendCustomization setCornerRadius:cornerRadius.integerValue];
558
+ }
559
+
560
+ if (textFontSize) {
561
+ [resendCustomization setTextFontSize:textFontSize.integerValue];
562
+ }
563
+
564
+ [customization setButtonCustomization:resendCustomization ofType:JP3DSButtonTypeResend];
565
+ }
566
+ }
567
+
568
+ if (labelCustomizations) {
569
+ JP3DSLabelCustomization *labelCustomization = [JP3DSLabelCustomization new];
570
+
571
+ NSString *textFontName = [labelCustomizations optionalStringForKey:@"textFontName"];
572
+ NSString *textColor = [labelCustomizations optionalStringForKey:@"textColor"];
573
+ NSString *headingTextFontName = [labelCustomizations optionalStringForKey:@"headingTextFontName"];
574
+ NSString *headingTextColor = [labelCustomizations optionalStringForKey:@"headingTextColor"];
575
+ NSNumber *headingTextFontSize = [labelCustomizations optionalNumberForKey:@"headingTextFontSize"];
576
+ NSNumber *textFontSize = [labelCustomizations optionalNumberForKey:@"textFontSize"];
577
+
578
+ if (textFontName) {
579
+ [labelCustomization setTextFontName:textFontName];
580
+ }
581
+
582
+ if (textColor) {
583
+ [labelCustomization setTextColor:textColor];
584
+ }
585
+
586
+ if (headingTextFontName) {
587
+ [labelCustomization setHeadingTextFontName:headingTextFontName];
588
+ }
589
+
590
+ if (headingTextColor) {
591
+ [labelCustomization setHeadingTextColor:headingTextColor];
592
+ }
593
+
594
+ if (headingTextFontSize) {
595
+ [labelCustomization setHeadingTextFontSize:headingTextFontSize.integerValue];
596
+ }
597
+
598
+ if (textFontSize) {
599
+ [labelCustomization setTextFontSize:textFontSize.integerValue];
600
+ }
601
+
602
+ [customization setLabelCustomization:labelCustomization];
603
+ }
604
+
605
+ if (toolbarCustomizations) {
606
+ JP3DSToolbarCustomization *toolbarCustomization = [JP3DSToolbarCustomization new];
607
+
608
+ NSNumber *textFontSize = [toolbarCustomizations optionalNumberForKey:@"textFontSize"];
609
+ NSString *textFontName = [toolbarCustomizations optionalStringForKey:@"textFontName"];
610
+ NSString *textColor = [toolbarCustomizations optionalStringForKey:@"textColor"];
611
+ NSString *backgroundColor = [toolbarCustomizations optionalStringForKey:@"backgroundColor"];
612
+ NSString *headerText = [toolbarCustomizations optionalStringForKey:@"headerText"];
613
+ NSString *buttonText = [toolbarCustomizations optionalStringForKey:@"buttonText"];
614
+
615
+ if (textFontName) {
616
+ [toolbarCustomization setTextFontName:textFontName];
617
+ }
618
+
619
+ if (textColor) {
620
+ [toolbarCustomization setTextColor:textColor];
621
+ }
622
+
623
+ if (backgroundColor) {
624
+ [toolbarCustomization setBackgroundColor:backgroundColor];
625
+ }
626
+
627
+ if (headerText) {
628
+ [toolbarCustomization setHeaderText:headerText];
629
+ }
630
+
631
+ if (buttonText) {
632
+ [toolbarCustomization setButtonText:buttonText];
633
+ }
634
+
635
+ if (textFontSize) {
636
+ [toolbarCustomization setTextFontSize:textFontSize.integerValue];
637
+ }
638
+
639
+ [customization setToolbarCustomization:toolbarCustomization];
640
+ }
641
+
642
+ if (textBoxCustomizations) {
643
+ JP3DSTextBoxCustomization *textBoxCustomization = [JP3DSTextBoxCustomization new];
644
+
645
+ NSString *textFontName = [textBoxCustomizations optionalStringForKey:@"textFontName"];
646
+ NSString *textColor = [textBoxCustomizations optionalStringForKey:@"textColor"];
647
+ NSString *borderColor = [textBoxCustomizations optionalStringForKey:@"borderColor"];
648
+ NSNumber *borderWidth = [textBoxCustomizations optionalNumberForKey:@"borderWidth"];
649
+ NSNumber *cornerRadius = [textBoxCustomizations optionalNumberForKey:@"cornerRadius"];
650
+ NSNumber *textFontSize = [textBoxCustomizations optionalNumberForKey:@"textFontSize"];
651
+
652
+ if (textFontName) {
653
+ [textBoxCustomization setTextFontName:textFontName];
654
+ }
655
+
656
+ if (textColor) {
657
+ [textBoxCustomization setTextColor:textColor];
658
+ }
659
+
660
+ if (borderColor) {
661
+ [textBoxCustomization setBorderColor:borderColor];
662
+ }
663
+
664
+ if (borderWidth) {
665
+ [textBoxCustomization setBorderWidth:borderWidth.integerValue];
666
+ }
667
+
668
+ if (cornerRadius) {
669
+ [textBoxCustomization setCornerRadius:cornerRadius.integerValue];
670
+ }
671
+
672
+ if (textFontSize) {
673
+ [textBoxCustomization setTextFontSize:textFontSize.integerValue];
674
+ }
675
+
676
+ [customization setTextBoxCustomization:textBoxCustomization];
677
+ }
678
+
679
+ return customization;
680
+ }
681
+
365
682
  + (JPTheme *)themeFromUIConfiguration:(NSDictionary *)uiConfiguration {
366
683
  JPTheme *theme = [JPTheme new];
367
684
 
@@ -463,38 +780,108 @@ static NSString *const kCardSchemeAMEX = @"amex";
463
780
  [mappedResponse setValue:response.appearsOnStatementAs forKey:@"appearsOnStatementAs"];
464
781
  [mappedResponse setValue:response.originalAmount forKey:@"originalAmount"];
465
782
  [mappedResponse setValue:response.netAmount forKey:@"netAmount"];
466
- [mappedResponse setValue:response.amount.amount forKey:@"amount"];
467
- [mappedResponse setValue:response.amount.currency forKey:@"currency"];
468
-
469
- NSMutableDictionary *cardDetailsResponse = [NSMutableDictionary new];
470
- [cardDetailsResponse setValue:response.cardDetails.cardLastFour forKey:@"cardLastFour"];
471
- [cardDetailsResponse setValue:response.cardDetails.endDate forKey:@"endDate"];
472
- [cardDetailsResponse setValue:response.cardDetails.cardToken forKey:@"cardToken"];
473
- [cardDetailsResponse setValue:@(response.cardDetails.cardNetwork) forKey:@"cardNetwork"];
474
- [cardDetailsResponse setValue:response.cardDetails.bank forKey:@"bank"];
475
- [cardDetailsResponse setValue:response.cardDetails.cardCategory forKey:@"cardCategory"];
476
- [cardDetailsResponse setValue:response.cardDetails.cardCountry forKey:@"cardCountry"];
477
- [cardDetailsResponse setValue:response.cardDetails.cardFunding forKey:@"cardFunding"];
478
- [cardDetailsResponse setValue:response.cardDetails.cardScheme forKey:@"cardScheme"];
479
-
480
- [mappedResponse setValue:cardDetailsResponse forKey:@"cardDetails"];
481
-
482
- NSMutableDictionary *consumerResponse = [NSMutableDictionary new];
483
- [consumerResponse setValue:response.consumer.consumerToken forKey:@"consumerToken"];
484
- [consumerResponse setValue:response.consumer.consumerReference forKey:@"consumerReference"];
485
-
486
- [mappedResponse setValue:consumerResponse forKey:@"consumerResponse"];
487
-
488
- NSMutableDictionary *orderDetailsResponse = [NSMutableDictionary new];
489
- [orderDetailsResponse setValue:response.orderDetails.orderId forKey:@"orderId"];
490
- [orderDetailsResponse setValue:response.orderDetails.orderStatus forKey:@"orderStatus"];
491
- [orderDetailsResponse setValue:response.orderDetails.orderFailureReason forKey:@"orderFailureReason"];
492
- [orderDetailsResponse setValue:response.orderDetails.timestamp forKey:@"timestamp"];
493
- [orderDetailsResponse setValue:@(response.orderDetails.amount) forKey:@"amount"];
494
-
495
- [mappedResponse setValue:orderDetailsResponse forKey:@"orderDetails"];
496
-
497
- return mappedResponse;
498
- }
783
+
784
+ JPAmount *amount = response.amount;
785
+
786
+ if (amount) {
787
+ [mappedResponse setValue:amount.amount forKey:@"amount"];
788
+ [mappedResponse setValue:amount.currency forKey:@"currency"];
789
+ }
790
+
791
+ JPCardDetails *cardDetails = response.cardDetails;
792
+
793
+ if (cardDetails) {
794
+ NSMutableDictionary *cardDetailsDictionary = [NSMutableDictionary new];
795
+
796
+ if (cardDetails.cardLastFour) {
797
+ cardDetailsDictionary[@"cardLastFour"] = cardDetails.cardLastFour;
798
+ }
799
+
800
+ if (cardDetails.endDate) {
801
+ cardDetailsDictionary[@"endDate"] = cardDetails.endDate;
802
+ }
803
+
804
+ if (cardDetails.cardToken) {
805
+ cardDetailsDictionary[@"cardToken"] = cardDetails.cardToken;
806
+ }
807
+
808
+ cardDetailsDictionary[@"cardNetwork"] = @(cardDetails.cardNetwork);
809
+
810
+ if (cardDetails.bank) {
811
+ cardDetailsDictionary[@"bank"] = cardDetails.bank;
812
+ }
813
+
814
+ if (cardDetails.cardCategory) {
815
+ cardDetailsDictionary[@"cardCategory"] = cardDetails.cardCategory;
816
+ }
817
+
818
+ if (cardDetails.cardCountry) {
819
+ cardDetailsDictionary[@"cardCountry"] = cardDetails.cardCountry;
820
+ }
821
+
822
+ if (cardDetails.cardFunding) {
823
+ cardDetailsDictionary[@"cardFunding"] = cardDetails.cardFunding;
824
+ }
825
+
826
+ if (cardDetails.cardScheme) {
827
+ cardDetailsDictionary[@"cardScheme"] = cardDetails.cardScheme;
828
+ }
829
+
830
+ if (cardDetails.cardHolderName) {
831
+ cardDetailsDictionary[@"cardHolderName"] = cardDetails.cardHolderName;
832
+ }
833
+
834
+ [mappedResponse setValue:[NSDictionary dictionaryWithDictionary:cardDetailsDictionary]
835
+ forKey:@"cardDetails"];
836
+ }
837
+
838
+ JPConsumer *consumer = response.consumer;
839
+
840
+ if (consumer) {
841
+ NSMutableDictionary *consumerDictionary = [NSMutableDictionary new];
842
+
843
+ if (consumer.consumerToken) {
844
+ consumerDictionary[@"consumerToken"] = consumer.consumerToken;
845
+ }
846
+
847
+ if (consumer.consumerReference) {
848
+ consumerDictionary[@"consumerReference"] = consumer.consumerReference;
849
+ }
850
+
851
+ if (consumerDictionary.count > 0) {
852
+ [mappedResponse setValue:[NSDictionary dictionaryWithDictionary:consumerDictionary]
853
+ forKey:@"consumerResponse"];
854
+ }
855
+ }
856
+
857
+ JPOrderDetails *orderDetails = response.orderDetails;
858
+
859
+ if (orderDetails) {
860
+ NSMutableDictionary *orderDetailsDictionary = [NSMutableDictionary new];
861
+
862
+ if (orderDetails.orderId) {
863
+ orderDetailsDictionary[@"orderId"] = orderDetails.orderId;
864
+ }
865
+
866
+ if (orderDetails.orderStatus) {
867
+ orderDetailsDictionary[@"orderStatus"] = orderDetails.orderStatus;
868
+ }
869
+
870
+ if (orderDetails.orderFailureReason) {
871
+ orderDetailsDictionary[@"orderFailureReason"] = orderDetails.orderFailureReason;
872
+ }
873
+
874
+ if (orderDetails.timestamp) {
875
+ orderDetailsDictionary[@"timestamp"] = orderDetails.timestamp;
876
+ }
877
+
878
+ orderDetailsDictionary[@"amount"] = @(orderDetails.amount);
879
+
880
+ [mappedResponse setValue:[NSDictionary dictionaryWithDictionary:orderDetailsDictionary]
881
+ forKey:@"orderDetails"];
882
+ }
883
+
884
+ return [NSDictionary dictionaryWithDictionary:mappedResponse];
885
+ }
499
886
 
500
887
  @end
package/ios/Podfile CHANGED
@@ -19,7 +19,9 @@ target 'RNJudo' do
19
19
  # use_frameworks!
20
20
 
21
21
  # Pods for RNJudo
22
- pod "JudoKit-iOS", "3.1.8"
22
+ pod "JudoKit-iOS", "3.1.10"
23
+ pod "Judo3DS2_iOS", "1.1.3"
24
+
23
25
  pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
24
26
  pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
25
27
  pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
package/ios/Podfile.lock CHANGED
@@ -22,7 +22,7 @@ PODS:
22
22
  - glog
23
23
  - glog (0.3.5)
24
24
  - Judo3DS2_iOS (1.1.3)
25
- - JudoKit-iOS (3.1.7):
25
+ - JudoKit-iOS (3.1.10):
26
26
  - DeviceDNA (~> 2.0.0)
27
27
  - Judo3DS2_iOS (~> 1.1.3)
28
28
  - TrustKit
@@ -236,7 +236,8 @@ DEPENDENCIES:
236
236
  - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
237
237
  - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
238
238
  - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
239
- - JudoKit-iOS (= 3.1.7)
239
+ - Judo3DS2_iOS (= 1.1.3)
240
+ - JudoKit-iOS (= 3.1.10)
240
241
  - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
241
242
  - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
242
243
  - React (from `../node_modules/react-native/`)
@@ -332,7 +333,7 @@ SPEC CHECKSUMS:
332
333
  Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
333
334
  glog: 1f3da668190260b06b429bb211bfbee5cd790c28
334
335
  Judo3DS2_iOS: b2396a1f0aa3e8d428fdd887c8b6eae5d3aa4f79
335
- JudoKit-iOS: 9bdb404617132df3a22ce0713b610258fbc9a817
336
+ JudoKit-iOS: c45f288b94eef55d779b4e61028b9bf04aa334b2
336
337
  OpenSSL-Universal: 84efb8a29841f2764ac5403e0c4119a28b713346
337
338
  RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1
338
339
  RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320
@@ -357,6 +358,6 @@ SPEC CHECKSUMS:
357
358
  Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b
358
359
  ZappMerchantLib: b14bc5814840426d351190309250347ca9b0983d
359
360
 
360
- PODFILE CHECKSUM: b43fb77cd20fdc9f4c3ce03edc13aa92f131243b
361
+ PODFILE CHECKSUM: f04f2f5cb9ebec38d61aa9e7bdf575cf58376fdd
361
362
 
362
363
  COCOAPODS: 1.11.3
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "judokit-react-native",
3
3
  "title": "Judopay React Native Module",
4
- "version": "3.4.8",
4
+ "version": "3.5.0",
5
5
  "description": "A React Native module for the Judopay native SDKs to take payments on iOS and Android.",
6
6
  "main": "JudoPay.tsx",
7
7
  "scripts": {
8
8
  "test": "jest --coverage",
9
- "lint": "tsc && eslint '*/**/*.{js,ts,tsx}' --quiet"
9
+ "lint": "tsc && eslint '*/**/*.{ts,tsx}' --quiet",
10
+ "format": "prettier --write \"**/*.+(tsx|ts)\""
10
11
  },
11
12
  "files": [
12
13
  "JudoPay.tsx",
@@ -52,6 +53,7 @@
52
53
  "eslint-plugin-react": "^7.19.0",
53
54
  "eslint-plugin-react-hooks": "^2.5.0",
54
55
  "jest": "^25.3.0",
56
+ "prettier": "^2.7.1",
55
57
  "react": "16.9.0",
56
58
  "react-native": "0.61.5"
57
59
  },
@@ -15,33 +15,34 @@ export interface JudoConfiguration {
15
15
  googlePayConfiguration?: JudoGooglePayConfiguration
16
16
  pbbaConfiguration?: JudoPBBAConfiguration
17
17
  isInitialRecurringPayment?: boolean
18
- networkTimeout?: NetworkTimeout,
19
- challengeRequestIndicator?: ChallengeRequestIndicator,
20
- scaExemption?: ScaExemption,
21
- mobileNumber?: string,
22
- phoneCountryCode?: string,
23
- emailAddress?: string,
24
- threeDSTwoMaxTimeout?: string,
18
+ networkTimeout?: NetworkTimeout
19
+ challengeRequestIndicator?: ChallengeRequestIndicator
20
+ scaExemption?: ScaExemption
21
+ mobileNumber?: string
22
+ phoneCountryCode?: string
23
+ emailAddress?: string
24
+ threeDSTwoMaxTimeout?: number
25
25
  threeDSTwoMessageVersion?: string
26
+ isDelayedAuthorisation?: boolean
26
27
  }
27
28
 
28
29
  export enum ChallengeRequestIndicator {
29
- NoPreference = "noPreference",
30
- NoChallenge = "noChallenge",
31
- ChallengePreferred = "challengePreferred",
32
- ChallengeAsMandate = "challengeAsMandate"
30
+ NoPreference = 'noPreference',
31
+ NoChallenge = 'noChallenge',
32
+ ChallengePreferred = 'challengePreferred',
33
+ ChallengeAsMandate = 'challengeAsMandate'
33
34
  }
34
35
 
35
36
  export enum ScaExemption {
36
- LowValue = "lowValue",
37
- SecureCorporate = "secureCorporate",
38
- TrustedBeneficiary = "trustedBeneficiary",
39
- TransactionRiskAnalysis = "transactionRiskAnalysis"
37
+ LowValue = 'lowValue',
38
+ SecureCorporate = 'secureCorporate',
39
+ TrustedBeneficiary = 'trustedBeneficiary',
40
+ TransactionRiskAnalysis = 'transactionRiskAnalysis'
40
41
  }
41
42
 
42
43
  export interface NetworkTimeout {
43
- connectTimeout?: number,
44
- readTimeout?: number,
44
+ connectTimeout?: number
45
+ readTimeout?: number
45
46
  writeTimeout?: number
46
47
  }
47
48
 
@@ -85,6 +86,58 @@ export interface JudoAddress {
85
86
  state?: string
86
87
  }
87
88
 
89
+ export interface JudoThreeDSToolbarCustomization {
90
+ backgroundColor?: string
91
+ headerText?: string
92
+ buttonText?: string
93
+ textFontName?: string
94
+ textColor?: string
95
+ textFontSize?: number
96
+ }
97
+
98
+ export interface JudoThreeDSLabelCustomization {
99
+ headingTextFontName?: string
100
+ headingTextColor?: string
101
+ headingTextFontSize?: number
102
+ textFontName?: string
103
+ textColor?: string
104
+ textFontSize?: number
105
+ }
106
+
107
+ export interface JudoThreeDSTextBoxCustomization {
108
+ borderWidth?: number
109
+ borderColor?: string
110
+ cornerRadius?: number
111
+ textFontName?: string
112
+ textColor?: string
113
+ textFontSize?: number
114
+ }
115
+
116
+ export enum JudoThreeDSButtonType {
117
+ SUBMIT = 'SUBMIT',
118
+ CONTINUE = 'CONTINUE',
119
+ NEXT = 'NEXT',
120
+ CANCEL = 'CANCEL',
121
+ RESEND = 'RESEND'
122
+ }
123
+
124
+ export interface JudoThreeDSButtonCustomization {
125
+ backgroundColor?: string
126
+ cornerRadius?: number
127
+ textFontName?: string
128
+ textColor?: string
129
+ textFontSize?: number
130
+ }
131
+
132
+ export interface JudoThreeDSUIConfiguration {
133
+ buttonCustomizations?: Partial<
134
+ Record<JudoThreeDSButtonType, JudoThreeDSButtonCustomization>
135
+ >
136
+ toolbarCustomization?: JudoThreeDSToolbarCustomization
137
+ labelCustomization?: JudoThreeDSLabelCustomization
138
+ textBoxCustomization?: JudoThreeDSTextBoxCustomization
139
+ }
140
+
88
141
  export interface JudoUIConfiguration {
89
142
  isAVSEnabled: boolean
90
143
  shouldPaymentMethodsDisplayAmount: boolean
@@ -92,6 +145,7 @@ export interface JudoUIConfiguration {
92
145
  shouldPaymentMethodsVerifySecurityCode: boolean
93
146
  shouldAskForBillingInformation?: boolean
94
147
  theme?: JudoTheme
148
+ threeDSUIConfiguration?: JudoThreeDSUIConfiguration
95
149
  }
96
150
 
97
151
  export interface JudoTheme {