flutter_stripe
flutter_stripe copied to clipboard
CardField onCardChanged(card) CardFieldInputDetails not correct when editing on Android and iOS
Describe the bug
There's a couple of issues on both Android and iOS for the CardField
's onCardChanged(card)
, which is causing issues for my error handling. The state of the CardFieldInputDetails
complete
, validExpiryDate
, validCVC
, and validNumber
fields are not correct when editing the CardField
widget.
To Reproduce Steps to reproduce the behavior:
- Issues depend on iOS or Android, although some issues are shared, so I'll separate them below:
- Before each bullet point, pre-fill the
CardField
with a valid test card and follow the steps to reproduce.
Android only
- Start editing from the CVC text field and hit backspace once. When removing 1 digit from CVC on Android, the
CardFieldInputDetails
'complete
field is still true. This is an issue because we use thecard?.complete
field to determine whether a user can proceed to pay. - Start editing from CVC and remove the CVC and expiration date. Remove 2 digits from card number and then re-add them in. Note the
CardFieldInputDetails
validNumber
field isCardValidationState.Incomplete
even though it is complete and we have focus on the expiration date text field. Pressing 1 digit into the expiration date textfield changesvalidNumber
toCardValidationState.Valid
but the timing is off here. This also occurs when re-adding the expiration date. It will focus on the CVC textfield, but thevalidExpiryDate
field is stillCardValidationState.Incomplete
until you press 1 digit into the CVC text field.
iOS only
- Start editing from the CVC text field and remove the CVC and expiration date until you are editing the card number. Note that the
CardFieldInputDetails
validExpiryDate
field is stillCardValidationState.Valid
even though it's empty and we're on the card number text field.
Both Android and iOS
- Start editing from the CVC text field and press backspace 3 times to completely remove the CVC. Press backspace 1 more time to remove 1 digit from the expiration date and note that the
CardFieldInputDetails
validExpiryDate
is stillCardValidationState.Valid
. This also happens when we remove the expiration date, press backspace 1 more time to remove 1 digit from the credit card number, and thevalidNumber
field is stillCardValidationState.Valid
.
Expected behavior
I expect the CardFieldInputDetails
that's passed back from onCardChanged(card)
to return correct states for complete
, validExpiryDate
, validCVC
, and validNumber
fields when editing the card information.
Smartphone / tablet
- Device: [e.g. iPhone 13, Android Pixel 4 XL]
- OS: [e.g. iOS 13, Android 11]
- Package version: [e.g. 3.1.0]
- Flutter version [e.g. 2.10.5.]
Additional context I can provide GIFs of these examples if that would help. Just let me know. Not sure if providing the code I tested on is helpful since it's pretty bare-boned, but here it is:
class StripeCardFieldPlaygroundPage extends HookWidget {
@override
Widget build(BuildContext context) {
final controller = CardEditController();
final controllerDetails = useState('');
final formKey = useMemoized(() => GlobalKey<FormState>());
return Scaffold(
appBar: AppBar(
leading: Semantics(
label: 'Back',
child: IconButton(
icon: Image.asset(
'assets/back.png',
color: Theme.of(context).iconTheme.color,
),
onPressed: () => context.popRoute(),
),
),
title: const Text('Stripe Playground'),
),
body: Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CardField(
autofocus: true,
controller: controller,
cursorColor: Colors.black.withOpacity(0.5),
onCardChanged: (card) {
// if (card?.complete ?? false) {
// FocusScope.of(context).unfocus();
// }
controllerDetails.value = '''
Card complete: ${card?.complete}
last4: ${card?.last4}
expiryMonth: ${card?.expiryMonth}
expiryYear: ${card?.expiryYear}
postalCode: ${card?.postalCode}
brand: ${card?.brand}
number: ${card?.number}
cvc: ${card?.cvc}
validExpiryDate: ${card?.validExpiryDate.name}
validCVC: ${card?.validCVC.name}
validNumber: ${card?.validNumber.name}
''';
},
),
Text(controllerDetails.value),
TextButton(
onPressed: () {
formKey.currentState!.validate();
},
child: const Text('Validate CardField'),
)
],
),
),
),
);
}
}
@samleecampspot I did try quite a lot of tests on both android and iOS and the only thing that I could reproduce is the iOS bug. Can you the Android behavior on a real device? If you already did can you explain to me which device you used? I did test on a pixel4a with Android 12 and 13 without any issue.
@remonh87 Hey, thank you for looking into this! So yeah, I did test on a physical Android device (e.g. Google Pixel 4 XL, Android 12). I'll attach some GIFs with the same physical device to give more context on my bullet points, except for the iOS only one since you were able to reproduce that.
From the Android only list:
Start editing from the CVC text field and hit backspace once. When removing 1 digit from CVC on Android, the CardFieldInputDetails' complete field is still true. This is an issue because we use the card?.complete field to determine whether a user can proceed to pay.
Notice how I remove 1 digit from CVC and the card?.complete == true && card?.validCVC == Valid
when the CVC is only 2 digits and incomplete.
Start editing from CVC and remove the CVC and expiration date. Remove 2 digits from card number and then re-add them in. Note the CardFieldInputDetails validNumber field is CardValidationState.Incomplete even though it is complete and we have focus on the expiration date text field. Pressing 1 digit into the expiration date textfield changes validNumber to CardValidationState.Valid but the timing is off here. This also occurs when re-adding the expiration date. It will focus on the CVC textfield, but the validExpiryDate field is still CardValidationState.Incomplete until you press 1 digit into the CVC text field.
So after I re-add the card number, the card?.validNumber == Incomplete
until I input a digit into the expiration date TextField. Same after I re-add the expiration date, the card?.validExpiryDate == Incomplete
until I input a digit into the CVC TextField. The timing is not right here for me.
From the Android and iOS list:
Start editing from the CVC text field and press backspace 3 times to completely remove the CVC. Press backspace 1 more time to remove 1 digit from the expiration date and note that the CardFieldInputDetails validExpiryDate is still CardValidationState.Valid. This also happens when we remove the expiration date, press backspace 1 more time to remove 1 digit from the credit card number, and the validNumber field is still CardValidationState.Valid.
When I remove the CVC and 1 digit from the expiration date, then the card?.validExpiryDate == Valid
. Same when I remove the expiration date and 1 digit from the card number, the card?.validNumber == Valid
.
Let me know if you need any additional information.
Hi @samleecampspot
We released 3.2.0 which includes the following fixes: https://github.com/stripe/stripe-react-native/blob/v0.13.0/CHANGELOG.md#fixes
Fixed an issue on Android where the complete field in the onCardChange callback would incorrectly be set to true even if the postal code wasn't filled out. https://github.com/stripe/stripe-react-native/pull/989
Regarding the iOS issue: it will be fixed with our next version
https://github.com/stripe/stripe-react-native/releases/tag/v0.14.0
Fixed an instance on iOS where CardField's expiry date would remain marked as valid, even when it's invalid. https://github.com/stripe/stripe-react-native/issues/1018
Hey @jonasbark Thank you for the updates! I'm looking forward to testing it out and will report back if I find any other issues.
At least the iOS issue should be fixed with 3.3.0
@jonasbark:
We released 3.2.0 which includes the following fixes: https://github.com/stripe/stripe-react-native/blob/v0.13.0/CHANGELOG.md#fixes
Fixed an issue on Android where the complete field in the onCardChange callback would incorrectly be set to true even if the postal code wasn't filled out. stripe/stripe-react-native#989
Could these changes cause #817 and #808?
At least during my testing with Android all of the issues from the initial post seem to be fixed
I have the same error. Did anybody find some solution ?
I have the same error. Did anybody find some solution ?
There are 2 temporary workarounds mentioned in #817: either downgrade to 3.1.0 or set enablePostalCode
to true.
@romatallinn thanks to you. Worked when i set enablePostalCode to true.
I've tried downgrade flutter_stripe to 3.1.0 but not worked but worked when set enablePostalCode to true.
Hey, @remonh87 when can we expect fix for this in the new version ?
it should be fixed in version 4.0.0 let me know what is missing.
Hi @remonh87, I need to help to save the card details on the stripe, so whenever i have to pay i do not have to enter the card details. I should have to select the previously entered card and good to go.
can anybody help me to do this in flutter ?
I need to help to save the card details on the stripe
You make it happen with SetupIntents.
Learn More:
How can i create "ephemeralKey" in flutter ?
How can i create "ephemeralKey" in flutter ?
You can't. It's the backend's role to create the secret.
How can i create "ephemeralKey" in flutter ?
You can't. It's the backend's role to create the secret.
how can i get it ?
i need it and pass it to the sheet params.
i need it and pass it to the sheet params.
I'm afraid it's beyond what I can describe in a simple comment. You need a backend application that creates this secret and passes it over to the app via API. 80% of the implementation of how you do it -- is up to you.
For the reference, you can read Stripe's Set up future payments guide.
I am trying to implement it for two days but could not understand the flow.
I need to understand the proper flow how card will be saved and retrieve cards on checkout.
i need it and pass it to the sheet params.
I'm afraid it's beyond what I can describe in a simple comment. You need a backend application that creates this secret and passes it over to the app via API. 80% of the implementation of how you do it -- is up to you.
For the reference, you can read Stripe's Set up future payments guide.
I appreciate it. It helped me a lot. Thanks @romatallinn.
Hi @romatallinn I am trying to get the ephemeral key for the customer from the connected account of the stripe but getting this error.
//Creates a temporary secret key linked with the customer
const ephemeralKey = await stripe.ephemeralKeys.create(
{ customer: customerId },
{ apiVersion: '2020-08-27' },
{
stripeAccount: connectedId, // account's access token from the Connect flow
}
);
getting this error.
Stripe: Unknown arguments ([object Object]). Did you mean to pass an options object? See https://github.com/stripe/stripe-node/wiki/Passing-Options. (on API request to POST /ephemeral_keys
)}
Do you know about this ?
this should be fix as off version 4.0.0. Feel free to reopen if the issue still is there.