diff --git a/AdyenCheckout/Setup/Checkout.swift b/AdyenCheckout/Setup/Checkout.swift index 991d28d21f..2c9bbb7e90 100644 --- a/AdyenCheckout/Setup/Checkout.swift +++ b/AdyenCheckout/Setup/Checkout.swift @@ -5,6 +5,9 @@ // import Adyen +#if canImport(AdyenActions) + import AdyenActions +#endif #if canImport(AdyenSession) import AdyenSession #endif @@ -71,6 +74,43 @@ public enum Checkout { provider: CheckoutProvider.default ) } + + #if canImport(AdyenActions) + /// Passes a URL to the SDK to resume an active redirect action after the shopper returns from a browser or external app. + /// + /// Call this from every entry point where your app receives incoming URLs: + /// + /// **UIKit - AppDelegate:** + /// ```swift + /// func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { + /// Checkout.handleReturn(url: url) + /// return true + /// } + /// ``` + /// + /// **UIKit - SceneDelegate:** + /// ```swift + /// func scene(_ scene: UIScene, openURLContexts contexts: Set) { + /// guard let url = contexts.first?.url else { return } + /// Checkout.handleReturn(url: url) + /// } + /// ``` + /// + /// **SwiftUI:** + /// ```swift + /// ContentView() + /// .onOpenURL { url in Checkout.handleReturn(url: url) } + /// ``` + /// + /// It is safe to pass all incoming URLs; any URL not belonging to an active checkout redirect is ignored. + /// + /// - Parameter url: The URL received when the shopper returns to the app. + /// - Returns: `true` if the URL was handled by an active checkout redirect; `false` otherwise. + @discardableResult + public static func handleReturn(url: URL) -> Bool { + RedirectComponent.applicationDidOpen(from: url) + } + #endif } internal extension Checkout { diff --git a/Tests/UnitTests/AdyenCheckout/CheckoutTests.swift b/Tests/UnitTests/AdyenCheckout/CheckoutTests.swift index 6eebfe98d5..682c42da3e 100644 --- a/Tests/UnitTests/AdyenCheckout/CheckoutTests.swift +++ b/Tests/UnitTests/AdyenCheckout/CheckoutTests.swift @@ -718,6 +718,36 @@ final class CheckoutTests: XCTestCase { XCTAssertFalse(onFailureCalled) } + // MARK: - handleReturn + + func test_handleReturn_withRegisteredHandler_returnsTrue() throws { + // Given + let url = try XCTUnwrap(URL(string: "adyen://redirect?payload=test")) + let handlerExpectation = expectation(description: "URL handler called") + RedirectListener.registerForURL { receivedURL in + XCTAssertEqual(receivedURL, url) + handlerExpectation.fulfill() + } + + // When + let result = Checkout.handleReturn(url: url) + + // Then + XCTAssertTrue(result) + waitForExpectations(timeout: 1) + } + + func test_handleReturn_withNoRegisteredHandler_returnsFalse() throws { + // Given + let url = try XCTUnwrap(URL(string: "adyen://redirect?payload=test")) + + // When + let result = Checkout.handleReturn(url: url) + + // Then + XCTAssertFalse(result) + } + private func makeSessionCheckoutCore( session: SessionProtocol, callbackStore: SessionCheckoutCallbackStore = SessionCheckoutCallbackStore() diff --git a/docs/redirect/index.html b/docs/redirect/index.html deleted file mode 100644 index cf503ba9a8..0000000000 --- a/docs/redirect/index.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/v6/README.md b/docs/v6/README.md index 22ad3a4d9a..e52aa09164 100644 --- a/docs/v6/README.md +++ b/docs/v6/README.md @@ -220,21 +220,31 @@ Use app-bundle `.strings` or `.xcstrings` files when you want to add a fully new ## Redirect return URLs -If you need to build redirect details from a return URL yourself, use the public `RedirectDetails` API: +Pass incoming URLs to the SDK so active redirect actions can resume after the shopper returns from a browser or external app. +**UIKit - AppDelegate:** ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { - guard let redirectDetails = try? RedirectDetails(returnURL: url) else { - return false - } - - let actionData = ActionComponentData(details: redirectDetails, paymentData: nil) - // Submit actionData to your /payments/details handling. + Checkout.handleReturn(url: url) return true } ``` -If your backend expects `paymentData`, use the value from your previous `/payments` response instead of `nil`. +**UIKit - SceneDelegate:** +```swift +func scene(_ scene: UIScene, openURLContexts contexts: Set) { + guard let url = contexts.first?.url else { return } + Checkout.handleReturn(url: url) +} +``` + +**SwiftUI:** +```swift +ContentView() + .onOpenURL { url in Checkout.handleReturn(url: url) } +``` + +It is safe to pass all incoming URLs; any URL not belonging to an active checkout redirect is ignored. ## Next steps