-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Errors and Contact Management (#6)
Clean errors and its codes and messages accordingly. New OSPMTContact struct that allows the management of the correct shipping and billing information to use on a payment request. This is required due to a limitation on OutSystems related with nullable lists. Change the OSPMTConfigurationDelegate to OSPMTConfigurationModel, in order to comply with the new OutSystems structure. Clean code (privatise local methods and make OSPMTPayment's delegate property weak, in order to avoid possible retain cycles).
- Loading branch information
1 parent
0232cc1
commit 78504d7
Showing
13 changed files
with
363 additions
and
439 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,33 @@ | ||
/// All plugin errors that can be thrown | ||
enum OSPMTError: Int, CustomNSError, LocalizedError { | ||
case invalidConfiguration = 1 | ||
case walletNotAvailable = 2 | ||
case paymentNotAvailable = 3 | ||
case setupPaymentNotAvailable = 4 | ||
case invalidDecodeDetails = 6 | ||
case paymentTriggerPresentationFailed = 7 | ||
case paymentTriggerNotCompleted = 8 | ||
case walletNotAvailable = 3 | ||
case paymentNotAvailable = 5 | ||
case setupPaymentNotAvailable = 6 | ||
case invalidDecodeDetails = 8 | ||
case invalidEncodeScope = 9 | ||
case paymentTriggerPresentationFailed = 10 | ||
case paymentCancelled = 11 | ||
|
||
/// Textual description | ||
var errorDescription: String? { | ||
switch self { | ||
case .invalidConfiguration: | ||
return "An invalid configuration was provided to the plugin." | ||
return "Couldn't obtain the payment's informations from the configurations file." | ||
case .walletNotAvailable: | ||
return "Wallet is not available on this device." | ||
return "The Apple Pay is not available in the device." | ||
case .paymentNotAvailable: | ||
return "Payment is not available on this device." | ||
return "There is no payment method configured." | ||
case .setupPaymentNotAvailable: | ||
return "Payment through the configured networks and capabilities is not available on this device." | ||
return "There are no valid payment cards for the supported networks and/or capabilities." | ||
case .invalidDecodeDetails: | ||
return "Couldn't decode payment details." | ||
return "Couldn't decode the payment details." | ||
case .invalidEncodeScope: | ||
return "Couldn't encode the payment scope." | ||
case .paymentTriggerPresentationFailed: | ||
return "Couldn't present the Apple Pay screen." | ||
case .paymentTriggerNotCompleted: | ||
return "Couldn't complete the payment trigger process." | ||
case .invalidEncodeScope: | ||
return "Couldn't encode payment scope." | ||
case .paymentCancelled: | ||
return "Payment was cancelled by the user." | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import PassKit | ||
|
||
/// Protocol that contains all properties needed to configure a payment service. | ||
class OSPMTConfigurationModel: Encodable { | ||
// MARK: Merchant Information | ||
var merchantID: String? | ||
var merchantName: String? | ||
var merchantCountryCode: String? | ||
|
||
// MARK: Payment Information | ||
var paymentAllowedNetworks: [String]? | ||
var paymentSupportedCapabilities: [String]? | ||
var paymentSupportedCardCountries: [String]? | ||
|
||
// MARK: Shipping Information | ||
var shippingSupportedContacts: [String]? | ||
|
||
// MARK: Billing Information | ||
var billingSupportedContacts: [String]? | ||
|
||
/// Keys used to encode and decode the model. | ||
enum CodingKeys: String, CodingKey { | ||
case merchantID | ||
case merchantName | ||
case merchantCountryCode | ||
case paymentAllowedNetworks | ||
case paymentSupportedCapabilities | ||
case paymentSupportedCardCountries | ||
case shippingSupportedContacts | ||
case billingSupportedContacts | ||
} | ||
|
||
/// Constructor method. | ||
/// - Parameters: | ||
/// - merchantID: Merchant ID configured | ||
/// - merchantName: Merchant Name configured | ||
/// - merchantCountryCode: Merchant Country Code configured | ||
/// - paymentAllowedNetworks: Payment Allowed Networks configured | ||
/// - paymentSupportedCapabilities: Payment Supported Capabilities configured | ||
/// - paymentSupportedCardCountries: Payment Support Card Countries configured | ||
/// - shippingSupportedContacts: Shipping Supported Contacts configured | ||
/// - billingSupportedContacts: Billing Supported Contacts configured | ||
init(merchantID: String?, merchantName: String?, merchantCountryCode: String?, paymentAllowedNetworks: [String]?, paymentSupportedCapabilities: [String]?, paymentSupportedCardCountries: [String]?, shippingSupportedContacts: [String]?, billingSupportedContacts: [String]?) { | ||
self.merchantID = merchantID | ||
self.merchantName = merchantName | ||
self.merchantCountryCode = merchantCountryCode | ||
self.paymentAllowedNetworks = paymentAllowedNetworks | ||
self.paymentSupportedCapabilities = paymentSupportedCapabilities | ||
self.paymentSupportedCardCountries = paymentSupportedCardCountries | ||
self.shippingSupportedContacts = shippingSupportedContacts | ||
self.billingSupportedContacts = billingSupportedContacts | ||
} | ||
|
||
/// Encodes this value into the given encoder. | ||
/// | ||
/// If the value fails to encode anything, `encoder` will encode an empty | ||
/// keyed container in its place. | ||
/// | ||
/// This function throws an error if any values are invalid for the given | ||
/// encoder's format. | ||
/// | ||
/// - Parameter encoder: The encoder to write data to. | ||
func encode(to encoder: Encoder) throws { | ||
var container = encoder.container(keyedBy: CodingKeys.self) | ||
|
||
// MARK: Merchant Information | ||
try container.encodeIfPresent(merchantID, forKey: .merchantID) | ||
try container.encodeIfPresent(merchantName, forKey: .merchantName) | ||
try container.encodeIfPresent(merchantCountryCode, forKey: .merchantCountryCode) | ||
|
||
// MARK: Payment Information | ||
try container.encodeIfPresent(paymentAllowedNetworks, forKey: .paymentAllowedNetworks) | ||
try container.encodeIfPresent(paymentSupportedCapabilities, forKey: .paymentSupportedCapabilities) | ||
try container.encodeIfPresent(paymentSupportedCardCountries, forKey: .paymentSupportedCardCountries) | ||
|
||
// MARK: Shipping Information | ||
try container.encodeIfPresent(shippingSupportedContacts, forKey: .shippingSupportedContacts) | ||
|
||
// MARK: Billing Information | ||
try container.encodeIfPresent(billingSupportedContacts, forKey: .billingSupportedContacts) | ||
} | ||
} | ||
|
||
typealias OSPMTConfiguration = [String: Any] | ||
|
||
/// Manages all configuration properties required to enable Apple Pay in the plugin. | ||
class OSPMTApplePayConfiguration: OSPMTConfigurationModel { | ||
struct ConfigurationKeys { | ||
static let merchantID = "ApplePayMerchantID" | ||
static let merchantName = "ApplePayMerchantName" | ||
static let merchantCountryCode = "ApplePayMerchantCountryCode" | ||
|
||
static let paymentAllowedNetworks = "ApplePayPaymentAllowedNetworks" | ||
static let paymentSupportedCapabilities = "ApplePayPaymentSupportedCapabilities" | ||
static let paymentSupportedCardCountries = "ApplePayPaymentSupportedCardCountries" | ||
|
||
static let shippingSupportedContacts = "ApplePayShippingSupportedContacts" | ||
|
||
static let billingSupportedContacts = "ApplePayBillingSupportedContacts" | ||
} | ||
|
||
/// Constructor method. | ||
/// - Parameter source: Source class contaning the configuration. | ||
convenience init(source: OSPMTConfiguration) { | ||
// MARK: Merchant Information | ||
let merchantID = Self.getRequiredProperty( | ||
ofType: String.self, forSource: source, andKey: ConfigurationKeys.merchantID | ||
) | ||
let merchantName = Self.getRequiredProperty( | ||
ofType: String.self, forSource: source, andKey: ConfigurationKeys.merchantName | ||
) | ||
let merchantCountryCode = Self.getRequiredProperty( | ||
ofType: String.self, forSource: source, andKey: ConfigurationKeys.merchantCountryCode | ||
) | ||
|
||
// MARK: Payment Information | ||
let paymentAllowedNetworks = Self.getRequiredProperty( | ||
ofType: [String].self, forSource: source, andKey: ConfigurationKeys.paymentAllowedNetworks | ||
) | ||
let paymentSupportedCapabilities = Self.getRequiredProperty( | ||
ofType: [String].self, forSource: source, andKey: ConfigurationKeys.paymentSupportedCapabilities | ||
) | ||
let paymentSupportedCardCountries = Self.getProperty( | ||
ofType: [String].self, forSource: source, andKey: ConfigurationKeys.paymentSupportedCardCountries | ||
) | ||
|
||
// MARK: Shipping Information | ||
let shippingSupportedContacts = Self.getProperty( | ||
ofType: [String].self, forSource: source, andKey: ConfigurationKeys.shippingSupportedContacts | ||
) | ||
|
||
// MARK: Billing Information | ||
let billingSupportedContacts = Self.getProperty( | ||
ofType: [String].self, forSource: source, andKey: ConfigurationKeys.billingSupportedContacts | ||
) | ||
|
||
self.init( | ||
merchantID: merchantID, | ||
merchantName: merchantName, | ||
merchantCountryCode: merchantCountryCode, | ||
paymentAllowedNetworks: paymentAllowedNetworks, | ||
paymentSupportedCapabilities: paymentSupportedCapabilities, | ||
paymentSupportedCardCountries: paymentSupportedCardCountries, | ||
shippingSupportedContacts: shippingSupportedContacts, | ||
billingSupportedContacts: billingSupportedContacts | ||
) | ||
} | ||
} | ||
|
||
private extension OSPMTApplePayConfiguration { | ||
/// Fetches the parameter property, if it exists. | ||
/// - Parameters: | ||
/// - type: Type of variable to return. | ||
/// - key: Property key to search from. | ||
/// - isRequired: Indicates if the property is mandatory or not. | ||
/// - Returns: The configuration property, if it exists. | ||
static func getProperty<T: Collection>(ofType type: T.Type, forSource source: OSPMTConfiguration, andKey key: String, isRequired: Bool = false) -> T? { | ||
let result = source[key] as? T | ||
return !isRequired || result?.isEmpty == false ? result : nil | ||
} | ||
|
||
/// An acelerator for `getProperty(ofType:forKey:isRequired:)`, that should be used for mandatory properties. | ||
/// - Parameters: | ||
/// - type: Type of variable to return. | ||
/// - key: Property key to search from. | ||
/// - Returns: The configuration property, if it exists. | ||
static func getRequiredProperty<T: Collection>(ofType type: T.Type, forSource source: OSPMTConfiguration, andKey key: String) -> T? { | ||
self.getProperty(ofType: type, forSource: source, andKey: key, isRequired: true) | ||
} | ||
} | ||
|
||
extension OSPMTApplePayConfiguration { | ||
var supportedNetworks: [PKPaymentNetwork]? { | ||
guard let paymentAllowedNetworks = self.paymentAllowedNetworks else { return nil } | ||
let result = paymentAllowedNetworks.compactMap(PKPaymentNetwork.convert(from:)) | ||
|
||
return !result.isEmpty ? result : nil | ||
} | ||
|
||
var merchantCapabilities: PKMerchantCapability? { | ||
guard let paymentSupportedCapabilities = self.paymentSupportedCapabilities else { return nil } | ||
|
||
var result: PKMerchantCapability = [] | ||
result = paymentSupportedCapabilities.reduce(into: result) { partialResult, capability in | ||
let merchantCapability = PKMerchantCapability.convert(from: capability) | ||
if let merchantCapability = merchantCapability { | ||
partialResult.insert(merchantCapability) | ||
} | ||
} | ||
|
||
return !result.isEmpty ? result : nil | ||
} | ||
|
||
var supportedCountries: Set<String>? { | ||
guard let paymentSupportedCardCountries = self.paymentSupportedCardCountries, !paymentSupportedCardCountries.isEmpty else { return nil } | ||
return Set(paymentSupportedCardCountries) | ||
} | ||
} |
Oops, something went wrong.