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

Support CBC in native Link #4693

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,11 @@ private extension LinkSettings.FundingSource {

struct UpdatePaymentDetailsParams {
enum DetailsType {
case card(expiryDate: CardExpiryDate? = nil, billingDetails: STPPaymentMethodBillingDetails? = nil)
case card(
expiryDate: CardExpiryDate? = nil,
billingDetails: STPPaymentMethodBillingDetails? = nil,
preferredNetwork: String? = nil
)
// updating bank not supported
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,15 @@ extension ConsumerPaymentDetails.Details {
let expiryYear: Int
let expiryMonth: Int
let brand: String
let networks: [String]
let last4: String
let checks: CardChecks?

private enum CodingKeys: String, CodingKey {
case expiryYear = "expYear"
case expiryMonth = "expMonth"
case brand
case networks
case last4
case checks
}
Expand All @@ -149,11 +151,13 @@ extension ConsumerPaymentDetails.Details {
init(expiryYear: Int,
expiryMonth: Int,
brand: String,
networks: [String],
last4: String,
checks: CardChecks?) {
self.expiryYear = expiryYear
self.expiryMonth = expiryMonth
self.brand = brand
self.networks = networks
self.last4 = last4
self.checks = checks
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ extension STPAPIClient {
"request_surface": "ios_payment_element",
]

if let details = updateParams.details, case .card(let expiryDate, let billingDetails) = details {
if let details = updateParams.details, case .card(let expiryDate, let billingDetails, let preferredNetwork) = details {
if let expiryDate {
parameters["exp_month"] = expiryDate.month
parameters["exp_year"] = expiryDate.year
Expand All @@ -402,6 +402,10 @@ extension STPAPIClient {
// This email address needs to be lowercase or the API will reject it
parameters["billing_email_address"] = billingEmailAddress.lowercased()
}

if let preferredNetwork {
parameters["preferred_network"] = preferredNetwork
}
}

if let isDefault = updateParams.isDefault {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,11 @@ extension PayWithLinkViewController {
// "Can't unset payment details when it's not the default", so send nil instead of false
let updateParams = UpdatePaymentDetailsParams(
isDefault: params.setAsDefault ? true : nil,
details: .card(expiryDate: params.expiryDate, billingDetails: params.billingDetails)
details: .card(
expiryDate: params.expiryDate,
billingDetails: params.billingDetails,
preferredNetwork: params.preferredNetwork
)
)

linkAccount.updatePaymentDetails(id: paymentMethod.stripeID, updateParams: updateParams) { [weak self] result in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ final class LinkCardEditElement: Element {
let cvc: String?
let billingDetails: STPPaymentMethodBillingDetails
let setAsDefault: Bool
let preferredNetwork: String?
}

var view: UIView {
Expand Down Expand Up @@ -72,11 +73,14 @@ final class LinkCardEditElement: Element {
billingDetails.nonnil_address.state = billingAddressSection?.state?.rawData
billingDetails.nonnil_address.postalCode = billingAddressSection?.postalCode?.text

let preferredNetwork = cardBrandDropdownElement?.element.selectedItem.rawData

return Params(
expiryDate: expiryDate,
cvc: useCVCPlaceholder ? nil : cvcElement.text,
billingDetails: billingDetails,
setAsDefault: checkboxElement.checkboxButton.isSelected
setAsDefault: checkboxElement.checkboxButton.isSelected,
preferredNetwork: preferredNetwork
)
}

Expand Down Expand Up @@ -116,13 +120,49 @@ final class LinkCardEditElement: Element {
theme: theme)
}()

private lazy var cardBrandDropdownElement: PaymentMethodElementWrapper<DropdownFieldElement>? = {
guard let cardBrands = paymentMethod.cardDetails?.availableNetworks, cardBrands.count > 1 else {
return nil
}

let cardBrandDropdown = DropdownFieldElement.makeCardBrandDropdown(
cardBrands: Set(cardBrands),
disallowedCardBrands: [
// We will add brands from card brand filtering here
],
theme: theme,
includePlaceholder: false
)

if let selectedBrand = paymentMethod.cardDetails?.cardBrand {
let index = cardBrandDropdown.items.firstIndex { item in
item.rawData == STPCardBrandUtilities.apiValue(from: selectedBrand)
}

if let index {
cardBrandDropdown.selectedIndex = Int(index)
}
}

return PaymentMethodElementWrapper<DropdownFieldElement>(cardBrandDropdown) { field, params in
let cardBrand = cardBrands[field.selectedIndex]
let preferredNetworkAPIValue = STPCardBrandUtilities.apiValue(from: cardBrand)
params.paymentMethodParams.card?.networks = .init(preferred: preferredNetworkAPIValue)
return params
}
}()

private lazy var panElement: TextFieldElement = {
let panElement = TextFieldElement(
configuration: PANConfiguration(paymentMethod: paymentMethod),
theme: theme
let isCoBranded = cardBrandDropdownElement != nil

let panElementConfig = TextFieldElement.LastFourConfiguration(
lastFour: paymentMethod.cardDetails?.last4 ?? "",
editConfiguration: isCoBranded ? .readOnlyWithoutDisabledAppearance : .readOnly,
cardBrand: paymentMethod.cardDetails?.cardBrand,
cardBrandDropDown: cardBrandDropdownElement?.element
)
panElement.view.isUserInteractionEnabled = false
return panElement

return panElementConfig.makeElement(theme: configuration.appearance.asElementsTheme)
}()

private lazy var cvcElement: TextFieldElement = {
Expand Down Expand Up @@ -170,7 +210,7 @@ final class LinkCardEditElement: Element {
private lazy var cardSection: SectionElement = {
let allElements: [Element?] = [
nameElement,
panElement,
panElement, SectionElement.HiddenElement(cardBrandDropdownElement),
SectionElement.MultiElementRow([expiryDateElement, cvcElement], theme: theme),
]
let elements = allElements.compactMap { $0 }
Expand Down Expand Up @@ -224,25 +264,13 @@ extension LinkCardEditElement: ElementDelegate {

}

private extension LinkCardEditElement {

struct PANConfiguration: TextFieldElementConfiguration {
let paymentMethod: ConsumerPaymentDetails
private extension ConsumerPaymentDetails.Details.Card {

var label: String {
String.Localized.card_number
}

var defaultValue: String? {
paymentMethod.cardDetails.map { "•••• \($0.last4)" }
}

func accessoryView(for text: String, theme: ElementsAppearance) -> UIView? {
paymentMethod.cardDetails.map { cardDetails in
let image = STPImageLibrary.cardBrandImage(for: cardDetails.stpBrand)
return UIImageView(image: image)
}
}
var cardBrand: STPCardBrand {
STPCard.brand(from: brand)
}

var availableNetworks: [STPCardBrand] {
networks.map { STPCard.brand(from: $0) }.filter { $0 != .unknown }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ final class LinkCardEditElementSnapshotTests: STPSnapshotTestCase {
verify(sut)
}

func testCoBrandedCard() {
let sut = makeSUT(isDefault: false, networks: ["cartes_bancaires", "visa"])
verify(sut)
}

func verify(
_ element: LinkCardEditElement,
identifier: String? = nil,
Expand All @@ -59,7 +64,8 @@ extension LinkCardEditElementSnapshotTests {

func makeSUT(
isDefault: Bool,
useCVCPlaceholder: Bool = false
useCVCPlaceholder: Bool = false,
networks: [String] = ["visa"]
) -> LinkCardEditElement {
let paymentMethod = ConsumerPaymentDetails(
stripeID: "1",
Expand All @@ -68,6 +74,7 @@ extension LinkCardEditElementSnapshotTests {
expiryYear: 2032,
expiryMonth: 1,
brand: "visa",
networks: networks,
last4: "4242",
checks: nil
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extension LinkStubs {
expiryYear: 30,
expiryMonth: 10,
brand: "visa",
networks: ["visa"],
last4: "1234",
checks: nil)
),
Expand All @@ -48,6 +49,7 @@ extension LinkStubs {
expiryYear: 30,
expiryMonth: 10,
brand: "mastercard",
networks: ["mastercard"],
last4: "4321",
checks: .init(cvcCheck: .fail))
),
Expand All @@ -68,6 +70,7 @@ extension LinkStubs {
expiryYear: 20,
expiryMonth: 10,
brand: "discover",
networks: ["discover"],
last4: "1111",
checks: nil)
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ final class PaymentSheetAPIMockTest: APIStubbedTestCase {
.init(expiryYear: 2055,
expiryMonth: 12,
brand: "visa",
networks: ["visa"],
last4: "1234",
checks: nil)
),
Expand Down Expand Up @@ -190,7 +191,7 @@ final class PaymentSheetAPIMockTest: APIStubbedTestCase {
useMobileEndpoints: false),
paymentDetails: .init(
stripeID: "pd1",
details: .card(card: .init(expiryYear: 2055, expiryMonth: 12, brand: "visa", last4: "1234", checks: nil)),
details: .card(card: .init(expiryYear: 2055, expiryMonth: 12, brand: "visa", networks: ["visa"], last4: "1234", checks: nil)),
billingAddress: nil,
billingEmailAddress: nil,
isDefault: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ extension PaymentSheetLinkAccountTests {
func makePaymentDetailsStub() -> ConsumerPaymentDetails {
return ConsumerPaymentDetails(
stripeID: "1",
details: .card(card: .init(expiryYear: 30, expiryMonth: 10, brand: "visa", last4: "1234", checks: nil)),
details: .card(card: .init(expiryYear: 30, expiryMonth: 10, brand: "visa", networks: ["visa"], last4: "1234", checks: nil)),
billingAddress: nil,
billingEmailAddress: nil,
isDefault: false
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading