Skip to content

Commit d9e1725

Browse files
DSP-1867 create a factory provider for the ThreDSService
1 parent d4181d1 commit d9e1725

18 files changed

Lines changed: 315 additions & 45 deletions

Adyen.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@
108108
21C7111E2D6D386F00A08111 /* ADYServiceAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21C7111D2D6D386F00A08111 /* ADYServiceAdapter.swift */; };
109109
21C711202D6D388700A08111 /* AnyADYTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21C7111F2D6D388700A08111 /* AnyADYTransaction.swift */; };
110110
21C711242D6D3B2500A08111 /* ThreeDSServiceableMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21C711232D6D3B2100A08111 /* ThreeDSServiceableMock.swift */; };
111+
21CBEF642D8B0A0700178809 /* DefaultThreeDSServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CBEF632D8B0A0500178809 /* DefaultThreeDSServiceProvider.swift */; };
112+
21CBEF662D8B6B3A00178809 /* DefaultThreeDSServiceProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CBEF652D8B6B2F00178809 /* DefaultThreeDSServiceProviderTests.swift */; };
111113
5A1315C926296B100092366D /* ProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A1315C826296B100092366D /* ProgressViewStyle.swift */; };
112114
5A15D589264BB0BD00A8E3C7 /* BoletoComponentExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A15D588264BB0BD00A8E3C7 /* BoletoComponentExtensions.swift */; };
113115
5A15D5A1264BE1E500A8E3C7 /* PrefilledShopperInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A15D5A0264BE1E500A8E3C7 /* PrefilledShopperInformation.swift */; };
@@ -1594,6 +1596,8 @@
15941596
21C7111D2D6D386F00A08111 /* ADYServiceAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ADYServiceAdapter.swift; sourceTree = "<group>"; };
15951597
21C7111F2D6D388700A08111 /* AnyADYTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyADYTransaction.swift; sourceTree = "<group>"; };
15961598
21C711232D6D3B2100A08111 /* ThreeDSServiceableMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeDSServiceableMock.swift; sourceTree = "<group>"; };
1599+
21CBEF632D8B0A0500178809 /* DefaultThreeDSServiceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultThreeDSServiceProvider.swift; sourceTree = "<group>"; };
1600+
21CBEF652D8B6B2F00178809 /* DefaultThreeDSServiceProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultThreeDSServiceProviderTests.swift; sourceTree = "<group>"; };
15971601
5A1315C826296B100092366D /* ProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressViewStyle.swift; sourceTree = "<group>"; };
15981602
5A15D588264BB0BD00A8E3C7 /* BoletoComponentExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoletoComponentExtensions.swift; sourceTree = "<group>"; };
15991603
5A15D5A0264BE1E500A8E3C7 /* PrefilledShopperInformation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefilledShopperInformation.swift; sourceTree = "<group>"; };
@@ -5193,6 +5197,7 @@
51935197
F957AA432552C3A90099AD73 /* 3DS2 Component */ = {
51945198
isa = PBXGroup;
51955199
children = (
5200+
21CBEF652D8B6B2F00178809 /* DefaultThreeDSServiceProviderTests.swift */,
51965201
21C7111B2D6D36A400A08111 /* ThreeDS2ServiceLegacyTests.swift */,
51975202
F957AA442552C3BB0099AD73 /* 3DS2 Fingerprint Submitter */,
51985203
F957AA592552D8BB0099AD73 /* ThreeDS2ComponentTests.swift */,
@@ -5590,6 +5595,7 @@
55905595
F9A7AC592554062400012EBC /* 3DS2 SDK Adapters */ = {
55915596
isa = PBXGroup;
55925597
children = (
5598+
21CBEF632D8B0A0500178809 /* DefaultThreeDSServiceProvider.swift */,
55935599
21C7110D2D67F86700A08111 /* ThreeDSServiceable.swift */,
55945600
21C7111A2D67FEFF00A08111 /* LegacySDK */,
55955601
21C711142D67FE9A00A08111 /* FingerprintServiceParameters.swift */,
@@ -7311,6 +7317,7 @@
73117317
F97C83F725BF0C2800D7F85C /* DokuComponentTests.swift in Sources */,
73127318
81F177AE2B4C49B9009CAD96 /* PresenterMock.swift in Sources */,
73137319
F924C072246BEEF0006DDAB8 /* CardDetailsTests.swift in Sources */,
7320+
21CBEF662D8B6B3A00178809 /* DefaultThreeDSServiceProviderTests.swift in Sources */,
73147321
F9FE39E32679E09200F02A9B /* LoadingViewTests.swift in Sources */,
73157322
B6EE0F3D2BBEBF5100B9810D /* AnalyticsProviderMock.swift in Sources */,
73167323
A0290DF82C413056005BE269 /* StoredPaymentMethodDelegateMock.swift in Sources */,
@@ -7617,6 +7624,7 @@
76177624
F9976BB326D903F400D2D7CE /* MultibancoVoucherAction.swift in Sources */,
76187625
F9175FFA259499D500D653BE /* AwaitViewController.swift in Sources */,
76197626
21C711112D67FE6B00A08111 /* ThreeDSServiceChallengeError.swift in Sources */,
7627+
21CBEF642D8B0A0700178809 /* DefaultThreeDSServiceProvider.swift in Sources */,
76207628
F96757C327CF909900A16FB6 /* AnyWeChatPaySDKActionComponent.swift in Sources */,
76217629
F9175FD22594999600D653BE /* RedirectComponent.swift in Sources */,
76227630
00165FE02C05DA8600347399 /* RedireactableAwaitAction.swift in Sources */,
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// Copyright (c) 2025 Adyen N.V.
3+
//
4+
// This file is open source and available under the MIT license. See the LICENSE file for more info.
5+
//
6+
7+
import Foundation
8+
@_spi(AdyenInternal) import Adyen
9+
10+
internal protocol ThreeDSConfigurable {
11+
var configuration: ThreeDSConfiguration? { get set }
12+
}
13+
14+
internal protocol ThreeDSConfiguration {
15+
func isFeatureEnabled(_ name: String) -> Bool
16+
}
17+
18+
internal final class DefaultThreeDSServiceProvider: ThreeDSServiceable, ThreeDSConfigurable {
19+
20+
/// An optional configuration that can be provided to the service provider.
21+
internal var configuration: ThreeDSConfiguration?
22+
23+
private var service: ThreeDSServiceable?
24+
private let serviceHandler: (ThreeDSConfiguration?) -> ThreeDSServiceable
25+
26+
internal init(
27+
serviceHandler: @escaping (ThreeDSConfiguration?) -> ThreeDSServiceable = creator
28+
) {
29+
self.serviceHandler = serviceHandler
30+
}
31+
32+
internal func performFingerprint(
33+
parameters: FingerprintServiceParameters,
34+
completionHandler: @escaping (Result<any AnyAuthenticationRequestParameters, ThreeDSServiceFingerprintError>) -> Void
35+
) {
36+
self.resetTransaction()
37+
let service = serviceHandler(configuration)
38+
self.service = service
39+
service.performFingerprint(
40+
parameters: parameters,
41+
completionHandler: completionHandler
42+
)
43+
}
44+
45+
internal func performChallenge(
46+
with parameters: ChallengeParameters,
47+
completionHandler: @escaping (Result<any AnyChallengeResult, ThreeDSServiceChallengeError>) -> Void
48+
) {
49+
// The service needs to be already created during the fingerprint step.
50+
// There is no way a challenge action should happen without a fingerprint action.
51+
guard let service = self.service else {
52+
completionHandler(.failure(.transactionNotInitialized))
53+
return
54+
}
55+
service.performChallenge(with: parameters) { result in
56+
self.resetTransaction()
57+
completionHandler(result)
58+
}
59+
}
60+
61+
internal func resetTransaction() {
62+
service?.resetTransaction()
63+
service = nil
64+
}
65+
66+
internal static func creator(
67+
configuration: ThreeDSConfiguration?
68+
) -> ThreeDSServiceable {
69+
ThreeDSServiceLegacy()
70+
}
71+
}

AdyenActions/Components/3DS2/3DS2 SDK Adapters/LegacySDK/ThreeDSServiceLegacy.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ internal final class ThreeDSServiceLegacy: ThreeDSServiceable {
6363
)
6464

6565
guard let transaction else {
66-
return completionHandler(.failure(.transactionNotInitialized(
67-
errorPayload: opaqueErrorObject(error: ThreeDSServiceChallengeError.transactionNotInitialized(errorPayload: ""))
68-
)))
66+
return completionHandler(.failure(.transactionNotInitialized))
6967
}
7068

7169
transaction.performChallenge(

AdyenActions/Components/3DS2/3DS2 SDK Adapters/ThreeDSServiceChallengeError.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import Foundation
88

99
/// Errors that could happen during a challenge
10-
internal enum ThreeDSServiceChallengeError: Error {
10+
internal enum ThreeDSServiceChallengeError: Error, Equatable {
1111
/// The transaction was not initialized during fingerprinting.
12-
case transactionNotInitialized(errorPayload: String)
12+
case transactionNotInitialized
1313
/// Edge case which should never occur.
1414
case errorAndResultAreNil(errorPayload: String)
1515
/// The challenge has been cancelled.

AdyenActions/Components/3DS2/Action handlers/3DS2 Without Delegated Authentication/ThreeDS2CoreActionHandler.swift

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import Foundation
1111
internal protocol AnyThreeDS2CoreActionHandler: Component {
1212
var threeDSRequestorAppURL: URL? { get set }
1313

14-
var service: ThreeDSServiceable { get set }
15-
1614
var presentationDelegate: PresentationDelegate? { get set }
1715

1816
func handle(
@@ -28,6 +26,8 @@ internal protocol AnyThreeDS2CoreActionHandler: Component {
2826
)
2927
}
3028

29+
internal typealias ThreeDSService = ThreeDSConfigurable & ThreeDSServiceable
30+
3131
/// Handles the 3D Secure 2 fingerprint and challenge actions separately.
3232
internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
3333
private enum Constants {
@@ -40,8 +40,8 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
4040

4141
/// The appearance configuration of the 3D Secure 2 challenge UI.
4242
internal let appearanceConfiguration: ADYAppearanceConfiguration
43-
44-
internal lazy var service: ThreeDSServiceable = ThreeDSServiceLegacy()
43+
44+
private let service: ThreeDSService
4545

4646
internal weak var presentationDelegate: PresentationDelegate?
4747

@@ -55,7 +55,7 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
5555
/// - Parameter appearanceConfiguration: The appearance configuration of the 3D Secure 2 challenge UI.
5656
internal init(
5757
context: AdyenContext,
58-
service: ThreeDSServiceable = ThreeDSServiceLegacy(),
58+
service: ThreeDSService,
5959
appearanceConfiguration: ADYAppearanceConfiguration = ADYAppearanceConfiguration()
6060
) {
6161
self.context = context
@@ -222,8 +222,7 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
222222
message: "cancelled"
223223
)
224224

225-
case let .transactionNotInitialized(errorPayload):
226-
opaqueRepresentationOfError = errorPayload
225+
case .transactionNotInitialized:
227226
sendErrorEvent(
228227
.threeDS2TransactionMissing,
229228
for: Constants.challengeEvent
@@ -246,7 +245,6 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
246245
threeDS2SDKError: opaqueRepresentationOfError,
247246
transStatus: Constants.transStatusWhenError
248247
)
249-
self.service.resetTransaction()
250248
completionHandler(.success(threeDSResult))
251249
} catch {
252250
didFail(with: error, completionHandler: completionHandler)
@@ -265,7 +263,6 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
265263
authorizationToken: authorizationToken,
266264
threeDS2SDKError: nil
267265
)
268-
self.service.resetTransaction()
269266
completionHandler(.success(threeDSResult))
270267
} catch {
271268
didFail(with: error, completionHandler: completionHandler)
@@ -276,7 +273,7 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
276273
with error: Error,
277274
completionHandler: @escaping (Result<R, Error>) -> Void
278275
) {
279-
self.service.resetTransaction()
276+
service.resetTransaction()
280277
completionHandler(.failure(error))
281278
}
282279

AdyenActions/Components/3DS2/Action handlers/3DS2+Delegated Authentication/ThreeDS2PlusDACoreActionHandler.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ internal typealias VoidHandler = () -> Void
4242
/// - Parameter presentationDelegate: The presentation delegate
4343
internal convenience init(
4444
context: AdyenContext,
45+
service: ThreeDSService,
4546
appearanceConfiguration: ADYAppearanceConfiguration,
4647
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication
4748
) {
4849
self.init(
4950
context: context,
51+
service: service,
5052
presenter: ThreeDS2PlusDAScreenPresenter(
5153
style: delegatedAuthenticationConfiguration.delegatedAuthenticationComponentStyle,
5254
localizedParameters: delegatedAuthenticationConfiguration.localizationParameters,
@@ -69,7 +71,7 @@ internal typealias VoidHandler = () -> Void
6971
/// - Parameter delegatedAuthenticationService: The Delegated Authentication service.
7072
internal init(
7173
context: AdyenContext,
72-
service: ThreeDSServiceable = ThreeDSServiceLegacy(),
74+
service: ThreeDSService,
7375
presenter: ThreeDS2PlusDAScreenPresenterProtocol,
7476
appearanceConfiguration: ADYAppearanceConfiguration = .init(),
7577
style: DelegatedAuthenticationComponentStyle = .init(),

AdyenActions/Components/3DS2/Action handlers/AnyThreeDS2ActionHandler.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,29 @@ extension ComponentWrapper {
5050

5151
internal func createDefaultThreeDS2CoreActionHandler(
5252
context: AdyenContext,
53+
service: ThreeDSService,
5354
appearanceConfiguration: ADYAppearanceConfiguration,
5455
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication?
5556
) -> AnyThreeDS2CoreActionHandler {
5657
#if canImport(AdyenAuthentication)
5758
if #available(iOS 16.0, *), let delegatedAuthenticationConfiguration {
5859
return ThreeDS2PlusDACoreActionHandler(
5960
context: context,
61+
service: service,
6062
appearanceConfiguration: appearanceConfiguration,
6163
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
6264
)
6365
} else {
6466
return ThreeDS2CoreActionHandler(
6567
context: context,
68+
service: service,
6669
appearanceConfiguration: appearanceConfiguration
6770
)
6871
}
6972
#else
7073
return ThreeDS2CoreActionHandler(
7174
context: context,
75+
service: service,
7276
appearanceConfiguration: appearanceConfiguration
7377
)
7478
#endif

AdyenActions/Components/3DS2/Action handlers/ThreeDS2ClassicActionHandler+Initializers.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@ extension ThreeDS2ClassicActionHandler {
1616
/// Initializes the 3D Secure 2 action handler.
1717
internal convenience init(
1818
context: AdyenContext,
19+
service: ThreeDSService,
1920
appearanceConfiguration: ADYAppearanceConfiguration,
2021
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication?
2122
) {
2223
let defaultHandler = createDefaultThreeDS2CoreActionHandler(
2324
context: context,
25+
service: service,
2426
appearanceConfiguration: appearanceConfiguration,
2527
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
2628
)
2729
self.init(
2830
context: context,
2931
appearanceConfiguration: appearanceConfiguration,
32+
service: service,
3033
coreActionHandler: defaultHandler,
3134
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
3235
)

AdyenActions/Components/3DS2/Action handlers/ThreeDS2ClassicActionHandler.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,18 @@ internal class ThreeDS2ClassicActionHandler: AnyThreeDS2ActionHandler, Component
3636

3737
internal init(
3838
context: AdyenContext,
39-
service: ThreeDSServiceable = ThreeDSServiceLegacy(),
4039
appearanceConfiguration: ADYAppearanceConfiguration = ADYAppearanceConfiguration(),
40+
service: ThreeDSService,
4141
coreActionHandler: AnyThreeDS2CoreActionHandler? = nil,
4242
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication? = nil
4343
) {
4444
self.coreActionHandler = coreActionHandler ?? createDefaultThreeDS2CoreActionHandler(
4545
context: context,
46+
service: service,
4647
appearanceConfiguration: appearanceConfiguration,
4748
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
4849
)
4950
self.context = context
50-
self.coreActionHandler.service = service
5151
}
5252

5353
// MARK: - Fingerprint

AdyenActions/Components/3DS2/Action handlers/ThreeDS2CompactActionHandler+Initializers.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extension ThreeDS2CompactActionHandler {
1616
/// Initializes the 3D Secure 2 action handler.
1717
internal convenience init(
1818
context: AdyenContext,
19+
service: ThreeDSService,
1920
appearanceConfiguration: ADYAppearanceConfiguration,
2021
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication?
2122
) {
@@ -25,8 +26,10 @@ extension ThreeDS2CompactActionHandler {
2526
context: context,
2627
fingerprintSubmitter: fingerprintSubmitter,
2728
appearanceConfiguration: appearanceConfiguration,
29+
service: service,
2830
coreActionHandler: createDefaultThreeDS2CoreActionHandler(
2931
context: context,
32+
service: service,
3033
appearanceConfiguration: appearanceConfiguration,
3134
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
3235
),

0 commit comments

Comments
 (0)