diff --git a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift index f55730b25cd..b4af202655c 100644 --- a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift +++ b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift @@ -2816,10 +2816,8 @@ class PaymentSheetDefaultSPMUITests: PaymentSheetUITestCase { var saveThisCardToggle = app.switches["Save payment details to Example, Inc. for future purchases"] saveThisCardToggle.tap() XCTAssertTrue(saveThisCardToggle.isSelected) - // toggle set this card as default - var setDefaultToggle = app.switches["Set as default payment method"] - setDefaultToggle.tap() - XCTAssertTrue(setDefaultToggle.isSelected) + // this card should be automatically set as the default, as there are no other saved pms + XCTAssertFalse(app.switches["Set as default payment method"].waitForExistence(timeout: 3)) // Complete payment app.buttons["Pay $50.99"].waitForExistenceAndTap() @@ -2854,8 +2852,7 @@ class PaymentSheetDefaultSPMUITests: PaymentSheetUITestCase { saveThisCardToggle.tap() XCTAssertTrue(saveThisCardToggle.isSelected) // do not set this card as default - setDefaultToggle = app.switches["Set as default payment method"] - XCTAssertFalse(setDefaultToggle.isSelected) + XCTAssertFalse(app.switches["Set as default payment method"].isSelected) // Complete payment app.buttons["Pay $50.99"].waitForExistenceAndTap() diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory+Card.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory+Card.swift index 2166b11e30a..955be1f9860 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory+Card.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory+Card.swift @@ -13,20 +13,23 @@ import StripePayments import UIKit extension PaymentSheetFormFactory { - func makeCard(cardBrandChoiceEligible: Bool = false, showSetAsDefaultCheckbox: Bool) -> PaymentMethodElement { + func makeCard(cardBrandChoiceEligible: Bool = false) -> PaymentMethodElement { let showLinkInlineSignup = showLinkInlineCardSignup - var defaultCheckbox: PaymentMethodElementWrapper? - if showSetAsDefaultCheckbox { - defaultCheckbox = makeDefaultCheckbox() - } + let defaultCheckbox: Element? = { + guard allowsSetAsDefaultPM else { + return nil + } + let defaultCheckbox = makeDefaultCheckbox() + return shouldDisplayDefaultCheckbox ? defaultCheckbox : SectionElement.HiddenElement(defaultCheckbox) + }() let saveCheckbox = makeSaveCheckbox( label: String.Localized.save_payment_details_for_future_$merchant_payments( merchantDisplayName: configuration.merchantDisplayName ) ) { selected in - defaultCheckbox?.element.checkboxButton.isHidden = !selected + defaultCheckbox?.view.isHidden = !selected } - defaultCheckbox?.element.checkboxButton.isHidden = !saveCheckbox.element.isSelected + defaultCheckbox?.view.isHidden = !saveCheckbox.element.isSelected // Make section titled "Contact Information" w/ phone and email if merchant requires it. let optionalPhoneAndEmailInformationSection: SectionElement? = { diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift index 0aee7ab704a..0eb809c7f18 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift @@ -36,7 +36,8 @@ class PaymentSheetFormFactory { let countryCode: String? let cardBrandChoiceEligible: Bool let savePaymentMethodConsentBehavior: SavePaymentMethodConsentBehavior - let showSetAsDefaultCheckbox: Bool + let allowsSetAsDefaultPM: Bool + let isFirstSavedPaymentMethod: Bool let analyticsHelper: PaymentSheetAnalyticsHelper? let paymentMethodIncentive: PaymentMethodIncentive? @@ -55,6 +56,10 @@ class PaymentSheetFormFactory { } } + var shouldDisplayDefaultCheckbox: Bool { + return allowsSetAsDefaultPM && !isFirstSavedPaymentMethod + } + var theme: ElementsAppearance { return configuration.appearance.asElementsTheme } @@ -108,7 +113,8 @@ class PaymentSheetFormFactory { isSettingUp: intent.isSettingUp, countryCode: elementsSession.countryCode(overrideCountry: configuration.overrideCountry), savePaymentMethodConsentBehavior: elementsSession.savePaymentMethodConsentBehavior, - showSetAsDefaultCheckbox: elementsSession.paymentMethodSetAsDefaultForPaymentSheet, + allowsSetAsDefaultPM: elementsSession.paymentMethodSetAsDefaultForPaymentSheet, + isFirstSavedPaymentMethod: elementsSession.customer?.paymentMethods.isEmpty ?? true, analyticsHelper: analyticsHelper, paymentMethodIncentive: elementsSession.incentive) } @@ -126,7 +132,8 @@ class PaymentSheetFormFactory { isSettingUp: Bool, countryCode: String?, savePaymentMethodConsentBehavior: SavePaymentMethodConsentBehavior, - showSetAsDefaultCheckbox: Bool = false, + allowsSetAsDefaultPM: Bool = false, + isFirstSavedPaymentMethod: Bool = true, analyticsHelper: PaymentSheetAnalyticsHelper?, paymentMethodIncentive: PaymentMethodIncentive? ) { @@ -147,7 +154,8 @@ class PaymentSheetFormFactory { self.countryCode = countryCode self.cardBrandChoiceEligible = cardBrandChoiceEligible self.savePaymentMethodConsentBehavior = savePaymentMethodConsentBehavior - self.showSetAsDefaultCheckbox = showSetAsDefaultCheckbox + self.allowsSetAsDefaultPM = allowsSetAsDefaultPM + self.isFirstSavedPaymentMethod = isFirstSavedPaymentMethod self.analyticsHelper = analyticsHelper self.paymentMethodIncentive = paymentMethodIncentive } @@ -164,9 +172,9 @@ class PaymentSheetFormFactory { // We have two ways to create the form for a payment method // 1. Custom, one-off forms if paymentMethod == .card { - return makeCard(cardBrandChoiceEligible: cardBrandChoiceEligible, showSetAsDefaultCheckbox: showSetAsDefaultCheckbox) + return makeCard(cardBrandChoiceEligible: cardBrandChoiceEligible) } else if paymentMethod == .USBankAccount { - return makeUSBankAccount(merchantName: configuration.merchantDisplayName, showSetAsDefaultCheckbox: showSetAsDefaultCheckbox) + return makeUSBankAccount(merchantName: configuration.merchantDisplayName) } else if paymentMethod == .UPI { return makeUPI() } else if paymentMethod == .cashApp && isSettingUp { @@ -374,7 +382,7 @@ extension PaymentSheetFormFactory { let element = CheckboxElement( theme: configuration.appearance.asElementsTheme, label: String.Localized.set_as_default_payment_method, - isSelectedByDefault: false, + isSelectedByDefault: isFirstSavedPaymentMethod, didToggle: didToggle ) return PaymentMethodElementWrapper(element) { checkbox, params in @@ -556,12 +564,15 @@ extension PaymentSheetFormFactory { ) } - func makeUSBankAccount(merchantName: String, showSetAsDefaultCheckbox: Bool) -> PaymentMethodElement { + func makeUSBankAccount(merchantName: String) -> PaymentMethodElement { let isSaving = BoolReference() - var defaultCheckbox: PaymentMethodElementWrapper? - if showSetAsDefaultCheckbox { - defaultCheckbox = makeDefaultCheckbox() - } + let defaultCheckbox: Element? = { + guard allowsSetAsDefaultPM else { + return nil + } + let defaultCheckbox = makeDefaultCheckbox() + return shouldDisplayDefaultCheckbox ? defaultCheckbox : SectionElement.HiddenElement(defaultCheckbox) + }() let saveCheckbox = makeSaveCheckbox( label: String( format: .Localized.save_this_account_for_future_payments, diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/USBankAccountPaymentMethodElement.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/USBankAccountPaymentMethodElement.swift index 732c1fe33b2..53cd6712aea 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/USBankAccountPaymentMethodElement.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/USBankAccountPaymentMethodElement.swift @@ -38,7 +38,7 @@ final class USBankAccountPaymentMethodElement: ContainerElement { private let bankInfoSectionElement: SectionElement private let bankInfoView: BankAccountInfoView private let saveCheckboxElement: PaymentMethodElementWrapper? - private let defaultCheckboxElement: PaymentMethodElement? + private let defaultCheckboxElement: Element? private var savingAccount: BoolReference private let theme: ElementsAppearance @@ -89,7 +89,7 @@ final class USBankAccountPaymentMethodElement: ContainerElement { phoneElement: PaymentMethodElementWrapper?, addressElement: PaymentMethodElementWrapper?, saveCheckboxElement: PaymentMethodElementWrapper?, - defaultCheckboxElement: PaymentMethodElement?, + defaultCheckboxElement: Element?, savingAccount: BoolReference, merchantName: String, initialLinkedBank: FinancialConnectionsLinkedBank?, @@ -132,7 +132,7 @@ final class USBankAccountPaymentMethodElement: ContainerElement { addressElement, bankInfoSectionElement, saveCheckboxElement, - defaultCheckboxElement + defaultCheckboxElement, ] let autoSectioningElements = allElements.compactMap { $0 } self.formElement = FormElement(autoSectioningElements: autoSectioningElements, theme: theme) diff --git a/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/AddPaymentMethodViewControllerSnapshotTests.swift b/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/AddPaymentMethodViewControllerSnapshotTests.swift index 9cca68d7f96..c0db3004f38 100644 --- a/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/AddPaymentMethodViewControllerSnapshotTests.swift +++ b/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/AddPaymentMethodViewControllerSnapshotTests.swift @@ -66,7 +66,7 @@ final class AddPaymentMethodViewControllerSnapshotTests: STPSnapshotTestCase { let sut = AddPaymentMethodViewController( intent: intent, // ...and a "Set as default" checkbox... - elementsSession: ._testValue(intent: intent, allowsSetAsDefaultPM: true), + elementsSession: ._testValue(intent: intent, paymentMethods: [STPPaymentMethod._testCardJSON], allowsSetAsDefaultPM: true), configuration: config, previousCustomerInput: previousCustomerInput, paymentMethodTypes: [.stripe(.payPal), .stripe(.card), .stripe(.cashApp)], diff --git a/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/STPFixtures+PaymentSheet.swift b/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/STPFixtures+PaymentSheet.swift index 1d05ab8952b..2aaaef1349c 100644 --- a/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/STPFixtures+PaymentSheet.swift +++ b/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/STPFixtures+PaymentSheet.swift @@ -182,6 +182,8 @@ extension STPElementsSession { intent: Intent, linkMode: LinkMode? = nil, linkFundingSources: Set = [], + defaultPaymentMethod: String? = nil, + paymentMethods: [[AnyHashable: Any]]? = nil, allowsSetAsDefaultPM: Bool = false ) -> STPElementsSession { let paymentMethodTypes: [String] = { @@ -213,7 +215,9 @@ extension STPElementsSession { paymentMethodTypes: paymentMethodTypes, customerSessionData: customerSessionData, linkMode: linkMode, - linkFundingSources: linkFundingSources + linkFundingSources: linkFundingSources, + defaultPaymentMethod: defaultPaymentMethod, + paymentMethods: paymentMethods ) } } @@ -247,19 +251,21 @@ extension Intent { } extension STPPaymentMethod { + static let _testCardJSON = [ + "id": "pm_123card", + "type": "card", + "card": [ + "last4": "4242", + "brand": "visa", + "fingerprint": "B8XXs2y2JsVBtB9f", + "networks": ["available": ["visa"]], + "exp_month": "01", + "exp_year": "2040", + ], + ] as [AnyHashable: Any] + static func _testCard() -> STPPaymentMethod { - return STPPaymentMethod.decodedObject(fromAPIResponse: [ - "id": "pm_123card", - "type": "card", - "card": [ - "last4": "4242", - "brand": "visa", - "fingerprint": "B8XXs2y2JsVBtB9f", - "networks": ["available": ["visa"]], - "exp_month": "01", - "exp_year": "2040", - ], - ])! + return STPPaymentMethod.decodedObject(fromAPIResponse: _testCardJSON)! } static func _testCardAmex() -> STPPaymentMethod {