From e418b334018e3be1aa8dbb27a27205b0a6703326 Mon Sep 17 00:00:00 2001 From: Mark Murray Date: Thu, 18 Apr 2024 16:09:21 +0100 Subject: [PATCH] Fire checkoutDidComplete with orderId if available on the URL --- .../CheckoutBridge.swift | 2 +- .../CheckoutCompletedEvent.swift | 64 ++++++++++--------- .../CheckoutCompletedEventDecoder.swift | 2 +- .../CheckoutWebView.swift | 36 ++++++++++- .../CheckoutWebViewController.swift | 3 +- .../FallbackViewController.swift | 40 +----------- 6 files changed, 72 insertions(+), 75 deletions(-) diff --git a/Sources/ShopifyCheckoutSheetKit/CheckoutBridge.swift b/Sources/ShopifyCheckoutSheetKit/CheckoutBridge.swift index 1407ed67..ddda8e0b 100644 --- a/Sources/ShopifyCheckoutSheetKit/CheckoutBridge.swift +++ b/Sources/ShopifyCheckoutSheetKit/CheckoutBridge.swift @@ -119,7 +119,7 @@ extension CheckoutBridge { self = .checkoutComplete(event: checkoutCompletedEvent) } catch { logger.logError(error, "Error decoding CheckoutCompletedEvent") - self = .checkoutComplete(event: emptyCheckoutCompletedEvent) + self = .checkoutComplete(event: createEmptyCheckoutCompletedEvent()) } case "error": let errorDecoder = CheckoutErrorEventDecoder() diff --git a/Sources/ShopifyCheckoutSheetKit/CheckoutCompletedEvent.swift b/Sources/ShopifyCheckoutSheetKit/CheckoutCompletedEvent.swift index 02bcd9c4..40f0ba1c 100644 --- a/Sources/ShopifyCheckoutSheetKit/CheckoutCompletedEvent.swift +++ b/Sources/ShopifyCheckoutSheetKit/CheckoutCompletedEvent.swift @@ -117,36 +117,38 @@ extension CheckoutCompletedEvent { // swiftlint:enable identifier_name -internal let emptyCheckoutCompletedEvent = CheckoutCompletedEvent( - orderDetails: CheckoutCompletedEvent.OrderDetails( - billingAddress: CheckoutCompletedEvent.Address( - address1: nil, - address2: nil, - city: nil, - countryCode: nil, - firstName: nil, - lastName: nil, - name: nil, - phone: nil, - postalCode: nil, - referenceId: nil, - zoneCode: nil - ), - cart: CheckoutCompletedEvent.CartInfo( - lines: [], - price: CheckoutCompletedEvent.Price( - discounts: nil, - shipping: CheckoutCompletedEvent.Money(amount: nil, currencyCode: nil), - subtotal: CheckoutCompletedEvent.Money(amount: nil, currencyCode: nil), - taxes: CheckoutCompletedEvent.Money(amount: nil, currencyCode: nil), - total: CheckoutCompletedEvent.Money(amount: nil, currencyCode: nil) +internal func createEmptyCheckoutCompletedEvent(id: String? = "") -> CheckoutCompletedEvent { + return CheckoutCompletedEvent( + orderDetails: CheckoutCompletedEvent.OrderDetails( + billingAddress: CheckoutCompletedEvent.Address( + address1: nil, + address2: nil, + city: nil, + countryCode: nil, + firstName: nil, + lastName: nil, + name: nil, + phone: nil, + postalCode: nil, + referenceId: nil, + zoneCode: nil ), - token: "" - ), - deliveries: nil, - email: nil, - id: "", - paymentMethods: nil, - phone: nil + cart: CheckoutCompletedEvent.CartInfo( + lines: [], + price: CheckoutCompletedEvent.Price( + discounts: nil, + shipping: CheckoutCompletedEvent.Money(amount: nil, currencyCode: nil), + subtotal: CheckoutCompletedEvent.Money(amount: nil, currencyCode: nil), + taxes: CheckoutCompletedEvent.Money(amount: nil, currencyCode: nil), + total: CheckoutCompletedEvent.Money(amount: nil, currencyCode: nil) + ), + token: "" + ), + deliveries: nil, + email: nil, + id: id ?? "", + paymentMethods: nil, + phone: nil + ) ) -) +} diff --git a/Sources/ShopifyCheckoutSheetKit/CheckoutCompletedEventDecoder.swift b/Sources/ShopifyCheckoutSheetKit/CheckoutCompletedEventDecoder.swift index 9d0efc94..551092eb 100644 --- a/Sources/ShopifyCheckoutSheetKit/CheckoutCompletedEventDecoder.swift +++ b/Sources/ShopifyCheckoutSheetKit/CheckoutCompletedEventDecoder.swift @@ -28,7 +28,7 @@ class CheckoutCompletedEventDecoder { let messageBody = try container.decode(String.self, forKey: .body) guard let data = messageBody.data(using: .utf8) else { - return emptyCheckoutCompletedEvent + return createEmptyCheckoutCompletedEvent() } return try JSONDecoder().decode(CheckoutCompletedEvent.self, from: data) diff --git a/Sources/ShopifyCheckoutSheetKit/CheckoutWebView.swift b/Sources/ShopifyCheckoutSheetKit/CheckoutWebView.swift index 226a7760..189c931e 100644 --- a/Sources/ShopifyCheckoutSheetKit/CheckoutWebView.swift +++ b/Sources/ShopifyCheckoutSheetKit/CheckoutWebView.swift @@ -34,7 +34,7 @@ protocol CheckoutWebViewDelegate: AnyObject { func checkoutViewDidEmitWebPixelEvent(event: PixelEvent) } -private let retryQueryParameter = "checkoutSheetRetry" +private let retryQueryParameter = "checkout_sheet_retry" private let deprecatedReasonHeader = "X-Shopify-API-Deprecated-Reason" private let checkoutLiquidNotSupportedReason = "checkout_liquid_not_supported" @@ -295,6 +295,15 @@ extension CheckoutWebView: WKNavigationDelegate { forStatusCode: statusCode ) + if let url = response.url { + let isRecoveryConfirmationPage = isRecovery && statusCode == 200 && isConfirmation(url: url) + + if isRecoveryConfirmationPage { + let event = createEmptyCheckoutCompletedEvent(id: getOrderIdFromQuery(url: url)) + viewDelegate?.checkoutViewDidCompleteCheckout(event: event) + } + } + guard isCheckout(url: response.url) else { return .allow } @@ -405,6 +414,31 @@ extension CheckoutWebView: WKNavigationDelegate { return self.url == url } + private func isConfirmation(url: URL) -> Bool { + do { + let urlString = url.absoluteString + let regex = try NSRegularExpression(pattern: "thank[-_]you", options: .caseInsensitive) + let range = NSRange(urlString.startIndex..., in: urlString) + return regex.firstMatch(in: urlString, options: [], range: range) != nil + } catch { + return false + } + } + + private func getOrderIdFromQuery(url: URL) -> String? { + guard let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) else { + return nil + } + + let queryItems = urlComponents.queryItems ?? [] + for item in queryItems { + if item.name == "orderId" { + return item.value + } + } + + return nil + } } extension CheckoutWebView { diff --git a/Sources/ShopifyCheckoutSheetKit/CheckoutWebViewController.swift b/Sources/ShopifyCheckoutSheetKit/CheckoutWebViewController.swift index 6a3157b6..8d09995e 100644 --- a/Sources/ShopifyCheckoutSheetKit/CheckoutWebViewController.swift +++ b/Sources/ShopifyCheckoutSheetKit/CheckoutWebViewController.swift @@ -33,8 +33,7 @@ class CheckoutWebViewController: UIViewController, UIAdaptivePresentationControl progressObserver?.invalidate() checkoutView.removeFromSuperview() - let prod = URL(string: "https://checkout-sdk.myshopify.com/checkouts/cn/Z2NwLWV1cm9wZS13ZXN0MTowMUhWUDg0QkRKOUo5SkdWV0hFWDFHUzFNMw") - self.checkoutView = CheckoutWebView.for(checkout: prod!, recovery: true) + self.checkoutView = CheckoutWebView.for(checkout: url, recovery: true) checkoutView.translatesAutoresizingMaskIntoConstraints = false checkoutView.scrollView.contentInsetAdjustmentBehavior = .never checkoutView.viewDelegate = self diff --git a/Sources/ShopifyCheckoutSheetKit/FallbackViewController.swift b/Sources/ShopifyCheckoutSheetKit/FallbackViewController.swift index a722284d..2714b16b 100644 --- a/Sources/ShopifyCheckoutSheetKit/FallbackViewController.swift +++ b/Sources/ShopifyCheckoutSheetKit/FallbackViewController.swift @@ -25,43 +25,6 @@ import Foundation import SafariServices import WebKit -class FallbackViewController: NSObject { - private let checkoutURL: URL - - var checkoutDelegate: CheckoutDelegate? - - init(checkout url: URL, delegate: CheckoutDelegate?) { - self.checkoutURL = url - self.checkoutDelegate = delegate - } - - func createSafariViewController() -> SFSafariViewController { - let config = SFSafariViewController.Configuration() - config.barCollapsingEnabled = true - config.entersReaderIfAvailable = false - - let url = URL(string: "https://checkout-sdk.myshopify.com/checkouts/cn/Z2NwLWV1cm9wZS13ZXN0MTowMUhWUDg0QkRKOUo5SkdWV0hFWDFHUzFNMw")! - let fallback = SFSafariViewController(url: url, configuration: config) - fallback.delegate = self - fallback.presentationController?.delegate = self - return fallback - } -} - -/// Safari delegate methods - -extension FallbackViewController: SFSafariViewControllerDelegate, UIAdaptivePresentationControllerDelegate { - func safariViewControllerDidFinish(_ controller: SFSafariViewController) { - checkoutDelegate?.checkoutDidCancel() - } - - func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) { - if !didLoadSuccessfully { - checkoutDelegate?.checkoutDidFail(error: .checkoutUnavailable(message: "Checkout is unavailable", code: .httpError(statusCode: 500), recoverable: false)) - } - } -} - class FallbackWebViewController: UIViewController { internal let checkoutView: FallbackWebView internal let checkoutURL: URL @@ -131,8 +94,7 @@ class FallbackWebView: WKWebView { } func load(checkout url: URL) { - let prod = URL(string: "https://checkout-sdk.myshopify.com/checkouts/cn/Z2NwLWV1cm9wZS13ZXN0MTowMUhWUDg0QkRKOUo5SkdWV0hFWDFHUzFNMw") - load(URLRequest(url: prod!)) + load(URLRequest(url: url)) } required init?(coder: NSCoder) {