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

SCA WebView crashes #1645

Closed
hitokiri82 opened this issue Oct 1, 2019 · 18 comments
Closed

SCA WebView crashes #1645

hitokiri82 opened this issue Oct 1, 2019 · 18 comments
Assignees
Labels
triaged Issue has been reviewed by Stripe and is being tracked internally

Comments

@hitokiri82
Copy link

hitokiri82 commented Oct 1, 2019

Summary

I'm running some tests with storing a card that requires SCA for charging it later. I'm using Stripe's test cards as found here.
The issue I'm running into is that the WebView with Stripe's 3DS authorization page work fine as long as I always authorize the operation. If I fail the authorization the WebView stops working for all subsequent requests, for example if I try to add another card the WebView starts, shows a little spinner for a short period, never shows Stripe authorization mock page and eventually the screen goes blank, and then I'm back at my app's last screen. In my activity's onActivityResult I receive the correct requestCode, resultCode is RESULT_CANCELED and data is null.

Once that happens once, it will happen again every time I cede control to stripe-android for authorizing any operation. The only way to reset this behavior is uninstalling the app and installing it again, in which case everything will work fine until I intentionally fail an authorization.

When the error happens, the log (which I am attaching to the issue) shows a block that starts with --------- beginning of crash and that contains these lines:
2019-10-01 17:34:03.249 11000-11000/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x54

2019-10-01 17:34:03.249 11000-11000/? A/DEBUG: Cause: null pointer dereference

Code to reproduce

package com.mycompany.myapp.topup.ui;


import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.NonNull;

import com.mycompany.myapp.Constants;
import com.mycompany.myapp.R;
import com.mycompany.myapp.account.async.AccountTasks;
import com.mycompany.myapp.topup.domain.events.AssignPaymentMethodEvent;
import com.mycompany.myapp.core.ui.AbstractActivity;
import com.mycompany.myapp.topup.domain.events.NewSetupIntentEvent;
import com.squareup.otto.Subscribe;
import com.stripe.android.ApiResultCallback;
import com.stripe.android.PaymentConfiguration;
import com.stripe.android.SetupIntentResult;
import com.stripe.android.Stripe;
import com.stripe.android.model.Card;
import com.stripe.android.model.ConfirmSetupIntentParams;
import com.stripe.android.model.PaymentMethod;
import com.stripe.android.model.PaymentMethodCreateParams;
import com.stripe.android.model.SetupIntent;
import com.stripe.android.view.CardMultilineWidget;

import butterknife.ButterKnife;
import butterknife.BindView;
import butterknife.OnClick;

public class NewCreditCardActivity extends AbstractActivity {

    @BindView(R.id.card_widget)
    CardMultilineWidget cardWidget;

    @BindView(R.id.save)
    Button saveButton;

    private ProgressDialog progressDialog;

    private Stripe mStripe;

    private String clientSecret;

    private PaymentMethod paymentMethod;

    public static Intent createIntent(Context context) {
        return new Intent(context, NewCreditCardActivity.class);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new_credit_card);

        ButterKnife.bind(this);

        PaymentConfiguration.init(this, Constants.STRIPE_PUBLIC_KEY);
        mStripe = new Stripe(this,
                PaymentConfiguration.getInstance(this).getPublishableKey());

        progressDialog = ProgressDialog.show(
        NewCreditCardActivity.this, "",
        "Setting up", true);
        AccountTasks.getSetupIntent(this);
    }

    private void confirmSetupIntent(@NonNull String paymentMethodId) {
        mStripe.confirmSetupIntent(
                this,
                ConfirmSetupIntentParams.create(paymentMethodId, clientSecret)
        );
    }

    @OnClick(R.id.save)
    public void onSavePressed(View v){

        final Card cardToSave = cardWidget.getCard();
        if (cardToSave != null) {
            saveButton.setEnabled(false);
            progressDialog = ProgressDialog.show(NewCreditCardActivity.this, "",
                                                 getString(R.string.processing_credit_card),
                                     true);
            PaymentMethodCreateParams.Card paymentMethodParamsCard =
                    cardToSave.toPaymentMethodParamsCard();
            PaymentMethodCreateParams cardPaymentMethodCreateParams =
                    PaymentMethodCreateParams.create(paymentMethodParamsCard);
            mStripe.createPaymentMethod(cardPaymentMethodCreateParams,
                    new ApiResultCallback<PaymentMethod>() {
                        @Override
                        public void onSuccess(@NonNull PaymentMethod result) {
                            paymentMethod = result;
                            confirmSetupIntent(result.id);
                        }

                        @Override
                        public void onError(@NonNull Exception e) {
                            Log.e(Constants.LOGTAG, "Error creating PaymentMethod", e);
                            Toast.makeText(getApplicationContext(), e.getMessage(),
                                    Toast.LENGTH_LONG).show();
                            progressDialog.dismiss();
                            Intent returnIntent = new Intent();
                            setResult(RESULT_CANCELED, returnIntent);
                            finish();
                        }
                    });

        }
    }


    @Subscribe
    public void onNewSetupIntentEvent(NewSetupIntentEvent event) {

        progressDialog.dismiss();

        clientSecret = event.getSetupIntent().getClientSecret();
    }

    @Subscribe
    public void onAssignPaymentMethodEvent(AssignPaymentMethodEvent event) {

        progressDialog.dismiss();

        Intent returnIntent = new Intent();
        setResult(RESULT_OK, returnIntent);
        finish();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(Constants.LOGTAG, "SETUP_RESULT: requestCode: " + requestCode);
        Log.d(Constants.LOGTAG, "SETUP_RESULT: resultCode: " + resultCode);
        Log.d(Constants.LOGTAG, "SETUP_RESULT: data: " + data);

        super.onActivityResult(requestCode, resultCode, data);
        if (data == null){
            Intent returnIntent = new Intent();
            setResult(RESULT_CANCELED, returnIntent);
            finish();
        }
        Log.d(Constants.LOGTAG, "SETUP_RESULT:after the super()");
        mStripe.onSetupResult(requestCode, data,
                new ApiResultCallback<SetupIntentResult>() {
                    @Override
                    public void onSuccess(@NonNull SetupIntentResult result) {
                        // If confirmation and authentication succeeded,
                        // the SetupIntent will have user actions resolved;
                        // otherwise, handle the failure as appropriate
                        // (e.g. the customer may need to choose a new payment
                        // method)
                        Log.d(Constants.LOGTAG, "SETUP_RESULT: Outcome" + result.getOutcome());
                        final SetupIntent setupIntent = result.getIntent();
                        final SetupIntent.Status status =
                                setupIntent.getStatus();
                        Log.d(Constants.LOGTAG, "SETUP_RESULT: Intent Status" + status);
                        if (status == SetupIntent.Status.Succeeded) {
                            AccountTasks.assignPaymentMethod(getApplicationContext(), paymentMethod.id);
                        } else if (setupIntent.requiresConfirmation()) {
                            // handle confirmation
                            Toast.makeText(getApplicationContext(), "No se pudo procesar la tarjeta",
                                    Toast.LENGTH_LONG).show();
                            Intent returnIntent = new Intent();
                            setResult(RESULT_CANCELED, returnIntent);
                            finish();
                        }
                    }

                    @Override
                    public void onError(@NonNull Exception e) {
                        // handle error
                        Log.d(Constants.LOGTAG, "ERR SETUP_RESULT: " + e);
                        progressDialog.dismiss();
                        Toast.makeText(getApplicationContext(), e.getLocalizedMessage(),
                                Toast.LENGTH_LONG).show();
                        Intent returnIntent = new Intent();
                        setResult(RESULT_CANCELED, returnIntent);
                        finish();
                    }
                });
        Log.d(Constants.LOGTAG, "SETUP_RESULT:after the call to Stripe API");
    }
}

Android version

9

Impacted devices

Pixel 3a

Installation method

Gradle

SDK version

com.stripe:stripe-android:11.1.4

Log

crash_log.txt

@mshafrir-stripe
Copy link
Collaborator

@hitokiri82 thanks for filing. Did this issue start happening after upgrading from an earlier Stripe SDK version? Does it happen on different Android API versions? How about emulator vs physical device? Also, can you provide a video recording of the behavior you're describing? This will help me investigate.

@hitokiri82
Copy link
Author

@mshafrir-stripe I have tried with SDK version 11.1.3 and 11.1.4. Havent tried on different Android API versions, nor in an emulator. I will try those out and let you know if it behaves differently.
Is there any way I can get the video to you without posting it here publicly?

@mshafrir-stripe
Copy link
Collaborator

@hitokiri82 can you add your email address to your profile? I can contact you there.

@hitokiri82
Copy link
Author

Done

@mshafrir-stripe mshafrir-stripe self-assigned this Oct 2, 2019
@mshafrir-stripe mshafrir-stripe added the triaged Issue has been reviewed by Stripe and is being tracked internally label Oct 2, 2019
@JulienDev
Copy link

JulienDev commented Oct 7, 2019

I have exactly the same issue on a Pixel 3A XL. No problem with my Pixel 3 XL on the same Android version (10). I've tested with two different versions of the SDK : 10.2.1 and 11.1.4

@mshafrir-stripe
Copy link
Collaborator

@JulienDev you're seeing the crash on both 10.2.1 and 11.1.4?

@JulienDev
Copy link

Yes

@mshafrir-stripe
Copy link
Collaborator

@JulienDev can you share a stacktrace?

@JulienDev
Copy link

Sure, but not publicly, please contact me by email

@mshafrir-stripe
Copy link
Collaborator

@JulienDev can you give me the exact steps to repro the crash? For example, what do you see and what do you click on?

@JulienDev
Copy link

The SCA payment activity is shown with a loader, and when the loader disappear the app crash with the stacktrace I've provided. Nothing is shown inside the webview, it just stays blank

@jasonblood
Copy link

jasonblood commented Oct 9, 2019

We are seeing the same on a One Plus 7 pro on Android 10. Have not reproduced it on another device. It did seem to go away for awhile. But it keeps happening again. Tried uninstalling the app etc and it still keeps happening. We are only on stripe sdk 10.3.1 so we are going to update to the latest and see if that resolves it. But sounds like its happening on more newer version of sdk anyway.

@mshafrir-stripe
Copy link
Collaborator

@jasonblood thanks for reporting this issue. I have a potential fix for the issue, but I can't reproduce the crash myself, so would you be able to confirm?

You'll need to update your top-level build.gradle to include:

	allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}

Then update your dependency to:

implementation 'com.github.stripe:stripe-android:98ce93b112'

@jasonblood
Copy link

Hi @mshafrir-stripe, Will do, sadly I won't be able to do it until Monday but will post back here if no one else beats me to it!

@mshafrir-stripe
Copy link
Collaborator

This should now be resolved in 11.2.2. Please confirm if it's resolved on your end.

@jasonblood
Copy link

Hi @mshafrir-stripe,

That seems to have fixed the issue alright for me. Nice work! Thanks for the help and quick response.

@mshafrir-stripe
Copy link
Collaborator

@jasonblood that's great to hear. Thanks for confirming.

@hitokiri82
Copy link
Author

Fixed it for me too. Great job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triaged Issue has been reviewed by Stripe and is being tracked internally
Projects
None yet
Development

No branches or pull requests

4 participants