Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.payments; | 5 package org.chromium.chrome.browser.payments; |
| 6 | 6 |
| 7 import android.app.Activity; | 7 import android.app.Activity; |
| 8 import android.graphics.Bitmap; | 8 import android.graphics.Bitmap; |
| 9 import android.os.Handler; | 9 import android.os.Handler; |
| 10 import android.support.v4.util.ArrayMap; | |
| 10 import android.text.TextUtils; | 11 import android.text.TextUtils; |
| 11 | 12 |
| 12 import org.json.JSONException; | 13 import org.json.JSONException; |
| 13 import org.json.JSONObject; | 14 import org.json.JSONObject; |
| 14 | 15 |
| 15 import org.chromium.base.Callback; | 16 import org.chromium.base.Callback; |
| 16 import org.chromium.base.Log; | 17 import org.chromium.base.Log; |
| 17 import org.chromium.base.VisibleForTesting; | 18 import org.chromium.base.VisibleForTesting; |
| 18 import org.chromium.base.metrics.RecordHistogram; | 19 import org.chromium.base.metrics.RecordHistogram; |
| 19 import org.chromium.chrome.R; | 20 import org.chromium.chrome.R; |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 34 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; | 35 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; |
| 35 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; | 36 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; |
| 36 import org.chromium.chrome.browser.tabmodel.TabModel; | 37 import org.chromium.chrome.browser.tabmodel.TabModel; |
| 37 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; | 38 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; |
| 38 import org.chromium.chrome.browser.tabmodel.TabModelObserver; | 39 import org.chromium.chrome.browser.tabmodel.TabModelObserver; |
| 39 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; | 40 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; |
| 40 import org.chromium.components.safejson.JsonSanitizer; | 41 import org.chromium.components.safejson.JsonSanitizer; |
| 41 import org.chromium.components.url_formatter.UrlFormatter; | 42 import org.chromium.components.url_formatter.UrlFormatter; |
| 42 import org.chromium.content_public.browser.WebContents; | 43 import org.chromium.content_public.browser.WebContents; |
| 43 import org.chromium.mojo.system.MojoException; | 44 import org.chromium.mojo.system.MojoException; |
| 45 import org.chromium.payments.mojom.ActivePaymentQueryResult; | |
| 44 import org.chromium.payments.mojom.PaymentComplete; | 46 import org.chromium.payments.mojom.PaymentComplete; |
| 45 import org.chromium.payments.mojom.PaymentDetails; | 47 import org.chromium.payments.mojom.PaymentDetails; |
| 46 import org.chromium.payments.mojom.PaymentErrorReason; | 48 import org.chromium.payments.mojom.PaymentErrorReason; |
| 47 import org.chromium.payments.mojom.PaymentItem; | 49 import org.chromium.payments.mojom.PaymentItem; |
| 48 import org.chromium.payments.mojom.PaymentMethodData; | 50 import org.chromium.payments.mojom.PaymentMethodData; |
| 49 import org.chromium.payments.mojom.PaymentOptions; | 51 import org.chromium.payments.mojom.PaymentOptions; |
| 50 import org.chromium.payments.mojom.PaymentRequest; | 52 import org.chromium.payments.mojom.PaymentRequest; |
| 51 import org.chromium.payments.mojom.PaymentRequestClient; | 53 import org.chromium.payments.mojom.PaymentRequestClient; |
| 52 import org.chromium.payments.mojom.PaymentResponse; | 54 import org.chromium.payments.mojom.PaymentResponse; |
| 53 import org.chromium.payments.mojom.PaymentShippingOption; | 55 import org.chromium.payments.mojom.PaymentShippingOption; |
| 54 import org.chromium.payments.mojom.PaymentShippingType; | 56 import org.chromium.payments.mojom.PaymentShippingType; |
| 55 | 57 |
| 56 import java.io.IOException; | 58 import java.io.IOException; |
| 57 import java.util.ArrayList; | 59 import java.util.ArrayList; |
| 58 import java.util.Arrays; | 60 import java.util.Arrays; |
| 59 import java.util.Collections; | 61 import java.util.Collections; |
| 60 import java.util.Comparator; | 62 import java.util.Comparator; |
| 61 import java.util.HashMap; | |
| 62 import java.util.HashSet; | 63 import java.util.HashSet; |
| 63 import java.util.List; | 64 import java.util.List; |
| 64 import java.util.Locale; | 65 import java.util.Locale; |
| 65 import java.util.Map; | 66 import java.util.Map; |
| 66 import java.util.Set; | 67 import java.util.Set; |
| 67 | 68 |
| 68 /** | 69 /** |
| 69 * Android implementation of the PaymentRequest service defined in | 70 * Android implementation of the PaymentRequest service defined in |
| 70 * components/payments/payment_request.mojom. | 71 * components/payments/payment_request.mojom. |
| 71 */ | 72 */ |
| 72 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, | 73 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, |
| 73 PaymentApp.InstrumentsCallback, PaymentInstrument.InstrumentDetailsCallb ack, | 74 PaymentApp.InstrumentsCallback, PaymentInstrument.InstrumentDetailsCallb ack, |
| 74 PaymentResponseHelper.PaymentResponseRequesterDelegate { | 75 PaymentResponseHelper.PaymentResponseRequesterDelegate { |
| 75 /** | 76 /** |
| 76 * Observer to be notified when PaymentRequest UI has been dismissed. | |
| 77 */ | |
| 78 public interface PaymentRequestDismissObserver { | |
| 79 /** | |
| 80 * Called when PaymentRequest UI has been dismissed. | |
| 81 */ | |
| 82 void onPaymentRequestDismissed(); | |
| 83 } | |
| 84 | |
| 85 /** | |
| 86 * A test-only observer for the PaymentRequest service implementation. | 77 * A test-only observer for the PaymentRequest service implementation. |
| 87 */ | 78 */ |
| 88 public interface PaymentRequestServiceObserverForTest { | 79 public interface PaymentRequestServiceObserverForTest { |
| 89 /** | 80 /** |
| 90 * Called when an abort request was denied. | 81 * Called when an abort request was denied. |
| 91 */ | 82 */ |
| 92 void onPaymentRequestServiceUnableToAbort(); | 83 void onPaymentRequestServiceUnableToAbort(); |
| 93 | 84 |
| 94 /** | 85 /** |
| 95 * Called when the controller is notified of billing address change, but does not alter the | 86 * Called when the controller is notified of billing address change, but does not alter the |
| 96 * editor UI. | 87 * editor UI. |
| 97 */ | 88 */ |
| 98 void onPaymentRequestServiceBillingAddressChangeProcessed(); | 89 void onPaymentRequestServiceBillingAddressChangeProcessed(); |
| 99 | 90 |
| 100 /** | 91 /** |
| 101 * Called when the controller is notified of an expiration month change. | 92 * Called when the controller is notified of an expiration month change. |
| 102 */ | 93 */ |
| 103 void onPaymentRequestServiceExpirationMonthChange(); | 94 void onPaymentRequestServiceExpirationMonthChange(); |
| 104 | 95 |
| 105 /** | 96 /** |
| 106 * Called when a show request failed. This can happen when: | 97 * Called when a show request failed. This can happen when: |
| 107 * <ul> | 98 * <ul> |
| 108 * <li>The merchant requests only unsupported payment methods.</li> | 99 * <li>The merchant requests only unsupported payment methods.</li> |
| 109 * <li>The merchant requests only payment methods that don't have inst ruments and are not | 100 * <li>The merchant requests only payment methods that don't have inst ruments and are not |
| 110 * able to add instruments from PaymentRequest UI.</li> | 101 * able to add instruments from PaymentRequest UI.</li> |
| 111 * </ul> | 102 * </ul> |
| 112 */ | 103 */ |
| 113 void onPaymentRequestServiceShowFailed(); | 104 void onPaymentRequestServiceShowFailed(); |
| 105 | |
| 106 /** | |
| 107 * Called when the canMakeActivePayment() request has been responded. | |
| 108 */ | |
| 109 void onPaymentRequestServiceActivePaymentQueryResponded(); | |
| 114 } | 110 } |
| 115 | 111 |
| 116 private static final String TAG = "cr_PaymentRequest"; | 112 private static final String TAG = "cr_PaymentRequest"; |
| 117 private static final String ANDROID_PAY_METHOD_NAME = "https://android.com/p ay"; | 113 private static final String ANDROID_PAY_METHOD_NAME = "https://android.com/p ay"; |
| 118 private static final int SUGGESTIONS_LIMIT = 4; | 114 private static final int SUGGESTIONS_LIMIT = 4; |
| 119 private static final Comparator<Completable> COMPLETENESS_COMPARATOR = | 115 private static final Comparator<Completable> COMPLETENESS_COMPARATOR = |
| 120 new Comparator<Completable>() { | 116 new Comparator<Completable>() { |
| 121 @Override | 117 @Override |
| 122 public int compare(Completable a, Completable b) { | 118 public int compare(Completable a, Completable b) { |
| 123 return (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0); | 119 return (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0); |
| 124 } | 120 } |
| 125 }; | 121 }; |
| 126 | 122 |
| 123 /** Every origin can call canMakeActivePayment() every 30 minutes. */ | |
| 124 private static final int CAN_MAKE_ACTIVE_PAYMENT_QUERY_PERIOD_MS = 30 * 60 * 1000; | |
| 125 | |
| 127 private static PaymentRequestServiceObserverForTest sObserverForTest; | 126 private static PaymentRequestServiceObserverForTest sObserverForTest; |
| 128 | 127 |
| 128 /** True if show() was called in any PaymentRequestImpl object. */ | |
| 129 private static boolean sIsShowing; | |
| 130 | |
| 131 /** | |
| 132 * In-memory mapping of the origins of websites that have recently called ca nMakeActivePayment() | |
| 133 * to the list of the payment methods that were been queried. Used for throt tling the usage of | |
| 134 * this call. The user can clear the list by restarting the browser. | |
| 135 */ | |
| 136 private static Map<String, Set<String>> sCanMakeActivePaymentQueries; | |
| 137 | |
| 129 /** Monitors changes in the TabModelSelector. */ | 138 /** Monitors changes in the TabModelSelector. */ |
| 130 private final TabModelSelectorObserver mSelectorObserver = new EmptyTabModel SelectorObserver() { | 139 private final TabModelSelectorObserver mSelectorObserver = new EmptyTabModel SelectorObserver() { |
| 131 @Override | 140 @Override |
| 132 public void onTabModelSelected(TabModel newModel, TabModel oldModel) { | 141 public void onTabModelSelected(TabModel newModel, TabModel oldModel) { |
| 133 onDismiss(); | 142 onDismiss(); |
| 134 } | 143 } |
| 135 }; | 144 }; |
| 136 | 145 |
| 137 /** Monitors changes in the current TabModel. */ | 146 /** Monitors changes in the current TabModel. */ |
| 138 private final TabModelObserver mTabModelObserver = new EmptyTabModelObserver () { | 147 private final TabModelObserver mTabModelObserver = new EmptyTabModelObserver () { |
| 139 @Override | 148 @Override |
| 140 public void didSelectTab(Tab tab, TabSelectionType type, int lastId) { | 149 public void didSelectTab(Tab tab, TabSelectionType type, int lastId) { |
| 141 if (tab == null || tab.getId() != lastId) onDismiss(); | 150 if (tab == null || tab.getId() != lastId) onDismiss(); |
| 142 } | 151 } |
| 143 }; | 152 }; |
| 144 | 153 |
| 145 private final Handler mHandler = new Handler(); | 154 private final Handler mHandler = new Handler(); |
| 146 private final ChromeActivity mContext; | 155 private final ChromeActivity mContext; |
| 147 private final PaymentRequestDismissObserver mDismissObserver; | |
| 148 private final String mMerchantName; | 156 private final String mMerchantName; |
| 149 private final String mOrigin; | 157 private final String mOrigin; |
| 150 private final List<PaymentApp> mApps; | 158 private final List<PaymentApp> mApps; |
| 151 private final AddressEditor mAddressEditor; | 159 private final AddressEditor mAddressEditor; |
| 152 private final CardEditor mCardEditor; | 160 private final CardEditor mCardEditor; |
| 153 private final PaymentRequestJourneyLogger mJourneyLogger = new PaymentReques tJourneyLogger(); | 161 private final PaymentRequestJourneyLogger mJourneyLogger = new PaymentReques tJourneyLogger(); |
| 154 | 162 |
| 155 private Bitmap mFavicon; | 163 private Bitmap mFavicon; |
| 156 private PaymentRequestClient mClient; | 164 private PaymentRequestClient mClient; |
| 157 | 165 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 185 private List<PaymentApp> mPendingApps; | 193 private List<PaymentApp> mPendingApps; |
| 186 private List<PaymentInstrument> mPendingInstruments; | 194 private List<PaymentInstrument> mPendingInstruments; |
| 187 private List<PaymentInstrument> mPendingAutofillInstruments; | 195 private List<PaymentInstrument> mPendingAutofillInstruments; |
| 188 private SectionInformation mPaymentMethodsSection; | 196 private SectionInformation mPaymentMethodsSection; |
| 189 private PaymentRequestUI mUI; | 197 private PaymentRequestUI mUI; |
| 190 private Callback<PaymentInformation> mPaymentInformationCallback; | 198 private Callback<PaymentInformation> mPaymentInformationCallback; |
| 191 private boolean mPaymentAppRunning; | 199 private boolean mPaymentAppRunning; |
| 192 private boolean mMerchantSupportsAutofillPaymentInstruments; | 200 private boolean mMerchantSupportsAutofillPaymentInstruments; |
| 193 private ContactEditor mContactEditor; | 201 private ContactEditor mContactEditor; |
| 194 private boolean mHasRecordedAbortReason; | 202 private boolean mHasRecordedAbortReason; |
| 203 private boolean mQueriedCanMakeActivePayment; | |
| 195 | 204 |
| 196 /** True if any of the requested payment methods are supported. */ | 205 /** True if any of the requested payment methods are supported. */ |
| 197 private boolean mArePaymentMethodsSupported; | 206 private boolean mArePaymentMethodsSupported; |
| 198 | 207 |
| 199 /** True if show() was called. */ | |
| 200 private boolean mIsShowing; | |
| 201 | |
| 202 /** The helper to create and fill the response to send to the merchant. */ | 208 /** The helper to create and fill the response to send to the merchant. */ |
| 203 private PaymentResponseHelper mPaymentResponseHelper; | 209 private PaymentResponseHelper mPaymentResponseHelper; |
| 204 | 210 |
| 205 /** | 211 /** |
| 206 * Builds the PaymentRequest service implementation. | 212 * Builds the PaymentRequest service implementation. |
| 207 * | 213 * |
| 208 * @param context The context where PaymentRequest has been invoked. | 214 * @param context The context where PaymentRequest has been invoked. |
| 209 * @param webContents The web contents that have invoked the PaymentRequ est API. | 215 * @param webContents The web contents that have invoked the PaymentRequ est API. |
| 210 * @param dismissObserver The observer to notify when PaymentRequest UI has been dismissed. | |
| 211 */ | 216 */ |
| 212 public PaymentRequestImpl(Activity context, WebContents webContents, | 217 public PaymentRequestImpl(Activity context, WebContents webContents) { |
| 213 PaymentRequestDismissObserver dismissObserver) { | |
| 214 assert context != null; | 218 assert context != null; |
| 215 assert webContents != null; | 219 assert webContents != null; |
| 216 assert dismissObserver != null; | |
| 217 | 220 |
| 218 assert context instanceof ChromeActivity; | 221 assert context instanceof ChromeActivity; |
| 219 mContext = (ChromeActivity) context; | 222 mContext = (ChromeActivity) context; |
| 220 | 223 |
| 221 mDismissObserver = dismissObserver; | |
| 222 mMerchantName = webContents.getTitle(); | 224 mMerchantName = webContents.getTitle(); |
| 223 // The feature is available only in secure context, so it's OK to not sh ow HTTPS. | 225 // The feature is available only in secure context, so it's OK to not sh ow HTTPS. |
| 224 mOrigin = UrlFormatter.formatUrlForSecurityDisplay(webContents.getVisibl eUrl(), false); | 226 mOrigin = UrlFormatter.formatUrlForSecurityDisplay(webContents.getVisibl eUrl(), false); |
| 225 | 227 |
| 226 final FaviconHelper faviconHelper = new FaviconHelper(); | 228 final FaviconHelper faviconHelper = new FaviconHelper(); |
| 227 faviconHelper.getLocalFaviconImageForURL(Profile.getLastUsedProfile(), | 229 faviconHelper.getLocalFaviconImageForURL(Profile.getLastUsedProfile(), |
| 228 webContents.getVisibleUrl(), | 230 webContents.getVisibleUrl(), |
| 229 mContext.getResources().getDimensionPixelSize(R.dimen.payments_f avicon_size), | 231 mContext.getResources().getDimensionPixelSize(R.dimen.payments_f avicon_size), |
| 230 new FaviconHelper.FaviconImageCallback() { | 232 new FaviconHelper.FaviconImageCallback() { |
| 231 @Override | 233 @Override |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 400 | 402 |
| 401 PaymentRequestMetrics.recordRequestedInformationHistogram(requestPayerEm ail, | 403 PaymentRequestMetrics.recordRequestedInformationHistogram(requestPayerEm ail, |
| 402 requestPayerPhone, requestShipping, requestPayerName); | 404 requestPayerPhone, requestShipping, requestPayerName); |
| 403 } | 405 } |
| 404 | 406 |
| 405 /** | 407 /** |
| 406 * Called by the merchant website to show the payment request to the user. | 408 * Called by the merchant website to show the payment request to the user. |
| 407 */ | 409 */ |
| 408 @Override | 410 @Override |
| 409 public void show() { | 411 public void show() { |
| 410 if (mClient == null || mIsShowing) return; | 412 if (mClient == null) return; |
| 411 | 413 |
| 412 mIsShowing = true; | 414 if (sIsShowing) { |
| 415 disconnectFromClientWithDebugMessage("A PaymentRequest UI is already showing"); | |
| 416 return; | |
| 417 } | |
| 418 | |
| 419 sIsShowing = true; | |
| 413 if (disconnectIfNoPaymentMethodsSupported()) return; | 420 if (disconnectIfNoPaymentMethodsSupported()) return; |
| 414 | 421 |
| 415 // Catch any time the user switches tabs. Because the dialog is modal, a user shouldn't be | 422 // Catch any time the user switches tabs. Because the dialog is modal, a user shouldn't be |
| 416 // allowed to switch tabs, which can happen if the user receives an exte rnal Intent. | 423 // allowed to switch tabs, which can happen if the user receives an exte rnal Intent. |
| 417 mContext.getTabModelSelector().addObserver(mSelectorObserver); | 424 mContext.getTabModelSelector().addObserver(mSelectorObserver); |
| 418 mContext.getCurrentTabModel().addObserver(mTabModelObserver); | 425 mContext.getCurrentTabModel().addObserver(mTabModelObserver); |
| 419 | 426 |
| 420 mUI.show(); | 427 mUI.show(); |
| 421 recordSuccessFunnelHistograms("Shown"); | 428 recordSuccessFunnelHistograms("Shown"); |
| 422 } | 429 } |
| 423 | 430 |
| 424 private static Map<String, JSONObject> getValidatedMethodData( | 431 private static Map<String, JSONObject> getValidatedMethodData( |
| 425 PaymentMethodData[] methodData, CardEditor paymentMethodsCollector) { | 432 PaymentMethodData[] methodData, CardEditor paymentMethodsCollector) { |
| 426 // Payment methodData are required. | 433 // Payment methodData are required. |
| 427 if (methodData == null || methodData.length == 0) return null; | 434 if (methodData == null || methodData.length == 0) return null; |
| 428 Map<String, JSONObject> result = new HashMap<>(); | 435 Map<String, JSONObject> result = new ArrayMap<>(); |
| 429 for (int i = 0; i < methodData.length; i++) { | 436 for (int i = 0; i < methodData.length; i++) { |
| 430 JSONObject data = null; | 437 JSONObject data = null; |
| 431 if (!TextUtils.isEmpty(methodData[i].stringifiedData)) { | 438 if (!TextUtils.isEmpty(methodData[i].stringifiedData)) { |
| 432 try { | 439 try { |
| 433 data = new JSONObject(JsonSanitizer.sanitize(methodData[i].s tringifiedData)); | 440 data = new JSONObject(JsonSanitizer.sanitize(methodData[i].s tringifiedData)); |
| 434 } catch (JSONException | IOException | IllegalStateException e) { | 441 } catch (JSONException | IOException | IllegalStateException e) { |
| 435 // Payment method specific data should be a JSON object. | 442 // Payment method specific data should be a JSON object. |
| 436 // According to the payment request spec[1], for each method data, | 443 // According to the payment request spec[1], for each method data, |
| 437 // if the data field is supplied but is not a JSON-serializa ble object, | 444 // if the data field is supplied but is not a JSON-serializa ble object, |
| 438 // then should throw a TypeError. So, we should return null here even if | 445 // then should throw a TypeError. So, we should return null here even if |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 457 } | 464 } |
| 458 return result; | 465 return result; |
| 459 } | 466 } |
| 460 | 467 |
| 461 /** Queries the installed payment apps for their instruments that merchant s upports. */ | 468 /** Queries the installed payment apps for their instruments that merchant s upports. */ |
| 462 private void getMatchingPaymentInstruments() { | 469 private void getMatchingPaymentInstruments() { |
| 463 mPendingApps = new ArrayList<>(mApps); | 470 mPendingApps = new ArrayList<>(mApps); |
| 464 mPendingInstruments = new ArrayList<>(); | 471 mPendingInstruments = new ArrayList<>(); |
| 465 mPendingAutofillInstruments = new ArrayList<>(); | 472 mPendingAutofillInstruments = new ArrayList<>(); |
| 466 | 473 |
| 467 Map<PaymentApp, Map<String, JSONObject>> queryApps = new HashMap<>(); | 474 Map<PaymentApp, Map<String, JSONObject>> queryApps = new ArrayMap<>(); |
| 468 for (int i = 0; i < mApps.size(); i++) { | 475 for (int i = 0; i < mApps.size(); i++) { |
| 469 PaymentApp app = mApps.get(i); | 476 PaymentApp app = mApps.get(i); |
| 470 Map<String, JSONObject> appMethods = | 477 Map<String, JSONObject> appMethods = |
| 471 filterMerchantMethodData(mMethodData, app.getAppMethodNames( )); | 478 filterMerchantMethodData(mMethodData, app.getAppMethodNames( )); |
| 472 if (appMethods == null) { | 479 if (appMethods == null) { |
| 473 mPendingApps.remove(app); | 480 mPendingApps.remove(app); |
| 474 } else { | 481 } else { |
| 475 mArePaymentMethodsSupported = true; | 482 mArePaymentMethodsSupported = true; |
| 476 mMerchantSupportsAutofillPaymentInstruments |= app instanceof Au tofillPaymentApp; | 483 mMerchantSupportsAutofillPaymentInstruments |= app instanceof Au tofillPaymentApp; |
| 477 queryApps.put(app, appMethods); | 484 queryApps.put(app, appMethods); |
| 478 } | 485 } |
| 479 } | 486 } |
| 480 | 487 |
| 481 // Query instruments after mMerchantSupportsAutofillPaymentInstruments h as been initialized, | 488 // Query instruments after mMerchantSupportsAutofillPaymentInstruments h as been initialized, |
| 482 // so a fast response from a non-autofill payment app at the front of th e app list does not | 489 // so a fast response from a non-autofill payment app at the front of th e app list does not |
| 483 // cause NOT_SUPPORTED payment rejection. | 490 // cause NOT_SUPPORTED payment rejection. |
| 484 for (Map.Entry<PaymentApp, Map<String, JSONObject>> q : queryApps.entryS et()) { | 491 for (Map.Entry<PaymentApp, Map<String, JSONObject>> q : queryApps.entryS et()) { |
| 485 q.getKey().getInstruments(q.getValue(), this); | 492 q.getKey().getInstruments(q.getValue(), this); |
| 486 } | 493 } |
| 487 } | 494 } |
| 488 | 495 |
| 489 /** Filter out merchant method data that's not relevant to a payment app. Ca n return null. */ | 496 /** Filter out merchant method data that's not relevant to a payment app. Ca n return null. */ |
| 490 private static Map<String, JSONObject> filterMerchantMethodData( | 497 private static Map<String, JSONObject> filterMerchantMethodData( |
| 491 Map<String, JSONObject> merchantMethodData, Set<String> appMethods) { | 498 Map<String, JSONObject> merchantMethodData, Set<String> appMethods) { |
| 492 Map<String, JSONObject> result = null; | 499 Map<String, JSONObject> result = null; |
| 493 for (String method : appMethods) { | 500 for (String method : appMethods) { |
| 494 if (merchantMethodData.containsKey(method)) { | 501 if (merchantMethodData.containsKey(method)) { |
| 495 if (result == null) result = new HashMap<>(); | 502 if (result == null) result = new ArrayMap<>(); |
| 496 result.put(method, merchantMethodData.get(method)); | 503 result.put(method, merchantMethodData.get(method)); |
| 497 } | 504 } |
| 498 } | 505 } |
| 499 return result; | 506 return result; |
| 500 } | 507 } |
| 501 | 508 |
| 502 /** | 509 /** |
| 503 * Called by merchant to update the shipping options and line items after th e user has selected | 510 * Called by merchant to update the shipping options and line items after th e user has selected |
| 504 * their shipping address or shipping option. | 511 * their shipping address or shipping option. |
| 505 */ | 512 */ |
| (...skipping 422 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 928 * Called when the merchant website has processed the payment. | 935 * Called when the merchant website has processed the payment. |
| 929 */ | 936 */ |
| 930 @Override | 937 @Override |
| 931 public void complete(int result) { | 938 public void complete(int result) { |
| 932 if (mClient == null) return; | 939 if (mClient == null) return; |
| 933 recordSuccessFunnelHistograms("Completed"); | 940 recordSuccessFunnelHistograms("Completed"); |
| 934 closeUI(PaymentComplete.FAIL != result); | 941 closeUI(PaymentComplete.FAIL != result); |
| 935 } | 942 } |
| 936 | 943 |
| 937 /** | 944 /** |
| 945 * Called by the merchant website to check if the user has complete payment instruments. | |
| 946 */ | |
| 947 @Override | |
| 948 public void canMakeActivePayment() { | |
| 949 if (mClient == null) return; | |
| 950 | |
| 951 if (sCanMakeActivePaymentQueries == null) sCanMakeActivePaymentQueries = new ArrayMap<>(); | |
| 952 | |
| 953 if (sCanMakeActivePaymentQueries.containsKey(mOrigin)) { | |
|
Marijn Kruisselbrink
2016/11/17 18:06:21
Is mOrigin the correct key to use here? I realize
please use gerrit instead
2016/11/17 20:07:21
After reading the comments on these methods, I sho
| |
| 954 if (!mMethodData.keySet().equals(sCanMakeActivePaymentQueries.get(mO rigin))) { | |
| 955 mClient.onCanMakeActivePayment(ActivePaymentQueryResult.QUERY_QU OTA_EXCEEDED); | |
| 956 if (sObserverForTest != null) { | |
| 957 sObserverForTest.onPaymentRequestServiceActivePaymentQueryRe sponded(); | |
| 958 } | |
| 959 return; | |
| 960 } | |
| 961 } else { | |
| 962 sCanMakeActivePaymentQueries.put(mOrigin, mMethodData.keySet()); | |
| 963 mHandler.postDelayed(new Runnable() { | |
| 964 @Override | |
| 965 public void run() { | |
| 966 sCanMakeActivePaymentQueries.remove(mOrigin); | |
| 967 } | |
| 968 }, CAN_MAKE_ACTIVE_PAYMENT_QUERY_PERIOD_MS); | |
| 969 } | |
| 970 | |
| 971 if (!mPendingApps.isEmpty() || !mPendingInstruments.isEmpty()) { | |
| 972 mQueriedCanMakeActivePayment = true; | |
| 973 } else { | |
| 974 mClient.onCanMakeActivePayment(mPaymentMethodsSection == null | |
| 975 || mPaymentMethodsSection.getSelectedItem() == null | |
| 976 ? ActivePaymentQueryResult.CANNOT_MAKE_ACTIVE_PAYMEN T | |
| 977 : ActivePaymentQueryResult.CAN_MAKE_ACTIVE_PAYMENT); | |
| 978 if (sObserverForTest != null) { | |
| 979 sObserverForTest.onPaymentRequestServiceActivePaymentQueryRespon ded(); | |
| 980 } | |
| 981 } | |
| 982 } | |
| 983 | |
| 984 /** | |
| 938 * Called when the renderer closes the Mojo connection. | 985 * Called when the renderer closes the Mojo connection. |
| 939 */ | 986 */ |
| 940 @Override | 987 @Override |
| 941 public void close() { | 988 public void close() { |
| 942 if (mClient == null) return; | 989 if (mClient == null) return; |
| 943 closeClient(); | 990 closeClient(); |
| 944 closeUI(true); | 991 closeUI(true); |
| 945 recordAbortReasonHistogram(PaymentRequestMetrics.ABORT_REASON_MOJO_RENDE RER_CLOSING); | 992 recordAbortReasonHistogram(PaymentRequestMetrics.ABORT_REASON_MOJO_RENDE RER_CLOSING); |
| 946 } | 993 } |
| 947 | 994 |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1014 // Pre-select the first instrument on the list, if it is complete. | 1061 // Pre-select the first instrument on the list, if it is complete. |
| 1015 int selection = SectionInformation.NO_SELECTION; | 1062 int selection = SectionInformation.NO_SELECTION; |
| 1016 if (!mPendingInstruments.isEmpty()) { | 1063 if (!mPendingInstruments.isEmpty()) { |
| 1017 PaymentInstrument first = mPendingInstruments.get(0); | 1064 PaymentInstrument first = mPendingInstruments.get(0); |
| 1018 if (!(first instanceof AutofillPaymentInstrument) | 1065 if (!(first instanceof AutofillPaymentInstrument) |
| 1019 || ((AutofillPaymentInstrument) first).isComplete()) { | 1066 || ((AutofillPaymentInstrument) first).isComplete()) { |
| 1020 selection = 0; | 1067 selection = 0; |
| 1021 } | 1068 } |
| 1022 } | 1069 } |
| 1023 | 1070 |
| 1071 if (mQueriedCanMakeActivePayment) { | |
| 1072 mQueriedCanMakeActivePayment = false; | |
| 1073 mClient.onCanMakeActivePayment(selection == 0 | |
| 1074 ? ActivePaymentQueryResult.CAN_MAKE_ACTIVE_PAYMENT | |
| 1075 : ActivePaymentQueryResult.CANNOT_MAKE_ACTIVE_PAYMEN T); | |
| 1076 if (sObserverForTest != null) { | |
| 1077 sObserverForTest.onPaymentRequestServiceActivePaymentQueryRespon ded(); | |
| 1078 } | |
| 1079 } | |
| 1080 | |
| 1024 // The list of payment instruments is ready to display. | 1081 // The list of payment instruments is ready to display. |
| 1025 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYPE_PA YMENT_METHODS, | 1082 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYPE_PA YMENT_METHODS, |
| 1026 selection, mPendingInstruments); | 1083 selection, mPendingInstruments); |
| 1027 | 1084 |
| 1028 mPendingInstruments.clear(); | 1085 mPendingInstruments.clear(); |
| 1029 | 1086 |
| 1030 // UI has requested the full list of payment instruments. Provide it now . | 1087 // UI has requested the full list of payment instruments. Provide it now . |
| 1031 if (mPaymentInformationCallback != null) providePaymentInformation(); | 1088 if (mPaymentInformationCallback != null) providePaymentInformation(); |
| 1032 } | 1089 } |
| 1033 | 1090 |
| 1034 /** | 1091 /** |
| 1035 * If no payment methods are supported, disconnect from the client and retur n true. | 1092 * If no payment methods are supported, disconnect from the client and retur n true. |
| 1036 * | 1093 * |
| 1037 * @return True if no payment methods are supported | 1094 * @return True if no payment methods are supported |
| 1038 */ | 1095 */ |
| 1039 private boolean disconnectIfNoPaymentMethodsSupported() { | 1096 private boolean disconnectIfNoPaymentMethodsSupported() { |
| 1040 boolean waitingForPaymentApps = !mPendingApps.isEmpty() || !mPendingInst ruments.isEmpty(); | 1097 boolean waitingForPaymentApps = !mPendingApps.isEmpty() || !mPendingInst ruments.isEmpty(); |
| 1041 boolean foundPaymentMethods = | 1098 boolean foundPaymentMethods = |
| 1042 mPaymentMethodsSection != null && !mPaymentMethodsSection.isEmpt y(); | 1099 mPaymentMethodsSection != null && !mPaymentMethodsSection.isEmpt y(); |
| 1043 boolean userCanAddCreditCard = mMerchantSupportsAutofillPaymentInstrumen ts | 1100 boolean userCanAddCreditCard = mMerchantSupportsAutofillPaymentInstrumen ts |
| 1044 && !ChromeFeatureList.isEnabled(ChromeFeatureList.NO_CREDIT_CARD _ABORT); | 1101 && !ChromeFeatureList.isEnabled(ChromeFeatureList.NO_CREDIT_CARD _ABORT); |
| 1045 | 1102 |
| 1046 if (!mArePaymentMethodsSupported | 1103 if (!mArePaymentMethodsSupported |
| 1047 || (mIsShowing && !waitingForPaymentApps && !foundPaymentMethods | 1104 || (sIsShowing && !waitingForPaymentApps && !foundPaymentMethods |
| 1048 && !userCanAddCreditCard)) { | 1105 && !userCanAddCreditCard)) { |
| 1049 // All payment apps have responded, but none of them have instrument s. It's possible to | 1106 // All payment apps have responded, but none of them have instrument s. It's possible to |
| 1050 // add credit cards, but the merchant does not support them either. The payment request | 1107 // add credit cards, but the merchant does not support them either. The payment request |
| 1051 // must be rejected. | 1108 // must be rejected. |
| 1052 disconnectFromClientWithDebugMessage("Requested payment methods have no instruments", | 1109 disconnectFromClientWithDebugMessage("Requested payment methods have no instruments", |
| 1053 PaymentErrorReason.NOT_SUPPORTED); | 1110 PaymentErrorReason.NOT_SUPPORTED); |
| 1054 recordAbortReasonHistogram(mArePaymentMethodsSupported | 1111 recordAbortReasonHistogram(mArePaymentMethodsSupported |
| 1055 ? PaymentRequestMetrics.ABORT_REASON_NO_MATCHING_PAY MENT_METHOD | 1112 ? PaymentRequestMetrics.ABORT_REASON_NO_MATCHING_PAY MENT_METHOD |
| 1056 : PaymentRequestMetrics.ABORT_REASON_NO_SUPPORTED_PA YMENT_METHOD); | 1113 : PaymentRequestMetrics.ABORT_REASON_NO_SUPPORTED_PA YMENT_METHOD); |
| 1057 if (sObserverForTest != null) sObserverForTest.onPaymentRequestServi ceShowFailed(); | 1114 if (sObserverForTest != null) sObserverForTest.onPaymentRequestServi ceShowFailed(); |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1154 mPaymentMethodsSection = null; | 1211 mPaymentMethodsSection = null; |
| 1155 } | 1212 } |
| 1156 | 1213 |
| 1157 mContext.getTabModelSelector().removeObserver(mSelectorObserver); | 1214 mContext.getTabModelSelector().removeObserver(mSelectorObserver); |
| 1158 mContext.getCurrentTabModel().removeObserver(mTabModelObserver); | 1215 mContext.getCurrentTabModel().removeObserver(mTabModelObserver); |
| 1159 } | 1216 } |
| 1160 | 1217 |
| 1161 private void closeClient() { | 1218 private void closeClient() { |
| 1162 if (mClient != null) mClient.close(); | 1219 if (mClient != null) mClient.close(); |
| 1163 mClient = null; | 1220 mClient = null; |
| 1164 mDismissObserver.onPaymentRequestDismissed(); | 1221 sIsShowing = false; |
| 1165 } | 1222 } |
| 1166 | 1223 |
| 1167 @VisibleForTesting | 1224 @VisibleForTesting |
| 1168 public static void setObserverForTest(PaymentRequestServiceObserverForTest o bserverForTest) { | 1225 public static void setObserverForTest(PaymentRequestServiceObserverForTest o bserverForTest) { |
| 1169 sObserverForTest = observerForTest; | 1226 sObserverForTest = observerForTest; |
| 1170 } | 1227 } |
| 1171 | 1228 |
| 1172 /** | 1229 /** |
| 1173 * Records specific histograms related to the different steps of a successfu l checkout. | 1230 * Records specific histograms related to the different steps of a successfu l checkout. |
| 1174 */ | 1231 */ |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 1194 "PaymentRequest.CheckoutFunnel.Aborted", abortReason, | 1251 "PaymentRequest.CheckoutFunnel.Aborted", abortReason, |
| 1195 PaymentRequestMetrics.ABORT_REASON_MAX); | 1252 PaymentRequestMetrics.ABORT_REASON_MAX); |
| 1196 | 1253 |
| 1197 if (abortReason == PaymentRequestMetrics.ABORT_REASON_ABORTED_BY_USER) { | 1254 if (abortReason == PaymentRequestMetrics.ABORT_REASON_ABORTED_BY_USER) { |
| 1198 mJourneyLogger.recordJourneyStatsHistograms("UserAborted"); | 1255 mJourneyLogger.recordJourneyStatsHistograms("UserAborted"); |
| 1199 } else { | 1256 } else { |
| 1200 mJourneyLogger.recordJourneyStatsHistograms("OtherAborted"); | 1257 mJourneyLogger.recordJourneyStatsHistograms("OtherAborted"); |
| 1201 } | 1258 } |
| 1202 } | 1259 } |
| 1203 } | 1260 } |
| OLD | NEW |