-
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.
feat: Set Details and Trigger Payment
Configure the missing payment details and, by mixing it with the configuration info, trigger the payment request.
- Loading branch information
1 parent
a39c32d
commit a58f9cb
Showing
30 changed files
with
1,684 additions
and
47 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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,18 @@ | ||
import PassKit | ||
|
||
extension PKContactField { | ||
static func convert(from text: String) -> PKContactField? { | ||
switch text.lowercased() { | ||
case "email": | ||
return .emailAddress | ||
case "name": | ||
return .name | ||
case "phone": | ||
return .phoneNumber | ||
case "postaladdress": | ||
return .postalAddress | ||
default: | ||
return nil | ||
} | ||
} | ||
} |
4 changes: 0 additions & 4 deletions
4
...ary+OSPMTWalletAvailabilityDelegate.swift → ...ib/Extensions/PKPassLibrary+Adapter.swift
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,79 @@ | ||
import PassKit | ||
|
||
extension PKPayment { | ||
func createScopeModel() -> OSPMTScopeModel? { | ||
var result: [String: Any] = [OSPMTScopeModel.CodingKeys.paymentData.rawValue: self.createTokenDataData()] | ||
if let shippingContact = self.shippingContact { | ||
result[OSPMTScopeModel.CodingKeys.shippingInfo.rawValue] = self.createContactInfoData(for: shippingContact) | ||
} | ||
|
||
guard | ||
let scopeData = try? JSONSerialization.data(withJSONObject: result), | ||
let scopeModel = try? JSONDecoder().decode(OSPMTScopeModel.self, from: scopeData) | ||
else { return nil } | ||
return scopeModel | ||
} | ||
|
||
func createTokenDataData() -> [String: Any] { | ||
var result: [String: Any] = [ | ||
OSPMTDataModel.CodingKeys.tokenData.rawValue: self.createTokenData(for: self.token.paymentData) | ||
] | ||
if let billingContact = self.billingContact { | ||
result[OSPMTDataModel.CodingKeys.billingInfo.rawValue] = self.createContactInfoData(for: billingContact) | ||
} | ||
if let paymentMethodName = self.token.paymentMethod.displayName { | ||
let cardInfo = paymentMethodName.components(separatedBy: " ") | ||
if let cardNetwork = cardInfo.first, let cardDetails = cardInfo.last { | ||
result[OSPMTDataModel.CodingKeys.cardDetails.rawValue] = cardDetails | ||
result[OSPMTDataModel.CodingKeys.cardNetwork.rawValue] = cardNetwork | ||
} | ||
} | ||
|
||
return result | ||
} | ||
|
||
func createTokenData(for paymentData: Data) -> [String: Any] { | ||
var result: [String: Any] = [OSPMTTokenInfoModel.CodingKeys.type.rawValue: "Payment Service Provider Name"] | ||
|
||
if let token = String(data: paymentData, encoding: .utf8) { | ||
result[OSPMTTokenInfoModel.CodingKeys.token.rawValue] = token | ||
} | ||
|
||
return result | ||
} | ||
|
||
func createContactInfoData(for contact: PKContact) -> [String: Any] { | ||
var result = [String: Any]() | ||
if let address = contact.postalAddress { | ||
result[OSPMTContactInfoModel.CodingKeys.address.rawValue] = self.createAddressData(for: address) | ||
} | ||
if let phoneNumber = contact.phoneNumber { | ||
result[OSPMTContactInfoModel.CodingKeys.phoneNumber.rawValue] = phoneNumber.stringValue | ||
} | ||
if let name = contact.name, let givenName = name.givenName, let familyName = name.familyName { | ||
result[OSPMTContactInfoModel.CodingKeys.name.rawValue] = "\(givenName) \(familyName)" | ||
} | ||
if let email = contact.emailAddress { | ||
result[OSPMTContactInfoModel.CodingKeys.email.rawValue] = email | ||
} | ||
|
||
return result | ||
} | ||
|
||
func createAddressData(for postalAddress: CNPostalAddress) -> [String: Any] { | ||
var result: [String: Any] = [ | ||
OSPMTAddressModel.CodingKeys.postalCode.rawValue: postalAddress.postalCode, | ||
OSPMTAddressModel.CodingKeys.fullAddress.rawValue: postalAddress.street, | ||
OSPMTAddressModel.CodingKeys.countryCode.rawValue: postalAddress.isoCountryCode, | ||
OSPMTAddressModel.CodingKeys.city.rawValue: postalAddress.city | ||
] | ||
if !postalAddress.subAdministrativeArea.isEmpty { | ||
result[OSPMTAddressModel.CodingKeys.administrativeArea.rawValue] = postalAddress.subAdministrativeArea | ||
} | ||
if !postalAddress.state.isEmpty { | ||
result[OSPMTAddressModel.CodingKeys.state.rawValue] = postalAddress.state | ||
} | ||
|
||
return result | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
OSPaymentsLib/Extensions/PKPaymentAuthorizationController+Adapter.swift
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,44 @@ | ||
import PassKit | ||
|
||
extension PKPaymentAuthorizationController: OSPMTApplePaySetupAvailabilityDelegate { | ||
static func isPaymentAvailable() -> Bool { | ||
Self.canMakePayments() | ||
} | ||
|
||
static func isPaymentAvailable(using networks: [PKPaymentNetwork], and merchantCapabilities: PKMerchantCapability) -> Bool { | ||
Self.canMakePayments(usingNetworks: networks, capabilities: merchantCapabilities) | ||
} | ||
} | ||
|
||
extension PKPaymentAuthorizationController: OSPMTApplePayRequestTriggerDelegate { | ||
static func createRequestTriggerBehaviour(for detailsModel: OSPMTDetailsModel, andDelegate delegate: OSPMTApplePayRequestBehaviour?) -> Result<OSPMTApplePayRequestTriggerDelegate, OSPMTError> { | ||
guard | ||
let delegate = delegate, | ||
let merchantIdentifier = delegate.configuration.merchantID, | ||
let countryCode = delegate.configuration.merchantCountryCode, | ||
let merchantCapabilities = delegate.configuration.merchantCapabilities, | ||
let paymentSummaryItems = delegate.getPaymentSummaryItems(for: detailsModel), | ||
let requiredBillingContactFields = detailsModel.billingContactArray ?? delegate.configuration.billingSupportedContacts, | ||
let requiredShippingContactFields = detailsModel.shippingContactArray ?? delegate.configuration.shippingSupportedContacts, | ||
let supportedNetworks = delegate.configuration.supportedNetworks | ||
else { return .failure(.invalidConfiguration) } | ||
|
||
let paymentRequest = PKPaymentRequest() | ||
paymentRequest.merchantIdentifier = merchantIdentifier | ||
paymentRequest.countryCode = countryCode | ||
paymentRequest.currencyCode = detailsModel.currency | ||
paymentRequest.merchantCapabilities = merchantCapabilities | ||
paymentRequest.paymentSummaryItems = paymentSummaryItems | ||
paymentRequest.requiredBillingContactFields = delegate.getContactFields(for: requiredBillingContactFields) | ||
paymentRequest.requiredShippingContactFields = delegate.getContactFields(for: requiredShippingContactFields) | ||
paymentRequest.supportedNetworks = supportedNetworks | ||
|
||
let paymentAuthorizationController = PKPaymentAuthorizationController(paymentRequest: paymentRequest) | ||
paymentAuthorizationController.delegate = delegate | ||
return .success(paymentAuthorizationController) | ||
} | ||
|
||
func triggerPayment(_ completion: @escaping OSPMTRequestTriggerCompletion) { | ||
self.present(completion: completion) | ||
} | ||
} |
19 changes: 0 additions & 19 deletions
19
...mentsLib/Extensions/PKPaymentAuthorizationController+OSPMTSetupAvailabilityDelegate.swift
This file was deleted.
Oops, something went wrong.
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,49 @@ | ||
struct OSPMTAddressModel: Codable, Equatable { | ||
let postalCode: String | ||
let fullAddress: String | ||
let countryCode: String | ||
let city: String | ||
let administrativeArea: String? | ||
let state: String? | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case postalCode, fullAddress, countryCode, city, administrativeArea, state | ||
} | ||
|
||
init(postalCode: String, fullAddress: String, countryCode: String, city: String, administrativeArea: String? = nil, state: String? = nil) { | ||
self.postalCode = postalCode | ||
self.fullAddress = fullAddress | ||
self.countryCode = countryCode | ||
self.city = city | ||
self.administrativeArea = administrativeArea | ||
self.state = state | ||
} | ||
|
||
init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
let postalCode = try container.decode(String.self, forKey: .postalCode) | ||
let fullAddress = try container.decode(String.self, forKey: .fullAddress) | ||
let countryCode = try container.decode(String.self, forKey: .countryCode) | ||
let city = try container.decode(String.self, forKey: .city) | ||
let administrativeArea = try container.decodeIfPresent(String.self, forKey: .administrativeArea) | ||
let state = try container.decodeIfPresent(String.self, forKey: .state) | ||
self.init( | ||
postalCode: postalCode, | ||
fullAddress: fullAddress, | ||
countryCode: countryCode, | ||
city: city, | ||
administrativeArea: administrativeArea, | ||
state: state | ||
) | ||
} | ||
|
||
func encode(to encoder: Encoder) throws { | ||
var container = encoder.container(keyedBy: CodingKeys.self) | ||
try container.encode(postalCode, forKey: .postalCode) | ||
try container.encode(fullAddress, forKey: .fullAddress) | ||
try container.encode(countryCode, forKey: .countryCode) | ||
try container.encode(city, forKey: .city) | ||
try container.encodeIfPresent(administrativeArea, forKey: .administrativeArea) | ||
try container.encodeIfPresent(state, forKey: .state) | ||
} | ||
} |
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,40 @@ | ||
struct OSPMTContactInfoModel: Codable { | ||
let address: OSPMTAddressModel? | ||
let phoneNumber: String? | ||
let name: String? | ||
let email: String? | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case address, phoneNumber, name, email | ||
} | ||
|
||
init(address: OSPMTAddressModel? = nil, phoneNumber: String? = nil, name: String? = nil, email: String? = nil) { | ||
self.address = address | ||
self.phoneNumber = phoneNumber | ||
self.name = name | ||
self.email = email | ||
} | ||
|
||
init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
let address = try container.decodeIfPresent(OSPMTAddressModel.self, forKey: .address) | ||
let phoneNumber = try container.decodeIfPresent(String.self, forKey: .phoneNumber) | ||
let name = try container.decodeIfPresent(String.self, forKey: .name) | ||
let email = try container.decodeIfPresent(String.self, forKey: .email) | ||
self.init(address: address, phoneNumber: phoneNumber, name: name, email: email) | ||
} | ||
|
||
func encode(to encoder: Encoder) throws { | ||
var container = encoder.container(keyedBy: CodingKeys.self) | ||
try container.encodeIfPresent(address, forKey: .address) | ||
try container.encodeIfPresent(phoneNumber, forKey: .phoneNumber) | ||
try container.encodeIfPresent(name, forKey: .name) | ||
try container.encodeIfPresent(email, forKey: .email) | ||
} | ||
} | ||
|
||
extension OSPMTContactInfoModel: Equatable { | ||
static func == (lhs: OSPMTContactInfoModel, rhs: OSPMTContactInfoModel) -> Bool { | ||
lhs.address == rhs.address && lhs.phoneNumber == rhs.phoneNumber && lhs.name == rhs.name && lhs.email == rhs.email | ||
} | ||
} |
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,43 @@ | ||
struct OSPMTDataModel: Codable { | ||
let billingInfo: OSPMTContactInfoModel | ||
let cardDetails: String | ||
let cardNetwork: String | ||
let tokenData: OSPMTTokenInfoModel | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case billingInfo, cardDetails, cardNetwork, tokenData | ||
} | ||
|
||
init(billingInfo: OSPMTContactInfoModel, cardDetails: String, cardNetwork: String, tokenData: OSPMTTokenInfoModel) { | ||
self.billingInfo = billingInfo | ||
self.cardDetails = cardDetails | ||
self.cardNetwork = cardNetwork | ||
self.tokenData = tokenData | ||
} | ||
|
||
init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
let billingInfo = try container.decode(OSPMTContactInfoModel.self, forKey: .billingInfo) | ||
let cardDetails = try container.decode(String.self, forKey: .cardDetails) | ||
let cardNetwork = try container.decode(String.self, forKey: .cardNetwork) | ||
let tokenData = try container.decode(OSPMTTokenInfoModel.self, forKey: .tokenData) | ||
self.init(billingInfo: billingInfo, cardDetails: cardDetails, cardNetwork: cardNetwork, tokenData: tokenData) | ||
} | ||
|
||
func encode(to encoder: Encoder) throws { | ||
var container = encoder.container(keyedBy: CodingKeys.self) | ||
try container.encode(billingInfo, forKey: .billingInfo) | ||
try container.encode(cardDetails, forKey: .cardDetails) | ||
try container.encode(cardNetwork, forKey: .cardNetwork) | ||
try container.encode(tokenData, forKey: .tokenData) | ||
} | ||
} | ||
|
||
extension OSPMTDataModel: Equatable { | ||
static func == (lhs: OSPMTDataModel, rhs: OSPMTDataModel) -> Bool { | ||
lhs.billingInfo == rhs.billingInfo | ||
&& lhs.cardDetails == rhs.cardDetails | ||
&& lhs.cardNetwork == rhs.cardNetwork | ||
&& lhs.tokenData == rhs.tokenData | ||
} | ||
} |
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,53 @@ | ||
import PassKit | ||
|
||
enum OSPMTStatus: String, Codable { | ||
case final | ||
case pending | ||
} | ||
|
||
struct OSPMTDetailsModel: Codable { | ||
let amount: Decimal | ||
let currency: String | ||
let status: OSPMTStatus | ||
let shippingContactArray: [String]? | ||
let billingContactArray: [String]? | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case amount | ||
case currency | ||
case status | ||
case shippingContactArray = "shippingContacts" | ||
case billingContactArray = "billingContacts" | ||
} | ||
|
||
init(amount: Decimal, currency: String, status: OSPMTStatus, shippingContactArray: [String]? = nil, billingContactArray: [String]? = nil) { | ||
self.amount = amount | ||
self.currency = currency | ||
self.status = status | ||
self.shippingContactArray = shippingContactArray | ||
self.billingContactArray = billingContactArray | ||
} | ||
|
||
init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
let amount = try container.decode(Decimal.self, forKey: .amount) | ||
let currency = try container.decode(String.self, forKey: .currency) | ||
let status = try container.decode(OSPMTStatus.self, forKey: .status) | ||
let shippingContactArray = try container.decodeIfPresent([String].self, forKey: .shippingContactArray) | ||
let billingContactArray = try container.decodeIfPresent([String].self, forKey: .billingContactArray) | ||
self.init( | ||
amount: amount, currency: currency, status: status, shippingContactArray: shippingContactArray, billingContactArray: billingContactArray | ||
) | ||
} | ||
} | ||
|
||
// MARK: Apple Pay extension | ||
extension OSPMTDetailsModel { | ||
var paymentAmount: NSDecimalNumber { | ||
NSDecimalNumber(decimal: self.amount) | ||
} | ||
|
||
var paymentSummaryItemType: PKPaymentSummaryItemType { | ||
self.status == .final ? .final : .pending | ||
} | ||
} |
Oops, something went wrong.