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);
     }
 }