Skip to content

Commit

Permalink
support error payload on body from checkout bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
cianbuckley committed Oct 9, 2023
1 parent bed3e67 commit 5b53b2e
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 10 deletions.
69 changes: 64 additions & 5 deletions Sources/ShopifyCheckout/CheckoutBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ extension CheckoutBridge {
enum WebEvent: Decodable {
case checkoutComplete
case checkoutCanceled
case checkoutExpired
case checkoutUnavailable
case checkoutExpired(String)
case checkoutUnavailable(String)
case unsupported(String)

enum CodingKeys: String, CodingKey {
Expand All @@ -67,7 +67,6 @@ extension CheckoutBridge {

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

let name = try container.decode(String.self, forKey: .name)

switch name {
Expand All @@ -76,11 +75,71 @@ extension CheckoutBridge {
case "close":
self = .checkoutCanceled
case "error":
// needs to support .checkoutUnavailable by parsing error payload on body
self = .checkoutExpired
let errorPayloads = try container.decode([ErrorPayload].self, forKey: .body)
let errorMessage = errorPayloads.first?.reason ?? Constants.defaultCheckoutUnavailableMsg
guard let errorGroup = errorPayloads.first?.group else {
self = .checkoutUnavailable(errorMessage)
return
}

switch errorGroup {
case ErrorGroup.internal:
self = .checkoutExpired(Constants.defaultCheckoutExpiredMsg)
default:
self = .checkoutUnavailable(errorMessage)
}
default:
self = .unsupported(name)
}
}
}
}

class ErrorPayload: Decodable {
var flowType: FlowType
var group: ErrorGroup
var type: ErrorType
var code: String?
var reason: String?

init(flowType: FlowType, group: ErrorGroup, type: ErrorType, code: String? = nil, reason: String? = nil) {
self.flowType = flowType
self.group = group
self.type = type
self.code = code
self.reason = reason
}
}

enum FlowType: String, Decodable {
case regular
case shopPay
case shopPayLogin
case checkoutDefaults
case applePay
case googlePay
case payPal
case amazonPay
case facebookPay
case shopifyInstallments
}

enum ErrorGroup: String, Decodable {
case `internal`
case violation
case checkout
case vaultedPayment
case defaults
case authentication
case unrecoverable
}

enum ErrorType: String, Decodable {
case inventory
case payment
case other
case discount
case order
case customerPersistence
case checkoutBlocking
}
5 changes: 5 additions & 0 deletions Sources/ShopifyCheckout/CheckoutError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ public enum CheckoutError: Swift.Error {
/// In event of checkoutExpired, a new checkout url will need to be generated
case checkoutExpired(message: String)
}

struct Constants {

Check failure on line 41 in Sources/ShopifyCheckout/CheckoutError.swift

View workflow job for this annotation

GitHub Actions / lint

Convenience Type Violation: Types used for hosting only static members should be implemented as a caseless enum to avoid instantiation (convenience_type)
static let defaultCheckoutExpiredMsg = "Checkout expired. Checkout needs to be reinitialised"
static let defaultCheckoutUnavailableMsg = "Checkout unavailable due to error"
}
11 changes: 7 additions & 4 deletions Sources/ShopifyCheckout/CheckoutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,12 @@ extension CheckoutView: WKScriptMessageHandler {
case .checkoutComplete:
CheckoutView.cache = nil
viewDelegate?.checkoutViewDidCompleteCheckout()
case .checkoutUnavailable:
case .checkoutUnavailable(let message):
CheckoutView.cache = nil
viewDelegate?.checkoutViewDidFailWithError(error: .checkoutUnavailable(message: "Checkout unavailable."))
viewDelegate?.checkoutViewDidFailWithError(error: .checkoutUnavailable(message: message))
case .checkoutExpired(let message):
CheckoutView.cache = nil
viewDelegate?.checkoutViewDidFailWithError(error: .checkoutExpired(message: message))
default:
()
}
Expand Down Expand Up @@ -143,9 +146,9 @@ extension CheckoutView: WKNavigationDelegate {
CheckoutView.cache = nil
switch response.statusCode {
case 404, 410:
viewDelegate?.checkoutViewDidFailWithError(error: .checkoutExpired(message: "Checkout has expired"))
viewDelegate?.checkoutViewDidFailWithError(error: .checkoutExpired(message: Constants.defaultCheckoutExpiredMsg))
case 500:
viewDelegate?.checkoutViewDidFailWithError(error: .checkoutUnavailable(message: "Checkout unavailable due to error"))
viewDelegate?.checkoutViewDidFailWithError(error: .checkoutUnavailable(message: Constants.defaultCheckoutUnavailableMsg))
default:
()
}
Expand Down
34 changes: 33 additions & 1 deletion Tests/ShopifyCheckoutTests/CheckoutBridgeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,42 @@ class CheckoutBridgeTests: XCTestCase {
}
}

func testDecodeSupportsCheckoutExpiredEvent() throws {
let mock = WKScriptMessageMock(body: """
{
"name": "error",
"body": [
{
"flowType": "regular",
"group": "internal",
"type": "other",
"code": "0",
"reason": "An error occurred"
}
]
}
""")

let result = try CheckoutBridge.decode(mock)

guard case CheckoutBridge.WebEvent.checkoutExpired = result else {
return XCTFail("expected CheckoutScriptMessage.checkoutExpired, got \(result)")
}
}

func testDecodeSupportsCheckoutUnavailableEvent() throws {
let mock = WKScriptMessageMock(body: """
{
"name": "error"
"name": "error",
"body": [
{
"flowType": "regular",
"group": "internal",
"type": "other",
"code": "0",
"reason": "An error occurred"
}
]
}
""")

Expand Down

0 comments on commit 5b53b2e

Please sign in to comment.