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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Sources/ProcessOut/Sources/Api/ProcessOut.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public final class ProcessOut: @unchecked Sendable {
@_spi(PO)
public let eventEmitter: POEventEmitter

/// Web authentication session.
@_spi(PO)
public let webAuthenticationSession: POWebAuthenticationSession

@_spi(PO)
public func replace(configuration newConfiguration: ProcessOutConfiguration) {
_configuration.withLock { configuration in
Expand Down Expand Up @@ -135,7 +139,7 @@ public final class ProcessOut: @unchecked Sendable {
logger: connectorLogger
)
eventEmitter = LocalEventEmitter(logger: serviceLogger)
let webAuthenticationSession = ThrottledWebAuthenticationSessionDecorator(
webAuthenticationSession = ThrottledWebAuthenticationSessionDecorator(
session: DefaultWebAuthenticationSession(eventEmitter: eventEmitter)
)
let customerActionsService = Self.createCustomerActionsService(
Expand Down Expand Up @@ -189,7 +193,7 @@ public final class ProcessOut: @unchecked Sendable {

private static func createAlternativePaymentsService(
configuration: ProcessOutConfiguration,
webAuthenticationSession webSession: WebAuthenticationSession,
webAuthenticationSession webSession: POWebAuthenticationSession,
logger: POLogger
) -> DefaultAlternativePaymentsService {
let serviceConfiguration = Self.alternativePaymentsConfiguration(with: configuration)
Expand Down Expand Up @@ -234,7 +238,7 @@ public final class ProcessOut: @unchecked Sendable {
}

private static func createCustomerActionsService(
webAuthenticationSession webSession: WebAuthenticationSession,
webAuthenticationSession webSession: POWebAuthenticationSession,
logger: POLogger
) -> CustomerActionsService {
let decoder = JSONDecoder(), encoder = JSONEncoder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@ public struct PONativeAlternativePaymentUrlResolutionRequestV2: Sendable, Encoda

/// Result URL.
public let url: URL

public init(url: URL) {
self.url = url
}
}

/// Redirect result.
public let result: Result

public init(result: Result) {
self.result = result
}
}

/// Redirect information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,36 @@
// Created by Andrii Vysotskyi on 23.12.2025.
//

import Foundation

public struct PONativeAlternativePaymentRedirectResultV2: Sendable, Encodable {

public init(success: Bool) {
self.success = success
@_spi(PO)
public struct Result: Sendable, Encodable {

/// Result URL.
public let url: URL

public init(url: URL) {
self.url = url
}
}

/// Indicates whether customer was redirected successfully.
public let success: Bool

/// Redirect result.
@_spi(PO)
public let result: Result?

@_spi(PO)
public init(success: Bool, result: Result?) {
self.success = success
self.result = result
}

public init(success: Bool) {
self.success = success
self.result = nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class DefaultAlternativePaymentsService: POAlternativePaymentsService {

init(
configuration: AlternativePaymentsServiceConfiguration,
webSession: WebAuthenticationSession,
webSession: POWebAuthenticationSession,
logger: POLogger
) {
self.configuration = .init(wrappedValue: configuration)
Expand Down Expand Up @@ -130,7 +130,7 @@ final class DefaultAlternativePaymentsService: POAlternativePaymentsService {

private let configuration: POUnfairlyLocked<AlternativePaymentsServiceConfiguration>
private let logger: POLogger
private let webSession: WebAuthenticationSession
private let webSession: POWebAuthenticationSession

// MARK: - Request

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ final class DefaultCustomerActionsService: CustomerActionsService {
decoder: JSONDecoder,
encoder: JSONEncoder,
jsonWritingOptions: JSONSerialization.WritingOptions = [],
webSession: WebAuthenticationSession,
webSession: POWebAuthenticationSession,
logger: POLogger
) {
self.decoder = decoder
Expand Down Expand Up @@ -80,7 +80,7 @@ final class DefaultCustomerActionsService: CustomerActionsService {
private let decoder: JSONDecoder
private let encoder: JSONEncoder
private let jsonWritingOptions: JSONSerialization.WritingOptions
private let webSession: WebAuthenticationSession
private let webSession: POWebAuthenticationSession
private let logger: POLogger
private let semaphore: AsyncSemaphore

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ final class DefaultInvoicesService: POInvoicesService {
self.customerActionsService = customerActionsService
self.eventEmitter = eventEmitter
self.logger = logger
commonInit()
}

// MARK: - POInvoicesService
Expand Down Expand Up @@ -48,6 +47,12 @@ final class DefaultInvoicesService: POInvoicesService {
try await repository.authorizeInvoice(request: request)
}

func resolveUrl(
request: PONativeAlternativePaymentUrlResolutionRequestV2
) async throws -> PONativeAlternativePaymentUrlResolutionResponseV2 {
try await repository.resolveUrl(request: request)
}

// MARK: - Deprecated

func nativeAlternativePaymentMethodTransactionDetails(
Expand Down Expand Up @@ -107,10 +112,6 @@ final class DefaultInvoicesService: POInvoicesService {

// MARK: - Private Methods

private func commonInit() {
observeEvents()
}

private func _authorizeInvoice(request: POInvoiceAuthorizationRequest, threeDSService: PO3DS2Service) async throws {
let request = request.replacing(
thirdPartySdkVersion: request.thirdPartySdkVersion ?? threeDSService.version
Expand All @@ -135,46 +136,6 @@ final class DefaultInvoicesService: POInvoicesService {
}
try await _authorizeInvoice(request: newRequest, threeDSService: threeDSService)
}

// MARK: - Events

private func observeEvents() {
let deepLinkEventsListener = eventEmitter.on(PODeepLinkReceivedEvent.self) { [weak self] event in
self?.didReceive(deepLinkEvent: event) ?? false
}
eventListeners.append(deepLinkEventsListener)
}

private func didReceive(deepLinkEvent event: PODeepLinkReceivedEvent) -> Bool {
let shouldResolveDeepLink = eventEmitter.hasListeners(of: PONativeAlternativePaymentDeepLinkResolvedEvent.self)
|| eventEmitter.hasListeners(of: PONativeAlternativePaymentDeepLinkResolutionFailedEvent.self)
guard shouldResolveDeepLink else {
logger.debug("Deep link resolution is not requested, ignored.")
return false
}
Task {
do {
let response = try await repository.resolveUrl(
request: .init(redirect: .init(result: .init(url: event.url)))
)
eventEmitter.emit(event: PONativeAlternativePaymentDeepLinkResolvedEvent(resolutionResponse: response))
} catch let error as POFailure {
eventEmitter.emit(
event: PONativeAlternativePaymentDeepLinkResolutionFailedEvent(url: event.url, error: error)
)
} catch {
let error = POFailure(
message: "Unable to resolve deep link URL.", code: .Mobile.generic, underlyingError: error
)
eventEmitter.emit(
event: PONativeAlternativePaymentDeepLinkResolutionFailedEvent(url: event.url, error: error)
)
}
}
return true
}

private nonisolated(unsafe) var eventListeners: [AnyObject] = []
}

private extension POInvoiceAuthorizationRequest { // swiftlint:disable:this no_extension_access_modifier
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public protocol POInvoicesService: POService { // sourcery: AutoCompletion
request: PONativeAlternativePaymentAuthorizationRequestV2
) async throws -> PONativeAlternativePaymentAuthorizationResponseV2

/// Resolves native alternative payment return URL.
@_spi(PO)
func resolveUrl( // sourcery:completion: skip
request: PONativeAlternativePaymentUrlResolutionRequestV2
) async throws -> PONativeAlternativePaymentUrlResolutionResponseV2

// MARK: - Alternative Payment (Deprecated)

/// Requests information needed to continue existing payment or start new one.
Expand All @@ -52,4 +58,11 @@ extension POInvoicesService {
public func createInvoice(request: POInvoiceCreationRequest) async throws -> POInvoice {
throw POFailure(code: .Mobile.generic)
}

@_spi(PO)
public func resolveUrl(
request: PONativeAlternativePaymentUrlResolutionRequestV2
) async throws -> PONativeAlternativePaymentUrlResolutionResponseV2 {
throw POFailure(code: .Mobile.generic)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import AuthenticationServices

@MainActor
final class DefaultWebAuthenticationSession:
NSObject, WebAuthenticationSession, ASWebAuthenticationPresentationContextProviding {
NSObject, POWebAuthenticationSession, ASWebAuthenticationPresentationContextProviding {

nonisolated init(eventEmitter: POEventEmitter) {
self.eventEmitter = eventEmitter
Expand All @@ -18,7 +18,7 @@ final class DefaultWebAuthenticationSession:

// MARK: - WebAuthenticationSession

func authenticate(using request: WebAuthenticationRequest) async throws -> URL {
func authenticate(using request: POWebAuthenticationRequest) async throws -> URL {
let operationProxy = WebAuthenticationOperationProxy(
callback: request.callback, eventEmitter: eventEmitter
)
Expand Down Expand Up @@ -75,7 +75,9 @@ final class DefaultWebAuthenticationSession:
// MARK: - Private Methods

private static func createAuthenticationSession(
with request: WebAuthenticationRequest, redirectUrl: URL, completion: @escaping (Result<URL, POFailure>) -> Void
with request: POWebAuthenticationRequest,
redirectUrl: URL,
completion: @escaping (Result<URL, POFailure>) -> Void
) -> ASWebAuthenticationSession {
let completionHandler = { (url: URL?, error: Error?) in
if let url {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// POWebAuthenticationRequest.swift
// ProcessOut
//
// Created by Andrii Vysotskyi on 31.10.2024.
//

import Foundation

@_spi(PO)
public struct POWebAuthenticationRequest: Sendable {

/// A URL pointing to the authentication webpage.
public let url: URL

/// Callback.
public let callback: POWebAuthenticationCallback?

/// A boolean value that indicates whether the session should ask the browser for
/// a private authentication session.
public let prefersEphemeralSession: Bool

public init(url: URL, callback: POWebAuthenticationCallback?, prefersEphemeralSession: Bool) {
self.url = url
self.callback = callback
self.prefersEphemeralSession = prefersEphemeralSession
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// POWebAuthenticationSession.swift
// ProcessOut
//
// Created by Andrii Vysotskyi on 01.08.2024.
//

import Foundation

@_spi(PO)
public protocol POWebAuthenticationSession: Sendable {

/// Begins a web authentication session.
func authenticate(using request: POWebAuthenticationRequest) async throws -> URL
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@

import Foundation

actor ThrottledWebAuthenticationSessionDecorator: WebAuthenticationSession {
actor ThrottledWebAuthenticationSessionDecorator: POWebAuthenticationSession {

init(session: WebAuthenticationSession) {
init(session: POWebAuthenticationSession) {
self.session = session
semaphore = AsyncSemaphore(value: 1)
}

// MARK: - WebAuthenticationSession

func authenticate(using request: WebAuthenticationRequest) async throws -> URL {
func authenticate(using request: POWebAuthenticationRequest) async throws -> URL {
try await semaphore.waitUnlessCancelled(
cancellationError: POFailure(message: "Authentication session was cancelled.", code: .Mobile.cancelled)
)
Expand All @@ -30,7 +30,7 @@ actor ThrottledWebAuthenticationSessionDecorator: WebAuthenticationSession {

// MARK: - Private Properties

private let session: WebAuthenticationSession
private let session: POWebAuthenticationSession
private let semaphore: AsyncSemaphore

private var lastAuthenticationTime: DispatchTime?
Expand Down

This file was deleted.

Loading
Loading