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

Vertical mode "New card" special casing #3717

Merged
merged 6 commits into from
Jun 27, 2024
Merged
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 @@ -140,6 +140,9 @@
/* Label for name on card field */
"Name on card" = "Name on card";

/* Label of a button that appears on a checkout screen. When tapped, it displays a credit card form. This button is shown next to another button representing the customer's saved card; the word 'new' is meant to differentiate this button's action with the saved card button. */
"New card" = "New card";

/* Title shown above a section containing payment methods that a customer can choose to pay with e.g. card, bank account, etc. */
"New payment method" = "New payment method";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,11 @@ extension String.Localized {
"Subtitle shown on a button allowing a user to select to pay with Link."
)
}

static var new_card: String {
STPLocalizedString(
"New card",
"Label of a button that appears on a checkout screen. When tapped, it displays a credit card form. This button is shown next to another button representing the customer's saved card; the word 'new' is meant to differentiate this button's action with the saved card button."
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class FormHeaderView: UIView {
private lazy var label: UILabel = {
let label = PaymentSheetUI.makeHeaderLabel(appearance: appearance)
if paymentMethodType == .stripe(.card) {
label.text = hasASavedCard ? String.Localized.add_card : String.Localized.add_new_card
label.text = shouldUseNewCardHeader ? String.Localized.add_new_card : String.Localized.add_card
} else if paymentMethodType == .stripe(.USBankAccount) {
label.text = String.Localized.add_us_bank_account
} else {
Expand Down Expand Up @@ -43,12 +43,12 @@ final class FormHeaderView: UIView {
}()

private let paymentMethodType: PaymentSheet.PaymentMethodType
private let hasASavedCard: Bool // true if the customer has a saved payment method that is type card
private let shouldUseNewCardHeader: Bool // true if the customer has a saved payment method that is type card
private let appearance: PaymentSheet.Appearance

init(paymentMethodType: PaymentSheet.PaymentMethodType, hasASavedCard: Bool, appearance: PaymentSheet.Appearance) {
init(paymentMethodType: PaymentSheet.PaymentMethodType, shouldUseNewCardHeader: Bool, appearance: PaymentSheet.Appearance) {
self.paymentMethodType = paymentMethodType
self.hasASavedCard = hasASavedCard
self.shouldUseNewCardHeader = shouldUseNewCardHeader
self.appearance = appearance
super.init(frame: .zero)
addAndPinSubview(stackView)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,17 @@ class RowButton: UIView {

// MARK: - Helpers
extension RowButton {
static func makeForPaymentMethodType(paymentMethodType: PaymentSheet.PaymentMethodType, subtitle: String? = nil, appearance: PaymentSheet.Appearance, didTap: @escaping (RowButton) -> Void) -> RowButton {
static func makeForPaymentMethodType(paymentMethodType: PaymentSheet.PaymentMethodType, subtitle: String? = nil, savedPaymentMethodType: STPPaymentMethodType?, appearance: PaymentSheet.Appearance, didTap: @escaping (RowButton) -> Void) -> RowButton {
let imageView = PaymentMethodTypeImageView(paymentMethodType: paymentMethodType, backgroundColor: appearance.colors.componentBackground)
imageView.contentMode = .scaleAspectFit
return RowButton(appearance: appearance, imageView: imageView, text: paymentMethodType.displayName, subtext: subtitle, didTap: didTap)
// Special case "New card" vs "Card" title
let text: String = {
if savedPaymentMethodType == .card && paymentMethodType == .stripe(.card) {
return .Localized.new_card
}
return paymentMethodType.displayName
}()
return RowButton(appearance: appearance, imageView: imageView, text: text, subtext: subtitle, didTap: didTap)
}

static func makeForApplePay(appearance: PaymentSheet.Appearance, didTap: @escaping (RowButton) -> Void) -> RowButton {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class VerticalPaymentMethodListViewController: UIViewController {

// Create stack view views after super.init so that we can reference `self`
var views = [UIView]()
// Saved payment methods:
// Saved payment method:
if let savedPaymentMethod {
let selection = VerticalPaymentMethodListSelection.saved(paymentMethod: savedPaymentMethod)
let accessoryButton: RowButton.RightAccessoryButton? = {
Expand All @@ -81,6 +81,20 @@ class VerticalPaymentMethodListViewController: UIViewController {
]
}

// Special case - order "New Card" immediately after saved card:
let shouldReorderNewCard: Bool = paymentMethodTypes.contains(.stripe(.card)) && savedPaymentMethod?.type == .card
if shouldReorderNewCard {
let selection = VerticalPaymentMethodListSelection.new(paymentMethodType: .stripe(.card))
let rowButton = RowButton.makeForPaymentMethodType(paymentMethodType: .stripe(.card), savedPaymentMethodType: savedPaymentMethod?.type, appearance: appearance) { [weak self] in
self?.didTap(rowButton: $0, selection: selection)
}
views.append(rowButton)
if initialSelection == selection {
rowButton.isSelected = true
currentSelection = selection
}
}

// Apple Pay and Link:
if shouldShowApplePay {
let selection = VerticalPaymentMethodListSelection.applePay
Expand All @@ -105,11 +119,13 @@ class VerticalPaymentMethodListViewController: UIViewController {
}
}

// All other payment methods:
// All other payment methods (excluding card, if it was already added above):
let paymentMethodTypes = shouldReorderNewCard ? paymentMethodTypes.filter({ $0 != .stripe(.card) }) : paymentMethodTypes
for paymentMethodType in paymentMethodTypes {
let selection = VerticalPaymentMethodListSelection.new(paymentMethodType: paymentMethodType)
let rowButton = RowButton.makeForPaymentMethodType(paymentMethodType: paymentMethodType,
subtitle: subtitleText(for: paymentMethodType, currency: currency, amount: amount),
savedPaymentMethodType: savedPaymentMethod?.type,
appearance: appearance) { [weak self] in
self?.didTap(rowButton: $0, selection: selection)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ extension PaymentSheetVerticalViewController: VerticalPaymentMethodListViewContr
} else {
return FormHeaderView(
paymentMethodType: paymentMethodType,
hasASavedCard: !savedPaymentMethods.filter({ $0.type == .card }).isEmpty,
// Special case: use "New Card" instead of "Card" if the displayed saved PM is a card
shouldUseNewCardHeader: savedPaymentMethods.first?.type == .card,
appearance: configuration.appearance
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ final class PaymentSheetVerticalViewControllerSnapshotTest: STPSnapshotTestCase
waitForExpectations(timeout: 1)
}

func verify(_ sut: PaymentSheetVerticalViewController, identifier: String? = nil) {
func makeBottomSheetAndLayout(_ sut: PaymentSheetVerticalViewController) -> BottomSheetViewController {
let bottomSheet = BottomSheetViewController(contentViewController: sut, appearance: .default, isTestMode: false, didCancelNative3DS2: {})
bottomSheet.view.setNeedsLayout()
bottomSheet.view.layoutIfNeeded()
let height = bottomSheet.view.systemLayoutSizeFitting(.init(width: 375, height: UIView.noIntrinsicMetric)).height
bottomSheet.view.frame = .init(origin: .zero, size: .init(width: 375, height: height))
return bottomSheet
}

func verify(_ sut: PaymentSheetVerticalViewController, identifier: String? = nil) {
let bottomSheet = makeBottomSheetAndLayout(sut)
STPSnapshotVerifyView(bottomSheet.view, identifier: identifier)
}

Expand Down Expand Up @@ -216,4 +221,19 @@ final class PaymentSheetVerticalViewControllerSnapshotTest: STPSnapshotTestCase
sut.didTapPaymentMethod(.new(paymentMethodType: .stripe(.USBankAccount)))
verify(sut, identifier: "under_form")
}

func testAddNewCardFormTitle() {
// If we're displaying a saved card in the list, the card form title should be "New card" and not "Card"
let loadResult = PaymentSheetLoader.LoadResult(
intent: ._testPaymentIntent(paymentMethodTypes: [.card]),
savedPaymentMethods: [._testCard()],
isLinkEnabled: false,
isApplePayEnabled: false
)
let sut = PaymentSheetVerticalViewController(configuration: ._testValue_MostPermissive(), loadResult: loadResult, isFlowController: false, previousPaymentOption: nil)
_ = makeBottomSheetAndLayout(sut) // Laying out before calling `didTap` avoids breaking constraints due to zero size
let listVC = sut.paymentMethodListViewController!
listVC.didTap(rowButton: listVC.getRowButton(accessibilityIdentifier: "New card"), selection: .new(paymentMethodType: .stripe(.card)))
verify(sut)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ final class VerticalPaymentMethodListViewControllerTest: XCTestCase {

// Selecting card...
shouldSelectPaymentMethodReturnValue = false // (and mocking `didTapPaymentMethod` to return false)
let cardButton = sut.getRowButton(accessibilityIdentifier: "Card")
let cardButton = sut.getRowButton(accessibilityIdentifier: "New card")
sut.didTap(rowButton: cardButton, selection: .new(paymentMethodType: .stripe(.card)))
// ...should not change the current selection...
XCTAssertFalse(cardButton.isSelected)
Expand Down

This file was deleted.

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.
Binary file modified ...icalViewControllerSnapshotTest/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ...ollerSnapshotTest/[email protected]
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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading