flutter_stripe icon indicating copy to clipboard operation
flutter_stripe copied to clipboard

CardField onCardChanged(card) CardFieldInputDetails not correct when editing on Android and iOS

Open samleecampspot opened this issue 2 years ago • 23 comments

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 the card?.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 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.

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 still CardValidationState.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 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.

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 avatar Jun 15 '22 23:06 samleecampspot

@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 avatar Jun 26 '22 14:06 remonh87

@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.

a-1

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.

android2

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.

a-3

Let me know if you need any additional information.

samleecampspot avatar Jun 27 '22 17:06 samleecampspot

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

jonasbark avatar Jul 03 '22 08:07 jonasbark

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

jonasbark avatar Jul 03 '22 08:07 jonasbark

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.

samleecampspot avatar Jul 05 '22 22:07 samleecampspot

At least the iOS issue should be fixed with 3.3.0

jonasbark avatar Jul 12 '22 14:07 jonasbark

@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?

MrLightful avatar Jul 13 '22 21:07 MrLightful

At least during my testing with Android all of the issues from the initial post seem to be fixed

jonasbark avatar Jul 14 '22 14:07 jonasbark

I have the same error. Did anybody find some solution ?

mhanzla80 avatar Jul 14 '22 15:07 mhanzla80

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.

MrLightful avatar Jul 14 '22 15:07 MrLightful

@romatallinn thanks to you. Worked when i set enablePostalCode to true.

mhanzla80 avatar Jul 14 '22 15:07 mhanzla80

I've tried downgrade flutter_stripe to 3.1.0 but not worked but worked when set enablePostalCode to true.

mhanzla80 avatar Jul 14 '22 15:07 mhanzla80

Hey, @remonh87 when can we expect fix for this in the new version ?

niteshsh4rma avatar Jul 21 '22 14:07 niteshsh4rma

it should be fixed in version 4.0.0 let me know what is missing.

remonh87 avatar Jul 22 '22 07:07 remonh87

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 ?

mhanzla80 avatar Jul 25 '22 12:07 mhanzla80

I need to help to save the card details on the stripe

You make it happen with SetupIntents.

Learn More:

MrLightful avatar Jul 25 '22 12:07 MrLightful

How can i create "ephemeralKey" in flutter ?

mhanzla80 avatar Jul 25 '22 13:07 mhanzla80

How can i create "ephemeralKey" in flutter ?

You can't. It's the backend's role to create the secret.

MrLightful avatar Jul 25 '22 14:07 MrLightful

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 ? Screenshot 2022-07-25 at 7 10 02 PM

i need it and pass it to the sheet params.

mhanzla80 avatar Jul 25 '22 14:07 mhanzla80

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.

MrLightful avatar Jul 25 '22 14:07 MrLightful

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.

mhanzla80 avatar Jul 25 '22 14:07 mhanzla80

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.

mhanzla80 avatar Jul 25 '22 14:07 mhanzla80

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 ?

mhanzla80 avatar Jul 27 '22 11:07 mhanzla80

this should be fix as off version 4.0.0. Feel free to reopen if the issue still is there.

remonh87 avatar Nov 14 '22 19:11 remonh87