From 6e2998292a4b96e59c0e9c927bbccdb5a97f49be Mon Sep 17 00:00:00 2001 From: Michael Shafrir <mshafrir@stripe.com> Date: Mon, 29 Jul 2019 14:55:03 -0400 Subject: [PATCH] Add Status flag to PaymentAuthWebViewActivity result Browser-based auth was previously not returning a `StripeIntentResult.Status`. Fixes #1253 --- .../example/activity/PaymentAuthActivity.java | 1 + .../android/view/PaymentAuthWebView.java | 36 ++++++++++------- .../view/PaymentAuthWebViewActivity.java | 30 +++++++++++--- .../android/view/PaymentAuthWebViewTest.java | 40 ++++++++++--------- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/example/src/main/java/com/stripe/example/activity/PaymentAuthActivity.java b/example/src/main/java/com/stripe/example/activity/PaymentAuthActivity.java index 666623609a7..7803f9ad427 100644 --- a/example/src/main/java/com/stripe/example/activity/PaymentAuthActivity.java +++ b/example/src/main/java/com/stripe/example/activity/PaymentAuthActivity.java @@ -244,6 +244,7 @@ public void onSuccess(@NonNull PaymentIntentResult paymentIntentResult) { final PaymentIntent paymentIntent = paymentIntentResult.getIntent(); activity.mStatusTextView.append("\n\n" + + "Auth status: " + paymentIntentResult.getStatus() + "\n\n" + activity.getString(R.string.payment_intent_status, paymentIntent.getStatus())); activity.onAuthComplete(); } diff --git a/stripe/src/main/java/com/stripe/android/view/PaymentAuthWebView.java b/stripe/src/main/java/com/stripe/android/view/PaymentAuthWebView.java index 1a687696a4b..b747e8a77df 100644 --- a/stripe/src/main/java/com/stripe/android/view/PaymentAuthWebView.java +++ b/stripe/src/main/java/com/stripe/android/view/PaymentAuthWebView.java @@ -2,7 +2,6 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; -import android.app.Activity; import android.content.Context; import android.net.Uri; import android.os.Build; @@ -14,17 +13,14 @@ import android.webkit.WebViewClient; import android.widget.ProgressBar; -import com.stripe.android.R; +import com.stripe.android.StripeIntentResult; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** - * A {@link WebView} used for authenticating payment and deep-linking the user back to the - * PaymentIntent's return_url[0]. - * <p> - * [0] https://stripe.com/docs/api/payment_intents/confirm#confirm_payment_intent-return_url + * A {@link WebView} used for authenticating payment details */ class PaymentAuthWebView extends WebView { @SuppressWarnings("RedundantModifier") @@ -45,8 +41,10 @@ public PaymentAuthWebView(@NonNull Context context, @Nullable AttributeSet attrs configureSettings(); } - void init(@NonNull Activity activity, @NonNull String clientSecret, @NonNull String returnUrl) { - setWebViewClient(new PaymentAuthWebViewClient(activity, clientSecret, returnUrl)); + void init(@NonNull PaymentAuthWebViewClient.Listener listener, @NonNull ProgressBar progressBar, + @NonNull String clientSecret, @NonNull String returnUrl) { + setWebViewClient(new PaymentAuthWebViewClient(listener, progressBar, clientSecret, + returnUrl)); } @SuppressLint("SetJavaScriptEnabled") @@ -65,17 +63,17 @@ static class PaymentAuthWebViewClient extends WebViewClient { "https://hooks.stripe.com/3d_secure/complete/tdsrc_" )); - @NonNull private final Activity mActivity; @NonNull private final String mClientSecret; @Nullable private final Uri mReturnUrl; @NonNull private final ProgressBar mProgressBar; + @NonNull private final Listener mListener; - PaymentAuthWebViewClient(@NonNull Activity activity, @NonNull String clientSecret, - @Nullable String returnUrl) { - mActivity = activity; + PaymentAuthWebViewClient(@NonNull Listener listener, @NonNull ProgressBar progressBar, + @NonNull String clientSecret, @Nullable String returnUrl) { + mListener = listener; mClientSecret = clientSecret; mReturnUrl = returnUrl != null ? Uri.parse(returnUrl) : null; - mProgressBar = activity.findViewById(R.id.auth_web_view_progress_bar); + mProgressBar = progressBar; } @Override @@ -88,7 +86,7 @@ public void onPageCommitVisible(@NonNull WebView view, @NonNull String url) { public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); if (url != null && isCompletionUrl(url)) { - mActivity.finish(); + onAuthCompleted(); } } @@ -107,7 +105,7 @@ public boolean shouldOverrideUrlLoading(@NonNull WebView view, @NonNull String urlString) { final Uri uri = Uri.parse(urlString); if (isReturnUrl(uri)) { - mActivity.finish(); + onAuthCompleted(); return true; } @@ -154,5 +152,13 @@ private boolean isReturnUrl(@NonNull Uri uri) { private boolean isPredefinedReturnUrl(@NonNull Uri uri) { return "stripejs://use_stripe_sdk/return_url".equals(uri.toString()); } + + private void onAuthCompleted() { + mListener.onAuthCompleted(StripeIntentResult.Status.SUCCEEDED); + } + + interface Listener { + void onAuthCompleted(@StripeIntentResult.Status int status); + } } } diff --git a/stripe/src/main/java/com/stripe/android/view/PaymentAuthWebViewActivity.java b/stripe/src/main/java/com/stripe/android/view/PaymentAuthWebViewActivity.java index ab9abb567ba..370939adf10 100644 --- a/stripe/src/main/java/com/stripe/android/view/PaymentAuthWebViewActivity.java +++ b/stripe/src/main/java/com/stripe/android/view/PaymentAuthWebViewActivity.java @@ -12,18 +12,23 @@ import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; +import android.widget.ProgressBar; import com.stripe.android.PaymentAuthWebViewStarter; import com.stripe.android.R; +import com.stripe.android.StripeIntentResult; import com.stripe.android.StripeTextUtils; import com.stripe.android.stripe3ds2.init.ui.ToolbarCustomization; import com.stripe.android.stripe3ds2.utils.CustomizeUtils; import static com.ults.listeners.SdkChallengeInterface.UL_HANDLE_CHALLENGE_ACTION; -public class PaymentAuthWebViewActivity extends AppCompatActivity { +public class PaymentAuthWebViewActivity + extends AppCompatActivity + implements PaymentAuthWebView.PaymentAuthWebViewClient.Listener { @Nullable private ToolbarCustomization mToolbarCustomization; + private Intent mResultIntent; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -44,11 +49,12 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { final String returnUrl = getIntent() .getStringExtra(PaymentAuthWebViewStarter.EXTRA_RETURN_URL); - setResult(Activity.RESULT_OK, - new Intent().putExtra(StripeIntentResultExtras.CLIENT_SECRET, clientSecret)); + mResultIntent = new Intent() + .putExtra(StripeIntentResultExtras.CLIENT_SECRET, clientSecret); final PaymentAuthWebView webView = findViewById(R.id.auth_web_view); - webView.init(this, clientSecret, returnUrl); + final ProgressBar progressBar = findViewById(R.id.auth_web_view_progress_bar); + webView.init(this, progressBar, clientSecret, returnUrl); webView.loadUrl(getIntent().getStringExtra(PaymentAuthWebViewStarter.EXTRA_AUTH_URL)); } @@ -65,16 +71,21 @@ public boolean onCreateOptionsMenu(Menu menu) { return super.onCreateOptionsMenu(menu); } + @Override + public void onBackPressed() { + onAuthCompleted(StripeIntentResult.Status.CANCELED); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.action_close) { - finish(); + onAuthCompleted(StripeIntentResult.Status.CANCELED); return true; } return super.onOptionsItemSelected(item); } - void customizeToolbar(@NonNull Toolbar toolbar) { + private void customizeToolbar(@NonNull Toolbar toolbar) { if (mToolbarCustomization != null) { if (!StripeTextUtils.isBlank(mToolbarCustomization.getHeaderText())) { toolbar.setTitle(CustomizeUtils.buildStyledText(this, @@ -90,4 +101,11 @@ void customizeToolbar(@NonNull Toolbar toolbar) { } } } + + @Override + public void onAuthCompleted(@StripeIntentResult.Status int status) { + setResult(Activity.RESULT_OK, + mResultIntent.putExtra(StripeIntentResultExtras.AUTH_STATUS, status)); + finish(); + } } diff --git a/stripe/src/test/java/com/stripe/android/view/PaymentAuthWebViewTest.java b/stripe/src/test/java/com/stripe/android/view/PaymentAuthWebViewTest.java index 2dd11c9d799..b93f546264e 100644 --- a/stripe/src/test/java/com/stripe/android/view/PaymentAuthWebViewTest.java +++ b/stripe/src/test/java/com/stripe/android/view/PaymentAuthWebViewTest.java @@ -1,7 +1,9 @@ package com.stripe.android.view; -import android.app.Activity; import android.webkit.WebView; +import android.widget.ProgressBar; + +import com.stripe.android.StripeIntentResult; import org.junit.Before; import org.junit.Test; @@ -10,13 +12,15 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @RunWith(RobolectricTestRunner.class) public class PaymentAuthWebViewTest { - @Mock private Activity mActivity; + @Mock private PaymentAuthWebView.PaymentAuthWebViewClient.Listener mListener; + @Mock private ProgressBar mProgressBar; @Mock private WebView mWebView; @Before @@ -29,11 +33,11 @@ public void shouldOverrideUrlLoading_withPaymentIntent_shouldSetResult() { final String deepLink = "stripe://payment_intent_return?payment_intent=pi_123&" + "payment_intent_client_secret=pi_123_secret_456&source_type=card"; final PaymentAuthWebView.PaymentAuthWebViewClient paymentAuthWebViewClient = - new PaymentAuthWebView.PaymentAuthWebViewClient(mActivity, + new PaymentAuthWebView.PaymentAuthWebViewClient(mListener, mProgressBar, "pi_123_secret_456", "stripe://payment_intent_return"); paymentAuthWebViewClient.shouldOverrideUrlLoading(mWebView, deepLink); - verify(mActivity).finish(); + verify(mListener).onAuthCompleted(StripeIntentResult.Status.SUCCEEDED); } @Test @@ -42,11 +46,11 @@ public void shouldOverrideUrlLoading_withSetupIntent_shouldSetResult() { "&setup_intent_client_secret=seti_1234_secret_5678&source_type=card"; final PaymentAuthWebView.PaymentAuthWebViewClient paymentAuthWebViewClient = - new PaymentAuthWebView.PaymentAuthWebViewClient(mActivity, + new PaymentAuthWebView.PaymentAuthWebViewClient(mListener, mProgressBar, "seti_1234_secret_5678", "stripe://payment_auth"); paymentAuthWebViewClient.shouldOverrideUrlLoading(mWebView, deepLink); - verify(mActivity).finish(); + verify(mListener).onAuthCompleted(StripeIntentResult.Status.SUCCEEDED); } @Test @@ -54,10 +58,10 @@ public void shouldOverrideUrlLoading_withoutReturnUrl_onPaymentIntentImplicitRet final String deepLink = "stripe://payment_intent_return?payment_intent=pi_123&" + "payment_intent_client_secret=pi_123_secret_456&source_type=card"; final PaymentAuthWebView.PaymentAuthWebViewClient paymentAuthWebViewClient = - new PaymentAuthWebView.PaymentAuthWebViewClient(mActivity, + new PaymentAuthWebView.PaymentAuthWebViewClient(mListener, mProgressBar, "pi_123_secret_456", null); paymentAuthWebViewClient.shouldOverrideUrlLoading(mWebView, deepLink); - verify(mActivity).finish(); + verify(mListener).onAuthCompleted(StripeIntentResult.Status.SUCCEEDED); } @Test @@ -65,49 +69,49 @@ public void shouldOverrideUrlLoading_withoutReturnUrl_onSetupIntentImplicitRetur final String deepLink = "stripe://payment_auth?setup_intent=seti_1234" + "&setup_intent_client_secret=seti_1234_secret_5678&source_type=card"; final PaymentAuthWebView.PaymentAuthWebViewClient paymentAuthWebViewClient = - new PaymentAuthWebView.PaymentAuthWebViewClient(mActivity, + new PaymentAuthWebView.PaymentAuthWebViewClient(mListener, mProgressBar, "seti_1234_secret_5678", null); paymentAuthWebViewClient.shouldOverrideUrlLoading(mWebView, deepLink); - verify(mActivity).finish(); + verify(mListener).onAuthCompleted(StripeIntentResult.Status.SUCCEEDED); } @Test public void shouldOverrideUrlLoading_withoutReturnUrl_shouldNotAutoFinishActivity() { final PaymentAuthWebView.PaymentAuthWebViewClient paymentAuthWebViewClient = - new PaymentAuthWebView.PaymentAuthWebViewClient(mActivity, + new PaymentAuthWebView.PaymentAuthWebViewClient(mListener, mProgressBar, "pi_123_secret_456", null); paymentAuthWebViewClient.shouldOverrideUrlLoading(mWebView, "https://example.com"); - verify(mActivity, never()).finish(); + verify(mListener, never()).onAuthCompleted(anyInt()); } @Test public void shouldOverrideUrlLoading_witKnownReturnUrl_shouldFinish() { final PaymentAuthWebView.PaymentAuthWebViewClient paymentAuthWebViewClient = - new PaymentAuthWebView.PaymentAuthWebViewClient(mActivity, + new PaymentAuthWebView.PaymentAuthWebViewClient(mListener, mProgressBar, "pi_123_secret_456", null); paymentAuthWebViewClient.shouldOverrideUrlLoading(mWebView, "stripejs://use_stripe_sdk/return_url"); - verify(mActivity).finish(); + verify(mListener).onAuthCompleted(StripeIntentResult.Status.SUCCEEDED); } @Test public void onPageFinished_wit3DSecureCompleteUrl_shouldFinish() { final PaymentAuthWebView.PaymentAuthWebViewClient paymentAuthWebViewClient = - new PaymentAuthWebView.PaymentAuthWebViewClient(mActivity, + new PaymentAuthWebView.PaymentAuthWebViewClient(mListener, mProgressBar, "pi_123_secret_456", null); paymentAuthWebViewClient.onPageFinished(mWebView, "https://hooks.stripe.com/3d_secure/complete/tdsrc_1ExLWoCRMbs6FrXfjPJRYtng"); - verify(mActivity).finish(); + verify(mListener).onAuthCompleted(StripeIntentResult.Status.SUCCEEDED); } @Test public void onPageFinished_witRedirectCompleteUrl_shouldFinish() { final PaymentAuthWebView.PaymentAuthWebViewClient paymentAuthWebViewClient = - new PaymentAuthWebView.PaymentAuthWebViewClient(mActivity, + new PaymentAuthWebView.PaymentAuthWebViewClient(mListener, mProgressBar, "pi_123_secret_456", null); paymentAuthWebViewClient.onPageFinished(mWebView, "https://hooks.stripe.com/redirect/complete/src_1ExLWoCRMbs6FrXfjPJRYtng"); - verify(mActivity).finish(); + verify(mListener).onAuthCompleted(StripeIntentResult.Status.SUCCEEDED); } }