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

Read the default_payment_method field from elements session and use that as the default PM #4313

Merged
merged 45 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
1403094
reading default_payment_method from back end?
joyceqin-stripe Nov 27, 2024
71c5516
revert accidental space changes;
joyceqin-stripe Nov 27, 2024
e3ecc2c
customersheet default from back end
joyceqin-stripe Nov 27, 2024
fb86faa
removed unused value
joyceqin-stripe Dec 2, 2024
b283446
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 2, 2024
9ed5ed2
remove unused property
joyceqin-stripe Dec 2, 2024
e689449
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 2, 2024
3c36c97
fix build issue
joyceqin-stripe Dec 2, 2024
c749e6e
Merge branch 'joyceqin-MOBILESDK-2799' of github.com:stripe/stripe-io…
joyceqin-stripe Dec 2, 2024
aac6ae1
fix test
joyceqin-stripe Dec 2, 2024
b25eb4b
merged with master
joyceqin-stripe Dec 4, 2024
5936f96
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 4, 2024
0fca0dc
check default pm against savedPaymentMethods instead of customer.paym…
joyceqin-stripe Dec 5, 2024
c226e09
removed alternateUpdatePaymentMethodNavigation, consolidated reading …
joyceqin-stripe Dec 5, 2024
4d146b6
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 5, 2024
f858536
remove alternateUpdatePaymentMethodNavigation flag and updated tests
joyceqin-stripe Dec 6, 2024
8d1c90b
update snapshot tests
joyceqin-stripe Dec 6, 2024
c70c6a1
update row button chevron condition
joyceqin-stripe Dec 6, 2024
d999b9e
remove removeonly mode, update tests
joyceqin-stripe Dec 6, 2024
ac5189a
fixing tests
joyceqin-stripe Dec 6, 2024
ffcf6e4
remove wait for chevron existence in tests for last card
joyceqin-stripe Dec 6, 2024
403fdcb
localizable string
joyceqin-stripe Dec 6, 2024
c8fbfef
edit a test
joyceqin-stripe Dec 6, 2024
2f515cd
merged with remove alternateupdatepaymentmethodnavigation flag
joyceqin-stripe Dec 6, 2024
0a92c87
remove removeOnly snapshots
joyceqin-stripe Dec 6, 2024
a89890c
Merge branch 'master' into joyceqin-remove-alternateupdatepaymentmeth…
joyceqin-stripe Dec 6, 2024
bd51b31
changelog
joyceqin-stripe Dec 6, 2024
3c69384
Merge branch 'master' into joyceqin-remove-alternateupdatepaymentmeth…
joyceqin-stripe Dec 6, 2024
f239d20
update changelog and tests
joyceqin-stripe Dec 6, 2024
5fad2d3
Merge branch 'joyceqin-remove-alternateupdatepaymentmethodnavigation-…
joyceqin-stripe Dec 6, 2024
4da3034
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 6, 2024
05407cc
Merge branch 'joyceqin-remove-alternateupdatepaymentmethodnavigation-…
joyceqin-stripe Dec 6, 2024
eaae456
Merge branch 'master' into joyceqin-remove-alternateupdatepaymentmeth…
joyceqin-stripe Dec 6, 2024
7022a11
sleep for 1 second to allow label animation to finish testRemovalOfSa…
joyceqin-stripe Dec 6, 2024
f5fbd58
Merge branch 'joyceqin-remove-alternateupdatepaymentmethodnavigation-…
joyceqin-stripe Dec 6, 2024
cb12138
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 6, 2024
79f2a94
Revert space change
joyceqin-stripe Dec 6, 2024
b592b55
fallback when opted in to set as default not local default but first …
joyceqin-stripe Dec 9, 2024
74c9db8
added tests
joyceqin-stripe Dec 9, 2024
dbc2beb
Merge branch 'joyceqin-MOBILESDK-2799' of github.com:stripe/stripe-io…
joyceqin-stripe Dec 9, 2024
40574a6
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 9, 2024
2e8b49e
no saved pms customersessionadapter test
joyceqin-stripe Dec 9, 2024
a2a37e6
removed explicit nil assignments when already implied
joyceqin-stripe Dec 9, 2024
0b6781f
Merge branch 'joyceqin-MOBILESDK-2799' of github.com:stripe/stripe-io…
joyceqin-stripe Dec 9, 2024
7d3f1c4
Merge branch 'master' into joyceqin-MOBILESDK-2799
joyceqin-stripe Dec 9, 2024
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 @@ -53,6 +53,7 @@ struct CustomerSheetTestPlayground: View {
SettingPickerView(setting: $playgroundController.settings.paymentMethodRemove)
SettingPickerView(setting: $playgroundController.settings.paymentMethodRemoveLast)
SettingPickerView(setting: $playgroundController.settings.paymentMethodAllowRedisplayFilters)
SettingPickerView(setting: $playgroundController.settings.allowsSetAsDefaultPM)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//

import Combine
@_spi(STP) @_spi(CustomerSessionBetaAccess) @_spi(CardBrandFilteringBeta) import StripePaymentSheet
@_spi(STP) @_spi(CustomerSessionBetaAccess) @_spi(CardBrandFilteringBeta) @_spi(AllowsSetAsDefaultPM) import StripePaymentSheet
import SwiftUI

class CustomerSheetTestPlaygroundController: ObservableObject {
Expand Down Expand Up @@ -147,6 +147,7 @@ class CustomerSheetTestPlaygroundController: ObservableObject {
case .allowVisa:
configuration.cardBrandAcceptance = .allowed(brands: [.visa])
}
configuration.allowsSetAsDefaultPM = settings.allowsSetAsDefaultPM == .on
return configuration
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ public struct CustomerSheetTestPlaygroundSettings: Codable, Equatable {
case allowVisa
}

enum AllowsSetAsDefaultPM: String, PickerEnum {
static let enumName: String = "allowsSetAsDefaultPM"
case on
case off
}

var customerMode: CustomerMode
var customerId: String?
var customerKeyType: CustomerKeyType
Expand All @@ -169,6 +175,7 @@ public struct CustomerSheetTestPlaygroundSettings: Codable, Equatable {
var paymentMethodRemoveLast: PaymentMethodRemoveLast
var paymentMethodAllowRedisplayFilters: PaymentMethodAllowRedisplayFilters
var cardBrandAcceptance: CardBrandAcceptance
var allowsSetAsDefaultPM: AllowsSetAsDefaultPM

static func defaultValues() -> CustomerSheetTestPlaygroundSettings {
return CustomerSheetTestPlaygroundSettings(customerMode: .new,
Expand All @@ -190,7 +197,8 @@ public struct CustomerSheetTestPlaygroundSettings: Codable, Equatable {
paymentMethodRemove: .enabled,
paymentMethodRemoveLast: .enabled,
paymentMethodAllowRedisplayFilters: .always,
cardBrandAcceptance: .all)
cardBrandAcceptance: .all,
allowsSetAsDefaultPM: .off)
}

var base64Data: String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ struct PaymentSheetTestPlayground: View {
if playgroundController.settings.paymentMethodRedisplay == .enabled {
SettingPickerView(setting: $playgroundController.settings.paymentMethodAllowRedisplayFilters)
}
SettingPickerView(setting: $playgroundController.settings.allowsSetAsDefaultPM)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,12 @@ struct PaymentSheetTestPlaygroundSettings: Codable, Equatable {
case allowVisa
}

enum AllowsSetAsDefaultPM: String, PickerEnum {
static let enumName: String = "allowsSetAsDefaultPM"
case on
case off
}

var uiStyle: UIStyle
var layout: Layout
var mode: Mode
Expand Down Expand Up @@ -490,6 +496,7 @@ struct PaymentSheetTestPlaygroundSettings: Codable, Equatable {
var formSheetAction: FormSheetAction
var embeddedViewDisplaysMandateText: DisplaysMandateTextEnabled
var cardBrandAcceptance: CardBrandAcceptance
var allowsSetAsDefaultPM: AllowsSetAsDefaultPM

static func defaultValues() -> PaymentSheetTestPlaygroundSettings {
return PaymentSheetTestPlaygroundSettings(
Expand Down Expand Up @@ -535,7 +542,8 @@ struct PaymentSheetTestPlaygroundSettings: Codable, Equatable {
collectAddress: .automatic,
formSheetAction: .confirm,
embeddedViewDisplaysMandateText: .on,
cardBrandAcceptance: .all)
cardBrandAcceptance: .all,
allowsSetAsDefaultPM: .off)
}

static let nsUserDefaultsKey = "PaymentSheetTestPlaygroundSettings"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Contacts
import PassKit
@_spi(STP) import StripeCore
@_spi(STP) import StripePayments
@_spi(CustomerSessionBetaAccess) @_spi(STP) @_spi(PaymentSheetSkipConfirmation) @_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(EmbeddedPaymentElementPrivateBeta) @_spi(CardBrandFilteringBeta) import StripePaymentSheet
@_spi(CustomerSessionBetaAccess) @_spi(STP) @_spi(PaymentSheetSkipConfirmation) @_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(EmbeddedPaymentElementPrivateBeta) @_spi(CardBrandFilteringBeta) @_spi(AllowsSetAsDefaultPM) import StripePaymentSheet
import SwiftUI
import UIKit

Expand Down Expand Up @@ -184,6 +184,7 @@ class PlaygroundController: ObservableObject {
case .allowVisa:
configuration.cardBrandAcceptance = .allowed(brands: [.visa])
}
configuration.allowsSetAsDefaultPM = settings.allowsSetAsDefaultPM == .on
return configuration
}

Expand Down Expand Up @@ -271,6 +272,7 @@ class PlaygroundController: ObservableObject {
case .allowVisa:
configuration.cardBrandAcceptance = .allowed(brands: [.visa])
}
configuration.allowsSetAsDefaultPM = settings.allowsSetAsDefaultPM == .on
return configuration
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@ struct ElementsCustomer: Equatable, Hashable {
}

// Optional
// to test default payment methods reading from back end, hard-code a valid default payment method
// later, when API calls to get and update default payment method are available, that will no longer be needed
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment doesn't make much sense to me, I don't see any code changes near this comment indicating we are hard coding a default PM.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What I was trying to say is that the only way I knew to test it was to add a payment method, get its stripe ID, and then hardcode that here so that the elements session will set that as its default. And then after adding other cards, even after paying with them, when the payment sheet is reloaded, it will have the hardcoded default selected, which shows that it's reading from the elements session instead of the local default.

Right now, there's a duping process where when you read from "default_payment_method", it returns the locally saved default that you pass into the API call— it's not actually retrieving a default from the back end. When the API endpoints are ready, then we should be able to actually get the value stored in the back end, but until then, the only way I could think to test that it's reading from the elementsSession value instead of the local default was to hard-code a default (e.g. let defaultPaymentMethod = "pm_1QSOUILu5o3P18ZpN4PY49SE")

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hrm, I see what you mean, but don't think we need a comment for that. I don't think anyone else will need this comment as it shouldn't be relevant once this feature is implemented. If you want to leave it let's add a TODO to remove it for yourself.

let defaultPaymentMethod = response["default_payment_method"] as? String
return ElementsCustomer(paymentMethods: paymentMethods, defaultPaymentMethod: defaultPaymentMethod, customerSession: customerSession)
}

static func getDefaultPaymentMethod(from customer: ElementsCustomer?) -> STPPaymentMethod? {
guard let customer = customer else { return nil }
return customer.paymentMethods.first { $0.stripeId == customer.defaultPaymentMethod }
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why static? Why not an instance function on ElementsCustomer with tests?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I can't really remember any reasoning of mine, you're right that it should be an instance function on ElementsCustomer

}
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,14 @@ extension CustomerSessionAdapter {
return stripePaymentMethodId
}

func fetchSelectedPaymentOption(for customerId: String) -> CustomerPaymentOption? {
return CustomerPaymentOption.defaultPaymentMethod(for: customerId)
func fetchSelectedPaymentOption(for customerId: String, customer: ElementsCustomer? = nil) -> CustomerPaymentOption? {
guard configuration.allowsSetAsDefaultPM,
let customer = customer,
let defaultPaymentMethod = customer.defaultPaymentMethod else {
return CustomerPaymentOption.defaultPaymentMethod(for: customerId)
}

return CustomerPaymentOption.stripeId(defaultPaymentMethod)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This behavior seems inconsistent with other places where we read the default payment method. If the user is opted into allowsSetAsDefaultPM but the customer does not have a default payment method we will fall through and read it from the stripeId on line 117. In other places we do not fall through. My understanding is that if we are opted into this feature we will use the customer default payment method as the single source of truth for their default. Let's add tests here too.

Correct me if I'm wrong.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure why I used guard here instead of if/else because it seems more confusing here, but in this scenario, if the user is opted in but the customer does not have a default, it reads from defaultPaymentMethod on line 114, which returns the local default. I saw the thread with Bella where she said that it should fall back to first saved payment method in the list instead, so I will have to change this to reflect that

}

func detachPaymentMethod(paymentMethodId: String) async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,17 @@ extension CustomerSheet {
switch customerSheetDataSource.dataSource {
case .customerSession(let customerSessionAdapter):
let (elementsSession, customerSessionClientSecret) = try await customerSessionAdapter.elementsSessionWithCustomerSessionClientSecret()
let selectedPaymentOption = CustomerPaymentOption.defaultPaymentMethod(for: customerSessionClientSecret.customerId)

var selectedPaymentOption: CustomerPaymentOption?

// get default payment method from elements session
if configuration.allowsSetAsDefaultPM,
let defaultPaymentMethod = ElementsCustomer.getDefaultPaymentMethod(from: elementsSession.customer) {
selectedPaymentOption = CustomerPaymentOption.stripeId(defaultPaymentMethod.stripeId)
}
else {
selectedPaymentOption = CustomerPaymentOption.defaultPaymentMethod(for: customerSessionClientSecret.customerId)
}

switch selectedPaymentOption {
case .applePay:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ extension CustomerSheet {
/// Note: Card brand filtering is not currently supported by Link.
@_spi(CardBrandFilteringBeta) public var cardBrandAcceptance: PaymentSheet.CardBrandAcceptance = .all

/// This is an experimental feature that may be removed at any time.
/// If true, users can set a payment method as default and sync their default payment method across web and mobile
/// If false (default), users cannot set default payment methods.
@_spi(AllowsSetAsDefaultPM) public var allowsSetAsDefaultPM = false

public init () {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class CustomerSheetDataSource {
// Ensure local specs are loaded prior to the ones from elementSession
await loadFormSpecs()
let customerId = try await customerSessionClientSecret.customerId
let paymentOption = customerSessionAdapter.fetchSelectedPaymentOption(for: customerId)
let elementSession = try await elementsSessionResult
let paymentOption = customerSessionAdapter.fetchSelectedPaymentOption(for: customerId, customer: elementSession.customer)

// Override with specs from elementSession
_ = FormSpecProvider.shared.loadFrom(elementSession.paymentMethodSpecs as Any)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ extension EmbeddedPaymentElement {
isFlatCheckmarkStyle: configuration.appearance.embeddedPaymentElement.row.style == .flatWithCheckmark
)
let initialSelection: EmbeddedPaymentMethodsView.Selection? = {
// get default payment method from elements session
if configuration.allowsSetAsDefaultPM,
let defaultPaymentMethod = ElementsCustomer.getDefaultPaymentMethod(from: loadResult.elementsSession.customer) {
return .saved(paymentMethod: defaultPaymentMethod)
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems to be in too high in the function, we should use previousPaymentOption instead of the default on the customer. See line 63 where we try to read the customer default.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You're right, I didn't consider that the view shouldn't automatically initially select the default because there could have been previous user input. I'll fix that

// Select the previous payment option
switch previousPaymentOption {
case .applePay:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ extension EmbeddedPaymentElement {
/// Note: Card brand filtering is not currently supported by Link.
@_spi(CardBrandFilteringBeta) public var cardBrandAcceptance: PaymentSheet.CardBrandAcceptance = .all

/// This is an experimental feature that may be removed at any time.
/// If true, users can set a payment method as default and sync their default payment method across web and mobile
/// If false (default), users cannot set default payment methods.
@_spi(AllowsSetAsDefaultPM) public var allowsSetAsDefaultPM = false

/// The view can display payment methods like “Card” that, when tapped, open a form sheet where customers enter their payment method details. The sheet has a button at the bottom. `FormSheetAction` enumerates the actions the button can perform.
public enum FormSheetAction {
/// The button says “Pay” or “Setup”. When tapped, we confirm the payment or setup in the form sheet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protocol PaymentElementConfiguration: PaymentMethodRequirementProvider {
var cardBrandAcceptance: PaymentSheet.CardBrandAcceptance { get set }
var analyticPayload: [String: Any] { get }
var disableWalletPaymentMethodFiltering: Bool { get set }
var allowsSetAsDefaultPM: Bool { get set }
var linkPaymentMethodsOnly: Bool { get set }
var forceNativeLinkEnabled: Bool { get set }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ extension PaymentSheet {
/// Note: Card brand filtering is not currently supported by Link.
@_spi(CardBrandFilteringBeta) public var cardBrandAcceptance: PaymentSheet.CardBrandAcceptance = .all

/// This is an experimental feature that may be removed at any time.
/// If true, users can set a payment method as default and sync their default payment method across web and mobile
/// If false (default), users cannot set default payment methods.
@_spi(AllowsSetAsDefaultPM) public var allowsSetAsDefaultPM = false
}

/// Defines the layout orientations available for displaying payment methods in PaymentSheet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ final class PaymentSheetLoader {
savedPaymentMethods: filteredSavedPaymentMethods,
customerID: configuration.customer?.id,
showApplePay: integrationShape.canDefaultToLinkOrApplePay ? isApplePayEnabled : false,
showLink: integrationShape.canDefaultToLinkOrApplePay ? isLinkEnabled : false
showLink: integrationShape.canDefaultToLinkOrApplePay ? isLinkEnabled : false,
allowsSetAsDefaultPM: configuration.allowsSetAsDefaultPM,
customer: elementsSession.customer
)
let paymentMethodTypes = PaymentSheet.PaymentMethodType.filteredPaymentMethodTypes(from: intent, elementsSession: elementsSession, configuration: configuration, logAvailability: true)

Expand Down Expand Up @@ -316,9 +318,17 @@ final class PaymentSheetLoader {

// Move default PM to front
if let customerID = configuration.customer?.id {
let defaultPaymentMethod = CustomerPaymentOption.defaultPaymentMethod(for: customerID)
var defaultPaymentMethodOption: CustomerPaymentOption?
// get default payment method from elements session
if configuration.allowsSetAsDefaultPM,
let defaultPaymentMethod = ElementsCustomer.getDefaultPaymentMethod(from: elementsSession.customer) {
defaultPaymentMethodOption = CustomerPaymentOption.stripeId(defaultPaymentMethod.stripeId)
}
else {
defaultPaymentMethodOption = CustomerPaymentOption.defaultPaymentMethod(for: customerID)
}
if let defaultPMIndex = savedPaymentMethods.firstIndex(where: {
$0.stripeId == defaultPaymentMethod?.value
$0.stripeId == defaultPaymentMethodOption?.value
}) {
let defaultPM = savedPaymentMethods.remove(at: defaultPMIndex)
savedPaymentMethods.insert(defaultPM, at: 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class SavedPaymentOptionsViewController: UIViewController {
let isTestMode: Bool
let allowsRemovalOfLastSavedPaymentMethod: Bool
let allowsRemovalOfPaymentMethods: Bool
let allowsSetAsDefaultPM: Bool
}

// MARK: - Internal Properties
Expand Down Expand Up @@ -216,6 +217,7 @@ class SavedPaymentOptionsViewController: UIViewController {
}
weak var delegate: SavedPaymentOptionsViewControllerDelegate?
var appearance = PaymentSheet.Appearance.default
var elementsSession: STPElementsSession

// MARK: - Private Properties
private var selectedViewModelIndex: Int?
Expand Down Expand Up @@ -311,6 +313,7 @@ class SavedPaymentOptionsViewController: UIViewController {
paymentSheetConfiguration: PaymentSheet.Configuration,
intent: Intent,
appearance: PaymentSheet.Appearance,
elementsSession: STPElementsSession,
cbcEligible: Bool = false,
analyticsHelper: PaymentSheetAnalyticsHelper,
delegate: SavedPaymentOptionsViewControllerDelegate? = nil
Expand All @@ -320,6 +323,7 @@ class SavedPaymentOptionsViewController: UIViewController {
self.paymentSheetConfiguration = paymentSheetConfiguration
self.intent = intent
self.appearance = appearance
self.elementsSession = elementsSession
self.cbcEligible = cbcEligible
self.delegate = delegate
self.analyticsHelper = analyticsHelper
Expand Down Expand Up @@ -363,7 +367,9 @@ class SavedPaymentOptionsViewController: UIViewController {
savedPaymentMethods: savedPaymentMethods,
customerID: configuration.customerID,
showApplePay: configuration.showApplePay,
showLink: configuration.showLink
showLink: configuration.showLink,
allowsSetAsDefaultPM: configuration.allowsSetAsDefaultPM,
customer: elementsSession.customer
)

collectionView.reloadData()
Expand Down Expand Up @@ -437,9 +443,17 @@ class SavedPaymentOptionsViewController: UIViewController {

/// Creates the list of viewmodels to display in the "saved payment methods" carousel e.g. `["+ Add", "Apple Pay", "Link", "Visa 4242"]`
/// - Returns defaultSelectedIndex: The index of the view model that is the default e.g. in the above list, if "Visa 4242" is the default, the index is 3.
static func makeViewModels(savedPaymentMethods: [STPPaymentMethod], customerID: String?, showApplePay: Bool, showLink: Bool) -> (defaultSelectedIndex: Int, viewModels: [Selection]) {
static func makeViewModels(savedPaymentMethods: [STPPaymentMethod], customerID: String?, showApplePay: Bool, showLink: Bool, allowsSetAsDefaultPM: Bool, customer: ElementsCustomer?) -> (defaultSelectedIndex: Int, viewModels: [Selection]) {
// Get the default
let defaultPaymentMethod = CustomerPaymentOption.defaultPaymentMethod(for: customerID)
var defaultPaymentMethodOption: CustomerPaymentOption?
// get default payment method from elements session
if allowsSetAsDefaultPM,
let defaultPaymentMethod = ElementsCustomer.getDefaultPaymentMethod(from: customer) {
defaultPaymentMethodOption = CustomerPaymentOption.stripeId(defaultPaymentMethod.stripeId)
}
else {
defaultPaymentMethodOption = CustomerPaymentOption.defaultPaymentMethod(for: customerID)
}

// Transform saved PaymentMethods into view models
let savedPMViewModels = savedPaymentMethods.compactMap { paymentMethod in
Expand All @@ -460,7 +474,7 @@ class SavedPaymentOptionsViewController: UIViewController {
let firstPaymentMethodIsLink = !showApplePay && showLink
let defaultIndex = firstPaymentMethodIsLink ? 2 : 1

let defaultSelectedIndex = viewModels.firstIndex(where: { $0 == defaultPaymentMethod }) ?? defaultIndex
let defaultSelectedIndex = viewModels.firstIndex(where: { $0 == defaultPaymentMethodOption }) ?? defaultIndex
return (defaultSelectedIndex, viewModels)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,13 @@ class PaymentSheetFlowControllerViewController: UIViewController, FlowController
isCVCRecollectionEnabled: false,
isTestMode: configuration.apiClient.isTestmode,
allowsRemovalOfLastSavedPaymentMethod: PaymentSheetViewController.allowsRemovalOfLastPaymentMethod(elementsSession: elementsSession, configuration: configuration),
allowsRemovalOfPaymentMethods: elementsSession.allowsRemovalOfPaymentMethodsForPaymentSheet()
allowsRemovalOfPaymentMethods: elementsSession.allowsRemovalOfPaymentMethodsForPaymentSheet(),
allowsSetAsDefaultPM: configuration.allowsSetAsDefaultPM
),
paymentSheetConfiguration: configuration,
intent: intent,
appearance: configuration.appearance,
elementsSession: elementsSession,
cbcEligible: elementsSession.isCardBrandChoiceEligible,
analyticsHelper: analyticsHelper
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ class PaymentSheetVerticalViewController: UIViewController, FlowControllerViewCo
if let selection {
return selection
}
// get default payment method from elements session
if configuration.allowsSetAsDefaultPM,
let defaultPaymentMethod = ElementsCustomer.getDefaultPaymentMethod(from: elementsSession.customer) {
return .saved(paymentMethod: defaultPaymentMethod)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment here as above about being too high in the function, it should not take precedence over previousPaymentOption. See below for where we look for the saved PM.


switch previousPaymentOption {
case .applePay:
Expand Down
Loading
Loading