Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Adyen.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@
21C711242D6D3B2500A08111 /* ThreeDSServiceableMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21C711232D6D3B2100A08111 /* ThreeDSServiceableMock.swift */; };
21CBEF4D2D88768700178809 /* ThreeDS2Component+FingerprintTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CBEF4C2D88766D00178809 /* ThreeDS2Component+FingerprintTokenTests.swift */; };
21CBEF4F2D88CA2300178809 /* ThreeDS2ComponentThreeDSConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CBEF4E2D88CA1600178809 /* ThreeDS2ComponentThreeDSConfiguration.swift */; };
21CBEF642D8B0A0700178809 /* ThreeDSServiceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CBEF632D8B0A0500178809 /* ThreeDSServiceProvider.swift */; };
21CBEF662D8B6B3A00178809 /* ThreeDSServiceProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21CBEF652D8B6B2F00178809 /* ThreeDSServiceProviderTests.swift */; };
5A1315C926296B100092366D /* ProgressViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A1315C826296B100092366D /* ProgressViewStyle.swift */; };
5A15D589264BB0BD00A8E3C7 /* BoletoComponentExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A15D588264BB0BD00A8E3C7 /* BoletoComponentExtensions.swift */; };
5A15D5A1264BE1E500A8E3C7 /* PrefilledShopperInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A15D5A0264BE1E500A8E3C7 /* PrefilledShopperInformation.swift */; };
Expand Down Expand Up @@ -1598,6 +1600,8 @@
21C711232D6D3B2100A08111 /* ThreeDSServiceableMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeDSServiceableMock.swift; sourceTree = "<group>"; };
21CBEF4C2D88766D00178809 /* ThreeDS2Component+FingerprintTokenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ThreeDS2Component+FingerprintTokenTests.swift"; sourceTree = "<group>"; };
21CBEF4E2D88CA1600178809 /* ThreeDS2ComponentThreeDSConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeDS2ComponentThreeDSConfiguration.swift; sourceTree = "<group>"; };
21CBEF632D8B0A0500178809 /* ThreeDSServiceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeDSServiceProvider.swift; sourceTree = "<group>"; };
21CBEF652D8B6B2F00178809 /* ThreeDSServiceProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeDSServiceProviderTests.swift; sourceTree = "<group>"; };
5A1315C826296B100092366D /* ProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressViewStyle.swift; sourceTree = "<group>"; };
5A15D588264BB0BD00A8E3C7 /* BoletoComponentExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoletoComponentExtensions.swift; sourceTree = "<group>"; };
5A15D5A0264BE1E500A8E3C7 /* PrefilledShopperInformation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefilledShopperInformation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5199,6 +5203,7 @@
isa = PBXGroup;
children = (
21CBEF4C2D88766D00178809 /* ThreeDS2Component+FingerprintTokenTests.swift */,
21CBEF652D8B6B2F00178809 /* ThreeDSServiceProviderTests.swift */,
21C7111B2D6D36A400A08111 /* ThreeDS2ServiceLegacyTests.swift */,
F957AA442552C3BB0099AD73 /* 3DS2 Fingerprint Submitter */,
F957AA592552D8BB0099AD73 /* ThreeDS2ComponentTests.swift */,
Expand Down Expand Up @@ -5596,6 +5601,7 @@
F9A7AC592554062400012EBC /* 3DS2 SDK Adapters */ = {
isa = PBXGroup;
children = (
21CBEF632D8B0A0500178809 /* ThreeDSServiceProvider.swift */,
21C7110D2D67F86700A08111 /* ThreeDSServiceable.swift */,
21C7111A2D67FEFF00A08111 /* LegacySDK */,
21C711142D67FE9A00A08111 /* FingerprintServiceParameters.swift */,
Expand Down Expand Up @@ -7317,6 +7323,7 @@
F97C83F725BF0C2800D7F85C /* DokuComponentTests.swift in Sources */,
81F177AE2B4C49B9009CAD96 /* PresenterMock.swift in Sources */,
F924C072246BEEF0006DDAB8 /* CardDetailsTests.swift in Sources */,
21CBEF662D8B6B3A00178809 /* ThreeDSServiceProviderTests.swift in Sources */,
F9FE39E32679E09200F02A9B /* LoadingViewTests.swift in Sources */,
B6EE0F3D2BBEBF5100B9810D /* AnalyticsProviderMock.swift in Sources */,
A0290DF82C413056005BE269 /* StoredPaymentMethodDelegateMock.swift in Sources */,
Expand Down Expand Up @@ -7625,6 +7632,7 @@
F9976BB326D903F400D2D7CE /* MultibancoVoucherAction.swift in Sources */,
F9175FFA259499D500D653BE /* AwaitViewController.swift in Sources */,
21C711112D67FE6B00A08111 /* ThreeDSServiceChallengeError.swift in Sources */,
21CBEF642D8B0A0700178809 /* ThreeDSServiceProvider.swift in Sources */,
F96757C327CF909900A16FB6 /* AnyWeChatPaySDKActionComponent.swift in Sources */,
F9175FD22594999600D653BE /* RedirectComponent.swift in Sources */,
00165FE02C05DA8600347399 /* RedireactableAwaitAction.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ internal final class ThreeDSServiceLegacy: ThreeDSServiceable {
)

guard let transaction else {
return completionHandler(.failure(.transactionNotInitialized(
errorPayload: opaqueErrorObject(error: ThreeDSServiceChallengeError.transactionNotInitialized(errorPayload: ""))
)))
return completionHandler(.failure(.transactionNotInitialized))
}

transaction.performChallenge(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import Foundation

/// Errors that could happen during a challenge
internal enum ThreeDSServiceChallengeError: Error {
internal enum ThreeDSServiceChallengeError: Error, Equatable {
/// The transaction was not initialized during fingerprinting.
case transactionNotInitialized(errorPayload: String)
case transactionNotInitialized
/// Edge case which should never occur.
case errorAndResultAreNil(errorPayload: String)
/// The challenge has been cancelled.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// Copyright (c) 2025 Adyen N.V.
//
// This file is open source and available under the MIT license. See the LICENSE file for more info.
//

import Foundation
@_spi(AdyenInternal) import Adyen

internal protocol ThreeDSConfigurable {
var configuration: ThreeDSFeatureChecker? { get set }
}

internal protocol ThreeDSFeatureChecker {
func isFeatureEnabled(_ name: String) -> Bool
}

/// This type creates a ThreeDSServiceable implementation and consumes ThreeDSConfigurable.
internal final class ThreeDSServiceProvider: ThreeDSServiceable, ThreeDSConfigurable {

/// An optional configuration that can be provided to the service provider.
internal var configuration: ThreeDSFeatureChecker?

private var service: ThreeDSServiceable?
private let serviceCreationHandler: (ThreeDSFeatureChecker?) -> ThreeDSServiceable

internal init(
serviceCreationHandler: @escaping (ThreeDSFeatureChecker?) -> ThreeDSServiceable = createService
) {
self.serviceCreationHandler = serviceCreationHandler
}

internal func performFingerprint(
parameters: FingerprintServiceParameters,
completionHandler: @escaping (Result<any AnyAuthenticationRequestParameters, ThreeDSServiceFingerprintError>) -> Void
) {
self.resetTransaction()
let service = serviceCreationHandler(configuration)
self.service = service
service.performFingerprint(
parameters: parameters,
completionHandler: completionHandler
)
}

internal func performChallenge(
with parameters: ChallengeParameters,
completionHandler: @escaping (Result<any AnyChallengeResult, ThreeDSServiceChallengeError>) -> Void
) {
// The service needs to be already created during the fingerprint step.
// There is no way a challenge action should happen without a fingerprint action.
guard let service = self.service else {
completionHandler(.failure(.transactionNotInitialized))
return
}
service.performChallenge(with: parameters) { result in
self.resetTransaction()
completionHandler(result)
}
}

internal func resetTransaction() {
service?.resetTransaction()
service = nil
}

internal static func createService(
configuration: ThreeDSFeatureChecker?
) -> ThreeDSServiceable {
ThreeDSServiceLegacy()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import Foundation
internal protocol AnyThreeDS2CoreActionHandler: Component {
var threeDSRequestorAppURL: URL? { get set }

var service: ThreeDSServiceable { get set }

var presentationDelegate: PresentationDelegate? { get set }

func handle(
Expand All @@ -28,6 +26,8 @@ internal protocol AnyThreeDS2CoreActionHandler: Component {
)
}

internal typealias ThreeDSService = ThreeDSConfigurable & ThreeDSServiceable

/// Handles the 3D Secure 2 fingerprint and challenge actions separately.
internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
private enum Constants {
Expand All @@ -40,8 +40,8 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {

/// The appearance configuration of the 3D Secure 2 challenge UI.
internal let appearanceConfiguration: ADYAppearanceConfiguration

internal lazy var service: ThreeDSServiceable = ThreeDSServiceLegacy()
private var service: ThreeDSService

internal weak var presentationDelegate: PresentationDelegate?

Expand All @@ -55,7 +55,7 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
/// - Parameter appearanceConfiguration: The appearance configuration of the 3D Secure 2 challenge UI.
internal init(
context: AdyenContext,
service: ThreeDSServiceable = ThreeDSServiceLegacy(),
service: ThreeDSService,
appearanceConfiguration: ADYAppearanceConfiguration = ADYAppearanceConfiguration()
) {
self.context = context
Expand Down Expand Up @@ -88,7 +88,7 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
appearanceConfiguration: appearanceConfiguration,
threeDSMessageVersion: token.threeDSMessageVersion
)

service.configuration = token.configuration
service.performFingerprint(
parameters: serviceParameters
) { result in
Expand Down Expand Up @@ -222,8 +222,7 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
message: "cancelled"
)

case let .transactionNotInitialized(errorPayload):
opaqueRepresentationOfError = errorPayload
case .transactionNotInitialized:
sendErrorEvent(
.threeDS2TransactionMissing,
for: Constants.challengeEvent
Expand All @@ -246,7 +245,6 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
threeDS2SDKError: opaqueRepresentationOfError,
transStatus: Constants.transStatusWhenError
)
self.service.resetTransaction()
completionHandler(.success(threeDSResult))
} catch {
didFail(with: error, completionHandler: completionHandler)
Expand All @@ -265,7 +263,6 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
authorizationToken: authorizationToken,
threeDS2SDKError: nil
)
self.service.resetTransaction()
completionHandler(.success(threeDSResult))
} catch {
didFail(with: error, completionHandler: completionHandler)
Expand All @@ -276,7 +273,7 @@ internal class ThreeDS2CoreActionHandler: AnyThreeDS2CoreActionHandler {
with error: Error,
completionHandler: @escaping (Result<R, Error>) -> Void
) {
self.service.resetTransaction()
service.resetTransaction()
completionHandler(.failure(error))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ internal typealias VoidHandler = () -> Void
/// - Parameter presentationDelegate: The presentation delegate
internal convenience init(
context: AdyenContext,
service: ThreeDSService,
appearanceConfiguration: ADYAppearanceConfiguration,
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication
) {
self.init(
context: context,
service: service,
presenter: ThreeDS2PlusDAScreenPresenter(
style: delegatedAuthenticationConfiguration.delegatedAuthenticationComponentStyle,
localizedParameters: delegatedAuthenticationConfiguration.localizationParameters,
Expand All @@ -69,7 +71,7 @@ internal typealias VoidHandler = () -> Void
/// - Parameter delegatedAuthenticationService: The Delegated Authentication service.
internal init(
context: AdyenContext,
service: ThreeDSServiceable = ThreeDSServiceLegacy(),
service: ThreeDSService,
presenter: ThreeDS2PlusDAScreenPresenterProtocol,
appearanceConfiguration: ADYAppearanceConfiguration = .init(),
style: DelegatedAuthenticationComponentStyle = .init(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,29 @@ extension ComponentWrapper {

internal func createDefaultThreeDS2CoreActionHandler(
context: AdyenContext,
service: ThreeDSService,
appearanceConfiguration: ADYAppearanceConfiguration,
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication?
) -> AnyThreeDS2CoreActionHandler {
#if canImport(AdyenAuthentication)
if #available(iOS 16.0, *), let delegatedAuthenticationConfiguration {
return ThreeDS2PlusDACoreActionHandler(
context: context,
service: service,
appearanceConfiguration: appearanceConfiguration,
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
)
} else {
return ThreeDS2CoreActionHandler(
context: context,
service: service,
appearanceConfiguration: appearanceConfiguration
)
}
#else
return ThreeDS2CoreActionHandler(
context: context,
service: service,
appearanceConfiguration: appearanceConfiguration
)
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@ extension ThreeDS2ClassicActionHandler {
/// Initializes the 3D Secure 2 action handler.
internal convenience init(
context: AdyenContext,
service: ThreeDSService,
appearanceConfiguration: ADYAppearanceConfiguration,
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication?
) {
let defaultHandler = createDefaultThreeDS2CoreActionHandler(
context: context,
service: service,
appearanceConfiguration: appearanceConfiguration,
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
)
self.init(
context: context,
appearanceConfiguration: appearanceConfiguration,
service: service,
coreActionHandler: defaultHandler,
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ internal class ThreeDS2ClassicActionHandler: AnyThreeDS2ActionHandler, Component

internal init(
context: AdyenContext,
service: ThreeDSServiceable = ThreeDSServiceLegacy(),
appearanceConfiguration: ADYAppearanceConfiguration = ADYAppearanceConfiguration(),
appearanceConfiguration: ADYAppearanceConfiguration,
service: ThreeDSService,
coreActionHandler: AnyThreeDS2CoreActionHandler? = nil,
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication? = nil
) {
self.coreActionHandler = coreActionHandler ?? createDefaultThreeDS2CoreActionHandler(
context: context,
service: service,
appearanceConfiguration: appearanceConfiguration,
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
)
self.context = context
self.coreActionHandler.service = service
}

// MARK: - Fingerprint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extension ThreeDS2CompactActionHandler {
/// Initializes the 3D Secure 2 action handler.
internal convenience init(
context: AdyenContext,
service: ThreeDSService,
appearanceConfiguration: ADYAppearanceConfiguration,
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication?
) {
Expand All @@ -25,8 +26,10 @@ extension ThreeDS2CompactActionHandler {
context: context,
fingerprintSubmitter: fingerprintSubmitter,
appearanceConfiguration: appearanceConfiguration,
service: service,
coreActionHandler: createDefaultThreeDS2CoreActionHandler(
context: context,
service: service,
appearanceConfiguration: appearanceConfiguration,
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,19 @@ internal final class ThreeDS2CompactActionHandler: AnyThreeDS2ActionHandler, Com
internal init(
context: AdyenContext,
fingerprintSubmitter: AnyThreeDS2FingerprintSubmitter? = nil,
service: ThreeDSServiceable = ThreeDSServiceLegacy(),
appearanceConfiguration: ADYAppearanceConfiguration = ADYAppearanceConfiguration(),
appearanceConfiguration: ADYAppearanceConfiguration,
service: ThreeDSService,
coreActionHandler: AnyThreeDS2CoreActionHandler? = nil,
delegatedAuthenticationConfiguration: ThreeDS2Component.Configuration.DelegatedAuthentication? = nil
) {
self.context = context
self.coreActionHandler = coreActionHandler ?? createDefaultThreeDS2CoreActionHandler(
context: context,
service: service,
appearanceConfiguration: appearanceConfiguration,
delegatedAuthenticationConfiguration: delegatedAuthenticationConfiguration
)
self.fingerprintSubmitter = fingerprintSubmitter ?? ThreeDS2FingerprintSubmitter(context: context)
self.coreActionHandler.service = service
}

// MARK: - Fingerprint
Expand Down
2 changes: 2 additions & 0 deletions AdyenActions/Components/3DS2/ThreeDS2Component.swift
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ public final class ThreeDS2Component: ActionComponent {
internal lazy var threeDS2CompactFlowHandler: AnyThreeDS2ActionHandler = {
let handler = ThreeDS2CompactActionHandler(
context: context,
service: ThreeDSServiceProvider(),
appearanceConfiguration: configuration.appearanceConfiguration,
delegatedAuthenticationConfiguration: configuration.delegateAuthentication
)
Expand All @@ -242,6 +243,7 @@ public final class ThreeDS2Component: ActionComponent {
internal lazy var threeDS2ClassicFlowHandler: AnyThreeDS2ActionHandler = {
let handler = ThreeDS2ClassicActionHandler(
context: context,
service: ThreeDSServiceProvider(),
appearanceConfiguration: configuration.appearanceConfiguration,
delegatedAuthenticationConfiguration: configuration.delegateAuthentication
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//

internal extension ThreeDS2Component {
struct ThreeDSConfiguration: Decodable {
struct ThreeDSConfiguration: Decodable, ThreeDSFeatureChecker {
private let version: String
internal let featureFlags: [String: Bool]?

Expand Down
Loading
Loading