diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index 10a00b258..0b02b59cf 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -9,11 +9,11 @@ on: - 'release/*' env: - destination: "platform=iOS Simulator,name=iPhone 16 Pro,OS=latest" + destination: "platform=iOS Simulator,name=iPhone 17 Pro,OS=26.1" configuration: "Debug" noIndex: "COMPILER_INDEX_STORE_ENABLE=NO" noSigning: "CODE_SIGNING_ALLOWED=NO" - versionXcode: "16.0" + versionXcode: "26.2" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -126,7 +126,7 @@ jobs: - name: Select Xcode run: | - sudo xcode-select -switch /Applications/Xcode_16.0.app + sudo xcode-select -switch /Applications/Xcode_26.2.app - name: Log xcodebuild Version run: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9a0586f4d..d50b41169 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,11 +20,11 @@ concurrency: cancel-in-progress: true env: - destination: "platform=iOS Simulator,name=iPhone 16 Pro,OS=latest" + destination: "platform=iOS Simulator,name=iPhone 17 Pro,OS=latest" configuration: "Debug" noIndex: "COMPILER_INDEX_STORE_ENABLE=NO" noSigning: "CODE_SIGNING_ALLOWED=NO" - versionXcode: "16.0" + versionXcode: "26.2" jobs: analyze: diff --git a/.github/workflows/run-regression-tests.yml b/.github/workflows/run-regression-tests.yml index 8a65fa071..fb55b84c8 100644 --- a/.github/workflows/run-regression-tests.yml +++ b/.github/workflows/run-regression-tests.yml @@ -1,18 +1,14 @@ name: Run Regression Tests on: - pull_request: - types: [ labeled ] - workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: run-regression-tests: - if: (github.event_name == 'workflow_dispatch') || (contains(github.event.pull_request.labels.*.name, 'Run Regression Tests')) name: Run Regression Tests runs-on: macos-15-xlarge @@ -22,7 +18,7 @@ jobs: - name: Select Xcode run: | - sudo xcode-select -switch /Applications/Xcode_16.0.app + sudo xcode-select -switch /Applications/Xcode_26.2.app - name: Log xcodebuild Version run: | @@ -30,7 +26,7 @@ jobs: - name: Run Regression Tests run: | - set -o pipefail && xcodebuild "test" "-project" "iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj" "-scheme" "Regression Tests" "-configuration" "Debug" "-destination" "platform=iOS Simulator,name=iPhone 16 Pro,OS=latest" | xcpretty + set -o pipefail && xcodebuild "test" "-project" "iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj" "-scheme" "Regression Tests" "-configuration" "Debug" "-destination" "platform=iOS Simulator,name=iPhone 17 Pro,OS=latest" | xcpretty # # Comment out below only when needed, to avoid overhead. diff --git a/.gitignore b/.gitignore index 8159e6faf..e9b2edc33 100644 --- a/.gitignore +++ b/.gitignore @@ -154,4 +154,7 @@ contents.xcworkspacedata !*.xcworkspace/contents.xcworkspacedata **/xcshareddata/WorkspaceSettings.xcsettings -# End of https://www.toptal.com/developers/gitignore/api/macos,swift,xcode,carthage,cocoapods,swiftpm \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/macos,swift,xcode,carthage,cocoapods,swiftpm + +*.build +Package.resolved \ No newline at end of file diff --git a/Checkout.podspec b/Checkout.podspec index 0f67541a5..e50ad14be 100644 --- a/Checkout.podspec +++ b/Checkout.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Checkout' - s.version = '4.3.8' + s.version = '4.4.0' s.summary = 'Checkout SDK for iOS' s.description = <<-DESC @@ -20,6 +20,6 @@ Pod::Spec.new do |s| s.exclude_files = "Checkout/Samples/**" s.dependency 'CheckoutEventLoggerKit', '~> 1.2.4' - s.dependency 'Risk', '~> 3.0.2' + s.dependency 'Risk', '~> 4.0.1' end diff --git a/Checkout/Samples/CocoapodsSample/Podfile b/Checkout/Samples/CocoapodsSample/Podfile index d24cd4522..656daec7b 100644 --- a/Checkout/Samples/CocoapodsSample/Podfile +++ b/Checkout/Samples/CocoapodsSample/Podfile @@ -5,5 +5,5 @@ target 'CheckoutCocoapodsSample' do use_frameworks! # Pods for CheckoutSDKCocoapodsSample - pod 'Checkout', '4.3.8' + pod 'Checkout', :path => '../../../' end diff --git a/Checkout/Samples/CocoapodsSample/Podfile.lock b/Checkout/Samples/CocoapodsSample/Podfile.lock index 54fa568ea..f5be86780 100644 --- a/Checkout/Samples/CocoapodsSample/Podfile.lock +++ b/Checkout/Samples/CocoapodsSample/Podfile.lock @@ -1,29 +1,32 @@ PODS: - - Checkout (4.3.8): + - Checkout (4.4.0): - CheckoutEventLoggerKit (~> 1.2.4) - - Risk (~> 3.0.2) + - Risk (~> 4.0.1) - CheckoutEventLoggerKit (1.2.4) - - FingerprintPro (2.7.0) - - Risk (3.0.4): + - FingerprintPro (2.13.0) + - Risk (4.0.1): - CheckoutEventLoggerKit (~> 1.2.4) - - FingerprintPro (~> 2.7.0) + - FingerprintPro (< 3.0.0, >= 2.7.0) DEPENDENCIES: - - Checkout (= 4.3.8) + - Checkout (from `../../../`) SPEC REPOS: trunk: - - Checkout - CheckoutEventLoggerKit - FingerprintPro - Risk +EXTERNAL SOURCES: + Checkout: + :path: "../../../" + SPEC CHECKSUMS: - Checkout: 609314d0c54a079d4c0ddfcfa7011367a12f225a + Checkout: 74ea5696bde3c3b85656c2b5c6446a07c8e89195 CheckoutEventLoggerKit: b780dec46295a34942780ea6230d0d5fd08aa05a - FingerprintPro: 0c7dbd28fc83751ca64b06328e2fb22bbc7ed118 - Risk: 6c0fdbf826f741b028ec9158198404ce267d0d3e + FingerprintPro: 2f419138022451a72f783db9c94967f5a68e9977 + Risk: e8ee31ba847b6cce1d4c384a9bb49d78fc34cdc5 -PODFILE CHECKSUM: 9b0372a870e3437a2232753690edfe8385c4d6e0 +PODFILE CHECKSUM: 36c08580c11dc09229142a4df18cbb3b7682087d COCOAPODS: 1.16.2 diff --git a/Checkout/Source/Logging/LogManager.swift b/Checkout/Source/Logging/LogManager.swift index a87b43083..5c7e1bfb1 100644 --- a/Checkout/Source/Logging/LogManager.swift +++ b/Checkout/Source/Logging/LogManager.swift @@ -106,6 +106,13 @@ enum LogManager: LogManaging { } } + /// Synchronously waits for all previously enqueued log work to finish. + /// Uses `loggingQueue.sync {}` so it is safe to call from any thread except the logging queue's own thread. + /// For test use only — not part of the public API. + static func _drainLoggingQueueForTesting() { + loggingQueue.sync { } + } + private static func log(_ event: CheckoutLogEvent, date: Date) { let logEvent = event.event(date: date) diff --git a/Checkout/Source/Model/Environment.swift b/Checkout/Source/Model/Environment.swift index 924004707..971820b0b 100644 --- a/Checkout/Source/Model/Environment.swift +++ b/Checkout/Source/Model/Environment.swift @@ -8,7 +8,21 @@ import Foundation protocol BaseURLProviding { - var baseURL: URL { get } + var baseURL: URL? { get } +} + +private enum APIHost { + static let production = "api.checkout.com" + static let sandbox = "api.sandbox.checkout.com" + + static func prefixed(_ prefix: String, environment: Environment) -> String { + switch environment { + case .production: + return "\(prefix).\(production)" + case .sandbox: + return "\(prefix).\(sandbox)" + } + } } /// Environment Enum for Production and Sandbox end points. @@ -16,12 +30,30 @@ public enum Environment: String, BaseURLProviding { case production case sandbox - var baseURL: URL { + var baseURL: URL? { + let host: String switch self { case .production: - return URL(string: "https://api.checkout.com/") + host = APIHost.production case .sandbox: - return URL(string: "https://api.sandbox.checkout.com/") + host = APIHost.sandbox + } + return URL(string: "https://\(host)/") + } +} + +/// Provides a base URL for a given environment, optionally prefixed with a subdomain. +/// When `baseURLPrefix` is set (after trimming), URLs follow the pattern `{prefix}.api[.sandbox].checkout.com`. +struct EnvironmentURLProvider: BaseURLProviding { + let environment: Environment + let baseURLPrefix: String? + + var baseURL: URL? { + let trimmed = baseURLPrefix?.trimmingCharacters(in: .whitespacesAndNewlines) + guard let prefix = trimmed, !prefix.isEmpty else { + return environment.baseURL } + let host = APIHost.prefixed(prefix, environment: environment) + return URL(string: "https://\(host)/") } } diff --git a/Checkout/Source/Network/RequestFactory.swift b/Checkout/Source/Network/RequestFactory.swift index eead6c641..860337e70 100644 --- a/Checkout/Source/Network/RequestFactory.swift +++ b/Checkout/Source/Network/RequestFactory.swift @@ -74,7 +74,9 @@ final class RequestFactory: RequestProviding { } func url(baseURLProvider: BaseURLProviding) -> Result { - guard var urlComponents = URLComponents(url: baseURLProvider.baseURL, resolvingAgainstBaseURL: false) else { + guard let baseURL = baseURLProvider.baseURL, + var urlComponents = URLComponents(url:baseURL, + resolvingAgainstBaseURL: false) else { return .failure(.baseURLCouldNotBeConvertedToComponents) } diff --git a/Checkout/Source/Tokenisation/CheckoutAPIService.swift b/Checkout/Source/Tokenisation/CheckoutAPIService.swift index 0a86ddd99..eb1b4d141 100644 --- a/Checkout/Source/Tokenisation/CheckoutAPIService.swift +++ b/Checkout/Source/Tokenisation/CheckoutAPIService.swift @@ -8,7 +8,11 @@ import Foundation import UIKit import CheckoutEventLoggerKit +#if canImport(RiskSDK) +import RiskSDK +#elseif canImport(Risk) import Risk +#endif public protocol CheckoutAPIProtocol { func createToken(_ paymentSource: PaymentSource, completion: @escaping (Result) -> Void) @@ -36,7 +40,7 @@ final public class CheckoutAPIService: CheckoutAPIProtocol { /// Initializes a CheckoutAPIService object with public key and Environment. /// CheckoutAPIService holds the core tokenisation logic methods to tokenise a user’s card details - public convenience init(publicKey: String, environment: Environment) { + public convenience init(publicKey: String, environment: Environment, baseURLPrefix: String? = nil) { let snakeCaseJSONEncoder = JSONEncoder() let snakeCaseJSONDecoder = JSONDecoder() @@ -46,7 +50,8 @@ final public class CheckoutAPIService: CheckoutAPIProtocol { let cardValidator = CardValidator(environment: environment) let networkManager = NetworkManager(decoder: snakeCaseJSONDecoder, session: .shared) - let requestFactory = RequestFactory(encoder: snakeCaseJSONEncoder, baseURLProvider: environment) + let baseURLProvider = EnvironmentURLProvider(environment: environment, baseURLPrefix: baseURLPrefix) + let requestFactory = RequestFactory(encoder: snakeCaseJSONEncoder, baseURLProvider: baseURLProvider) let tokenRequestFactory = TokenRequestFactory(cardValidator: cardValidator, decoder: snakeCaseJSONDecoder) let tokenDetailsFactory = TokenDetailsFactory() let logManager = LogManager.self diff --git a/CheckoutTests/Logging/LogManagerTests.swift b/CheckoutTests/Logging/LogManagerTests.swift index d43649795..3e8cf5459 100644 --- a/CheckoutTests/Logging/LogManagerTests.swift +++ b/CheckoutTests/Logging/LogManagerTests.swift @@ -21,9 +21,19 @@ final class LogManagerTests: XCTestCase { override func setUp() { super.setUp() - let logQueueFlushExpectation = XCTestExpectation() - subject.queue(event: .cardValidator) { logQueueFlushExpectation.fulfill() } - wait(for: [logQueueFlushExpectation], timeout: 5) + // Configure LogManager with stubs, then drain the serial logging queue synchronously. + // Using _drainLoggingQueueForTesting (loggingQueue.sync {}) avoids depending on the run loop + // to schedule .background QoS work, which is unreliable in CI. + subject.setup( + environment: .sandbox, + logger: stubCheckoutEventLogger, + uiDevice: stubDeviceInformationProvider, + dateProvider: stubDateProvider, + anyCodable: stubAnyCodable + ) + subject.queue(event: .cardValidator) { } + LogManager._drainLoggingQueueForTesting() + stubCheckoutEventLogger.resetLogCalledWith() } override func tearDown() { @@ -89,9 +99,6 @@ final class LogManagerTests: XCTestCase { } func test_setup_production() { - let correlationIDExpectation = XCTestExpectation() - stubCheckoutEventLogger.addMetadateExpectations.append(correlationIDExpectation) - subject.setup( environment: .production, logger: stubCheckoutEventLogger, @@ -99,6 +106,7 @@ final class LogManagerTests: XCTestCase { dateProvider: stubDateProvider, anyCodable: stubAnyCodable ) + LogManager._drainLoggingQueueForTesting() #if DEBUG XCTAssertEqual(stubCheckoutEventLogger.enableLocalProcessorCalledWith, .debug) @@ -142,8 +150,6 @@ final class LogManagerTests: XCTestCase { #endif #if !SWIFT_PACKAGE - wait(for: [correlationIDExpectation], timeout: 5) - XCTAssertEqual( stubCheckoutEventLogger.addMetadataCalledWith.first?.metadata, CheckoutEventLogger.MetadataKey.correlationID.rawValue @@ -154,8 +160,6 @@ final class LogManagerTests: XCTestCase { } func test_queue() { - let initialCorrelationIDExpectation = XCTestExpectation() - stubCheckoutEventLogger.addMetadateExpectations.append(initialCorrelationIDExpectation) subject.setup( environment: .sandbox, logger: stubCheckoutEventLogger, @@ -163,13 +167,10 @@ final class LogManagerTests: XCTestCase { dateProvider: stubDateProvider, anyCodable: stubAnyCodable ) - wait(for: [initialCorrelationIDExpectation], timeout: 3) + LogManager._drainLoggingQueueForTesting() - let expectation = XCTestExpectation(description: "Waiting for token creation") - stubCheckoutEventLogger.logExpectation = expectation subject.queue(event: .tokenRequested(CheckoutLogEvent.TokenRequestData(tokenType: .card, publicKey: "publicKey"))) - - wait(for: [expectation], timeout: 3) + LogManager._drainLoggingQueueForTesting() XCTAssertEqual( stubCheckoutEventLogger.logCalledWith, @@ -193,11 +194,8 @@ final class LogManagerTests: XCTestCase { anyCodable: stubAnyCodable ) - let expectation = XCTestExpectation(description: "Waiting for token creation") - stubCheckoutEventLogger.logExpectation = expectation subject.queue(event: .cardValidator) - - wait(for: [expectation], timeout: 10) + LogManager._drainLoggingQueueForTesting() XCTAssertEqual( stubCheckoutEventLogger.logCalledWith, @@ -211,16 +209,14 @@ final class LogManagerTests: XCTestCase { ] ) + // Queue cardValidator a second time — it should not log again (one-time event). subject.queue(event: .cardValidator) - // queueing another event here to flush the log queue - let expectation2 = XCTestExpectation(description: "Waiting for token creation") - stubCheckoutEventLogger.logExpectation = expectation2 + // Queue tokenRequested to ensure the second cardValidator has been processed. subject.queue(event: .tokenRequested(CheckoutLogEvent.TokenRequestData(tokenType: .card, publicKey: "publicKey"))) + LogManager._drainLoggingQueueForTesting() - wait(for: [expectation2], timeout: 10) - - // checking that card_validator only got logged once + // card_validator should appear only once. XCTAssertEqual( stubCheckoutEventLogger.logCalledWith, [ @@ -241,10 +237,8 @@ final class LogManagerTests: XCTestCase { } func test_resetCorrelationID() { - let initialCorrelationIDExpectation = XCTestExpectation() - let resetCorrelationIDExpectation = XCTestExpectation() - - stubCheckoutEventLogger.addMetadateExpectations.append(initialCorrelationIDExpectation) + // setUp's setup() call = add() #1. + // This test's setup() call = add() #2. subject.setup( environment: .sandbox, logger: stubCheckoutEventLogger, @@ -252,13 +246,13 @@ final class LogManagerTests: XCTestCase { dateProvider: stubDateProvider, anyCodable: stubAnyCodable ) + LogManager._drainLoggingQueueForTesting() - wait(for: [initialCorrelationIDExpectation], timeout: 3) - stubCheckoutEventLogger.addMetadateExpectations.append(resetCorrelationIDExpectation) + // Explicit reset = add() #3. subject.resetCorrelationID() - wait(for: [resetCorrelationIDExpectation], timeout: 3) + LogManager._drainLoggingQueueForTesting() - XCTAssertEqual(stubCheckoutEventLogger.addMetadataCalledWith.count, 2) + XCTAssertEqual(stubCheckoutEventLogger.addMetadataCalledWith.count, 3) XCTAssertTrue(stubCheckoutEventLogger.addMetadataCalledWith.allSatisfy { $0.metadata == CheckoutEventLogger.MetadataKey.correlationID.rawValue }) @@ -267,11 +261,11 @@ final class LogManagerTests: XCTestCase { // MARK: correlationID func test_correlationID() { + // setUp's setup() already set a new correlationID — capture it as the baseline. let initialCorrelationID = subject.correlationID XCTAssertNotNil(UUID(uuidString: initialCorrelationID), "failed to verify initialCorrelationID was a UUID") - let resetCorrelationIDExpectation = XCTestExpectation() - stubCheckoutEventLogger.addMetadateExpectations.append(resetCorrelationIDExpectation) + // A second setup() triggers resetCorrelationID() which generates a new UUID. subject.setup( environment: .sandbox, logger: stubCheckoutEventLogger, @@ -279,21 +273,16 @@ final class LogManagerTests: XCTestCase { dateProvider: stubDateProvider, anyCodable: stubAnyCodable ) - - wait(for: [resetCorrelationIDExpectation], timeout: 3) + LogManager._drainLoggingQueueForTesting() let newCorrelationID = subject.correlationID XCTAssertNotNil(UUID(uuidString: newCorrelationID), "failed to verify newCorrelationID was a UUID") - XCTAssertNotEqual(initialCorrelationID, newCorrelationID, "expected correlationIDs to change") } // MARK: registerTypes func test_registerTypes_TokenisationError_ServerError() { - let addCorrelationIDExpectation = XCTestExpectation() - stubCheckoutEventLogger.addMetadateExpectations.append(addCorrelationIDExpectation) - subject.setup( environment: .sandbox, logger: stubCheckoutEventLogger, @@ -301,6 +290,7 @@ final class LogManagerTests: XCTestCase { dateProvider: stubDateProvider, anyCodable: stubAnyCodable ) + LogManager._drainLoggingQueueForTesting() let serverError = TokenisationError.ServerError( requestID: "requestID", @@ -322,7 +312,5 @@ final class LogManagerTests: XCTestCase { let encodeCalledWith = stubSingleValueEncodingContainer.encodeCalledWith as? TokenisationError.ServerError XCTAssertEqual(encodeCalledWith, serverError) - - wait(for: [addCorrelationIDExpectation], timeout: 3) } } diff --git a/CheckoutTests/Model/EnvironmentTests.swift b/CheckoutTests/Model/EnvironmentTests.swift index ed9caad5f..59dd1f459 100644 --- a/CheckoutTests/Model/EnvironmentTests.swift +++ b/CheckoutTests/Model/EnvironmentTests.swift @@ -21,3 +21,67 @@ final class EnvironmentTests: XCTestCase { XCTAssertEqual(subject.baseURL, URL(string: "https://api.sandbox.checkout.com/")) } } + +final class EnvironmentURLProviderTests: XCTestCase { + + // MARK: - No prefix (falls back to default environment URLs) + + func test_baseURL_production_noPrefix() { + let subject = EnvironmentURLProvider(environment: .production, baseURLPrefix: nil) + XCTAssertEqual(subject.baseURL, URL(string: "https://api.checkout.com/")) + } + + func test_baseURL_sandbox_noPrefix() { + let subject = EnvironmentURLProvider(environment: .sandbox, baseURLPrefix: nil) + XCTAssertEqual(subject.baseURL, URL(string: "https://api.sandbox.checkout.com/")) + } + + func test_baseURL_production_emptyPrefix_fallsBackToDefault() { + let subject = EnvironmentURLProvider(environment: .production, baseURLPrefix: "") + XCTAssertEqual(subject.baseURL, URL(string: "https://api.checkout.com/")) + } + + func test_baseURL_sandbox_emptyPrefix_fallsBackToDefault() { + let subject = EnvironmentURLProvider(environment: .sandbox, baseURLPrefix: "") + XCTAssertEqual(subject.baseURL, URL(string: "https://api.sandbox.checkout.com/")) + } + + // MARK: - With prefix + + func test_baseURL_production_withPrefix() { + let subject = EnvironmentURLProvider(environment: .production, baseURLPrefix: "msdd") + XCTAssertEqual(subject.baseURL, URL(string: "https://msdd.api.checkout.com/")) + } + + func test_baseURL_sandbox_withPrefix() { + let subject = EnvironmentURLProvider(environment: .sandbox, baseURLPrefix: "msdd") + XCTAssertEqual(subject.baseURL, URL(string: "https://msdd.api.sandbox.checkout.com/")) + } + + func test_baseURL_production_withDifferentPrefix() { + let subject = EnvironmentURLProvider(environment: .production, baseURLPrefix: "custom-subdomain") + XCTAssertEqual(subject.baseURL, URL(string: "https://custom-subdomain.api.checkout.com/")) + } + + func test_baseURL_sandbox_withDifferentPrefix() { + let subject = EnvironmentURLProvider(environment: .sandbox, baseURLPrefix: "custom-subdomain") + XCTAssertEqual(subject.baseURL, URL(string: "https://custom-subdomain.api.sandbox.checkout.com/")) + } + + // MARK: - Trimmed prefix + + func test_baseURL_production_trimmedPrefix() { + let subject = EnvironmentURLProvider(environment: .production, baseURLPrefix: " msdd ") + XCTAssertEqual(subject.baseURL, URL(string: "https://msdd.api.checkout.com/")) + } + + func test_baseURL_sandbox_trimmedPrefix() { + let subject = EnvironmentURLProvider(environment: .sandbox, baseURLPrefix: "\tmsdd\n") + XCTAssertEqual(subject.baseURL, URL(string: "https://msdd.api.sandbox.checkout.com/")) + } + + func test_baseURL_production_whitespaceOnlyPrefix_fallsBackToDefault() { + let subject = EnvironmentURLProvider(environment: .production, baseURLPrefix: " ") + XCTAssertEqual(subject.baseURL, URL(string: "https://api.checkout.com/")) + } +} diff --git a/CheckoutTests/Stubs/StubBaseURLProvider.swift b/CheckoutTests/Stubs/StubBaseURLProvider.swift index 45ae0f905..e47229940 100644 --- a/CheckoutTests/Stubs/StubBaseURLProvider.swift +++ b/CheckoutTests/Stubs/StubBaseURLProvider.swift @@ -10,10 +10,10 @@ import Foundation // swiftlint:disable force_unwrapping final class StubBaseURLProvider: BaseURLProviding { - var baseURLToReturn = URL(string: "https://www.checkout.com/")! + var baseURLToReturn: URL? = URL(string: "https://www.checkout.com/")! private(set) var baseURLCalled = false - var baseURL: URL { + var baseURL: URL? { baseURLCalled = true return baseURLToReturn } diff --git a/CheckoutTests/Stubs/StubCheckoutEventLogger.swift b/CheckoutTests/Stubs/StubCheckoutEventLogger.swift index b439fe92d..dbc843b36 100644 --- a/CheckoutTests/Stubs/StubCheckoutEventLogger.swift +++ b/CheckoutTests/Stubs/StubCheckoutEventLogger.swift @@ -11,6 +11,12 @@ import XCTest class StubCheckoutEventLogger: CheckoutEventLogging { private(set) var logCalledWith: [Event] = [] var logExpectation: XCTestExpectation? + + /// Reset accumulated log events so tests can assert on a clean slate. Call after flushing the queue in setUp. + func resetLogCalledWith() { + logCalledWith = [] + } + func log(event: Event) { logCalledWith.append(event) logExpectation?.fulfill() diff --git a/CheckoutTests/Stubs/StubRisk.swift b/CheckoutTests/Stubs/StubRisk.swift index d2103a696..38058a017 100644 --- a/CheckoutTests/Stubs/StubRisk.swift +++ b/CheckoutTests/Stubs/StubRisk.swift @@ -6,7 +6,11 @@ // import Foundation +#if canImport(RiskSDK) +@testable import RiskSDK +#elseif canImport(Risk) @testable import Risk +#endif @testable import Checkout // swiftlint:disable large_tuple diff --git a/Frames.podspec b/Frames.podspec index d0010ab6b..b6079a431 100644 --- a/Frames.podspec +++ b/Frames.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Frames" - s.version = "4.3.8" + s.version = "4.4.0" s.summary = "Checkout API Client, Payment Form UI and Utilities in Swift" s.description = <<-DESC Checkout API Client and Payment Form Utilities in Swift. @@ -21,6 +21,6 @@ Pod::Spec.new do |s| s.dependency 'PhoneNumberKit', '~> 4.0' s.dependency 'CheckoutEventLoggerKit', '~> 1.2.4' - s.dependency 'Checkout', '4.3.8' + s.dependency 'Checkout', '4.4.0' end diff --git a/Package.swift b/Package.swift index 98be7f6d5..e540e2bde 100644 --- a/Package.swift +++ b/Package.swift @@ -20,7 +20,7 @@ let package = Package( from: "4.0.0"), .package( url: "https://github.com/checkout/checkout-risk-sdk-ios.git", - from: "3.0.2"), + from: "4.0.1"), .package( url: "https://github.com/checkout/checkout-event-logger-ios-framework.git", from: "1.2.4" diff --git a/Source/Core/CheckoutAPIService.swift b/Source/Core/CheckoutAPIService.swift index c7558433c..44472c0d5 100644 --- a/Source/Core/CheckoutAPIService.swift +++ b/Source/Core/CheckoutAPIService.swift @@ -12,7 +12,7 @@ import CheckoutEventLoggerKit protocol CheckoutAPIProtocol { var cardValidator: CardValidating { get } var logger: FramesEventLogging { get } - init(publicKey: String, environment: Environment) + init(publicKey: String, environment: Environment, baseURLPrefix: String?) func createToken(_ paymentSource: PaymentSource, completion: @escaping (Result) -> Void) func createSecurityCodeToken(securityCode: String, completion: @escaping (Result) -> Void) } @@ -23,8 +23,8 @@ public final class CheckoutAPIService: CheckoutAPIProtocol { let logger: FramesEventLogging private let checkoutAPIService: Checkout.CheckoutAPIProtocol - public init(publicKey: String, environment: Environment) { - let checkoutAPIService = Checkout.CheckoutAPIService(publicKey: publicKey, environment: environment.checkoutEnvironment) + public init(publicKey: String, environment: Environment, baseURLPrefix: String? = nil) { + let checkoutAPIService = Checkout.CheckoutAPIService(publicKey: publicKey, environment: environment.checkoutEnvironment, baseURLPrefix: baseURLPrefix) let cardValidator = CardValidator(environment: environment.checkoutEnvironment) let logger = FramesEventLogger(environment: environment, correlationID: checkoutAPIService.correlationID) diff --git a/Source/UI/PaymentForm/Factory/PaymentFormFactory.swift b/Source/UI/PaymentForm/Factory/PaymentFormFactory.swift index c8856fc57..d92d2eee9 100644 --- a/Source/UI/PaymentForm/Factory/PaymentFormFactory.swift +++ b/Source/UI/PaymentForm/Factory/PaymentFormFactory.swift @@ -9,7 +9,8 @@ public enum PaymentFormFactory { let logger = FramesEventLogger(environment: configuration.environment, correlationID: UUID().uuidString) let cardValidator = CardValidator(environment: configuration.environment.checkoutEnvironment) let checkoutAPIService = CheckoutAPIService(publicKey: configuration.serviceAPIKey, - environment: configuration.environment) + environment: configuration.environment, + baseURLPrefix: configuration.baseURLPrefix) var viewModel = DefaultPaymentViewModel(checkoutAPIService: checkoutAPIService, cardValidator: cardValidator, logger: logger, diff --git a/Source/UI/PaymentForm/Model/PaymentFormConfiguration.swift b/Source/UI/PaymentForm/Model/PaymentFormConfiguration.swift index 1a739476d..e7810a9ca 100644 --- a/Source/UI/PaymentForm/Model/PaymentFormConfiguration.swift +++ b/Source/UI/PaymentForm/Model/PaymentFormConfiguration.swift @@ -13,6 +13,7 @@ public struct PaymentFormConfiguration { let environment: Environment let supportedSchemes: [Card.Scheme] let billingFormData: BillingForm? + let baseURLPrefix: String? /** Create a configuration for the Payment form @@ -22,14 +23,17 @@ public struct PaymentFormConfiguration { - environment: Enum describing the environment the SDK is running in - supportedSchemes: Card schemes supported for receiving payments. Accurate declaration of supported schemes will improve customer experience - billingFormData: Pre filled Billing form information to be included and help reduce user input if known + - baseURLPrefix: Optional subdomain prefix for the API base URL (e.g. "msdd" produces "msdd.api.checkout.com") */ public init(apiKey: String, environment: Environment, supportedSchemes: [CardScheme], - billingFormData: BillingForm?) { + billingFormData: BillingForm?, + baseURLPrefix: String? = nil) { self.serviceAPIKey = apiKey self.environment = environment self.supportedSchemes = supportedSchemes.compactMap { $0.mapToCheckoutCardScheme() } self.billingFormData = billingFormData + self.baseURLPrefix = baseURLPrefix } } diff --git a/Source/UI/SecurityCodeComponent/SecurityCodeComponent.swift b/Source/UI/SecurityCodeComponent/SecurityCodeComponent.swift index d19878f66..1481ef0b8 100644 --- a/Source/UI/SecurityCodeComponent/SecurityCodeComponent.swift +++ b/Source/UI/SecurityCodeComponent/SecurityCodeComponent.swift @@ -48,7 +48,8 @@ extension SecurityCodeComponent { cardValidator = CardValidator(environment: configuration.environment.checkoutEnvironment) checkoutAPIService = CheckoutAPIService(publicKey: configuration.apiKey, - environment: configuration.environment) + environment: configuration.environment, + baseURLPrefix: configuration.baseURLPrefix) let viewModel = SecurityCodeViewModel(cardValidator: cardValidator) if let initialCardScheme = configuration.cardScheme { diff --git a/Source/UI/SecurityCodeComponent/SecurityCodeComponentConfiguration.swift b/Source/UI/SecurityCodeComponent/SecurityCodeComponentConfiguration.swift index ab401ec55..639b96b75 100644 --- a/Source/UI/SecurityCodeComponent/SecurityCodeComponentConfiguration.swift +++ b/Source/UI/SecurityCodeComponent/SecurityCodeComponentConfiguration.swift @@ -17,7 +17,9 @@ import UIKit - If provided, card scheme's validation rules apply (e.g. VISA = 3 digits, American Express = 4 digits etc.) - If not provided, security code is treated as valid for 3 and 4 digits - style: Security Code Component wraps a text field in a secure way. - To style the inner properties like font, textColor etc, you must alter the style. + To style the inner properties like font, textColor etc, you must alter the style. + - baseURLPrefix: Optional subdomain prefix for the API base URL (e.g. "msdd" produces "msdd.api.checkout.com"). + Whitespace is trimmed; empty or nil uses the default host. */ public struct SecurityCodeComponentConfiguration { @@ -25,14 +27,17 @@ public struct SecurityCodeComponentConfiguration { let environment: Environment public var style: SecurityCodeComponentStyle public var cardScheme: Card.Scheme? + let baseURLPrefix: String? public init(apiKey: String, environment: Environment, style: SecurityCodeComponentStyle? = nil, - cardScheme: Card.Scheme? = nil) { + cardScheme: Card.Scheme? = nil, + baseURLPrefix: String? = nil) { self.apiKey = apiKey self.environment = environment self.cardScheme = cardScheme + self.baseURLPrefix = baseURLPrefix if let style = style { self.style = style diff --git a/Tests/Mocks/StubCheckoutAPIService.swift b/Tests/Mocks/StubCheckoutAPIService.swift index fc736d28b..966c67cd7 100644 --- a/Tests/Mocks/StubCheckoutAPIService.swift +++ b/Tests/Mocks/StubCheckoutAPIService.swift @@ -27,7 +27,7 @@ final class StubCheckoutAPIService: Frames.CheckoutAPIProtocol { private(set) var cardValidatorCalled = false private(set) var loggerCalled = false - convenience init(publicKey: String, environment: Frames.Environment) { + convenience init(publicKey: String, environment: Frames.Environment, baseURLPrefix: String? = nil) { self.init() } diff --git a/iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj/project.pbxproj b/iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj/project.pbxproj index 324bf4701..f9a2b3aea 100644 --- a/iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj/project.pbxproj +++ b/iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -42,7 +42,6 @@ 16E91A452A70453B00DC0D98 /* TestCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E91A442A70453B00DC0D98 /* TestCard.swift */; }; 16E91A482A704AC300DC0D98 /* StaticTexts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E91A472A704AC300DC0D98 /* StaticTexts.swift */; }; 955B092B291CEF0F00DEEAF5 /* ApplePayCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955B092A291CEF0F00DEEAF5 /* ApplePayCreator.swift */; }; - 955DD85E2DEF369C00E57DA5 /* Frames in Frameworks */ = {isa = PBXBuildFile; productRef = 955DD85D2DEF369C00E57DA5 /* Frames */; }; 95C8C10D28AD5FEE00B8D3D0 /* UIViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C8C0B528AD5FEE00B8D3D0 /* UIViewControllerExtension.swift */; }; 95C8C10E28AD5FEE00B8D3D0 /* UIViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C8C0B628AD5FEE00B8D3D0 /* UIViewExtension.swift */; }; 95C8C10F28AD5FEE00B8D3D0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C8C0B728AD5FEE00B8D3D0 /* AppDelegate.swift */; }; @@ -90,6 +89,7 @@ 95C8C13F28AD603700B8D3D0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 95C8C13E28AD603700B8D3D0 /* Assets.xcassets */; }; 95EB97DC2A54753000EE6776 /* ThemeDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95EB97DA2A54753000EE6776 /* ThemeDemo.swift */; }; 95EB97DD2A54753000EE6776 /* ThemeDemo+Border.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95EB97DB2A54753000EE6776 /* ThemeDemo+Border.swift */; }; + AB60148E2F59BD6000526B0F /* Frames in Frameworks */ = {isa = PBXBuildFile; productRef = AB60148D2F59BD6000526B0F /* Frames */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -227,7 +227,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 955DD85E2DEF369C00E57DA5 /* Frames in Frameworks */, + AB60148E2F59BD6000526B0F /* Frames in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -530,7 +530,7 @@ packageProductDependencies = ( 1629C08B2AF90379001BD3D9 /* Frames */, 958E51042DE74B3E0003F4B0 /* Frames */, - 955DD85D2DEF369C00E57DA5 /* Frames */, + AB60148D2F59BD6000526B0F /* Frames */, ); productName = "iOS Example Frame"; productReference = 16AE74C32A5C1EBB0031F794 /* iOS Example Frame.app */; @@ -579,7 +579,7 @@ mainGroup = E6646F8120CE6C0900D8353A; packageReferences = ( 16C3F83E2A7927ED00690639 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */, - 955DD85C2DEF369C00E57DA5 /* XCRemoteSwiftPackageReference "frames-ios" */, + AB60148C2F59BD6000526B0F /* XCLocalSwiftPackageReference "../../frames-ios" */, ); productRefGroup = E6646F8120CE6C0900D8353A; projectDirPath = ""; @@ -1232,6 +1232,13 @@ }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + AB60148C2F59BD6000526B0F /* XCLocalSwiftPackageReference "../../frames-ios" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../../frames-ios"; + }; +/* End XCLocalSwiftPackageReference section */ + /* Begin XCRemoteSwiftPackageReference section */ 16C3F83E2A7927ED00690639 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = { isa = XCRemoteSwiftPackageReference; @@ -1241,14 +1248,6 @@ version = 1.12.0; }; }; - 955DD85C2DEF369C00E57DA5 /* XCRemoteSwiftPackageReference "frames-ios" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/checkout/frames-ios"; - requirement = { - kind = exactVersion; - version = 4.3.8; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -1269,12 +1268,11 @@ package = 16C3F83E2A7927ED00690639 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */; productName = SnapshotTesting; }; - 955DD85D2DEF369C00E57DA5 /* Frames */ = { + 958E51042DE74B3E0003F4B0 /* Frames */ = { isa = XCSwiftPackageProductDependency; - package = 955DD85C2DEF369C00E57DA5 /* XCRemoteSwiftPackageReference "frames-ios" */; productName = Frames; }; - 958E51042DE74B3E0003F4B0 /* Frames */ = { + AB60148D2F59BD6000526B0F /* Frames */ = { isa = XCSwiftPackageProductDependency; productName = Frames; }; diff --git a/Package.resolved b/iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 59% rename from Package.resolved rename to iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 989e5f09d..774cc5e18 100644 --- a/Package.resolved +++ b/iOS Example Frame SPM/iOS Example Frame SPM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "981fa1f5373edc5df30736289e51afb37c0d442fd1f452eb90bd458c57c83394", "pins" : [ { "identity" : "checkout-event-logger-ios-framework", @@ -14,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/checkout/checkout-risk-sdk-ios.git", "state" : { - "revision" : "26f77f09129e78bbab76b7c8916437e309c5051a", - "version" : "3.0.2" + "revision" : "7f944921720dc879b63ac3bb51f56dd6eb50919c", + "version" : "4.0.2" } }, { @@ -23,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/fingerprintjs/fingerprintjs-pro-ios", "state" : { - "revision" : "ceb8b845ec99727ee9f78869a51688c6daa16bd8", - "version" : "2.2.0" + "revision" : "21bcfcfd2e1bce943f668a7748f8a7e1154f0ac2", + "version" : "2.13.0" } }, { @@ -32,10 +33,19 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/marmelroy/PhoneNumberKit.git", "state" : { - "revision" : "288b99e9e99926bc8be41220802df83c059fac9b", - "version" : "4.0.2" + "revision" : "ad20aba8db84a6ba373abfcffe38b1c8e6f8258b", + "version" : "4.2.7" + } + }, + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "26ed3a2b4a2df47917ca9b790a57f91285b923fb", + "version" : "1.12.0" } } ], - "version" : 2 + "version" : 3 } diff --git a/iOS Example Frame/Podfile b/iOS Example Frame/Podfile index 7f51e005b..cbb76bd1f 100644 --- a/iOS Example Frame/Podfile +++ b/iOS Example Frame/Podfile @@ -5,8 +5,10 @@ target 'iOS Example Frame' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! - # Pods for iOS Example Custom - pod 'Frames', '4.3.8' + # Pods for iOS Example Custom (local Frames from repo root) + pod 'Frames', :path => '../' + pod 'Checkout', :path => '../' + end post_install do |installer| diff --git a/iOS Example Frame/Podfile.lock b/iOS Example Frame/Podfile.lock index c94010878..cd16b47be 100644 --- a/iOS Example Frame/Podfile.lock +++ b/iOS Example Frame/Podfile.lock @@ -1,43 +1,48 @@ PODS: - - Checkout (4.3.8): + - Checkout (4.4.0): - CheckoutEventLoggerKit (~> 1.2.4) - - Risk (~> 3.0.2) + - Risk (~> 4.0.1) - CheckoutEventLoggerKit (1.2.4) - - FingerprintPro (2.7.0) - - Frames (4.3.8): - - Checkout (= 4.3.8) + - FingerprintPro (2.13.0) + - Frames (4.4.0): + - Checkout (= 4.4.0) - CheckoutEventLoggerKit (~> 1.2.4) - PhoneNumberKit (~> 4.0) - - PhoneNumberKit (4.0.1): - - PhoneNumberKit/PhoneNumberKitCore (= 4.0.1) - - PhoneNumberKit/UIKit (= 4.0.1) - - PhoneNumberKit/PhoneNumberKitCore (4.0.1) - - PhoneNumberKit/UIKit (4.0.1): + - PhoneNumberKit (4.2.0): + - PhoneNumberKit/PhoneNumberKitCore (= 4.2.0) + - PhoneNumberKit/UIKit (= 4.2.0) + - PhoneNumberKit/PhoneNumberKitCore (4.2.0) + - PhoneNumberKit/UIKit (4.2.0): - PhoneNumberKit/PhoneNumberKitCore - - Risk (3.0.4): + - Risk (4.0.1): - CheckoutEventLoggerKit (~> 1.2.4) - - FingerprintPro (~> 2.7.0) + - FingerprintPro (< 3.0.0, >= 2.7.0) DEPENDENCIES: - - Frames (= 4.3.8) + - Checkout (from `../`) + - Frames (from `../`) SPEC REPOS: trunk: - - Checkout - CheckoutEventLoggerKit - FingerprintPro - - Frames - PhoneNumberKit - Risk +EXTERNAL SOURCES: + Checkout: + :path: "../" + Frames: + :path: "../" + SPEC CHECKSUMS: - Checkout: 609314d0c54a079d4c0ddfcfa7011367a12f225a + Checkout: 74ea5696bde3c3b85656c2b5c6446a07c8e89195 CheckoutEventLoggerKit: b780dec46295a34942780ea6230d0d5fd08aa05a - FingerprintPro: 0c7dbd28fc83751ca64b06328e2fb22bbc7ed118 - Frames: 7cad5e39665bf9d610dcf44b3ff42f6b051ca268 - PhoneNumberKit: a74155066daa6450475f6a029068eb919fb00d5d - Risk: 6c0fdbf826f741b028ec9158198404ce267d0d3e + FingerprintPro: 2f419138022451a72f783db9c94967f5a68e9977 + Frames: cfe3e6809097ca3a08d0e935103195833512bf61 + PhoneNumberKit: b05dfa2fd6ae53cc51e9684aa1967b00aa26899b + Risk: e8ee31ba847b6cce1d4c384a9bb49d78fc34cdc5 -PODFILE CHECKSUM: d9265d982988f39d7b723f95d839da2ec8d13da3 +PODFILE CHECKSUM: 731ac4454e9a98bc4604ec10a2460a43b5c042a5 COCOAPODS: 1.16.2 diff --git a/iOS Example Frame/iOS Example Frame/Factory/Factory+UITest.swift b/iOS Example Frame/iOS Example Frame/Factory/Factory+UITest.swift index 375f80793..433d25d74 100644 --- a/iOS Example Frame/iOS Example Frame/Factory/Factory+UITest.swift +++ b/iOS Example Frame/iOS Example Frame/Factory/Factory+UITest.swift @@ -19,7 +19,8 @@ extension Factory { let configuration = PaymentFormConfiguration(apiKey: apiKey, environment: environment, supportedSchemes: supportedSchemes, - billingFormData: nil) + billingFormData: nil, + baseURLPrefix: baseURLPrefix) let style = ThemeDemo.buildMinimalUITest() let viewController = PaymentFormFactory.buildViewController(configuration: configuration, @@ -35,7 +36,8 @@ extension Factory { let configuration = PaymentFormConfiguration(apiKey: apiKey, environment: environment, supportedSchemes: supportedSchemes, - billingFormData: nil) + billingFormData: nil, + baseURLPrefix: baseURLPrefix) let style = ThemeDemo.buildCompleteUITest() let viewController = PaymentFormFactory.buildViewController(configuration: configuration, diff --git a/iOS Example Frame/iOS Example Frame/Factory/Factory.swift b/iOS Example Frame/iOS Example Frame/Factory/Factory.swift index a5004d663..bf0624a41 100644 --- a/iOS Example Frame/iOS Example Frame/Factory/Factory.swift +++ b/iOS Example Frame/iOS Example Frame/Factory/Factory.swift @@ -17,6 +17,8 @@ enum Factory { static let failureURL = URL(string: "https://httpstat.us/403")! static let apiKey = "pk_sbox_ym4kqv5lzvjni7utqbliqs2vhqc" static let environment: Frames.Environment = .sandbox + /// Optional subdomain prefix for API base URL (e.g. "sa" → "sa.api.sandbox.checkout.com"). Set to nil to use default host. + static let baseURLPrefix: String? = nil static func getDefaultPaymentViewController(completionHandler: @escaping (Result) -> Void) -> UIViewController { #if UITEST @@ -50,7 +52,8 @@ enum Factory { let configuration = PaymentFormConfiguration(apiKey: apiKey, environment: environment, supportedSchemes: supportedSchemes, - billingFormData: billingFormData) + billingFormData: billingFormData, + baseURLPrefix: baseURLPrefix) let style = PaymentStyle(paymentFormStyle: paymentFormStyle, billingFormStyle: billingFormStyle) @@ -76,7 +79,8 @@ enum Factory { let configuration = PaymentFormConfiguration(apiKey: apiKey, environment: environment, supportedSchemes: supportedSchemes, - billingFormData: billingFormData) + billingFormData: billingFormData, + baseURLPrefix: baseURLPrefix) let style = ThemeDemo.buildBorderExample() let viewController = PaymentFormFactory.buildViewController(configuration: configuration, style: style, @@ -112,7 +116,8 @@ enum Factory { let configuration = PaymentFormConfiguration(apiKey: apiKey, environment: environment, supportedSchemes: supportedSchemes, - billingFormData: billingFormData) + billingFormData: billingFormData, + baseURLPrefix: baseURLPrefix) let style = PaymentStyle(paymentFormStyle: paymentFormStyle, billingFormStyle: billingFormStyle) @@ -140,7 +145,8 @@ enum Factory { let configuration = PaymentFormConfiguration(apiKey: apiKey, environment: environment, supportedSchemes: supportedSchemes, - billingFormData: billingFormData) + billingFormData: billingFormData, + baseURLPrefix: baseURLPrefix) let style = ThemeDemo.buildCustom2Example() diff --git a/iOS Example Frame/iOS Example Frame/ViewController/HomeViewController.swift b/iOS Example Frame/iOS Example Frame/ViewController/HomeViewController.swift index 7bd66b3b0..5a9088477 100644 --- a/iOS Example Frame/iOS Example Frame/ViewController/HomeViewController.swift +++ b/iOS Example Frame/iOS Example Frame/ViewController/HomeViewController.swift @@ -23,7 +23,7 @@ class HomeViewController: UIViewController { @IBOutlet weak var securityCodeComponentButton: UIButton! private var notificationCenter: NotificationCenter = .default - private lazy var checkoutAPIService = Frames.CheckoutAPIService(publicKey: Factory.apiKey, environment: Factory.environment) + private lazy var checkoutAPIService = Frames.CheckoutAPIService(publicKey: Factory.apiKey, environment: Factory.environment, baseURLPrefix: Factory.baseURLPrefix) override func viewDidLoad() { super.viewDidLoad() diff --git a/iOS Example Frame/iOS Example Frame/ViewController/SecurityCodeViewController.swift b/iOS Example Frame/iOS Example Frame/ViewController/SecurityCodeViewController.swift index b4bc79374..3126738f5 100644 --- a/iOS Example Frame/iOS Example Frame/ViewController/SecurityCodeViewController.swift +++ b/iOS Example Frame/iOS Example Frame/ViewController/SecurityCodeViewController.swift @@ -18,7 +18,8 @@ final class SecurityCodeViewController: UIViewController { @IBOutlet private weak var customPayButton: UIButton! var configuration = SecurityCodeComponentConfiguration(apiKey: Factory.apiKey, - environment: Factory.environment) + environment: Factory.environment, + baseURLPrefix: Factory.baseURLPrefix) func setupDefaultSecurityCodeComponent() { configuration.cardScheme = Card.Scheme(rawValue: "VISA")