From 92c9445863de64214468d84948fcd8be6e54517b Mon Sep 17 00:00:00 2001 From: erenbesel Date: Thu, 9 Oct 2025 15:37:39 +0200 Subject: [PATCH 01/10] new field sdkData for paymentComponentData Adds new field combining several fields for payments calls while deprecating the old ones. --- Adyen.xcodeproj/project.pbxproj | 4 + .../Core Protocols/PaymentComponent.swift | 32 +++- Adyen/Model/PaymentComponentData.swift | 37 ++++- Adyen/Model/SDKData.swift | 54 ++++++ AdyenCard/Components/Card/CardDetails.swift | 8 + AdyenDropIn/DropInComponentExtensions.swift | 11 +- .../API/Payments/PaymentsRequest.swift | 7 +- Demo/Common/Networking/PaymentsRequest.swift | 6 +- .../PaymentComponentSubjectTests.swift | 154 +++++++++++++++++- 9 files changed, 272 insertions(+), 41 deletions(-) create mode 100644 Adyen/Model/SDKData.swift diff --git a/Adyen.xcodeproj/project.pbxproj b/Adyen.xcodeproj/project.pbxproj index 369ac776eb..ef047a3a48 100644 --- a/Adyen.xcodeproj/project.pbxproj +++ b/Adyen.xcodeproj/project.pbxproj @@ -399,6 +399,7 @@ A09C704327032870004C01AA /* FormCardLogosItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A09C704227032870004C01AA /* FormCardLogosItemView.swift */; }; A09FB2EB2B8399B700270D51 /* TrackableComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A09FB2EA2B8399B700270D51 /* TrackableComponent.swift */; }; A09FB2ED2B86051000270D51 /* AnalyticsEventDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A09FB2EC2B86051000270D51 /* AnalyticsEventDataSource.swift */; }; + A0A0B37E2E93F87200E8567D /* SDKData.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A0B37D2E93F86F00E8567D /* SDKData.swift */; }; A0B180312A2DE445003C608E /* MealVoucherDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0B180302A2DE445003C608E /* MealVoucherDetails.swift */; }; A0B4B9B12BC027DE00C99926 /* CardComponentEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0B4B9B02BC027DE00C99926 /* CardComponentEventTests.swift */; }; A0BC64E628F062E400CED2A1 /* AdyenSessionAware.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0BC64E528F062E400CED2A1 /* AdyenSessionAware.swift */; }; @@ -1945,6 +1946,7 @@ A09C704227032870004C01AA /* FormCardLogosItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormCardLogosItemView.swift; sourceTree = ""; }; A09FB2EA2B8399B700270D51 /* TrackableComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackableComponent.swift; sourceTree = ""; }; A09FB2EC2B86051000270D51 /* AnalyticsEventDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEventDataSource.swift; sourceTree = ""; }; + A0A0B37D2E93F86F00E8567D /* SDKData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKData.swift; sourceTree = ""; }; A0B180302A2DE445003C608E /* MealVoucherDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealVoucherDetails.swift; sourceTree = ""; }; A0B4B9B02BC027DE00C99926 /* CardComponentEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardComponentEventTests.swift; sourceTree = ""; }; A0BC64E528F062E400CED2A1 /* AdyenSessionAware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenSessionAware.swift; sourceTree = ""; }; @@ -4790,6 +4792,7 @@ E788A4D82657FF5D00089448 /* Model */ = { isa = PBXGroup; children = ( + A0A0B37D2E93F86F00E8567D /* SDKData.swift */, F91664EF23E41D7300C10738 /* AnyEncodable.swift */, F9FE25222625ADD0001874BB /* PartialPaymentOrder.swift */, F9838D382631AD1900963483 /* Balance.swift */, @@ -7584,6 +7587,7 @@ 00BC8F552BF349FC00EBE0EB /* SelectableFormItemStyle.swift in Sources */, F90FB7C02448587C005BFE0E /* TextField.swift in Sources */, E224088D22B0FD220058923E /* StoredPayPalPaymentMethod.swift in Sources */, + A0A0B37E2E93F87200E8567D /* SDKData.swift in Sources */, F99D2F0A266136A700BB5B2F /* AppleWalletPassResponse.swift in Sources */, F9639B3524DD97A30073F38A /* PaymentStatusResponse.swift in Sources */, A0DB486E2AFD0BFC00348C83 /* AnalyticsEventError.swift in Sources */, diff --git a/Adyen/Core/Core Protocols/PaymentComponent.swift b/Adyen/Core/Core Protocols/PaymentComponent.swift index 3be39c3328..b2c6ff1d87 100644 --- a/Adyen/Core/Core Protocols/PaymentComponent.swift +++ b/Adyen/Core/Core Protocols/PaymentComponent.swift @@ -33,18 +33,32 @@ extension PaymentComponent { sendSubmitEvent() let component = component ?? self - let checkoutAttemptId = component.context.analyticsProvider?.checkoutAttemptId ?? AnalyticsConstants.fetchCheckoutAttemptIdFailed - let updatedData = data.replacing(checkoutAttemptId: checkoutAttemptId) - - guard updatedData.browserInfo == nil else { - delegate?.didSubmit(updatedData, from: component) - return - } - updatedData.dataByAddingBrowserInfo { [weak self] in - self?.delegate?.didSubmit($0, from: component) + + prepareSubmitData(from: data) { [weak self] updatedData in + guard let self else { return } + self.delegate?.didSubmit(updatedData, from: component) } } + public var checkoutAttemptId: String { + context.analyticsProvider?.checkoutAttemptId ?? AnalyticsConstants.fetchCheckoutAttemptIdFailed + } + + /// Adds SDK related info to payment data object and returns the final data in the completion. + public func prepareSubmitData(from data: PaymentComponentData, completion: @escaping (PaymentComponentData) -> Void) { + + let sdkData = SDKData( + checkoutAttemptId: checkoutAttemptId, + authenticationProvider: data.paymentMethod as? SDKDataAuthenticationProvider + ) + + let updatedData = data + .replacing(checkoutAttemptId: checkoutAttemptId) + .replacing(sdkData: sdkData) + + updatedData.dataByAddingBrowserInfo(completion: completion) + } + private func sendSubmitEvent() { let logEvent = AnalyticsEventLog(component: paymentMethod.type.rawValue, type: .submit) context.analyticsProvider?.add(log: logEvent) diff --git a/Adyen/Model/PaymentComponentData.swift b/Adyen/Model/PaymentComponentData.swift index b5abdfbd8c..8f5f72ca49 100644 --- a/Adyen/Model/PaymentComponentData.swift +++ b/Adyen/Model/PaymentComponentData.swift @@ -8,7 +8,7 @@ import Foundation /** The data supplied by a payment component upon completion. - + - SeeAlso: [API Reference](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__example_payments-klarna) */ @@ -36,6 +36,7 @@ public struct PaymentComponentData { public let installments: Installments? /// Indicates whether the current SDK version suports native redirect without glue pages. + @available(*, deprecated, message: "This property is deprecated. Use the new sdkData property instead.") public let supportNativeRedirect: Bool = true /// Shopper name. @@ -60,6 +61,7 @@ public struct PaymentComponentData { public let browserInfo: BrowserInfo? /// A unique identifier for a checkout attempt. + @available(*, deprecated, message: "This property is deprecated. Use the new sdkData property instead.") public var checkoutAttemptId: String? { paymentMethod.checkoutAttemptId } @@ -87,6 +89,10 @@ public struct PaymentComponentData { return paymentMethod.delegatedAuthenticationData } + /// An ecnoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum reliability. + public var sdkData: String? + /// Initializes the payment component data. /// /// @@ -98,6 +104,7 @@ public struct PaymentComponentData { /// - browserInfo: The device default browser info. /// - checkoutAttemptId: The checkoutAttempt identifier. /// - installments: Installments selection if specified. + /// - sdkData: The encoded SDK data if specified. @_spi(AdyenInternal) public init( paymentMethodDetails: some PaymentMethodDetails, @@ -105,7 +112,8 @@ public struct PaymentComponentData { order: PartialPaymentOrder?, storePaymentMethod: Bool? = nil, browserInfo: BrowserInfo? = nil, - installments: Installments? = nil + installments: Installments? = nil, + sdkData: String? = nil ) { self.amount = amount self.paymentMethod = paymentMethodDetails @@ -113,6 +121,19 @@ public struct PaymentComponentData { self.storePaymentMethod = storePaymentMethod self.browserInfo = browserInfo self.installments = installments + self.sdkData = sdkData + } + + internal func replacing(sdkData: SDKData) -> PaymentComponentData { + PaymentComponentData( + paymentMethodDetails: paymentMethod, + amount: amount, + order: order, + storePaymentMethod: storePaymentMethod, + browserInfo: browserInfo, + installments: installments, + sdkData: sdkData.encodedValue + ) } @_spi(AdyenInternal) @@ -123,7 +144,8 @@ public struct PaymentComponentData { order: order, storePaymentMethod: storePaymentMethod, browserInfo: browserInfo, - installments: installments + installments: installments, + sdkData: sdkData ) } @@ -135,7 +157,8 @@ public struct PaymentComponentData { order: order, storePaymentMethod: storePaymentMethod, browserInfo: browserInfo, - installments: installments + installments: installments, + sdkData: sdkData ) } @@ -150,7 +173,8 @@ public struct PaymentComponentData { order: order, storePaymentMethod: storePaymentMethod, browserInfo: browserInfo, - installments: installments + installments: installments, + sdkData: sdkData ) } @@ -168,7 +192,8 @@ public struct PaymentComponentData { order: order, storePaymentMethod: storePaymentMethod, browserInfo: $0, - installments: installments + installments: installments, + sdkData: sdkData )) } } diff --git a/Adyen/Model/SDKData.swift b/Adyen/Model/SDKData.swift new file mode 100644 index 0000000000..8adadb4f37 --- /dev/null +++ b/Adyen/Model/SDKData.swift @@ -0,0 +1,54 @@ +// +// 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) +public struct SDKData: Codable { + + internal struct Analytics: Codable { + let checkoutAttemptId: String + } + + public struct Authentication: Codable { + let threeDS2SdkVersion: String + + public init(threeDS2SdkVersion: String) { + self.threeDS2SdkVersion = threeDS2SdkVersion + } + } + + private let analytics: Analytics + private var authentication: Authentication? + private let supportNativeRedirect: Bool = true + private let schemaVersion: String = "1.0" + private let timestamp = Int(Date().timeIntervalSince1970 * 1000) + + internal var encodedValue: String? { + try? AdyenCoder.encodeBase64(self) + } + + internal init( + checkoutAttemptId: String, + authenticationProvider: SDKDataAuthenticationProvider? = nil + ) { + self.analytics = .init(checkoutAttemptId: checkoutAttemptId) + self.authentication = authenticationProvider?.authentication + } + + private enum CodingKeys: String, CodingKey { + case analytics + case authentication + case supportNativeRedirect + case schemaVersion + case timestamp = "createdAt" + } +} + +@_spi(AdyenInternal) +public protocol SDKDataAuthenticationProvider { + var authentication: SDKData.Authentication { get } +} diff --git a/AdyenCard/Components/Card/CardDetails.swift b/AdyenCard/Components/Card/CardDetails.swift index 9f6d1a4bcd..e788a459c2 100644 --- a/AdyenCard/Components/Card/CardDetails.swift +++ b/AdyenCard/Components/Card/CardDetails.swift @@ -57,6 +57,7 @@ public struct CardDetails: PaymentMethodDetails, ShopperInformation { public let socialSecurityNumber: String? /// The 3DS2 SDK version. + @available(*, deprecated, message: "This property is deprecated. Use the new sdkData property of the PaymentComponentData object instead.") public let threeDS2SDKVersion: String = threeDS2SdkVersion /// Brand of the card. @@ -157,3 +158,10 @@ public struct CardDetails: PaymentMethodDetails, ShopperInformation { @_spi(AdyenInternal) extension CardDetails: DelegatedAuthenticationAware {} + +@_spi(AdyenInternal) +extension CardDetails: SDKDataAuthenticationProvider { + public var authentication: SDKData.Authentication { + .init(threeDS2SdkVersion: threeDS2SdkVersion) + } +} diff --git a/AdyenDropIn/DropInComponentExtensions.swift b/AdyenDropIn/DropInComponentExtensions.swift index 1fd015c921..14788e5dc8 100644 --- a/AdyenDropIn/DropInComponentExtensions.swift +++ b/AdyenDropIn/DropInComponentExtensions.swift @@ -67,17 +67,10 @@ extension DropInComponent: PaymentComponentDelegate { public func didSubmit(_ data: PaymentComponentData, from component: PaymentComponent) { paymentInProgress = true - let updatedData = data.replacing(checkoutAttemptId: component.context.analyticsProvider?.checkoutAttemptId) - - guard updatedData.browserInfo == nil else { - self.delegate?.didSubmit(updatedData, from: component, in: self) - return - } - updatedData.dataByAddingBrowserInfo { [weak self] in + component.prepareSubmitData(from: data) { [weak self] updatedData in guard let self else { return } - self.delegate?.didSubmit($0, from: component, in: self) + self.delegate?.didSubmit(updatedData, from: component, in: self) } - } public func didFail(with error: Error, from component: PaymentComponent) { diff --git a/AdyenSession/API/Payments/PaymentsRequest.swift b/AdyenSession/API/Payments/PaymentsRequest.swift index 0b0a624e33..a1c390791e 100644 --- a/AdyenSession/API/Payments/PaymentsRequest.swift +++ b/AdyenSession/API/Payments/PaymentsRequest.swift @@ -44,7 +44,6 @@ internal struct PaymentsRequest: APIRequest { try container.encode(data.paymentMethod.encodable, forKey: .paymentMethod) try container.encode(sessionData, forKey: .sessionData) - try container.encode(data.supportNativeRedirect, forKey: .supportNativeRedirect) try container.encode(data.storePaymentMethod, forKey: .storePaymentMethod) try container.encodeIfPresent( data.delegatedAuthenticationData, @@ -57,18 +56,16 @@ internal struct PaymentsRequest: APIRequest { try container.encodeIfPresent(data.deliveryAddress, forKey: .deliveryAddress) try container.encodeIfPresent(data.socialSecurityNumber, forKey: .socialSecurityNumber) try container.encodeIfPresent(data.browserInfo, forKey: .browserInfo) - try container.encodeIfPresent(data.checkoutAttemptId, forKey: .checkoutAttemptId) try container.encodeIfPresent(data.order?.compactOrder, forKey: .order) + try container.encodeIfPresent(data.sdkData, forKey: .sdkData) } private enum CodingKeys: String, CodingKey { case sessionData case paymentMethod - case supportNativeRedirect case storePaymentMethod case shopperEmail case browserInfo - case checkoutAttemptId case shopperName case telephoneNumber case billingAddress @@ -76,6 +73,7 @@ internal struct PaymentsRequest: APIRequest { case socialSecurityNumber case order case delegatedAuthenticationData + case sdkData } } @@ -102,7 +100,6 @@ internal struct PaymentsResponse: SessionResponse, SessionPaymentResultAware { internal extension PaymentsResponse { - // swiftlint:disable:next explicit_acl enum ResultCode: String, Decodable { case authenticationFinished = "AuthenticationFinished" case authenticationNotRequired = "AuthenticationNotRequired" diff --git a/Demo/Common/Networking/PaymentsRequest.swift b/Demo/Common/Networking/PaymentsRequest.swift index b8d32e4258..a95a071109 100644 --- a/Demo/Common/Networking/PaymentsRequest.swift +++ b/Demo/Common/Networking/PaymentsRequest.swift @@ -40,7 +40,6 @@ internal struct PaymentsRequest: APIRequest { } try container.encode(data.paymentMethod.encodable, forKey: .details) - try container.encode(data.supportNativeRedirect, forKey: .supportNativeRedirect) try container.encode(data.storePaymentMethod, forKey: .storePaymentMethod) try container.encodeIfPresent( data.delegatedAuthenticationData, @@ -69,7 +68,7 @@ internal struct PaymentsRequest: APIRequest { try container.encodeIfPresent(data.installments, forKey: .installments) try container.encode(ConfigurationConstants.lineItems, forKey: .lineItems) try container.encode(ConfigurationConstants.recurringProcessingModel, forKey: .recurringProcessingModel) - try container.encodeIfPresent(data.checkoutAttemptId, forKey: .checkoutAttemptId) + try container.encodeIfPresent(data.sdkData, forKey: .sdkData) try container.encode(ConfigurationConstants.mandate, forKey: .mandate) } @@ -96,7 +95,6 @@ internal struct PaymentsRequest: APIRequest { private enum CodingKeys: String, CodingKey { case details = "paymentMethod" - case supportNativeRedirect case storePaymentMethod case amount case reference @@ -108,7 +106,6 @@ internal struct PaymentsRequest: APIRequest { case additionalData case merchantAccount case browserInfo - case checkoutAttemptId case shopperName case telephoneNumber case shopperLocale @@ -121,6 +118,7 @@ internal struct PaymentsRequest: APIRequest { case delegatedAuthenticationData case recurringProcessingModel case mandate + case sdkData } } diff --git a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift index 54ef9321e0..fc71220b55 100644 --- a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift +++ b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift @@ -40,7 +40,7 @@ class PaymentComponentSubjectTests: XCTestCase { try super.tearDownWithError() } - func testSubmitWithAnalyticsEnabledShouldSetCheckoutAttemptIdInPaymentComponentData() throws { + func test_submit_with_analytics_enabled_sets_checkoutAttemptId_in_sdkData() throws { // Given let expectedCheckoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" analyticsProviderMock.checkoutAttemptId = expectedCheckoutAttemptId @@ -50,10 +50,20 @@ class PaymentComponentSubjectTests: XCTestCase { let didSubmitExpectation = expectation(description: "didSubmit should get called") // When - XCTAssertNil(paymentComponentData.checkoutAttemptId) + XCTAssertNil(paymentComponentData.sdkData) // Then paymentComponentDelegate.onDidSubmit = { data, _ in + XCTAssertNotNil(data.sdkData) + + // Verify SDKData contains checkoutAttemptId + guard let sdkDataString = data.sdkData, + let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + XCTFail("SDKData should be present and decodable") + return + } + + // Verify through the deprecated property for backward compatibility XCTAssertEqual(expectedCheckoutAttemptId, data.checkoutAttemptId) didSubmitExpectation.fulfill() } @@ -63,7 +73,7 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } - func testSubmitWithNoAttemptIdShouldSetConstantInPaymentComponentData() throws { + func test_submit_with_no_attemptId_sets_constant_in_sdkData() throws { // Given analyticsProviderMock.checkoutAttemptId = nil let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") @@ -72,10 +82,11 @@ class PaymentComponentSubjectTests: XCTestCase { let didSubmitExpectation = expectation(description: "didSubmit should get called") // When - XCTAssertNil(paymentComponentData.checkoutAttemptId) + XCTAssertNil(paymentComponentData.sdkData) // Then paymentComponentDelegate.onDidSubmit = { data, _ in + XCTAssertNotNil(data.sdkData) XCTAssertEqual(data.checkoutAttemptId, "fetch-checkoutAttemptId-failed") didSubmitExpectation.fulfill() } @@ -85,7 +96,7 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } - func testSubmitWhenBrowserInfoIsNilShouldSetBrowserInfoInPaymentComponentData() throws { + func test_submit_when_browserInfo_is_nil_sets_browserInfo() throws { // Given let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) @@ -106,7 +117,7 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } - func testSubmitWhenBrowserInfoIsNotNilShouldNotSetBrowserInfoInPaymentComponentData() throws { + func test_submit_when_browserInfo_is_not_nil_preserves_browserInfo() throws { // Given let expectedBrowserInfo = BrowserInfo(userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)") let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") @@ -121,7 +132,6 @@ class PaymentComponentSubjectTests: XCTestCase { paymentComponentDelegate.onDidSubmit = { data, _ in XCTAssertNotNil(data.browserInfo) - XCTAssertEqual(expectedBrowserInfo.userAgent, data.browserInfo?.userAgent) didSubmitExpectation.fulfill() } @@ -130,7 +140,7 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } - func testSubmitEvent() throws { + func test_submit_event() throws { let expectedCheckoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" analyticsProviderMock._checkoutAttemptId = expectedCheckoutAttemptId let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") @@ -148,4 +158,132 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } + + // MARK: - SDKData Tests + + func test_submit_creates_sdkData_with_analytics() throws { + // Given + let expectedCheckoutAttemptId = "test-checkout-attempt-id" + analyticsProviderMock.checkoutAttemptId = expectedCheckoutAttemptId + let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") + let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) + + let didSubmitExpectation = expectation(description: "didSubmit should get called") + + // Then + paymentComponentDelegate.onDidSubmit = { data, _ in + XCTAssertNotNil(data.sdkData, "SDKData should be created") + + // Verify SDKData is properly encoded + guard let sdkDataString = data.sdkData else { + XCTFail("SDKData string should be present") + return + } + + XCTAssertFalse(sdkDataString.isEmpty, "SDKData should not be empty") + didSubmitExpectation.fulfill() + } + + sut.submit(data: paymentComponentData, component: sut) + + wait(for: [didSubmitExpectation], timeout: 3) + } + + func test_submit_with_authenticationProvider_includes_authentication_in_sdkData() throws { + // Given + let expectedCheckoutAttemptId = "test-checkout-attempt-id" + analyticsProviderMock.checkoutAttemptId = expectedCheckoutAttemptId + + // Create a mock payment method that provides authentication + let mockAuthProvider = MockSDKDataAuthenticationProvider() + let paymentMethodDetails = MockAuthenticationPaymentDetails(authProvider: mockAuthProvider) + let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) + + let didSubmitExpectation = expectation(description: "didSubmit should get called") + + // Then + paymentComponentDelegate.onDidSubmit = { data, _ in + XCTAssertNotNil(data.sdkData, "SDKData should be created") + + guard let sdkDataString = data.sdkData else { + XCTFail("SDKData string should be present") + return + } + + // Verify the SDKData contains both analytics and authentication + XCTAssertFalse(sdkDataString.isEmpty) + didSubmitExpectation.fulfill() + } + + sut.submit(data: paymentComponentData, component: sut) + + wait(for: [didSubmitExpectation], timeout: 3) + } + + func test_submit_without_authenticationProvider_includes_only_analytics_in_sdkData() throws { + // Given + let expectedCheckoutAttemptId = "test-checkout-attempt-id" + analyticsProviderMock.checkoutAttemptId = expectedCheckoutAttemptId + + // Use a payment method that doesn't provide authentication + let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") + let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) + + let didSubmitExpectation = expectation(description: "didSubmit should get called") + + // Then + paymentComponentDelegate.onDidSubmit = { data, _ in + XCTAssertNotNil(data.sdkData, "SDKData should be created even without authentication") + didSubmitExpectation.fulfill() + } + + sut.submit(data: paymentComponentData, component: sut) + + wait(for: [didSubmitExpectation], timeout: 3) + } + + func test_submit_includes_sdkData_timestamp() throws { + // Given + let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") + let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) + + let didSubmitExpectation = expectation(description: "didSubmit should get called") + + // Then + paymentComponentDelegate.onDidSubmit = { data, _ in + XCTAssertNotNil(data.sdkData) + + // Verify timestamp is recent (within last 5 seconds) + let now = Int(Date().timeIntervalSince1970 * 1000) + // We can't directly access timestamp due to private access, but we verify SDKData exists + XCTAssertNotNil(data.sdkData) + didSubmitExpectation.fulfill() + } + + sut.submit(data: paymentComponentData, component: sut) + + wait(for: [didSubmitExpectation], timeout: 3) + } +} + +// MARK: - Test Helpers + +private class MockSDKDataAuthenticationProvider: SDKDataAuthenticationProvider { + var authentication: SDKData.Authentication { + SDKData.Authentication(threeDS2SdkVersion: "2.2.0") + } +} + +private struct MockAuthenticationPaymentDetails: PaymentMethodDetails, SDKDataAuthenticationProvider { + let type: PaymentMethodType = .scheme + var checkoutAttemptId: String? + let authProvider: MockSDKDataAuthenticationProvider + + var authentication: SDKData.Authentication { + authProvider.authentication + } + + private enum CodingKeys: String, CodingKey { + case type + } } From f79455bb9fae6d172349311897577433caad2524 Mon Sep 17 00:00:00 2001 From: erenbesel Date: Thu, 9 Oct 2025 15:50:45 +0200 Subject: [PATCH 02/10] fix typo --- Adyen/Model/PaymentComponentData.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adyen/Model/PaymentComponentData.swift b/Adyen/Model/PaymentComponentData.swift index 8f5f72ca49..8592662a30 100644 --- a/Adyen/Model/PaymentComponentData.swift +++ b/Adyen/Model/PaymentComponentData.swift @@ -89,7 +89,7 @@ public struct PaymentComponentData { return paymentMethod.delegatedAuthenticationData } - /// An ecnoded string containing important SDK-specific data. + /// An encoded string containing important SDK-specific data. /// It is recommended to pass this field to your server to ensure maximum reliability. public var sdkData: String? From 30134889ca82112936f61546023998db46318f3f Mon Sep 17 00:00:00 2001 From: erenbesel Date: Fri, 10 Oct 2025 11:43:38 +0200 Subject: [PATCH 03/10] add schema version as a constant to track versions --- Adyen/Model/SDKData.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Adyen/Model/SDKData.swift b/Adyen/Model/SDKData.swift index 8adadb4f37..28a09c1646 100644 --- a/Adyen/Model/SDKData.swift +++ b/Adyen/Model/SDKData.swift @@ -10,11 +10,11 @@ import Foundation public struct SDKData: Codable { internal struct Analytics: Codable { - let checkoutAttemptId: String + internal let checkoutAttemptId: String } public struct Authentication: Codable { - let threeDS2SdkVersion: String + private let threeDS2SdkVersion: String public init(threeDS2SdkVersion: String) { self.threeDS2SdkVersion = threeDS2SdkVersion @@ -24,7 +24,7 @@ public struct SDKData: Codable { private let analytics: Analytics private var authentication: Authentication? private let supportNativeRedirect: Bool = true - private let schemaVersion: String = "1.0" + private let schemaVersion: String = SchemaVersions.v1_0 private let timestamp = Int(Date().timeIntervalSince1970 * 1000) internal var encodedValue: String? { @@ -46,6 +46,10 @@ public struct SDKData: Codable { case schemaVersion case timestamp = "createdAt" } + + private enum SchemaVersions { + internal static let v1_0 = "1.0" + } } @_spi(AdyenInternal) From 4f53ba5e6758e89b389afbe57d2b49d5c6e4e56b Mon Sep 17 00:00:00 2001 From: erenbesel Date: Fri, 10 Oct 2025 15:18:28 +0200 Subject: [PATCH 04/10] disable property rule for version constant --- Adyen/Model/PaymentComponentData.swift | 2 +- Adyen/Model/SDKData.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Adyen/Model/PaymentComponentData.swift b/Adyen/Model/PaymentComponentData.swift index 8592662a30..2ecd479021 100644 --- a/Adyen/Model/PaymentComponentData.swift +++ b/Adyen/Model/PaymentComponentData.swift @@ -90,7 +90,7 @@ public struct PaymentComponentData { } /// An encoded string containing important SDK-specific data. - /// It is recommended to pass this field to your server to ensure maximum reliability. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. public var sdkData: String? /// Initializes the payment component data. diff --git a/Adyen/Model/SDKData.swift b/Adyen/Model/SDKData.swift index 28a09c1646..8043fe4881 100644 --- a/Adyen/Model/SDKData.swift +++ b/Adyen/Model/SDKData.swift @@ -48,6 +48,7 @@ public struct SDKData: Codable { } private enum SchemaVersions { + // swiftlint:disable:next identifier_name internal static let v1_0 = "1.0" } } From 138b2f70abea933aefcf89aa915d4f071278943d Mon Sep 17 00:00:00 2001 From: erenbesel Date: Mon, 13 Oct 2025 14:07:00 +0200 Subject: [PATCH 05/10] made tests more to the point --- Adyen/Model/SDKData.swift | 6 +- .../PaymentComponentSubjectTests.swift | 112 +++++++----------- 2 files changed, 45 insertions(+), 73 deletions(-) diff --git a/Adyen/Model/SDKData.swift b/Adyen/Model/SDKData.swift index 8043fe4881..01026871d1 100644 --- a/Adyen/Model/SDKData.swift +++ b/Adyen/Model/SDKData.swift @@ -14,15 +14,15 @@ public struct SDKData: Codable { } public struct Authentication: Codable { - private let threeDS2SdkVersion: String + internal let threeDS2SdkVersion: String public init(threeDS2SdkVersion: String) { self.threeDS2SdkVersion = threeDS2SdkVersion } } - private let analytics: Analytics - private var authentication: Authentication? + internal let analytics: Analytics + internal private(set) var authentication: Authentication? private let supportNativeRedirect: Bool = true private let schemaVersion: String = SchemaVersions.v1_0 private let timestamp = Int(Date().timeIntervalSince1970 * 1000) diff --git a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift index fc71220b55..11d2d1abb0 100644 --- a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift +++ b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift @@ -40,10 +40,9 @@ class PaymentComponentSubjectTests: XCTestCase { try super.tearDownWithError() } - func test_submit_with_analytics_enabled_sets_checkoutAttemptId_in_sdkData() throws { + func test_submit_with_no_attemptId_sets_constant_in_sdkData() throws { // Given - let expectedCheckoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" - analyticsProviderMock.checkoutAttemptId = expectedCheckoutAttemptId + analyticsProviderMock.checkoutAttemptId = nil let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) @@ -51,42 +50,18 @@ class PaymentComponentSubjectTests: XCTestCase { // When XCTAssertNil(paymentComponentData.sdkData) - + // Then paymentComponentDelegate.onDidSubmit = { data, _ in XCTAssertNotNil(data.sdkData) - // Verify SDKData contains checkoutAttemptId guard let sdkDataString = data.sdkData, let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { XCTFail("SDKData should be present and decodable") return } - // Verify through the deprecated property for backward compatibility - XCTAssertEqual(expectedCheckoutAttemptId, data.checkoutAttemptId) - didSubmitExpectation.fulfill() - } - - sut.submit(data: paymentComponentData, component: sut) - - wait(for: [didSubmitExpectation], timeout: 3) - } - - func test_submit_with_no_attemptId_sets_constant_in_sdkData() throws { - // Given - analyticsProviderMock.checkoutAttemptId = nil - let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") - let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) - - let didSubmitExpectation = expectation(description: "didSubmit should get called") - - // When - XCTAssertNil(paymentComponentData.sdkData) - - // Then - paymentComponentDelegate.onDidSubmit = { data, _ in - XCTAssertNotNil(data.sdkData) + XCTAssertEqual(sdkDataDecoded.analytics.checkoutAttemptId, "fetch-checkoutAttemptId-failed") XCTAssertEqual(data.checkoutAttemptId, "fetch-checkoutAttemptId-failed") didSubmitExpectation.fulfill() } @@ -96,7 +71,7 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } - func test_submit_when_browserInfo_is_nil_sets_browserInfo() throws { + func test_submit_sets_browserInfo() throws { // Given let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) @@ -116,29 +91,6 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } - - func test_submit_when_browserInfo_is_not_nil_preserves_browserInfo() throws { - // Given - let expectedBrowserInfo = BrowserInfo(userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)") - let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") - let paymentComponentData = PaymentComponentData( - paymentMethodDetails: paymentMethodDetails, - amount: nil, - order: nil, - browserInfo: expectedBrowserInfo - ) - - let didSubmitExpectation = expectation(description: "didSubmit should get called") - - paymentComponentDelegate.onDidSubmit = { data, _ in - XCTAssertNotNil(data.browserInfo) - didSubmitExpectation.fulfill() - } - - sut.submit(data: paymentComponentData, component: sut) - - wait(for: [didSubmitExpectation], timeout: 3) - } func test_submit_event() throws { let expectedCheckoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" @@ -163,24 +115,30 @@ class PaymentComponentSubjectTests: XCTestCase { func test_submit_creates_sdkData_with_analytics() throws { // Given - let expectedCheckoutAttemptId = "test-checkout-attempt-id" + let expectedCheckoutAttemptId = "d06da733-ec41-4739-a532-5e8deab1262e16547639430681e1b021221a98c4bf13f7366b30fec4b376cc8450067ff98998682dd24fc9bda" analyticsProviderMock.checkoutAttemptId = expectedCheckoutAttemptId let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) - + let didSubmitExpectation = expectation(description: "didSubmit should get called") + // When + XCTAssertNil(paymentComponentData.sdkData) + // Then paymentComponentDelegate.onDidSubmit = { data, _ in - XCTAssertNotNil(data.sdkData, "SDKData should be created") + XCTAssertNotNil(data.sdkData) - // Verify SDKData is properly encoded - guard let sdkDataString = data.sdkData else { - XCTFail("SDKData string should be present") + guard let sdkDataString = data.sdkData, + let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + XCTFail("SDKData should be present and decodable") return } - XCTAssertFalse(sdkDataString.isEmpty, "SDKData should not be empty") + // Verify SDKData contains checkoutAttemptId + XCTAssertEqual(sdkDataDecoded.analytics.checkoutAttemptId, expectedCheckoutAttemptId) + // Verify through the deprecated property for backward compatibility + XCTAssertEqual(data.checkoutAttemptId, expectedCheckoutAttemptId) didSubmitExpectation.fulfill() } @@ -205,13 +163,15 @@ class PaymentComponentSubjectTests: XCTestCase { paymentComponentDelegate.onDidSubmit = { data, _ in XCTAssertNotNil(data.sdkData, "SDKData should be created") - guard let sdkDataString = data.sdkData else { - XCTFail("SDKData string should be present") + guard let sdkDataString = data.sdkData, + let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + XCTFail("SDKData should be present and decodable") return } - // Verify the SDKData contains both analytics and authentication - XCTAssertFalse(sdkDataString.isEmpty) + // Verify the SDKData contains authentication + XCTAssertNotNil(sdkDataDecoded.authentication) + XCTAssertEqual(sdkDataDecoded.authentication?.threeDS2SdkVersion, "0.0.0") didSubmitExpectation.fulfill() } @@ -233,7 +193,15 @@ class PaymentComponentSubjectTests: XCTestCase { // Then paymentComponentDelegate.onDidSubmit = { data, _ in - XCTAssertNotNil(data.sdkData, "SDKData should be created even without authentication") + + guard let sdkDataString = data.sdkData, + let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + XCTFail("SDKData should be present and decodable") + return + } + + XCTAssertEqual(sdkDataDecoded.analytics.checkoutAttemptId, expectedCheckoutAttemptId) + XCTAssertNil(sdkDataDecoded.authentication) didSubmitExpectation.fulfill() } @@ -253,10 +221,14 @@ class PaymentComponentSubjectTests: XCTestCase { paymentComponentDelegate.onDidSubmit = { data, _ in XCTAssertNotNil(data.sdkData) - // Verify timestamp is recent (within last 5 seconds) - let now = Int(Date().timeIntervalSince1970 * 1000) - // We can't directly access timestamp due to private access, but we verify SDKData exists - XCTAssertNotNil(data.sdkData) + // Verify timestamp is present + guard let sdkDataString = data.sdkData, + let decodedData = Data(base64Encoded: sdkDataString), + let jsonString = String(data: decodedData, encoding: .utf8) else { + XCTFail("SDKData should be present and decodable to a string") + return + } + XCTAssertTrue(jsonString.contains("\"createdAt\""), "SDKData should contain the createdAt timestamp") didSubmitExpectation.fulfill() } @@ -270,7 +242,7 @@ class PaymentComponentSubjectTests: XCTestCase { private class MockSDKDataAuthenticationProvider: SDKDataAuthenticationProvider { var authentication: SDKData.Authentication { - SDKData.Authentication(threeDS2SdkVersion: "2.2.0") + SDKData.Authentication(threeDS2SdkVersion: "0.0.0") } } From f4f8f7872fd3c8afa74ae49e78fee7fc5287471a Mon Sep 17 00:00:00 2001 From: erenbesel Date: Mon, 13 Oct 2025 16:16:58 +0200 Subject: [PATCH 06/10] clarify more test code --- Adyen/Model/SDKData.swift | 2 +- .../PaymentComponentSubjectTests.swift | 25 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Adyen/Model/SDKData.swift b/Adyen/Model/SDKData.swift index 01026871d1..b96f435a5f 100644 --- a/Adyen/Model/SDKData.swift +++ b/Adyen/Model/SDKData.swift @@ -23,8 +23,8 @@ public struct SDKData: Codable { internal let analytics: Analytics internal private(set) var authentication: Authentication? + internal let schemaVersion: String = SchemaVersions.v1_0 private let supportNativeRedirect: Bool = true - private let schemaVersion: String = SchemaVersions.v1_0 private let timestamp = Int(Date().timeIntervalSince1970 * 1000) internal var encodedValue: String? { diff --git a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift index 11d2d1abb0..9470ddf68a 100644 --- a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift +++ b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift @@ -55,8 +55,7 @@ class PaymentComponentSubjectTests: XCTestCase { paymentComponentDelegate.onDidSubmit = { data, _ in XCTAssertNotNil(data.sdkData) - guard let sdkDataString = data.sdkData, - let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + guard let sdkDataDecoded = self.sdkData(from: data.sdkData) else { XCTFail("SDKData should be present and decodable") return } @@ -129,14 +128,14 @@ class PaymentComponentSubjectTests: XCTestCase { paymentComponentDelegate.onDidSubmit = { data, _ in XCTAssertNotNil(data.sdkData) - guard let sdkDataString = data.sdkData, - let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + guard let sdkDataDecoded = self.sdkData(from: data.sdkData) else { XCTFail("SDKData should be present and decodable") return } // Verify SDKData contains checkoutAttemptId XCTAssertEqual(sdkDataDecoded.analytics.checkoutAttemptId, expectedCheckoutAttemptId) + XCTAssertEqual(sdkDataDecoded.schemaVersion, "1.0") // Verify through the deprecated property for backward compatibility XCTAssertEqual(data.checkoutAttemptId, expectedCheckoutAttemptId) didSubmitExpectation.fulfill() @@ -163,8 +162,7 @@ class PaymentComponentSubjectTests: XCTestCase { paymentComponentDelegate.onDidSubmit = { data, _ in XCTAssertNotNil(data.sdkData, "SDKData should be created") - guard let sdkDataString = data.sdkData, - let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + guard let sdkDataDecoded = self.sdkData(from: data.sdkData) else { XCTFail("SDKData should be present and decodable") return } @@ -194,8 +192,7 @@ class PaymentComponentSubjectTests: XCTestCase { // Then paymentComponentDelegate.onDidSubmit = { data, _ in - guard let sdkDataString = data.sdkData, - let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + guard let sdkDataDecoded = self.sdkData(from: data.sdkData) else { XCTFail("SDKData should be present and decodable") return } @@ -210,7 +207,7 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } - func test_submit_includes_sdkData_timestamp() throws { + func test_submit_includes_sdkData_other_fields() throws { // Given let paymentMethodDetails = MBWayDetails(paymentMethod: paymentMethod, telephoneNumber: "0284294824") let paymentComponentData = PaymentComponentData(paymentMethodDetails: paymentMethodDetails, amount: nil, order: nil) @@ -229,6 +226,7 @@ class PaymentComponentSubjectTests: XCTestCase { return } XCTAssertTrue(jsonString.contains("\"createdAt\""), "SDKData should contain the createdAt timestamp") + XCTAssertTrue(jsonString.contains("\"supportNativeRedirext\""), "SDKData should contain supportNativeRedirext ") didSubmitExpectation.fulfill() } @@ -236,6 +234,15 @@ class PaymentComponentSubjectTests: XCTestCase { wait(for: [didSubmitExpectation], timeout: 3) } + + private func sdkData(from sdkDataString: String?) -> SDKData? { + guard let sdkDataString, + let sdkDataDecoded: SDKData = try? AdyenCoder.decodeBase64(sdkDataString) else { + XCTFail("SDKData should be present and decodable") + return nil + } + return sdkDataDecoded + } } // MARK: - Test Helpers From 28cea5514b8ce0f770c49a10e2c3ca639ed77338 Mon Sep 17 00:00:00 2001 From: erenbesel Date: Mon, 13 Oct 2025 16:27:30 +0200 Subject: [PATCH 07/10] fix typo --- .../PaymentComponent/PaymentComponentSubjectTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift index 9470ddf68a..4b913b26ec 100644 --- a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift +++ b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift @@ -226,7 +226,7 @@ class PaymentComponentSubjectTests: XCTestCase { return } XCTAssertTrue(jsonString.contains("\"createdAt\""), "SDKData should contain the createdAt timestamp") - XCTAssertTrue(jsonString.contains("\"supportNativeRedirext\""), "SDKData should contain supportNativeRedirext ") + XCTAssertTrue(jsonString.contains("\"supportNativeRedirect\""), "SDKData should contain supportNativeRedirect ") didSubmitExpectation.fulfill() } From 782b34c9a8f18c476dd96b6070405d813ec6afd0 Mon Sep 17 00:00:00 2001 From: erenbesel Date: Wed, 15 Oct 2025 12:00:12 +0200 Subject: [PATCH 08/10] change schemaVersion to Int --- Adyen/Model/SDKData.swift | 5 ++--- .../PaymentComponent/PaymentComponentSubjectTests.swift | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Adyen/Model/SDKData.swift b/Adyen/Model/SDKData.swift index b96f435a5f..48118a7091 100644 --- a/Adyen/Model/SDKData.swift +++ b/Adyen/Model/SDKData.swift @@ -23,7 +23,7 @@ public struct SDKData: Codable { internal let analytics: Analytics internal private(set) var authentication: Authentication? - internal let schemaVersion: String = SchemaVersions.v1_0 + internal let schemaVersion: Int = SchemaVersions.v1 private let supportNativeRedirect: Bool = true private let timestamp = Int(Date().timeIntervalSince1970 * 1000) @@ -48,8 +48,7 @@ public struct SDKData: Codable { } private enum SchemaVersions { - // swiftlint:disable:next identifier_name - internal static let v1_0 = "1.0" + internal static let v1 = 1 } } diff --git a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift index 4b913b26ec..4881f90537 100644 --- a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift +++ b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift @@ -135,7 +135,7 @@ class PaymentComponentSubjectTests: XCTestCase { // Verify SDKData contains checkoutAttemptId XCTAssertEqual(sdkDataDecoded.analytics.checkoutAttemptId, expectedCheckoutAttemptId) - XCTAssertEqual(sdkDataDecoded.schemaVersion, "1.0") + XCTAssertEqual(sdkDataDecoded.schemaVersion, 1) // Verify through the deprecated property for backward compatibility XCTAssertEqual(data.checkoutAttemptId, expectedCheckoutAttemptId) didSubmitExpectation.fulfill() From 52265ab5498f7a540a5e4bb634deb53cc418fe3e Mon Sep 17 00:00:00 2001 From: erenbesel Date: Wed, 26 Nov 2025 15:57:13 +0100 Subject: [PATCH 09/10] COSDK-515: Move sdkData into payment method level SDKData being inside payment method object is easier for merchants even if more code on our side --- .../Components/InstantPaymentComponent.swift | 4 + .../StoredPaymentMethodComponent.swift | 5 + Adyen/Core/Core Protocols/Details.swift | 4 + Adyen/Model/PaymentComponentData.swift | 33 ++--- Adyen/Model/SDKData.swift | 3 +- AdyenCard/Components/Card/CardDetails.swift | 7 +- .../GiftCardComponent/GiftCardDetails.swift | 5 + .../MealVoucherDetails.swift | 4 + AdyenCashAppPay/CashAppPayDetails.swift | 5 + .../ACHDirectDebitDetails.swift | 5 + AdyenComponents/Affirm/AffirmDetails.swift | 5 + .../Apple Pay/ApplePayDetails.swift | 5 + AdyenComponents/Atome/AtomeDetails.swift | 5 + .../BACSDirectDebitDetails.swift | 5 + AdyenComponents/BLIK/BLIKDetails.swift | 5 + .../BasicPersonalInfoFormDetails.swift | 5 + AdyenComponents/Boleto/BoletoDetails.swift | 5 + AdyenComponents/Doku/DokuDetails.swift | 5 + .../Issuer List/IssuerListDetails.swift | 4 + AdyenComponents/MB Way/MBWayDetails.swift | 5 + .../OnlineBanking/OnlineBankingDetails.swift | 4 + AdyenComponents/PayTo/PayToDetails.swift | 5 + .../Qiwi Wallet/QiwiWalletDetails.swift | 5 + .../SEPADirectDebitDetails.swift | 5 + AdyenComponents/UPI/UPIComponentDetails.swift | 4 + .../API/Payments/PaymentsRequest.swift | 2 - AdyenTwint/TwintDetails.swift | 5 + Demo/Common/Networking/PaymentsRequest.swift | 2 - .../PaymentComponentSubjectTests.swift | 22 ++-- Tests/UnitTests/Core/PaymentMethodTests.swift | 118 +++++++++--------- 30 files changed, 200 insertions(+), 96 deletions(-) diff --git a/Adyen/Core/Components/InstantPaymentComponent.swift b/Adyen/Core/Components/InstantPaymentComponent.swift index e0bf296c69..06bfe789d7 100644 --- a/Adyen/Core/Components/InstantPaymentComponent.swift +++ b/Adyen/Core/Components/InstantPaymentComponent.swift @@ -82,6 +82,10 @@ public struct InstantPaymentDetails: PaymentMethodDetails { @_spi(AdyenInternal) public var checkoutAttemptId: String? + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// The payment method type name. public let type: PaymentMethodType diff --git a/Adyen/Core/Components/StoredPaymentMethodComponent.swift b/Adyen/Core/Components/StoredPaymentMethodComponent.swift index 04b39c21a4..b143b0f95a 100644 --- a/Adyen/Core/Components/StoredPaymentMethodComponent.swift +++ b/Adyen/Core/Components/StoredPaymentMethodComponent.swift @@ -112,6 +112,10 @@ public struct StoredPaymentDetails: PaymentMethodDetails { @_spi(AdyenInternal) public var checkoutAttemptId: String? + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + internal let type: PaymentMethodType internal let storedPaymentMethodIdentifier: String @@ -127,6 +131,7 @@ public struct StoredPaymentDetails: PaymentMethodDetails { private enum CodingKeys: String, CodingKey { case type case storedPaymentMethodIdentifier = "storedPaymentMethodId" + case sdkData } } diff --git a/Adyen/Core/Core Protocols/Details.swift b/Adyen/Core/Core Protocols/Details.swift index cc54cba166..9c68d6535f 100644 --- a/Adyen/Core/Core Protocols/Details.swift +++ b/Adyen/Core/Core Protocols/Details.swift @@ -14,6 +14,10 @@ public protocol PaymentMethodDetails: Details { @_spi(AdyenInternal) var checkoutAttemptId: String? { get set } + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + var sdkData: String? { get set } } public extension PaymentMethodDetails { diff --git a/Adyen/Model/PaymentComponentData.swift b/Adyen/Model/PaymentComponentData.swift index 2ecd479021..f831ba2273 100644 --- a/Adyen/Model/PaymentComponentData.swift +++ b/Adyen/Model/PaymentComponentData.swift @@ -36,7 +36,7 @@ public struct PaymentComponentData { public let installments: Installments? /// Indicates whether the current SDK version suports native redirect without glue pages. - @available(*, deprecated, message: "This property is deprecated. Use the new sdkData property instead.") + @available(*, deprecated, message: "This property is deprecated. Use the new paymentMethod.sdkData property instead.") public let supportNativeRedirect: Bool = true /// Shopper name. @@ -61,7 +61,7 @@ public struct PaymentComponentData { public let browserInfo: BrowserInfo? /// A unique identifier for a checkout attempt. - @available(*, deprecated, message: "This property is deprecated. Use the new sdkData property instead.") + @available(*, deprecated, message: "This property is deprecated. Use the new paymentMethod.sdkData property instead.") public var checkoutAttemptId: String? { paymentMethod.checkoutAttemptId } @@ -89,10 +89,6 @@ public struct PaymentComponentData { return paymentMethod.delegatedAuthenticationData } - /// An encoded string containing important SDK-specific data. - /// It is recommended to pass this field to your server to ensure maximum performance and reliability. - public var sdkData: String? - /// Initializes the payment component data. /// /// @@ -112,8 +108,7 @@ public struct PaymentComponentData { order: PartialPaymentOrder?, storePaymentMethod: Bool? = nil, browserInfo: BrowserInfo? = nil, - installments: Installments? = nil, - sdkData: String? = nil + installments: Installments? = nil ) { self.amount = amount self.paymentMethod = paymentMethodDetails @@ -121,18 +116,18 @@ public struct PaymentComponentData { self.storePaymentMethod = storePaymentMethod self.browserInfo = browserInfo self.installments = installments - self.sdkData = sdkData } internal func replacing(sdkData: SDKData) -> PaymentComponentData { - PaymentComponentData( - paymentMethodDetails: paymentMethod, + var paymentMethodDetails = paymentMethod + paymentMethodDetails.sdkData = sdkData.encodedValue + return PaymentComponentData( + paymentMethodDetails: paymentMethodDetails, amount: amount, order: order, storePaymentMethod: storePaymentMethod, browserInfo: browserInfo, - installments: installments, - sdkData: sdkData.encodedValue + installments: installments ) } @@ -144,8 +139,7 @@ public struct PaymentComponentData { order: order, storePaymentMethod: storePaymentMethod, browserInfo: browserInfo, - installments: installments, - sdkData: sdkData + installments: installments ) } @@ -157,8 +151,7 @@ public struct PaymentComponentData { order: order, storePaymentMethod: storePaymentMethod, browserInfo: browserInfo, - installments: installments, - sdkData: sdkData + installments: installments ) } @@ -173,8 +166,7 @@ public struct PaymentComponentData { order: order, storePaymentMethod: storePaymentMethod, browserInfo: browserInfo, - installments: installments, - sdkData: sdkData + installments: installments ) } @@ -192,8 +184,7 @@ public struct PaymentComponentData { order: order, storePaymentMethod: storePaymentMethod, browserInfo: $0, - installments: installments, - sdkData: sdkData + installments: installments )) } } diff --git a/Adyen/Model/SDKData.swift b/Adyen/Model/SDKData.swift index 48118a7091..8f7965c968 100644 --- a/Adyen/Model/SDKData.swift +++ b/Adyen/Model/SDKData.swift @@ -27,7 +27,8 @@ public struct SDKData: Codable { private let supportNativeRedirect: Bool = true private let timestamp = Int(Date().timeIntervalSince1970 * 1000) - internal var encodedValue: String? { + @_spi(AdyenInternal) + public var encodedValue: String? { try? AdyenCoder.encodeBase64(self) } diff --git a/AdyenCard/Components/Card/CardDetails.swift b/AdyenCard/Components/Card/CardDetails.swift index e788a459c2..77020e92c5 100644 --- a/AdyenCard/Components/Card/CardDetails.swift +++ b/AdyenCard/Components/Card/CardDetails.swift @@ -57,7 +57,7 @@ public struct CardDetails: PaymentMethodDetails, ShopperInformation { public let socialSecurityNumber: String? /// The 3DS2 SDK version. - @available(*, deprecated, message: "This property is deprecated. Use the new sdkData property of the PaymentComponentData object instead.") + @available(*, deprecated, message: "This property is deprecated. Use the new sdkData property instead.") public let threeDS2SDKVersion: String = threeDS2SdkVersion /// Brand of the card. @@ -65,6 +65,10 @@ public struct CardDetails: PaymentMethodDetails, ShopperInformation { /// Delegated Authentication Data. public let delegatedAuthenticationData: DelegatedAuthenticationData? + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the card payment details. /// @@ -152,6 +156,7 @@ public struct CardDetails: PaymentMethodDetails, ShopperInformation { case taxNumber case password = "encryptedPassword" case threeDS2SDKVersion = "threeDS2SdkVersion" + case sdkData } } diff --git a/AdyenCard/Components/GiftCardComponent/GiftCardDetails.swift b/AdyenCard/Components/GiftCardComponent/GiftCardDetails.swift index 4758dbc6b4..765f0b5467 100644 --- a/AdyenCard/Components/GiftCardComponent/GiftCardDetails.swift +++ b/AdyenCard/Components/GiftCardComponent/GiftCardDetails.swift @@ -27,6 +27,10 @@ public struct GiftCardDetails: PartialPaymentMethodDetails { /// The gift card brand. public let brand: String + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the gift card payment details. /// @@ -63,6 +67,7 @@ public struct GiftCardDetails: PartialPaymentMethodDetails { case encryptedCardNumber case encryptedSecurityCode case brand + case sdkData } } diff --git a/AdyenCard/Components/GiftCardComponent/MealVoucherDetails.swift b/AdyenCard/Components/GiftCardComponent/MealVoucherDetails.swift index 766c97cdcc..0d9e7b9043 100644 --- a/AdyenCard/Components/GiftCardComponent/MealVoucherDetails.swift +++ b/AdyenCard/Components/GiftCardComponent/MealVoucherDetails.swift @@ -34,6 +34,10 @@ public struct MealVoucherDetails: PartialPaymentMethodDetails { /// The encrypted expiration year. public let encryptedExpiryYear: String? + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the meal voucher payment details. /// /// - Parameters: diff --git a/AdyenCashAppPay/CashAppPayDetails.swift b/AdyenCashAppPay/CashAppPayDetails.swift index d4579fbd22..d017bb2cda 100644 --- a/AdyenCashAppPay/CashAppPayDetails.swift +++ b/AdyenCashAppPay/CashAppPayDetails.swift @@ -27,6 +27,10 @@ public struct CashAppPayDetails: PaymentMethodDetails { /// Public identifier for the customer on Cash App. public let cashtag: String? + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the Cash App Pay details. /// - Parameters: /// - paymentMethod: Cash App Pay payment method. @@ -56,5 +60,6 @@ public struct CashAppPayDetails: PaymentMethodDetails { case onFileGrantId case customerId case cashtag + case sdkData } } diff --git a/AdyenComponents/ACH Direct Debit/ACHDirectDebitDetails.swift b/AdyenComponents/ACH Direct Debit/ACHDirectDebitDetails.swift index cbaee0c7a6..f1d56cadb5 100644 --- a/AdyenComponents/ACH Direct Debit/ACHDirectDebitDetails.swift +++ b/AdyenComponents/ACH Direct Debit/ACHDirectDebitDetails.swift @@ -27,6 +27,10 @@ public struct ACHDirectDebitDetails: PaymentMethodDetails, ShopperInformation { /// The shopper's billing address. public let billingAddress: PostalAddress? + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the ACH Direct Debit details. /// - Parameters: /// - paymentMethod: ACH Direct Debit payment method. @@ -47,5 +51,6 @@ public struct ACHDirectDebitDetails: PaymentMethodDetails, ShopperInformation { case holderName = "ownerName" case encryptedBankAccountNumber case encryptedBankRoutingNumber = "encryptedBankLocationId" + case sdkData } } diff --git a/AdyenComponents/Affirm/AffirmDetails.swift b/AdyenComponents/Affirm/AffirmDetails.swift index f0bb9fafd9..6a006a001d 100644 --- a/AdyenComponents/Affirm/AffirmDetails.swift +++ b/AdyenComponents/Affirm/AffirmDetails.swift @@ -30,6 +30,10 @@ public struct AffirmDetails: PaymentMethodDetails, ShopperInformation { /// The shopper's delivery address. public let deliveryAddress: PostalAddress? + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the Affirm details. /// - Parameters: /// - paymentMethod: Affirm payment method. @@ -58,5 +62,6 @@ public struct AffirmDetails: PaymentMethodDetails, ShopperInformation { private enum CodingKeys: CodingKey { case type + case sdkData } } diff --git a/AdyenComponents/Apple Pay/ApplePayDetails.swift b/AdyenComponents/Apple Pay/ApplePayDetails.swift index 52c3053c8a..3751fb5e21 100644 --- a/AdyenComponents/Apple Pay/ApplePayDetails.swift +++ b/AdyenComponents/Apple Pay/ApplePayDetails.swift @@ -32,6 +32,10 @@ public struct ApplePayDetails: PaymentMethodDetails { /// The shipping method that the user chose. public let shippingMethod: PKShippingMethod? + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the Apple Pay details. /// /// - Parameters: @@ -62,6 +66,7 @@ public struct ApplePayDetails: PaymentMethodDetails { private enum CodingKeys: String, CodingKey { case token = "applePayToken" case type + case sdkData } } diff --git a/AdyenComponents/Atome/AtomeDetails.swift b/AdyenComponents/Atome/AtomeDetails.swift index e3069517ce..5fb2c83dab 100644 --- a/AdyenComponents/Atome/AtomeDetails.swift +++ b/AdyenComponents/Atome/AtomeDetails.swift @@ -23,6 +23,10 @@ public struct AtomeDetails: PaymentMethodDetails, ShopperInformation { /// The shopper's billing address. public let billingAddress: PostalAddress? + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the Atome details. /// - Parameters: @@ -46,5 +50,6 @@ public struct AtomeDetails: PaymentMethodDetails, ShopperInformation { private enum CodingKeys: CodingKey { case type + case sdkData } } diff --git a/AdyenComponents/BACS Direct Debit/BACSDirectDebitDetails.swift b/AdyenComponents/BACS Direct Debit/BACSDirectDebitDetails.swift index 7dd4ebddcf..daa38bd487 100644 --- a/AdyenComponents/BACS Direct Debit/BACSDirectDebitDetails.swift +++ b/AdyenComponents/BACS Direct Debit/BACSDirectDebitDetails.swift @@ -23,6 +23,10 @@ public struct BACSDirectDebitDetails: PaymentMethodDetails { /// The BACS location's ID. public let bankLocationId: String + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Creates and returns a BACS Direct Debit details instance. /// - Parameters: @@ -49,5 +53,6 @@ public struct BACSDirectDebitDetails: PaymentMethodDetails { case holderName case bankAccountNumber case bankLocationId + case sdkData } } diff --git a/AdyenComponents/BLIK/BLIKDetails.swift b/AdyenComponents/BLIK/BLIKDetails.swift index 8e6736f03e..65909dd1c1 100644 --- a/AdyenComponents/BLIK/BLIKDetails.swift +++ b/AdyenComponents/BLIK/BLIKDetails.swift @@ -18,6 +18,10 @@ public struct BLIKDetails: PaymentMethodDetails { /// The telephone number. public let blikCode: String + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the BLIK payment details. /// @@ -32,6 +36,7 @@ public struct BLIKDetails: PaymentMethodDetails { private enum CodingKeys: String, CodingKey { case type case blikCode + case sdkData } } diff --git a/AdyenComponents/BasicPersonalInfoFormComponent/BasicPersonalInfoFormDetails.swift b/AdyenComponents/BasicPersonalInfoFormComponent/BasicPersonalInfoFormDetails.swift index cff844a06c..efdde9b780 100644 --- a/AdyenComponents/BasicPersonalInfoFormComponent/BasicPersonalInfoFormDetails.swift +++ b/AdyenComponents/BasicPersonalInfoFormComponent/BasicPersonalInfoFormDetails.swift @@ -34,6 +34,10 @@ public struct BasicPersonalInfoFormDetails: PaymentMethodDetails, ShopperInforma /// The telephone number. public let telephoneNumber: String? + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the generic personal details. /// @@ -60,6 +64,7 @@ public struct BasicPersonalInfoFormDetails: PaymentMethodDetails, ShopperInforma private enum CodingKeys: String, CodingKey { case type + case sdkData } } diff --git a/AdyenComponents/Boleto/BoletoDetails.swift b/AdyenComponents/Boleto/BoletoDetails.swift index bdbb46cb77..66dcfe92cf 100644 --- a/AdyenComponents/Boleto/BoletoDetails.swift +++ b/AdyenComponents/Boleto/BoletoDetails.swift @@ -30,6 +30,10 @@ public struct BoletoDetails: PaymentMethodDetails, ShopperInformation { public let telephoneNumber: String? = nil + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the Boleto details /// - Parameters: /// - type: Boleto payment method. @@ -53,5 +57,6 @@ public struct BoletoDetails: PaymentMethodDetails, ShopperInformation { private enum CodingKeys: CodingKey { case type + case sdkData } } diff --git a/AdyenComponents/Doku/DokuDetails.swift b/AdyenComponents/Doku/DokuDetails.swift index c31067ed8b..baeb76c818 100644 --- a/AdyenComponents/Doku/DokuDetails.swift +++ b/AdyenComponents/Doku/DokuDetails.swift @@ -24,6 +24,10 @@ public struct DokuDetails: PaymentMethodDetails { /// The email address. public let emailAddress: String + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the MB Way details. /// @@ -50,6 +54,7 @@ public struct DokuDetails: PaymentMethodDetails { case firstName case lastName case emailAddress = "shopperEmail" + case sdkData } } diff --git a/AdyenComponents/Issuer List/IssuerListDetails.swift b/AdyenComponents/Issuer List/IssuerListDetails.swift index 76a8dd0060..3370663e10 100644 --- a/AdyenComponents/Issuer List/IssuerListDetails.swift +++ b/AdyenComponents/Issuer List/IssuerListDetails.swift @@ -19,6 +19,10 @@ public struct IssuerListDetails: PaymentMethodDetails { /// The selected issuer. public let issuer: String + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the Issuer List details. /// /// - Parameters: diff --git a/AdyenComponents/MB Way/MBWayDetails.swift b/AdyenComponents/MB Way/MBWayDetails.swift index a4f8dd9fc4..39d558e573 100644 --- a/AdyenComponents/MB Way/MBWayDetails.swift +++ b/AdyenComponents/MB Way/MBWayDetails.swift @@ -19,6 +19,10 @@ public struct MBWayDetails: PaymentMethodDetails { /// The telephone number. public let telephoneNumber: String + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the MB Way details. /// /// @@ -33,6 +37,7 @@ public struct MBWayDetails: PaymentMethodDetails { private enum CodingKeys: String, CodingKey { case type case telephoneNumber + case sdkData } } diff --git a/AdyenComponents/OnlineBanking/OnlineBankingDetails.swift b/AdyenComponents/OnlineBanking/OnlineBankingDetails.swift index 0496b61c8a..17a4c46eb6 100644 --- a/AdyenComponents/OnlineBanking/OnlineBankingDetails.swift +++ b/AdyenComponents/OnlineBanking/OnlineBankingDetails.swift @@ -17,6 +17,10 @@ public struct OnlineBankingDetails: PaymentMethodDetails { /// Selected issuer public let issuer: String + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the Online Banking details. /// - Parameters: diff --git a/AdyenComponents/PayTo/PayToDetails.swift b/AdyenComponents/PayTo/PayToDetails.swift index c89f6532f7..294b003fd5 100644 --- a/AdyenComponents/PayTo/PayToDetails.swift +++ b/AdyenComponents/PayTo/PayToDetails.swift @@ -20,6 +20,10 @@ public struct PayToDetails: PaymentMethodDetails, ShopperInformation { /// Name of the shopper. public let shopperName: ShopperName? + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the PayTo Component Details. /// - Parameters: @@ -39,5 +43,6 @@ public struct PayToDetails: PaymentMethodDetails, ShopperInformation { private enum CodingKeys: String, CodingKey { case type case accountIdentifier = "shopperAccountIdentifier" + case sdkData } } diff --git a/AdyenComponents/Qiwi Wallet/QiwiWalletDetails.swift b/AdyenComponents/Qiwi Wallet/QiwiWalletDetails.swift index 6d67dc511b..1e9240ce91 100644 --- a/AdyenComponents/Qiwi Wallet/QiwiWalletDetails.swift +++ b/AdyenComponents/Qiwi Wallet/QiwiWalletDetails.swift @@ -22,6 +22,10 @@ public struct QiwiWalletDetails: PaymentMethodDetails { /// The telephone number. public let phoneNumber: String + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the Qiwi Wallet details. /// /// @@ -39,6 +43,7 @@ public struct QiwiWalletDetails: PaymentMethodDetails { case type case phonePrefix = "qiwiwallet.telephoneNumberPrefix" case phoneNumber = "qiwiwallet.telephoneNumber" + case sdkData } } diff --git a/AdyenComponents/SEPA Direct Debit/SEPADirectDebitDetails.swift b/AdyenComponents/SEPA Direct Debit/SEPADirectDebitDetails.swift index e91d9f0b87..a17e24acc7 100644 --- a/AdyenComponents/SEPA Direct Debit/SEPADirectDebitDetails.swift +++ b/AdyenComponents/SEPA Direct Debit/SEPADirectDebitDetails.swift @@ -22,6 +22,10 @@ public struct SEPADirectDebitDetails: PaymentMethodDetails { /// The account owner name. public let ownerName: String + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? + /// Initializes the SEPA Direct Debit details. /// /// @@ -39,6 +43,7 @@ public struct SEPADirectDebitDetails: PaymentMethodDetails { case type case iban case ownerName + case sdkData } } diff --git a/AdyenComponents/UPI/UPIComponentDetails.swift b/AdyenComponents/UPI/UPIComponentDetails.swift index fd8375d71b..c36bec3266 100644 --- a/AdyenComponents/UPI/UPIComponentDetails.swift +++ b/AdyenComponents/UPI/UPIComponentDetails.swift @@ -20,6 +20,10 @@ public struct UPIComponentDetails: PaymentMethodDetails { /// The selected UPI app public let appId: String? + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the UPI Component Details. /// - Parameters: diff --git a/AdyenSession/API/Payments/PaymentsRequest.swift b/AdyenSession/API/Payments/PaymentsRequest.swift index a1c390791e..a8b81cdba4 100644 --- a/AdyenSession/API/Payments/PaymentsRequest.swift +++ b/AdyenSession/API/Payments/PaymentsRequest.swift @@ -57,7 +57,6 @@ internal struct PaymentsRequest: APIRequest { try container.encodeIfPresent(data.socialSecurityNumber, forKey: .socialSecurityNumber) try container.encodeIfPresent(data.browserInfo, forKey: .browserInfo) try container.encodeIfPresent(data.order?.compactOrder, forKey: .order) - try container.encodeIfPresent(data.sdkData, forKey: .sdkData) } private enum CodingKeys: String, CodingKey { @@ -73,7 +72,6 @@ internal struct PaymentsRequest: APIRequest { case socialSecurityNumber case order case delegatedAuthenticationData - case sdkData } } diff --git a/AdyenTwint/TwintDetails.swift b/AdyenTwint/TwintDetails.swift index c370c049d8..78c464742a 100644 --- a/AdyenTwint/TwintDetails.swift +++ b/AdyenTwint/TwintDetails.swift @@ -17,6 +17,10 @@ public struct TwintDetails: PaymentMethodDetails { /// The payment method subType. public let subType: String + + /// An encoded string containing important SDK-specific data. + /// It is recommended to pass this field to your server to ensure maximum performance and reliability. + public var sdkData: String? /// Initializes the Twint details. /// - Parameters: @@ -35,5 +39,6 @@ public struct TwintDetails: PaymentMethodDetails { private enum CodingKeys: String, CodingKey { case type case subType = "subtype" + case sdkData } } diff --git a/Demo/Common/Networking/PaymentsRequest.swift b/Demo/Common/Networking/PaymentsRequest.swift index a95a071109..c76e74218b 100644 --- a/Demo/Common/Networking/PaymentsRequest.swift +++ b/Demo/Common/Networking/PaymentsRequest.swift @@ -68,7 +68,6 @@ internal struct PaymentsRequest: APIRequest { try container.encodeIfPresent(data.installments, forKey: .installments) try container.encode(ConfigurationConstants.lineItems, forKey: .lineItems) try container.encode(ConfigurationConstants.recurringProcessingModel, forKey: .recurringProcessingModel) - try container.encodeIfPresent(data.sdkData, forKey: .sdkData) try container.encode(ConfigurationConstants.mandate, forKey: .mandate) } @@ -118,7 +117,6 @@ internal struct PaymentsRequest: APIRequest { case delegatedAuthenticationData case recurringProcessingModel case mandate - case sdkData } } diff --git a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift index 4881f90537..519244b551 100644 --- a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift +++ b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift @@ -49,13 +49,13 @@ class PaymentComponentSubjectTests: XCTestCase { let didSubmitExpectation = expectation(description: "didSubmit should get called") // When - XCTAssertNil(paymentComponentData.sdkData) + XCTAssertNil(paymentComponentData.paymentMethod.sdkData) // Then paymentComponentDelegate.onDidSubmit = { data, _ in - XCTAssertNotNil(data.sdkData) + XCTAssertNotNil(data.paymentMethod.sdkData) - guard let sdkDataDecoded = self.sdkData(from: data.sdkData) else { + guard let sdkDataDecoded = self.sdkData(from: data.paymentMethod.sdkData) else { XCTFail("SDKData should be present and decodable") return } @@ -122,13 +122,13 @@ class PaymentComponentSubjectTests: XCTestCase { let didSubmitExpectation = expectation(description: "didSubmit should get called") // When - XCTAssertNil(paymentComponentData.sdkData) + XCTAssertNil(paymentComponentData.paymentMethod.sdkData) // Then paymentComponentDelegate.onDidSubmit = { data, _ in - XCTAssertNotNil(data.sdkData) + XCTAssertNotNil(data.paymentMethod.sdkData) - guard let sdkDataDecoded = self.sdkData(from: data.sdkData) else { + guard let sdkDataDecoded = self.sdkData(from: data.paymentMethod.sdkData) else { XCTFail("SDKData should be present and decodable") return } @@ -160,9 +160,9 @@ class PaymentComponentSubjectTests: XCTestCase { // Then paymentComponentDelegate.onDidSubmit = { data, _ in - XCTAssertNotNil(data.sdkData, "SDKData should be created") + XCTAssertNotNil(data.paymentMethod.sdkData, "SDKData should be created") - guard let sdkDataDecoded = self.sdkData(from: data.sdkData) else { + guard let sdkDataDecoded = self.sdkData(from: data.paymentMethod.sdkData) else { XCTFail("SDKData should be present and decodable") return } @@ -192,7 +192,7 @@ class PaymentComponentSubjectTests: XCTestCase { // Then paymentComponentDelegate.onDidSubmit = { data, _ in - guard let sdkDataDecoded = self.sdkData(from: data.sdkData) else { + guard let sdkDataDecoded = self.sdkData(from: data.paymentMethod.sdkData) else { XCTFail("SDKData should be present and decodable") return } @@ -216,10 +216,10 @@ class PaymentComponentSubjectTests: XCTestCase { // Then paymentComponentDelegate.onDidSubmit = { data, _ in - XCTAssertNotNil(data.sdkData) + XCTAssertNotNil(data.paymentMethod.sdkData) // Verify timestamp is present - guard let sdkDataString = data.sdkData, + guard let sdkDataString = data.paymentMethod.sdkData, let decodedData = Data(base64Encoded: sdkDataString), let jsonString = String(data: decodedData, encoding: .utf8) else { XCTFail("SDKData should be present and decodable to a string") diff --git a/Tests/UnitTests/Core/PaymentMethodTests.swift b/Tests/UnitTests/Core/PaymentMethodTests.swift index c02c79dcd2..d1f962acf0 100644 --- a/Tests/UnitTests/Core/PaymentMethodTests.swift +++ b/Tests/UnitTests/Core/PaymentMethodTests.swift @@ -95,7 +95,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Payment Methods - func testPaymentMethodsCoding() throws { + func test_paymentMethodsCoding() throws { let paymentMethods: PaymentMethods = try getPaymentMethods() let encodedPaymentMethods: Data = try AdyenCoder.encode(paymentMethods) @@ -104,7 +104,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(paymentMethods, decodedPaymentMethods) } - func testDecodingPaymentMethods() throws { + func test_decodingPaymentMethods() throws { // Stored payment methods let paymentMethods = try getPaymentMethods() @@ -346,7 +346,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Display Information Override - func testOverridingDisplayInformationCard() throws { + func test_overridingDisplayInformation_card() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .scheme, @@ -360,7 +360,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(cardpaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationBCMC() throws { + func test_overridingDisplayInformation_BCMC() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .bcmc, @@ -374,7 +374,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(bcmcpaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationApplePay() throws { + func test_overridingDisplayInformation_applePay() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .applePay, @@ -388,7 +388,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(applePaypaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationPayPal() throws { + func test_overridingDisplayInformation_payPal() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .payPal, @@ -402,7 +402,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(payPalpaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationWeChat() throws { + func test_overridingDisplayInformation_weChat() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .weChatPaySDK, @@ -416,7 +416,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(weChatPaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationQiwiWallet() throws { + func test_overridingDisplayInformation_qiwiWallet() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .qiwiWallet, @@ -430,7 +430,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(qiwiWalletPaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationBLIK() throws { + func test_overridingDisplayInformation_BLIK() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .blik, @@ -444,7 +444,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(blikPaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationStoredBLIK() throws { + func test_overridingDisplayInformation_storedBLIK() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofStoredPaymentMethod: .blik, @@ -458,7 +458,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(storedBlikPaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationStoredCreditCard() throws { + func test_overridingDisplayInformation_storedCreditCard() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofStoredPaymentMethod: .scheme, @@ -481,7 +481,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertNotEqual(storedDebitPaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationStoredDebitCard() throws { + func test_overridingDisplayInformation_storedDebitCard() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofStoredPaymentMethod: .scheme, @@ -504,7 +504,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertNotEqual(storedCreditPaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationGiro() throws { + func test_overridingDisplayInformation_giro() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .other("giropay"), @@ -518,7 +518,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(giroPaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationGenericGiftCard() throws { + func test_overridingDisplayInformation_genericGiftCard() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .giftcard, @@ -545,7 +545,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertNil(givexGiftCardPaymentMethod?.displayInformation(using: nil).subtitle) } - func testOverridingDisplayInformationGivexGiftCard() throws { + func test_overridingDisplayInformation_givexGiftCard() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .giftcard, @@ -576,7 +576,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertNil(anyGiftCardPaymentMethod?.displayInformation(using: nil).subtitle) } - func testOverridingDisplayInformationAnyGivenGiftCard() throws { + func test_overridingDisplayInformation_anyGivenGiftCard() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .giftcard, @@ -607,7 +607,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertNil(givexGiftCardPaymentMethod?.displayInformation(using: nil).subtitle) } - func testOverridingDisplayInformationMealVoucher() throws { + func test_overridingDisplayInformation_mealVoucher() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .mealVoucherSodexo, @@ -622,7 +622,7 @@ class PaymentMethodTests: XCTestCase { } - func testOverridingDisplayInformationDukoWallet() throws { + func test_overridingDisplayInformation_dokuWallet() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .dokuWallet, @@ -636,7 +636,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertEqual(dukoWalletPaymentMethod?.displayInformation(using: nil).subtitle, "custom subtitle") } - func testOverridingDisplayInformationIdeal() throws { + func test_overridingDisplayInformation_ideal() throws { var paymentMethods = try getPaymentMethods() paymentMethods.overrideDisplayInformation( ofRegularPaymentMethod: .ideal, @@ -652,7 +652,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Misc - func testDecodingPaymentMethodsWithNullValues() throws { + func test_decodingPaymentMethods_withNullValues() throws { let json = """ { @@ -685,7 +685,7 @@ class PaymentMethodTests: XCTestCase { XCTAssertTrue(paymentMethods.regular[0] is CardPaymentMethod) } - func testEquality() { + func test_equality() { XCTAssertFalse(BLIKPaymentMethod(type: .blik, name: "blik") == StoredBLIKPaymentMethod( type: .blik, @@ -774,7 +774,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Card - func testDecodingCreditCardPaymentMethod() throws { + func test_decodingCreditCardPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(creditCardDictionary) as CardPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "scheme") XCTAssertEqual(paymentMethod.name, "Credit Card") @@ -783,7 +783,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingDebitCardPaymentMethod() throws { + func test_decodingDebitCardPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(debitCardDictionary) as CardPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "scheme") XCTAssertEqual(paymentMethod.name, "Credit Card") @@ -792,7 +792,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingBCMCCardPaymentMethod() throws { + func test_decodingBCMCCardPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(bcmcCardDictionary) as CardPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "bcmc") XCTAssertEqual(paymentMethod.name, "Bancontact card") @@ -800,7 +800,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingCardPaymentMethodWithoutBrands() throws { + func test_decodingCardPaymentMethod_withoutBrands() throws { var dictionary = creditCardDictionary dictionary.removeValue(forKey: "brands") @@ -811,7 +811,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingStoredCreditCardPaymentMethod() throws { + func test_decodingStoredCreditCardPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(storedCreditCardDictionary) as StoredCardPaymentMethod let expectedLocalizationParameters = LocalizationParameters(tableName: "AdyenUIHost", keySeparator: nil) XCTAssertEqual(paymentMethod.type.rawValue, "scheme") @@ -828,7 +828,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingStoredDebitCardPaymentMethod() throws { + func test_decodingStoredDebitCardPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(storedDebitCardDictionary) as StoredCardPaymentMethod let expectedLocalizationParameters = LocalizationParameters(tableName: "AdyenUIHost", keySeparator: nil) XCTAssertEqual(paymentMethod.type.rawValue, "scheme") @@ -867,7 +867,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Issuer List - func testDecodingIssuerListPaymentMethod() throws { + func test_decodingIssuerListPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(issuerListDictionary) as IssuerListPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "onlineBanking_PL") XCTAssertEqual(paymentMethod.name, "Online Banking") @@ -883,7 +883,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingIssuerListPaymentMethodWithoutDetailsObject() throws { + func test_decodingIssuerListPaymentMethod_withoutDetailsObject() throws { let paymentMethod = try AdyenCoder.decode(issuerListDictionaryWithoutDetailsObject) as IssuerListPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "ideal_100") XCTAssertEqual(paymentMethod.name, "iDEAL_100") @@ -906,7 +906,7 @@ class PaymentMethodTests: XCTestCase { "name": "SEPA Direct Debit" ] as [String: Any] - func testDecodingSEPADirectDebitPaymentMethod() throws { + func test_decodingSEPADirectDebitPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(sepaDirectDebitDictionary) as SEPADirectDebitPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "sepadirectdebit") XCTAssertEqual(paymentMethod.name, "SEPA Direct Debit") @@ -915,7 +915,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Stored PayPal - func testDecodingPayPalPaymentMethod() throws { + func test_decodingPayPalPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(storedPayPalDictionary) as StoredPayPalPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "paypal") XCTAssertEqual(paymentMethod.identifier, "9314881977134903") @@ -927,7 +927,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Apple Pay - func testDecodingApplePayPaymentMethod() throws { + func test_decodingApplePayPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(applePayDictionary) as ApplePayPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "applepay") XCTAssertEqual(paymentMethod.name, "Apple Pay") @@ -936,7 +936,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Bancontact - func testDecodingBancontactPaymentMethod() throws { + func test_decodingBancontactPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(bcmcCardDictionary) as BCMCPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "bcmc") XCTAssertEqual(paymentMethod.name, "Bancontact card") @@ -945,7 +945,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - GiroPay - func testDecodingGiropayPaymentMethod() throws { + func test_decodingGiropayPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(giroPayDictionaryWithOptionalDetails) as InstantPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "giropay") XCTAssertEqual(paymentMethod.name, "GiroPay") @@ -954,7 +954,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Seven Eleven - func testDecodingSevenElevenPaymentMethod() throws { + func test_decodingSevenElevenPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(sevenElevenDictionary) as SevenElevenPaymentMethod XCTAssertEqual(paymentMethod.name, "7-Eleven") XCTAssertEqual(paymentMethod.type.rawValue, "econtext_seven_eleven") @@ -963,7 +963,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - E-Context Online - func testDecodingEContextOnlinePaymentMethod() throws { + func test_decodingEContextOnlinePaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(econtextOnline) as EContextPaymentMethod XCTAssertEqual(paymentMethod.name, "Online Banking") XCTAssertEqual(paymentMethod.type.rawValue, "econtext_online") @@ -972,7 +972,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - OXXO - func testDecodingOXXOPaymentMethod() throws { + func test_decodingOXXOPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(oxxo) as OXXOPaymentMethod XCTAssertEqual(paymentMethod.name, "OXXO") XCTAssertEqual(paymentMethod.type.rawValue, "oxxo") @@ -981,7 +981,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - E-Context ATM - func testDecodingEContextATMPaymentMethod() throws { + func test_decodingEContextATMPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(econtextATM) as EContextPaymentMethod XCTAssertEqual(paymentMethod.name, "Pay-easy ATM") XCTAssertEqual(paymentMethod.type.rawValue, "econtext_atm") @@ -990,7 +990,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - E-Context Stores - func testDecodingEContextStoresPaymentMethod() throws { + func test_decodingEContextStoresPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(econtextStores) as EContextPaymentMethod XCTAssertEqual(paymentMethod.name, "Convenience Stores") XCTAssertEqual(paymentMethod.type.rawValue, "econtext_stores") @@ -999,7 +999,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Stored Bancontact - func testDecodingStoredBancontactPaymentMethod() throws { + func test_decodingStoredBancontactPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(storedBcmcDictionary) as StoredBCMCPaymentMethod let expectedLocalizationParameters = LocalizationParameters(tableName: "AdyenUIHost", keySeparator: nil) XCTAssertEqual(paymentMethod.type.rawValue, "bcmc") @@ -1022,7 +1022,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - MBWay - func testDecodingMBWayPaymentMethod() throws { + func test_decodingMBWayPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(mbway) as MBWayPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "mbway") XCTAssertEqual(paymentMethod.name, "MB WAY") @@ -1031,7 +1031,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Doku wallet - func testDecodingDokuWalletPaymentMethod() throws { + func test_decodingDokuWalletPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(dokuWallet) as DokuPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "doku_wallet") XCTAssertEqual(paymentMethod.name, "DOKU wallet") @@ -1063,7 +1063,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - GiftCard - func testDecodingGiftCardPaymentMethod() throws { + func test_decodingGiftCardPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(giftCard) as GiftCardPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "giftcard") XCTAssertEqual(paymentMethod.name, "Generic GiftCard") @@ -1072,7 +1072,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingMealVoucherPaymentMethod() throws { + func test_decodingMealVoucherPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(mealVoucherSodexo) as MealVoucherPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "mealVoucher_FR_sodexo") XCTAssertEqual(paymentMethod.name, "Sodexo") @@ -1083,7 +1083,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Boleto - func testDecodingBoletoBancarioPaymentMethod() throws { + func test_decodingBoletoBancarioPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(boletoBancario) as BoletoPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "boletobancario") XCTAssertEqual(paymentMethod.name, "Boleto Bancario") @@ -1092,7 +1092,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingBoletoBancarioSantanderPaymentMethod() throws { + func test_decodingBoletoBancarioSantanderPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(boletoBancarioSantander) as BoletoPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "boletobancario_santander") XCTAssertEqual(paymentMethod.name, "Boleto Bancario") @@ -1101,7 +1101,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingBoletoBancarioItauPaymentMethod() throws { + func test_decodingBoletoBancarioItauPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(boletoBancarioItau) as BoletoPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "boletobancario_itau") XCTAssertEqual(paymentMethod.name, "Boleto Bancario") @@ -1110,7 +1110,7 @@ class PaymentMethodTests: XCTestCase { testCoding(paymentMethod) } - func testDecodingPrimeiroPayBoletoPaymentMethod() throws { + func test_decodingPrimeiroPayBoletoPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(primeiroPayBoleto) as BoletoPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "primeiropay_boleto") XCTAssertEqual(paymentMethod.name, "Boleto Bancario") @@ -1121,7 +1121,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - BACS Direct Debit - func testDecodingBACSDirectDebitPaymentMethod() throws { + func test_decodingBACSDirectDebitPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(bacsDirectDebit) as BACSDirectDebitPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "directdebit_GB") XCTAssertEqual(paymentMethod.name, "BACS Direct Debit") @@ -1130,14 +1130,14 @@ class PaymentMethodTests: XCTestCase { // MARK: - ACH Direct Debit - func testDecodingACHDirectDebitPaymentMethod() throws { + func test_decodingACHDirectDebitPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(achDirectDebit) as ACHDirectDebitPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "ach") XCTAssertEqual(paymentMethod.name, "ACH Direct Debit") testCoding(paymentMethod) } - func testDecodingStoredACHDirectDebitPaymentMethod() throws { + func test_decodingStoredACHDirectDebitPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(storedACHDictionary) as StoredACHDirectDebitPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "ach") XCTAssertEqual(paymentMethod.name, "ACH Direct Debit") @@ -1147,7 +1147,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Cash App - func testDecodingCashAppPayPaymentMethod() throws { + func test_decodingCashAppPayPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(cashAppPay) as CashAppPayPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "cashapp") XCTAssertEqual(paymentMethod.name, "Cash App Pay") @@ -1156,7 +1156,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Qiwi App - func testDecodingQiwiPaymentMethod() throws { + func test_decodingQiwiPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(qiwiWallet) as QiwiWalletPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "qiwiwallet") XCTAssertEqual(paymentMethod.name, "Qiwi Wallet") @@ -1165,14 +1165,14 @@ class PaymentMethodTests: XCTestCase { // MARK: PayTo - func testDecodingPayToPaymentMethod() throws { + func test_decodingPayToPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(payto) as PayToPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "payto") XCTAssertEqual(paymentMethod.name, "payto") testCoding(paymentMethod) } - func testDecodingStoredPayToPaymentMethod() throws { + func test_decodingStoredPayToPaymentMethod() throws { let paymentMethod = try AdyenCoder.decode(storedPayToDictionary) as StoredPayToPaymentMethod XCTAssertEqual(paymentMethod.type.rawValue, "payto") XCTAssertEqual(paymentMethod.name, "payto") @@ -1182,9 +1182,11 @@ class PaymentMethodTests: XCTestCase { // MARK: - PaymentMethodDetails - func testMissingImplementationPaymentMethodDetails() throws { + func test_checkoutAttemptId_missingImplementation_on_concreteType() throws { - class DummyPaymentMethodDetails: PaymentMethodDetails {} + class DummyPaymentMethodDetails: PaymentMethodDetails { + var sdkData: String? + } var dummy = DummyPaymentMethodDetails() @@ -1206,7 +1208,7 @@ class PaymentMethodTests: XCTestCase { // MARK: - Accessibility - func testPaymentMethodTypeName() throws { + func test_paymentMethodTypeName() throws { [ PaymentMethodType.openBankingUK: "open banking UK", From 289297a42e0ef407bb6d2218a61a86dbbaa2094c Mon Sep 17 00:00:00 2001 From: erenbesel Date: Thu, 27 Nov 2025 10:27:57 +0100 Subject: [PATCH 10/10] add sdkdata to test struct --- .../PaymentComponent/PaymentComponentSubjectTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift index 519244b551..57e1f937e0 100644 --- a/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift +++ b/Tests/IntegrationTests/Components Tests/PaymentComponent/PaymentComponentSubjectTests.swift @@ -257,6 +257,7 @@ private struct MockAuthenticationPaymentDetails: PaymentMethodDetails, SDKDataAu let type: PaymentMethodType = .scheme var checkoutAttemptId: String? let authProvider: MockSDKDataAuthenticationProvider + var sdkData: String? var authentication: SDKData.Authentication { authProvider.authentication