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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public struct PONativeAlternativePaymentTokenizationRequestV2: Sendable, Encodab
/// Payment request parameters.
public let submitData: PONativeAlternativePaymentSubmitDataV2?

/// Redirect result.
public let redirect: PONativeAlternativePaymentRedirectResultV2?

/// Customer's locale identifier override.
@POExcludedEncodable
public private(set) var localeIdentifier: String?
Expand All @@ -28,12 +31,14 @@ public struct PONativeAlternativePaymentTokenizationRequestV2: Sendable, Encodab
customerTokenId: String,
gatewayConfigurationId: String,
submitData: PONativeAlternativePaymentSubmitDataV2? = nil,
redirect: PONativeAlternativePaymentRedirectResultV2? = nil,
localeIdentifier: String? = nil
) {
self.customerId = customerId
self.customerTokenId = customerTokenId
self.gatewayConfigurationId = gatewayConfigurationId
self.submitData = submitData
self.redirect = redirect
self.localeIdentifier = localeIdentifier
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public struct PONativeAlternativePaymentAuthorizationRequestV2: Sendable, Encoda
/// Payment request parameters.
public let submitData: PONativeAlternativePaymentSubmitDataV2?

/// Redirect result.
public let redirect: PONativeAlternativePaymentRedirectResultV2?

/// Customer's locale identifier override.
@POExcludedEncodable
public private(set) var localeIdentifier: String?
Expand All @@ -27,13 +30,15 @@ public struct PONativeAlternativePaymentAuthorizationRequestV2: Sendable, Encoda
invoiceId: String,
gatewayConfigurationId: String,
source: String? = nil,
submitData: PONativeAlternativePaymentSubmitDataV2?,
submitData: PONativeAlternativePaymentSubmitDataV2? = nil,
redirect: PONativeAlternativePaymentRedirectResultV2? = nil,
localeIdentifier: String? = nil
) {
self.invoiceId = invoiceId
self.gatewayConfigurationId = gatewayConfigurationId
self.source = source
self.submitData = submitData
self.redirect = redirect
self.localeIdentifier = localeIdentifier
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// PONativeAlternativePaymentRedirectResultV2.swift
// ProcessOut
//
// Created by Andrii Vysotskyi on 23.12.2025.
//

public struct PONativeAlternativePaymentRedirectResultV2: Sendable, Encodable {

public init(success: Bool) {
self.success = success
}

/// indicates whether customer was redirected successfully.
public let success: Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,43 @@ import Foundation
/// Redirect details.
public struct PONativeAlternativePaymentRedirectV2: Decodable, Sendable {

public struct RedirectType: Hashable, RawRepresentable, Sendable {

public init(rawValue: String) {
self.rawValue = rawValue
}

/// Raw value.
public let rawValue: String
}

/// Destination URL.
public let url: URL

/// Display hint describing redirect purpose.
public let hint: String

/// Redirect type.
public let type: RedirectType

/// Boolean value indicating whether backend expects redirect confirmation after customer
/// is redirected to url.
public let confirmationRequired: Bool
}

extension PONativeAlternativePaymentRedirectV2.RedirectType: Decodable {

public init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()
rawValue = try container.decode(String.self)
}
}

extension PONativeAlternativePaymentRedirectV2.RedirectType {

/// Web redirect.
public static let web = Self(rawValue: "web")

/// Deep link redirect.
public static let deepLink = Self(rawValue: "deep_link")
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ final class DefaultNativeAlternativePaymentServiceAdapter: NativeAlternativePaym
gatewayConfigurationId: flow.gatewayConfigurationId,
source: flow.customerTokenId,
submitData: request.submitData,
redirect: request.redirect,
localeIdentifier: request.localeIdentifier
)
let authorizationResponse = try await invoicesService.authorizeInvoice(request: authorizationRequest)
Expand All @@ -42,6 +43,7 @@ final class DefaultNativeAlternativePaymentServiceAdapter: NativeAlternativePaym
customerTokenId: flow.customerTokenId,
gatewayConfigurationId: flow.gatewayConfigurationId,
submitData: request.submitData,
redirect: request.redirect,
localeIdentifier: request.localeIdentifier
)
let tokenizationResponse = try await tokensService.tokenize(request: tokenizationRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ struct NativeAlternativePaymentServiceAdapterRequest {
init(
flow: PONativeAlternativePaymentConfiguration.Flow,
submitData: PONativeAlternativePaymentSubmitDataV2? = nil,
redirect: PONativeAlternativePaymentRedirectResultV2? = nil,
localeIdentifier: String?
) {
self.flow = flow
self.submitData = submitData
self.redirect = redirect
self.localeIdentifier = localeIdentifier
}

Expand All @@ -25,6 +27,9 @@ struct NativeAlternativePaymentServiceAdapterRequest {
/// Data to submit if any.
let submitData: PONativeAlternativePaymentSubmitDataV2?

/// Redirect result.
let redirect: PONativeAlternativePaymentRedirectResultV2?

/// Customer's locale identifier override.
let localeIdentifier: String?
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,25 @@ final class NativeAlternativePaymentDefaultInteractor:
}
let task = Task {
do {
let authenticationRequest = POAlternativePaymentAuthenticationRequest(
url: currentState.redirect.url,
callback: configuration.redirect.callback,
prefersEphemeralSession: configuration.redirect.prefersEphemeralSession
)
_ = try await alternativePaymentsService.authenticate(request: authenticationRequest)
let didOpenUrl: Bool
switch currentState.redirect.type {
case .deepLink:
didOpenUrl = await UIApplication.shared.open(currentState.redirect.url)
case .web:
let authenticationRequest = POAlternativePaymentAuthenticationRequest(
url: currentState.redirect.url,
callback: configuration.redirect.callback,
prefersEphemeralSession: configuration.redirect.prefersEphemeralSession
)
_ = try await alternativePaymentsService.authenticate(request: authenticationRequest)
didOpenUrl = true
default:
throw POFailure(errorDescription: "Unknown redirect type.", code: .Mobile.internal)
}
let response = try await serviceAdapter.continuePayment(
with: .init(
flow: configuration.flow,
redirect: currentState.redirect.confirmationRequired ? .init(success: didOpenUrl) : nil,
localeIdentifier: configuration.localization.localeOverride?.identifier
)
)
Expand Down Expand Up @@ -214,15 +224,27 @@ final class NativeAlternativePaymentDefaultInteractor:
logger.error("Attempted to handle headless redirect while not in starting state. Ignoring.")
return
}
let authenticationRequest = POAlternativePaymentAuthenticationRequest(
url: redirect.url,
callback: configuration.redirect.callback,
prefersEphemeralSession: configuration.redirect.prefersEphemeralSession
)
_ = try await alternativePaymentsService.authenticate(request: authenticationRequest)
let localeIdentifier = configuration.localization.localeOverride?.identifier
let didOpenUrl: Bool
switch redirect.type {
case .deepLink:
didOpenUrl = await UIApplication.shared.open(redirect.url)
case .web:
let authenticationRequest = POAlternativePaymentAuthenticationRequest(
url: redirect.url,
callback: configuration.redirect.callback,
prefersEphemeralSession: configuration.redirect.prefersEphemeralSession
)
_ = try await alternativePaymentsService.authenticate(request: authenticationRequest)
didOpenUrl = true
default:
throw POFailure(errorDescription: "Unknown redirect type.", code: .Mobile.internal)
}
let response = try await serviceAdapter.continuePayment(
with: .init(flow: configuration.flow, localeIdentifier: localeIdentifier)
with: .init(
flow: configuration.flow,
redirect: redirect.confirmationRequired ? .init(success: didOpenUrl) : nil,
localeIdentifier: configuration.localization.localeOverride?.identifier
)
)
try await setState(with: response)
}
Expand Down