Skip to content

Commit b22d3bc

Browse files
committed
Merge branch 'develop' into feature/stripe-sdk-2
2 parents 607cbc3 + 98f731a commit b22d3bc

File tree

49 files changed

+635
-151
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+635
-151
lines changed

Hardware/Hardware/CardReader/CardReaderServiceError.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ public enum UnderlyingError: Error {
186186
/// The SDK will attempt to auto-disconnect for you and you should instruct your user to reconnect it.
187187
case readerSessionExpired
188188

189+
/// The underlying request returned an API error.
190+
case processorAPIError
191+
189192
/// Catch-all error case. Indicates there is something wrong with the
190193
/// internal state of the CardReaderService.
191194
case internalServiceError
@@ -322,6 +325,9 @@ updating the application or using a different reader
322325
case .readerSessionExpired:
323326
return NSLocalizedString("The card reader session has expired - please disconnect and reconnect the card reader and then try again",
324327
comment: "Error message when the card reader session has timed out.")
328+
case .processorAPIError:
329+
return NSLocalizedString("The payment can not be processed by the payment processor.",
330+
comment: "Error message when the payment can not be processed (i.e. order amount is below the minimum amount allowed.)")
325331
case .internalServiceError:
326332
return NSLocalizedString("Sorry, this payment couldn’t be processed",
327333
comment: "Error message when the card reader service experiences an unexpected internal service error.")

Hardware/Hardware/CardReader/StripeCardReader/UnderlyingError+Stripe.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ extension UnderlyingError {
7979
self = .requestTimedOut
8080
case ErrorCode.Code.sessionExpired.rawValue:
8181
self = .readerSessionExpired
82+
case ErrorCode.Code.stripeAPIError.rawValue:
83+
self = .processorAPIError
8284
default:
8385
self = .internalServiceError
8486
}

Hardware/HardwareTests/ErrorCodesTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ final class CardReaderServiceErrorTests: XCTestCase {
154154
XCTAssertEqual(.readerSessionExpired, domainError(stripeCode: 9060))
155155
}
156156

157+
func test_stripe_error_api_maps_to_stripeAPI() {
158+
XCTAssertEqual(.processorAPIError, domainError(stripeCode: 9020))
159+
}
160+
157161
func test_stripe_catch_all_error() {
158162
// Any error code not mapped to an specific error will be
159163
// mapped to `internalServiceError`

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@
142142
31104E142630DDA700587C1E /* wcpay-account-wrong-json.json in Resources */ = {isa = PBXBuildFile; fileRef = 31104E132630DDA700587C1E /* wcpay-account-wrong-json.json */; };
143143
311976E02602BD4B006AC56C /* SitePluginsMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311976DF2602BD4B006AC56C /* SitePluginsMapperTests.swift */; };
144144
314703082670222500EF253A /* PaymentGatewayAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 314703072670222500EF253A /* PaymentGatewayAccount.swift */; };
145+
3158A49F2729F3F600C3CFA8 /* wcpay-account-live-live.json in Resources */ = {isa = PBXBuildFile; fileRef = 3158A49E2729F3F600C3CFA8 /* wcpay-account-live-live.json */; };
146+
3158A4A12729F40F00C3CFA8 /* wcpay-account-live-test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3158A4A02729F40F00C3CFA8 /* wcpay-account-live-test.json */; };
147+
3158A4A32729F42500C3CFA8 /* wcpay-account-dev-test.json in Resources */ = {isa = PBXBuildFile; fileRef = 3158A4A22729F42500C3CFA8 /* wcpay-account-dev-test.json */; };
145148
3158FE6026129ADD00E566B9 /* wcpay-account-none.json in Resources */ = {isa = PBXBuildFile; fileRef = 3158FE5F26129ADD00E566B9 /* wcpay-account-none.json */; };
146149
3158FE6426129B1300E566B9 /* wcpay-account-complete.json in Resources */ = {isa = PBXBuildFile; fileRef = 3158FE6326129B1300E566B9 /* wcpay-account-complete.json */; };
147150
3158FE6826129CE200E566B9 /* wcpay-account-rejected-fraud.json in Resources */ = {isa = PBXBuildFile; fileRef = 3158FE6726129CE200E566B9 /* wcpay-account-rejected-fraud.json */; };
@@ -709,6 +712,9 @@
709712
31104E132630DDA700587C1E /* wcpay-account-wrong-json.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-account-wrong-json.json"; sourceTree = "<group>"; };
710713
311976DF2602BD4B006AC56C /* SitePluginsMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitePluginsMapperTests.swift; sourceTree = "<group>"; };
711714
314703072670222500EF253A /* PaymentGatewayAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentGatewayAccount.swift; sourceTree = "<group>"; };
715+
3158A49E2729F3F600C3CFA8 /* wcpay-account-live-live.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-account-live-live.json"; sourceTree = "<group>"; };
716+
3158A4A02729F40F00C3CFA8 /* wcpay-account-live-test.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-account-live-test.json"; sourceTree = "<group>"; };
717+
3158A4A22729F42500C3CFA8 /* wcpay-account-dev-test.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-account-dev-test.json"; sourceTree = "<group>"; };
712718
3158FE5F26129ADD00E566B9 /* wcpay-account-none.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-account-none.json"; sourceTree = "<group>"; };
713719
3158FE6326129B1300E566B9 /* wcpay-account-complete.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-account-complete.json"; sourceTree = "<group>"; };
714720
3158FE6726129CE200E566B9 /* wcpay-account-rejected-fraud.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-account-rejected-fraud.json"; sourceTree = "<group>"; };
@@ -1719,6 +1725,9 @@
17191725
318E8FD826C324D900F519D7 /* wcpay-customer-error.json */,
17201726
3158FE5F26129ADD00E566B9 /* wcpay-account-none.json */,
17211727
3158FE6326129B1300E566B9 /* wcpay-account-complete.json */,
1728+
3158A49E2729F3F600C3CFA8 /* wcpay-account-live-live.json */,
1729+
3158A4A22729F42500C3CFA8 /* wcpay-account-dev-test.json */,
1730+
3158A4A02729F40F00C3CFA8 /* wcpay-account-live-test.json */,
17221731
31B8D6B326583662008E3DB2 /* wcpay-account-not-eligible.json */,
17231732
3158FE6726129CE200E566B9 /* wcpay-account-rejected-fraud.json */,
17241733
3158FE6B26129D2E00E566B9 /* wcpay-account-rejected-terms-of-service.json */,
@@ -2099,6 +2108,7 @@
20992108
31054734262E36AB00C5C02B /* wcpay-payment-intent-error.json in Resources */,
21002109
45ED4F12239E8C57004F1BE3 /* taxes-classes.json in Resources */,
21012110
B5A2417B217F98FC00595DEF /* broken-notifications.json in Resources */,
2111+
3158A4A32729F42500C3CFA8 /* wcpay-account-dev-test.json in Resources */,
21022112
31104E142630DDA700587C1E /* wcpay-account-wrong-json.json in Resources */,
21032113
3158FE7C26129E2100E566B9 /* wcpay-account-restricted-pending.json in Resources */,
21042114
D823D91422377EE600C90817 /* shipment_tracking_providers.json in Resources */,
@@ -2158,6 +2168,7 @@
21582168
74C947862193A6C70024CB60 /* comment-moderate-unapproved.json in Resources */,
21592169
3105472C262E303400C5C02B /* wcpay-payment-intent-unknown-status.json in Resources */,
21602170
B559EBAA20A0B5CD00836CD4 /* orders-load-all.json in Resources */,
2171+
3158A4A12729F40F00C3CFA8 /* wcpay-account-live-test.json in Resources */,
21612172
74C8F06620EEB76400B6EDC9 /* order-notes.json in Resources */,
21622173
7426CA1321AF34A3004E9FFC /* site-api.json in Resources */,
21632174
453305ED2459E1AA00264E50 /* site-post.json in Resources */,
@@ -2224,6 +2235,7 @@
22242235
74ABA1CB213F19FE00FFAD30 /* top-performers-week.json in Resources */,
22252236
456930AD2652AF00009ED69D /* shipping-label-carriers-and-rates-success.json in Resources */,
22262237
B524194721AC643900D6FC0A /* device-settings.json in Resources */,
2238+
3158A49F2729F3F600C3CFA8 /* wcpay-account-live-live.json in Resources */,
22272239
318E8FD926C324D900F519D7 /* wcpay-customer-error.json in Resources */,
22282240
CEF88DAB233E911A00BED485 /* order-fully-refunded.json in Resources */,
22292241
02698CFA24C188E9005337C4 /* product-variations-load-all-alternative-types.json in Resources */,

Networking/Networking/Model/Product/ProductCategory.swift

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ public struct ProductCategory: Codable, Equatable, GeneratedFakeable {
2727
/// Public initializer for ProductCategory.
2828
///
2929
public init(from decoder: Decoder) throws {
30-
guard let siteID = decoder.userInfo[.siteID] as? Int64 else {
30+
let container = try decoder.container(keyedBy: CodingKeys.self)
31+
32+
guard let siteID = ProductCategory.siteID(from: decoder, container: container) else {
3133
throw ProductCategoryDecodingError.missingSiteID
3234
}
3335

34-
let container = try decoder.container(keyedBy: CodingKeys.self)
35-
3636
let categoryID = try container.decode(Int64.self, forKey: .categoryID)
3737
// Some product endpoints don't include the parent category ID
3838
let parentID = container.failsafeDecodeIfPresent(Int64.self, forKey: .parentID) ?? 0
@@ -48,6 +48,8 @@ public struct ProductCategory: Codable, Equatable, GeneratedFakeable {
4848
try container.encode(categoryID, forKey: .categoryID)
4949
try container.encode(name, forKey: .name)
5050
try container.encode(slug, forKey: .slug)
51+
try container.encode(siteID, forKey: .siteID)
52+
try container.encode(parentID, forKey: .parentID)
5153
}
5254
}
5355

@@ -56,13 +58,30 @@ public struct ProductCategory: Codable, Equatable, GeneratedFakeable {
5658
///
5759
private extension ProductCategory {
5860
enum CodingKeys: String, CodingKey {
61+
case siteID = "siteID"
5962
case categoryID = "id"
6063
case name = "name"
6164
case slug = "slug"
6265
case parentID = "parent"
6366
}
6467
}
6568

69+
private extension ProductCategory {
70+
/// Provides the siteID, that can be found as a encoded value or in the Decoder user info
71+
///
72+
private static func siteID(from decoder: Decoder, container: KeyedDecodingContainer<ProductCategory.CodingKeys>) -> Int64? {
73+
var siteID: Int64?
74+
75+
if let userInfoSiteID = decoder.userInfo[.siteID] as? Int64 {
76+
siteID = userInfoSiteID
77+
} else if let decodedSiteID = try? container.decode(Int64.self, forKey: .siteID) {
78+
siteID = decodedSiteID
79+
}
80+
81+
return siteID
82+
}
83+
}
84+
6685

6786
// MARK: - Comparable Conformance
6887
//

Networking/Networking/Model/WCPayAccount.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ public struct WCPayAccount: Decodable {
44
public static let gatewayID = "woocommerce-payments"
55

66
public let status: WCPayAccountStatusEnum
7+
/// Indicates whether the payment gateway is a live account that can accept actual payments, or just a test/developer account.
8+
/// Not to be confused with "test mode" which is a separate concept (see `isInTestMode`)
9+
///
10+
public let isLiveAccount: Bool
11+
/// Indicates whether the payment gateway is currently in "Test" or "Debug" mode
12+
///
13+
public let isInTestMode: Bool
714
public let hasPendingRequirements: Bool
815
public let hasOverdueRequirements: Bool
916
public let currentDeadline: Date?
@@ -22,6 +29,8 @@ public struct WCPayAccount: Decodable {
2229

2330
public init(
2431
status: WCPayAccountStatusEnum,
32+
isLiveAccount: Bool,
33+
isInTestMode: Bool,
2534
hasPendingRequirements: Bool,
2635
hasOverdueRequirements: Bool,
2736
currentDeadline: Date?,
@@ -32,6 +41,8 @@ public struct WCPayAccount: Decodable {
3241
isCardPresentEligible: Bool
3342
) {
3443
self.status = status
44+
self.isLiveAccount = isLiveAccount
45+
self.isInTestMode = isInTestMode
3546
self.hasPendingRequirements = hasPendingRequirements
3647
self.hasOverdueRequirements = hasOverdueRequirements
3748
self.currentDeadline = currentDeadline
@@ -47,6 +58,8 @@ public struct WCPayAccount: Decodable {
4758
public init(from decoder: Decoder) throws {
4859
let container = try decoder.container(keyedBy: CodingKeys.self)
4960
let status = try container.decode(WCPayAccountStatusEnum.self, forKey: .status)
61+
let isLiveAccount = try container.decode(Bool.self, forKey: .isLive)
62+
let isInTestMode = try container.decode(Bool.self, forKey: .testMode)
5063
let hasPendingRequirements = try container.decode(Bool.self, forKey: .hasPendingRequirements)
5164
let hasOverdueRequirements = try container.decode(Bool.self, forKey: .hasOverdueRequirements)
5265
let currentDeadline = try container.decodeIfPresent(Date.self, forKey: .currentDeadline)
@@ -60,6 +73,8 @@ public struct WCPayAccount: Decodable {
6073

6174
self.init(
6275
status: status,
76+
isLiveAccount: isLiveAccount,
77+
isInTestMode: isInTestMode,
6378
hasPendingRequirements: hasPendingRequirements,
6479
hasOverdueRequirements: hasOverdueRequirements,
6580
currentDeadline: currentDeadline,
@@ -75,6 +90,8 @@ public struct WCPayAccount: Decodable {
7590
public extension WCPayAccount {
7691
static let noAccount = WCPayAccount(
7792
status: .noAccount,
93+
isLiveAccount: false,
94+
isInTestMode: false,
7895
hasPendingRequirements: false,
7996
hasOverdueRequirements: false,
8097
currentDeadline: nil,
@@ -89,17 +106,19 @@ public extension WCPayAccount {
89106
private extension WCPayAccount {
90107
enum CodingKeys: String, CodingKey {
91108
case status = "status"
109+
case isLive = "is_live"
110+
case testMode = "test_mode"
92111
case hasPendingRequirements = "has_pending_requirements"
93112
case hasOverdueRequirements = "has_overdue_requirements"
94113
case currentDeadline = "current_deadline"
95114
case statementDescriptor = "statement_descriptor"
96115
case storeCurrencies = "store_currencies"
97-
case country = "country"
116+
case country
98117
case cardPresentEligible = "card_present_eligible"
99118
}
100119

101120
enum CurrencyCodingKeys: String, CodingKey {
102121
case defaultCurrency = "default"
103-
case supported = "supported"
122+
case supported
104123
}
105124
}

Networking/Networking/Remote/OrdersRemote.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -245,16 +245,16 @@ public extension OrdersRemote {
245245

246246
enum ParameterValues {
247247
// Same as singleOrderFieldValues except we exclude the line_items and shipping fields
248-
static let listFieldValues: String = """
249-
id,parent_id,number,status,currency,customer_id,customer_note,date_created_gmt,date_modified_gmt,date_paid_gmt,\
250-
discount_total,discount_tax,shipping_total,shipping_tax,total,total_tax,payment_method,payment_method_title,\
251-
billing,coupon_lines,shipping_lines,refunds,fee_lines
252-
"""
253-
static let singleOrderFieldValues: String = """
254-
id,parent_id,number,status,currency,customer_id,customer_note,date_created_gmt,date_modified_gmt,date_paid_gmt,\
255-
discount_total,discount_tax,shipping_total,shipping_tax,total,total_tax,payment_method,payment_method_title,shipping,\
256-
billing,coupon_lines,shipping_lines,refunds,fee_lines,line_items
257-
"""
248+
static let listFieldValues: String = commonOrderFieldValues.joined(separator: ",")
249+
static let singleOrderFieldValues: String = (commonOrderFieldValues + singleOrderExtraFieldValues).joined(separator: ",")
250+
private static let commonOrderFieldValues = [
251+
"id", "parent_id", "number", "status", "currency", "customer_id", "customer_note", "date_created_gmt", "date_modified_gmt", "date_paid_gmt",
252+
"discount_total", "discount_tax", "shipping_total", "shipping_tax", "total", "total_tax", "payment_method", "payment_method_title",
253+
"billing", "coupon_lines", "shipping_lines", "refunds", "fee_lines"
254+
]
255+
private static let singleOrderExtraFieldValues = [
256+
"line_items", "shipping"
257+
]
258258
}
259259

260260
/// Order fields supported for update

Networking/Networking/Remote/ProductsRemote.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public protocol ProductsRemoteProtocol {
1616
stockStatus: ProductStockStatus?,
1717
productStatus: ProductStatus?,
1818
productType: ProductType?,
19+
productCategory: ProductCategory?,
1920
orderBy: ProductsRemote.OrderKey,
2021
order: ProductsRemote.Order,
2122
excludedProductIDs: [Int64],
@@ -104,16 +105,19 @@ public final class ProductsRemote: Remote, ProductsRemoteProtocol {
104105
stockStatus: ProductStockStatus? = nil,
105106
productStatus: ProductStatus? = nil,
106107
productType: ProductType? = nil,
108+
productCategory: ProductCategory? = nil,
107109
orderBy: OrderKey = .name,
108110
order: Order = .ascending,
109111
excludedProductIDs: [Int64] = [],
110112
completion: @escaping (Result<[Product], Error>) -> Void) {
111113
let stringOfExcludedProductIDs = excludedProductIDs.map { String($0) }
112114
.joined(separator: ",")
115+
113116
let filterParameters = [
114117
ParameterKey.stockStatus: stockStatus?.rawValue ?? "",
115118
ParameterKey.productStatus: productStatus?.rawValue ?? "",
116119
ParameterKey.productType: productType?.rawValue ?? "",
120+
ParameterKey.category: filterProductCategoryParemeterValue(from: productCategory),
117121
ParameterKey.exclude: stringOfExcludedProductIDs
118122
].filter({ $0.value.isEmpty == false })
119123

@@ -298,6 +302,7 @@ public extension ProductsRemote {
298302
static let productStatus: String = "status"
299303
static let productType: String = "type"
300304
static let stockStatus: String = "stock_status"
305+
static let category: String = "category"
301306
static let fields: String = "_fields"
302307
}
303308

@@ -306,6 +311,18 @@ public extension ProductsRemote {
306311
}
307312
}
308313

314+
private extension ProductsRemote {
315+
/// Returns the category Id in string format, or empty string if the product category is nil
316+
///
317+
func filterProductCategoryParemeterValue(from productCategory: ProductCategory?) -> String {
318+
guard let productCategory = productCategory else {
319+
return ""
320+
}
321+
322+
return String(productCategory.categoryID)
323+
}
324+
}
325+
309326
private extension ProductsRemote.OrderKey {
310327
var value: String {
311328
switch self {

Networking/NetworkingTests/Remote/OrdersRemoteTests.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ final class OrdersRemoteTests: XCTestCase {
3636
network.removeAllSimulatedResponses()
3737
}
3838

39+
/// Verifies that the parameter `_fields` in single order and order list requests do not contain whitespace.
40+
///
41+
func test_order_fields_parameter_values_do_not_contain_whitespace() throws {
42+
// When
43+
let orderListFieldsValue = OrdersRemote.ParameterValues.listFieldValues
44+
let orderFieldsValue = OrdersRemote.ParameterValues.singleOrderFieldValues
45+
46+
// Then
47+
XCTAssertFalse(orderListFieldsValue.contains(" "))
48+
XCTAssertFalse(orderFieldsValue.contains(" "))
49+
}
50+
3951
// MARK: - Load All Orders Tests
4052

4153
/// Verifies that loadAllOrders properly parses the `orders-load-all` sample response.

0 commit comments

Comments
 (0)