Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[V7] Add Encodable protocol for Local Payments. #1468

Merged
merged 4 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 2 additions & 61 deletions Sources/BraintreeLocalPayment/BTLocalPaymentClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ import BraintreeDataCollector
// MARK: - Private Methods

private func start(request: BTLocalPaymentRequest, configuration: BTConfiguration) {
let requestParameters = buildRequestDictionary(with: request)
apiClient.post("v1/local_payments/create", parameters: requestParameters) { body, _, error in
apiClient.post("v1/local_payments/create", parameters: request) { body, _, error in
if let error {
self.notifyFailure(with: error, completion: self.merchantCompletion)
return
Expand Down Expand Up @@ -207,65 +206,7 @@ import BraintreeDataCollector
}
}
}

private func buildRequestDictionary(with request: BTLocalPaymentRequest) -> [String: Any] {
var requestParameters: [String: Any] = [
"amount": request.amount,
"funding_source": request.paymentType,
"intent": "sale",
"currency_iso_code": request.currencyCode,
"return_url": "\(BTCoreConstants.callbackURLScheme)://x-callback-url/braintree/local-payment/success",
"cancel_url": "\(BTCoreConstants.callbackURLScheme)://x-callback-url/braintree/local-payment/cancel"
]

if let countryCode = request.paymentTypeCountryCode {
requestParameters["payment_type_country_code"] = countryCode
}

if let address = request.address {
requestParameters["line1"] = address.streetAddress
requestParameters["line2"] = address.extendedAddress
requestParameters["city"] = address.locality
requestParameters["state"] = address.region
requestParameters["postal_code"] = address.postalCode
requestParameters["country_code"] = address.countryCodeAlpha2
}

if let givenName = request.givenName {
requestParameters["first_name"] = givenName
}

if let surname = request.surname {
requestParameters["last_name"] = surname
}

if let email = request.email {
requestParameters["payer_email"] = email
}

if let phone = request.phone {
requestParameters["phone"] = phone
}

if let merchantAccountID = request.merchantAccountID {
requestParameters["merchant_account_id"] = merchantAccountID
}

if let bic = request.bic {
requestParameters["bic"] = bic
}

var experienceProfile: [String: Any] = ["no_shipping": !request.isShippingAddressRequired]

if let displayName = request.displayName {
experienceProfile["brand_name"] = displayName
}

requestParameters["experience_profile"] = experienceProfile

return requestParameters
}


private func onPayment(with url: URL?, error: Error?) {
if let error {
notifyFailure(with: error, completion: merchantCompletion)
Expand Down
89 changes: 86 additions & 3 deletions Sources/BraintreeLocalPayment/BTLocalPaymentRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import BraintreeDataCollector
#endif

/// Used to initialize a local payment flow
@objcMembers public class BTLocalPaymentRequest: NSObject {
/// The POST body for v1/local_payments/create
@objcMembers public class BTLocalPaymentRequest: NSObject, Encodable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take it or leave it but we could also consider separating the encodable portion out into a new file, we've done that in most places where we've made this change, like here for example: https://github.com/braintree/braintree_ios/blob/main/Sources/BraintreeSEPADirectDebit/SEPADebitAccountsRequest.swift

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, updated: 3e00b0e what do you think??


// MARK: - Public Properties

public weak var localPaymentFlowDelegate: BTLocalPaymentRequestDelegate?

// MARK: - Internal Properties

Expand All @@ -26,12 +31,20 @@ import BraintreeDataCollector
let phone: String?
let isShippingAddressRequired: Bool
let bic: String?

public weak var localPaymentFlowDelegate: BTLocalPaymentRequestDelegate?

var paymentID: String?
var correlationID: String?

// MARK: - Private Properties

private let intent: String
private let returnURL: String
private let cancelURL: String

private lazy var experienceProfile: ExperienceProfile = {
.init(noShipping: !isShippingAddressRequired, brandName: displayName)
}()

// MARK: - Initializer

/// Creates a LocalPaymentRequest
Expand Down Expand Up @@ -80,5 +93,75 @@ import BraintreeDataCollector
self.phone = phone
self.isShippingAddressRequired = isShippingAddressRequired
self.bic = bic
self.intent = "sale"
self.returnURL = BTCoreConstants.callbackURLScheme + "://x-callback-url/braintree/local-payment/success"
self.cancelURL = BTCoreConstants.callbackURLScheme + "://x-callback-url/braintree/local-payment/cancel"
}

enum CodingKeys: String, CodingKey {
case paymentType = "funding_source"
case amount
case currencyCode = "currency_iso_code"
case paymentTypeCountryCode = "payment_type_country_code"
case merchantAccountID = "merchant_account_id"
case email = "payer_email"
Copy link
Contributor Author

@richherrera richherrera Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reviewing Android, I noticed we’re not sending the same parameters, such as email, phone, givenName, surname, bic, and merchantAccountId. I verified with the LPMS team the keys needed to send these parameters. It seems the gateway supports both camel case and snake case, web and Android use camel case.

case givenName = "first_name"
case surname = "last_name"
case phone
case bic
case intent
case returnURL = "return_url"
case cancelURL = "cancel_url"
case experienceProfile = "experience_profile"

// Address keys
case streetAddress = "line1"
case extendedAddress = "line2"
case locality = "city"
case countryCodeAlpha2 = "country_code"
case postalCode = "postal_code"
case region = "state"
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(paymentType, forKey: .paymentType)
try container.encode(amount, forKey: .amount)
try container.encode(currencyCode, forKey: .currencyCode)
try container.encodeIfPresent(paymentTypeCountryCode, forKey: .paymentTypeCountryCode)
try container.encodeIfPresent(merchantAccountID, forKey: .merchantAccountID)
try container.encodeIfPresent(email, forKey: .email)
try container.encodeIfPresent(givenName, forKey: .givenName)
try container.encodeIfPresent(surname, forKey: .surname)
try container.encodeIfPresent(phone, forKey: .phone)
try container.encodeIfPresent(bic, forKey: .bic)
try container.encodeIfPresent(intent, forKey: .intent)
try container.encodeIfPresent(returnURL, forKey: .returnURL)
try container.encodeIfPresent(cancelURL, forKey: .cancelURL)
try container.encodeIfPresent(experienceProfile, forKey: .experienceProfile)

if let address {
try container.encodeIfPresent(address.streetAddress, forKey: .streetAddress)
try container.encodeIfPresent(address.extendedAddress, forKey: .extendedAddress)
try container.encodeIfPresent(address.locality, forKey: .locality)
try container.encodeIfPresent(address.countryCodeAlpha2, forKey: .countryCodeAlpha2)
try container.encodeIfPresent(address.postalCode, forKey: .postalCode)
try container.encodeIfPresent(address.region, forKey: .region)
}
}
}

extension BTLocalPaymentRequest {

struct ExperienceProfile: Encodable {

let noShipping: Bool
let brandName: String?

// swiftlint:disable nesting
enum CodingKeys: String, CodingKey {
case noShipping = "no_shipping"
case brandName = "brand_name"
}
}
}
Loading