Skip to content

Commit 2013271

Browse files
authored
Merge pull request #5799 from woocommerce/issue/5530-stripe-account-remote
[Mobile Payments] Add Stripe account remote
2 parents 1922249 + 0ce7cec commit 2013271

20 files changed

+837
-5
lines changed

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 68 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import Foundation
2+
3+
/// Mapper: Stripe Account
4+
///
5+
struct StripeAccountMapper: Mapper {
6+
7+
/// (Attempts) to convert a dictionary into an account.
8+
///
9+
func map(response: Data) throws -> StripeAccount {
10+
let decoder = JSONDecoder()
11+
12+
/// Needed for currentDeadline, which is given as a UNIX timestamp.
13+
/// Unfortunately other properties use other formats for dates, but we
14+
/// can cross that bridge when we need those decoded.
15+
decoder.dateDecodingStrategy = .secondsSince1970
16+
17+
return try decoder.decode(StripeAccountEnvelope.self, from: response).account
18+
}
19+
}
20+
21+
/// StripeAccountEnvelope Disposable Entity
22+
///
23+
/// Account endpoint returns the requested account in the `data` key. This entity
24+
/// allows us to parse it with JSONDecoder.
25+
///
26+
private struct StripeAccountEnvelope: Decodable {
27+
let account: StripeAccount
28+
29+
private enum CodingKeys: String, CodingKey {
30+
case account = "data"
31+
}
32+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/// Represent a Stripe Account Entity.
2+
///
3+
public struct StripeAccount: Decodable {
4+
public static let gatewayID = "woocommerce-stripe"
5+
6+
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+
/// Note: Using WCPayAccountStatusEnum is somewhat inappropriate here, since it ties this Stripe account concept
10+
/// to WooCommerce Payments - although in reality both extensions use the same account on the backend.
11+
/// TODO: Introduce a layer of abstraction and translate each extension's account status into a
12+
/// PaymentGatewayAccountStatusEnum or somesuch.
13+
///
14+
public let isLiveAccount: Bool
15+
/// Indicates whether the payment gateway is currently in "Test" or "Debug" mode
16+
///
17+
public let isInTestMode: Bool
18+
public let hasPendingRequirements: Bool
19+
public let hasOverdueRequirements: Bool
20+
public let currentDeadline: Date?
21+
/// An alphanumeric string set by the merchant, e.g. `MYSTORE.COM`
22+
/// See https://stripe.com/docs/statement-descriptors
23+
public let statementDescriptor: String
24+
/// A three character lowercase currency code, e.g. `usd`
25+
/// See https://stripe.com/docs/api/accounts/object#account_object-default_currency
26+
public let defaultCurrency: String
27+
public let supportedCurrencies: [String]
28+
/// A two character country code, e.g. `US`
29+
/// See https://stripe.com/docs/api/accounts/object#account_object-country
30+
public let country: String
31+
/// A boolean flag indicating if this Account is eligible for card present payments
32+
public let isCardPresentEligible: Bool
33+
34+
public init(
35+
status: WCPayAccountStatusEnum,
36+
isLiveAccount: Bool,
37+
isInTestMode: Bool,
38+
hasPendingRequirements: Bool,
39+
hasOverdueRequirements: Bool,
40+
currentDeadline: Date?,
41+
statementDescriptor: String,
42+
defaultCurrency: String,
43+
supportedCurrencies: [String],
44+
country: String,
45+
isCardPresentEligible: Bool
46+
) {
47+
self.status = status
48+
self.isLiveAccount = isLiveAccount
49+
self.isInTestMode = isInTestMode
50+
self.hasPendingRequirements = hasPendingRequirements
51+
self.hasOverdueRequirements = hasOverdueRequirements
52+
self.currentDeadline = currentDeadline
53+
self.statementDescriptor = statementDescriptor
54+
self.defaultCurrency = defaultCurrency
55+
self.supportedCurrencies = supportedCurrencies
56+
self.country = country
57+
self.isCardPresentEligible = isCardPresentEligible
58+
}
59+
60+
/// The public initializer for WCPay Account.
61+
///
62+
public init(from decoder: Decoder) throws {
63+
let container = try decoder.container(keyedBy: CodingKeys.self)
64+
let status = try container.decode(WCPayAccountStatusEnum.self, forKey: .status)
65+
let isLiveAccount = try container.decode(Bool.self, forKey: .isLive)
66+
let isInTestMode = try container.decode(Bool.self, forKey: .testMode)
67+
let hasPendingRequirements = try container.decode(Bool.self, forKey: .hasPendingRequirements)
68+
let hasOverdueRequirements = try container.decode(Bool.self, forKey: .hasOverdueRequirements)
69+
let currentDeadline = try container.decodeIfPresent(Date.self, forKey: .currentDeadline)
70+
let statementDescriptor = try container.decode(String.self, forKey: .statementDescriptor)
71+
let currencyContainer = try container.nestedContainer(keyedBy: CurrencyCodingKeys.self, forKey: .storeCurrencies)
72+
let defaultCurrency = try currencyContainer.decode(String.self, forKey: .defaultCurrency)
73+
let supportedCurrencies = try currencyContainer.decode([String].self, forKey: .supported)
74+
let country = try container.decode(String.self, forKey: .country)
75+
/// Ignore the state of the In-Person Payments Beta flag (`.cardPresentEligible`). Allow any site. See #5030
76+
/// let cardPresentEligible = try container.decodeIfPresent(Bool.self, forKey: .cardPresentEligible) ?? false
77+
78+
self.init(
79+
status: status,
80+
isLiveAccount: isLiveAccount,
81+
isInTestMode: isInTestMode,
82+
hasPendingRequirements: hasPendingRequirements,
83+
hasOverdueRequirements: hasOverdueRequirements,
84+
currentDeadline: currentDeadline,
85+
statementDescriptor: statementDescriptor,
86+
defaultCurrency: defaultCurrency,
87+
supportedCurrencies: supportedCurrencies,
88+
country: country,
89+
isCardPresentEligible: true
90+
)
91+
}
92+
}
93+
94+
public extension StripeAccount {
95+
static let noAccount = StripeAccount(
96+
status: .noAccount,
97+
isLiveAccount: false,
98+
isInTestMode: false,
99+
hasPendingRequirements: false,
100+
hasOverdueRequirements: false,
101+
currentDeadline: nil,
102+
statementDescriptor: "",
103+
defaultCurrency: "",
104+
supportedCurrencies: [],
105+
country: "",
106+
isCardPresentEligible: false
107+
)
108+
}
109+
110+
private extension StripeAccount {
111+
enum CodingKeys: String, CodingKey {
112+
case status
113+
case isLive = "is_live"
114+
case testMode = "test_mode"
115+
case hasPendingRequirements = "has_pending_requirements"
116+
case hasOverdueRequirements = "has_overdue_requirements"
117+
case currentDeadline = "current_deadline"
118+
case statementDescriptor = "statement_descriptor"
119+
case storeCurrencies = "store_currencies"
120+
case country
121+
case cardPresentEligible = "card_present_eligible"
122+
}
123+
124+
enum CurrencyCodingKeys: String, CodingKey {
125+
case defaultCurrency = "default"
126+
case supported
127+
}
128+
}

Networking/Networking/Model/WCPayAccount.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public extension WCPayAccount {
105105

106106
private extension WCPayAccount {
107107
enum CodingKeys: String, CodingKey {
108-
case status = "status"
108+
case status
109109
case isLive = "is_live"
110110
case testMode = "test_mode"
111111
case hasPendingRequirements = "has_pending_requirements"
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import Foundation
2+
3+
/// Stripe (Extension): Remote Endpoints
4+
///
5+
public class StripeRemote: Remote {
6+
/// Loads a Stripe account for a given site ID and parses the response
7+
/// - Parameters:
8+
/// - siteID: Site for which we'll fetch the Stripe account info.
9+
/// - completion: Closure to be executed upon completion.
10+
public func loadAccount(for siteID: Int64,
11+
completion: @escaping (Result<StripeAccount, Error>) -> Void) {
12+
let parameters = [AccountParameterKeys.fields: AccountParameterValues.fieldValues]
13+
14+
let request = JetpackRequest(wooApiVersion: .mark3, method: .get, siteID: siteID, path: Path.accounts, parameters: parameters)
15+
16+
let mapper = StripeAccountMapper()
17+
18+
enqueue(request, mapper: mapper, completion: completion)
19+
}
20+
21+
/// TODO loadConnectionToken(for siteID: Int64,...)
22+
23+
/// TODO captureOrderPayment(for siteID: Int64,...)
24+
25+
/// TODO fetchOrderCustomer(for siteID: Int64,...)
26+
27+
/// TODO loadDefaultReaderLocation(for siteID: Int64,...)
28+
}
29+
30+
// MARK: - Constants!
31+
//
32+
private extension StripeRemote {
33+
enum Path {
34+
static let accounts = "wc_stripe/account/summary"
35+
}
36+
37+
enum AccountParameterKeys {
38+
static let fields: String = "_fields"
39+
}
40+
41+
enum AccountParameterValues {
42+
static let fieldValues: String = """
43+
status,is_live,test_mode,has_pending_requirements,has_overdue_requirements,current_deadline,\
44+
statement_descriptor,store_currencies,country
45+
"""
46+
}
47+
}

0 commit comments

Comments
 (0)