Stripe-ios: Cannot change the default payment option

Created on 24 Jul 2019  路  13Comments  路  Source: stripe/stripe-ios

Summary

Prebuilt standard integration does not change default payment method.
Selecting a payment method on the paymentOptionsViewController results in a dismissal of the viewcontroller, but when examining the backend, the default card is not changed.
During the same session, if we re-open the paymentOptionsViewController, it shows the correct card being selected as the default option, but when killing the app and relaunching, it correctly pulls data from the backend, and shows no card being selected as a default payment method.

Code to reproduce

iOS version

iOS 13, Xcode 12 Beta

Installation method

Cocoapods

SDK version

16.0.0

Other information

triaged

Most helpful comment

Also I would actually be a little careful with this language https://github.com/stripe/stripe-ios/blob/v16.0.6/Stripe/PublicHeaders/STPPaymentContext.h#L143 as it could cause confusion. Customer objects actually do have native support for "Default payment methods" it's just named a bit confusing. If Invoice.create and Subscription.create aren't provided with a payment_method it waterfalls down to the customer object.

https://stripe.com/docs/api/invoices/create#create_invoice-default_payment_method

ID of the default payment method for the invoice. It must belong to the customer associated with the invoice. If not set, defaults to the subscription鈥檚 default payment method, if any, or to the default payment method in the customer鈥檚 invoice settings.

Similarly for default_source
https://stripe.com/docs/api/invoices/create#create_invoice-default_source

ID of the default payment source for the invoice. It must belong to the customer associated with the invoice and be in a chargeable state. If not set, defaults to the subscription鈥檚 default source, if any, or to the customer鈥檚 default source.

When I create invoices i do not pass these properties, i let stripe take what ever is assigned on the customer object. Remember these are off session.

Here's a question, what if a legacy customer have both a default_source and now also a invoice_settings.default_payment_method, what happens?

All 13 comments

Hi @srikanthsrnvs. The standard integration uses the new PaymentIntent+PaymentMethod API (as opposed to Sources), which does not have a concept of a default in general. You'll need to explicitly specify the PaymentMethod (https://stripe.com/docs/payments/payment-intents/migration#paying-saved-cards) when charging your customer using the PaymentIntent API.

That said, is this issue about which payment method is pre-selected in the UI by default? That seems like a valid thing the UI should be able to do - Stripe doesn't store the 'default' PaymentMethod, but we could expose an API to let you set which one is pre-selected by default. Let me know what you think!

Hi @yuki-stripe
Yes, the new API is definitely needed in order to be able to show the default method, because without this feature the user experience will be deteriorated.

@yuki-stripe Understood. However, we have had several customers complaining about this design, and currently dealing with it by using version 15.0.0 of the Stripe iOS component as a workaround.

Exposing such an API would be ideal for us to bake in our our code to handle this.

@srikanthsrnvs Great! We're tracking this work internally.

Having updated the Android version, and being in the middle of the iOS update. I can confirm that the user experience for the payment flow did deteriorate. We would really like to have a way to set a default payment method.

I agree. Currently the user can add a new payment method and the backend can set this as a default option for paying invoices etc, however there doesn't seem to be a way to reflect this in the iOS app without having a default payment option.

Not sure if it's related, but I need a way to change the invoice_settings.default_payment_method for a customer when they select a new card using the standard integration. Following a paymentContextDidChange can I return the payment_method ID that was selected so that I can update the customer details for a subscription?

@needlerp Yes, that sounds right.

I've merged https://github.com/stripe/stripe-ios/pull/1252 which I believe addresses the issue.
Feel free to open a new one if it doesn't work for you.

I see 16.0.2 has been release. I've successfully retrieved the default payment method for invoices (saved to Stripe.PaymentMethod) but when I pass the variable as follows:
paymentContext.defaultPaymentMethod = Stripe.PaymentMethod
paymentContext.presentPaymentOptionsViewController()

The app crashes when I press the payment method button:
-[STPPaymentContext setDefaultPaymentMethod:]: unrecognized selector sent to instance 0x2803ffb80

Is it not working or am I mis-understanding the issue?

I'm curious about the design arch departure from sources/default_source/customer to payment_methods/customer/invoice_settings.default_payment_method I understand a default_source may not always make contextual sense with SCA, but a payment_method with a off_session setup intent seems like a standard use case. I have business logic that relies on the existence of a default_source and has now gotten messy because of migrating, i now have to normalize legacy and new between default_source and invoice_settings.default_payment_method, additionally using webhooks to catch new attached payment methods as they won't have any defaults set on their own.

For example, lets say an account is in bad standing for NSF, in the past they could remedy the situation by retrying/ adding new source, and it becomes the new default. It allows for very easy state tracking, the backend can now attempt to bring the account into good standing and re-run any pending invoices. I just ran into the issue where new intents users were trying to recover their accounts by adding working cards, but were left bricked because it was using old defaults, which is used to mean "i want to pay for things with this thing". This lead users to believe their new cards were being declined, so they generated even more payment methods. When i caught it, now i have a collection of payment methods without really any idea of what the user intended.

Other problem i now have to deal with is existing users that utilized default_source, upgraded and added a new payment method(s) that are essentially orphaned. So i ended up having to add a bunch of branching to normalize, new webhooks to intercept and set defaults, so the business logic can continue to function just until i can roll out my own default payment state tracking.

Hopefully that makes sense, it's N/A for on_session but pretty valuable for off_session by very nature of being off session.

If i'm creating an intent with customer and payment method supplied I really need a way to tell stripe to make it the default payment method for the customer. This would solve all of my headaches. That's literally what the customer believes they are doing with the intent i'm creating.

let setupIntentParams = STPSetupIntentConfirmParams(clientSecret: setupIntent.clientSecret)
setupIntentParams.paymentMethodID = paymentMethod.stripeId
setupIntent.usage = "off_session"
setupIntent.customer = customerID
setupIntent.defaultPaymentMethod = true // <------ i really need this!!

Also I would actually be a little careful with this language https://github.com/stripe/stripe-ios/blob/v16.0.6/Stripe/PublicHeaders/STPPaymentContext.h#L143 as it could cause confusion. Customer objects actually do have native support for "Default payment methods" it's just named a bit confusing. If Invoice.create and Subscription.create aren't provided with a payment_method it waterfalls down to the customer object.

https://stripe.com/docs/api/invoices/create#create_invoice-default_payment_method

ID of the default payment method for the invoice. It must belong to the customer associated with the invoice. If not set, defaults to the subscription鈥檚 default payment method, if any, or to the default payment method in the customer鈥檚 invoice settings.

Similarly for default_source
https://stripe.com/docs/api/invoices/create#create_invoice-default_source

ID of the default payment source for the invoice. It must belong to the customer associated with the invoice and be in a chargeable state. If not set, defaults to the subscription鈥檚 default source, if any, or to the customer鈥檚 default source.

When I create invoices i do not pass these properties, i let stripe take what ever is assigned on the customer object. Remember these are off session.

Here's a question, what if a legacy customer have both a default_source and now also a invoice_settings.default_payment_method, what happens?

I'm curious about the design arch departure from sources/default_source/customer to payment_methods/customer/invoice_settings.default_payment_method I understand a default_source may not always make contextual sense with SCA, but a payment_method with a off_session setup intent seems like a standard use case. I have business logic that relies on the existence of a default_source and has now gotten messy because of migrating, i now have to normalize legacy and new between default_source and invoice_settings.default_payment_method, additionally using webhooks to catch new attached payment methods as they won't have any defaults set on their own.

For example, lets say an account is in bad standing for NSF, in the past they could remedy the situation by retrying/ adding new source, and it becomes the new default. It allows for very easy state tracking, the backend can now attempt to bring the account into good standing and re-run any pending invoices. I just ran into the issue where new intents users were trying to recover their accounts by adding working cards, but were left bricked because it was using old defaults, which is used to mean "i want to pay for things with this thing". This lead users to believe their new cards were being declined, so they generated even more payment methods. When i caught it, now i have a collection of payment methods without really any idea of what the user intended.

Other problem i now have to deal with is existing users that utilized default_source, upgraded and added a new payment method(s) that are essentially orphaned. So i ended up having to add a bunch of branching to normalize, new webhooks to intercept and set defaults, so the business logic can continue to function just until i can roll out my own default payment state tracking.

Hopefully that makes sense, it's N/A for on_session but pretty valuable for off_session by very nature of being off session.

Was this page helpful?
0 / 5 - 0 ratings