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.text.TextUtils; | 10 import android.text.TextUtils; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 62 import java.util.List; | 62 import java.util.List; |
| 63 import java.util.Locale; | 63 import java.util.Locale; |
| 64 import java.util.Map; | 64 import java.util.Map; |
| 65 import java.util.Set; | 65 import java.util.Set; |
| 66 | 66 |
| 67 /** | 67 /** |
| 68 * Android implementation of the PaymentRequest service defined in | 68 * Android implementation of the PaymentRequest service defined in |
| 69 * components/payments/payment_request.mojom. | 69 * components/payments/payment_request.mojom. |
| 70 */ | 70 */ |
| 71 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, | 71 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, |
| 72 PaymentAppFactory.PaymentAppsCallback, | |
| 72 PaymentApp.InstrumentsCallback, PaymentInstrument.InstrumentDetailsCallb ack, | 73 PaymentApp.InstrumentsCallback, PaymentInstrument.InstrumentDetailsCallb ack, |
| 73 PaymentResponseHelper.PaymentResponseRequesterDelegate { | 74 PaymentResponseHelper.PaymentResponseRequesterDelegate { |
| 74 /** | 75 /** |
| 75 * Observer to be notified when PaymentRequest UI has been dismissed. | 76 * Observer to be notified when PaymentRequest UI has been dismissed. |
| 76 */ | 77 */ |
| 77 public interface PaymentRequestDismissObserver { | 78 public interface PaymentRequestDismissObserver { |
| 78 /** | 79 /** |
| 79 * Called when PaymentRequest UI has been dismissed. | 80 * Called when PaymentRequest UI has been dismissed. |
| 80 */ | 81 */ |
| 81 void onPaymentRequestDismissed(); | 82 void onPaymentRequestDismissed(); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 131 /** Monitors changes in the current TabModel. */ | 132 /** Monitors changes in the current TabModel. */ |
| 132 private final TabModelObserver mTabModelObserver = new EmptyTabModelObserver () { | 133 private final TabModelObserver mTabModelObserver = new EmptyTabModelObserver () { |
| 133 @Override | 134 @Override |
| 134 public void didSelectTab(Tab tab, TabSelectionType type, int lastId) { | 135 public void didSelectTab(Tab tab, TabSelectionType type, int lastId) { |
| 135 if (tab == null || tab.getId() != lastId) onDismiss(); | 136 if (tab == null || tab.getId() != lastId) onDismiss(); |
| 136 } | 137 } |
| 137 }; | 138 }; |
| 138 | 139 |
| 139 private final Handler mHandler = new Handler(); | 140 private final Handler mHandler = new Handler(); |
| 140 private final ChromeActivity mContext; | 141 private final ChromeActivity mContext; |
| 142 private final WebContents mWebContents; | |
| 141 private final PaymentRequestDismissObserver mDismissObserver; | 143 private final PaymentRequestDismissObserver mDismissObserver; |
| 142 private final String mMerchantName; | 144 private final String mMerchantName; |
| 143 private final String mOrigin; | 145 private final String mOrigin; |
| 144 private final List<PaymentApp> mApps; | |
| 145 private final AddressEditor mAddressEditor; | 146 private final AddressEditor mAddressEditor; |
| 146 private final CardEditor mCardEditor; | 147 private final CardEditor mCardEditor; |
| 147 private final PaymentRequestJourneyLogger mJourneyLogger = new PaymentReques tJourneyLogger(); | 148 private final PaymentRequestJourneyLogger mJourneyLogger = new PaymentReques tJourneyLogger(); |
| 148 | 149 |
| 149 private Bitmap mFavicon; | 150 private Bitmap mFavicon; |
| 150 private PaymentRequestClient mClient; | 151 private PaymentRequestClient mClient; |
| 151 | 152 |
| 152 /** | 153 /** |
| 153 * The raw total amount being charged, as it was received from the website. This data is passed | 154 * The raw total amount being charged, as it was received from the website. This data is passed |
| 154 * to the payment app. | 155 * to the payment app. |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 179 private List<PaymentApp> mPendingApps; | 180 private List<PaymentApp> mPendingApps; |
| 180 private List<PaymentInstrument> mPendingInstruments; | 181 private List<PaymentInstrument> mPendingInstruments; |
| 181 private List<PaymentInstrument> mPendingAutofillInstruments; | 182 private List<PaymentInstrument> mPendingAutofillInstruments; |
| 182 private SectionInformation mPaymentMethodsSection; | 183 private SectionInformation mPaymentMethodsSection; |
| 183 private PaymentRequestUI mUI; | 184 private PaymentRequestUI mUI; |
| 184 private Callback<PaymentInformation> mPaymentInformationCallback; | 185 private Callback<PaymentInformation> mPaymentInformationCallback; |
| 185 private boolean mPaymentAppRunning; | 186 private boolean mPaymentAppRunning; |
| 186 private boolean mMerchantSupportsAutofillPaymentInstruments; | 187 private boolean mMerchantSupportsAutofillPaymentInstruments; |
| 187 private ContactEditor mContactEditor; | 188 private ContactEditor mContactEditor; |
| 188 private boolean mHasRecordedAbortReason; | 189 private boolean mHasRecordedAbortReason; |
| 190 private boolean mWaitingForPaymentFactory; | |
| 189 | 191 |
| 190 /** True if any of the requested payment methods are supported. */ | 192 /** True if any of the requested payment methods are supported. */ |
| 191 private boolean mArePaymentMethodsSupported; | 193 private boolean mArePaymentMethodsSupported; |
| 192 | 194 |
| 193 /** True if show() was called. */ | 195 /** True if show() was called. */ |
| 194 private boolean mIsShowing; | 196 private boolean mIsShowing; |
| 195 | 197 |
| 196 /** The helper to create and fill the response to send to the merchant. */ | 198 /** The helper to create and fill the response to send to the merchant. */ |
| 197 private PaymentResponseHelper mPaymentResponseHelper; | 199 private PaymentResponseHelper mPaymentResponseHelper; |
| 198 | 200 |
| 199 /** | 201 /** |
| 200 * Builds the PaymentRequest service implementation. | 202 * Builds the PaymentRequest service implementation. |
| 201 * | 203 * |
| 202 * @param context The context where PaymentRequest has been invoked. | 204 * @param context The context where PaymentRequest has been invoked. |
| 203 * @param webContents The web contents that have invoked the PaymentRequ est API. | 205 * @param webContents The web contents that have invoked the PaymentRequ est API. |
| 204 * @param dismissObserver The observer to notify when PaymentRequest UI has been dismissed. | 206 * @param dismissObserver The observer to notify when PaymentRequest UI has been dismissed. |
| 205 */ | 207 */ |
| 206 public PaymentRequestImpl(Activity context, WebContents webContents, | 208 public PaymentRequestImpl(Activity context, WebContents webContents, |
| 207 PaymentRequestDismissObserver dismissObserver) { | 209 PaymentRequestDismissObserver dismissObserver) { |
| 208 assert context != null; | 210 assert context != null; |
| 209 assert webContents != null; | 211 assert webContents != null; |
| 210 assert dismissObserver != null; | 212 assert dismissObserver != null; |
| 211 | 213 |
| 212 assert context instanceof ChromeActivity; | 214 assert context instanceof ChromeActivity; |
| 213 mContext = (ChromeActivity) context; | 215 mContext = (ChromeActivity) context; |
| 216 mWebContents = webContents; | |
| 214 | 217 |
| 215 mDismissObserver = dismissObserver; | 218 mDismissObserver = dismissObserver; |
| 216 mMerchantName = webContents.getTitle(); | 219 mMerchantName = mWebContents.getTitle(); |
| 217 // The feature is available only in secure context, so it's OK to not sh ow HTTPS. | 220 // The feature is available only in secure context, so it's OK to not sh ow HTTPS. |
| 218 mOrigin = UrlFormatter.formatUrlForSecurityDisplay(webContents.getVisibl eUrl(), false); | 221 mOrigin = UrlFormatter.formatUrlForSecurityDisplay(mWebContents.getVisib leUrl(), false); |
| 219 | 222 |
| 220 final FaviconHelper faviconHelper = new FaviconHelper(); | 223 final FaviconHelper faviconHelper = new FaviconHelper(); |
| 221 faviconHelper.getLocalFaviconImageForURL(Profile.getLastUsedProfile(), | 224 faviconHelper.getLocalFaviconImageForURL(Profile.getLastUsedProfile(), |
| 222 webContents.getVisibleUrl(), | 225 mWebContents.getVisibleUrl(), |
| 223 mContext.getResources().getDimensionPixelSize(R.dimen.payments_f avicon_size), | 226 mContext.getResources().getDimensionPixelSize(R.dimen.payments_f avicon_size), |
| 224 new FaviconHelper.FaviconImageCallback() { | 227 new FaviconHelper.FaviconImageCallback() { |
| 225 @Override | 228 @Override |
| 226 public void onFaviconAvailable(Bitmap bitmap, String iconUrl ) { | 229 public void onFaviconAvailable(Bitmap bitmap, String iconUrl ) { |
| 227 faviconHelper.destroy(); | 230 faviconHelper.destroy(); |
| 228 if (bitmap == null) return; | 231 if (bitmap == null) return; |
| 229 if (mUI == null) { | 232 if (mUI == null) { |
| 230 mFavicon = bitmap; | 233 mFavicon = bitmap; |
| 231 return; | 234 return; |
| 232 } | 235 } |
| 233 mUI.setTitleBitmap(bitmap); | 236 mUI.setTitleBitmap(bitmap); |
| 234 } | 237 } |
| 235 }); | 238 }); |
| 236 | 239 |
| 237 mApps = PaymentAppFactory.create(mContext, webContents); | |
| 238 | |
| 239 mAddressEditor = new AddressEditor(); | 240 mAddressEditor = new AddressEditor(); |
| 240 mCardEditor = new CardEditor(webContents, mAddressEditor, sObserverForTe st); | 241 mCardEditor = new CardEditor(mWebContents, mAddressEditor, sObserverForT est); |
| 241 | 242 |
| 242 recordSuccessFunnelHistograms("Initiated"); | 243 recordSuccessFunnelHistograms("Initiated"); |
| 243 } | 244 } |
| 244 | 245 |
| 245 /** | 246 /** |
| 246 * Called by the merchant website to initialize the payment request data. | 247 * Called by the merchant website to initialize the payment request data. |
| 247 */ | 248 */ |
| 248 @Override | 249 @Override |
| 249 public void init(PaymentRequestClient client, PaymentMethodData[] methodData , | 250 public void init(PaymentRequestClient client, PaymentMethodData[] methodData , |
| 250 PaymentDetails details, PaymentOptions options) { | 251 PaymentDetails details, PaymentOptions options) { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 261 mMethodData = getValidatedMethodData(methodData, mCardEditor); | 262 mMethodData = getValidatedMethodData(methodData, mCardEditor); |
| 262 if (mMethodData == null) { | 263 if (mMethodData == null) { |
| 263 disconnectFromClientWithDebugMessage("Invalid payment methods or dat a"); | 264 disconnectFromClientWithDebugMessage("Invalid payment methods or dat a"); |
| 264 recordAbortReasonHistogram( | 265 recordAbortReasonHistogram( |
| 265 PaymentRequestMetrics.ABORT_REASON_INVALID_DATA_FROM_RENDERE R); | 266 PaymentRequestMetrics.ABORT_REASON_INVALID_DATA_FROM_RENDERE R); |
| 266 return; | 267 return; |
| 267 } | 268 } |
| 268 | 269 |
| 269 if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return; | 270 if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return; |
| 270 | 271 |
| 271 getMatchingPaymentInstruments(); | 272 mWaitingForPaymentFactory = true; |
| 273 PaymentAppFactory.create(mContext, mWebContents, mMethodData.keySet(), t his); | |
| 272 | 274 |
| 273 boolean requestShipping = options != null && options.requestShipping; | 275 boolean requestShipping = options != null && options.requestShipping; |
| 274 boolean requestPayerName = options != null && options.requestPayerName; | 276 boolean requestPayerName = options != null && options.requestPayerName; |
| 275 boolean requestPayerPhone = options != null && options.requestPayerPhone ; | 277 boolean requestPayerPhone = options != null && options.requestPayerPhone ; |
| 276 boolean requestPayerEmail = options != null && options.requestPayerEmail ; | 278 boolean requestPayerEmail = options != null && options.requestPayerEmail ; |
| 277 | 279 |
| 278 List<AutofillProfile> profiles = null; | 280 List<AutofillProfile> profiles = null; |
| 279 if (requestShipping || requestPayerName || requestPayerPhone || requestP ayerEmail) { | 281 if (requestShipping || requestPayerName || requestPayerPhone || requestP ayerEmail) { |
| 280 profiles = PersonalDataManager.getInstance().getProfilesToSuggest( | 282 profiles = PersonalDataManager.getInstance().getProfilesToSuggest( |
| 281 false /* includeNameInLabel */); | 283 false /* includeNameInLabel */); |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 444 // Payment methods should be non-empty. | 446 // Payment methods should be non-empty. |
| 445 if (TextUtils.isEmpty(methods[j])) return null; | 447 if (TextUtils.isEmpty(methods[j])) return null; |
| 446 result.put(methods[j], data); | 448 result.put(methods[j], data); |
| 447 } | 449 } |
| 448 | 450 |
| 449 paymentMethodsCollector.addAcceptedPaymentMethodsIfRecognized(method s); | 451 paymentMethodsCollector.addAcceptedPaymentMethodsIfRecognized(method s); |
| 450 } | 452 } |
| 451 return result; | 453 return result; |
| 452 } | 454 } |
| 453 | 455 |
| 454 /** Queries the installed payment apps for their instruments that merchant s upports. */ | 456 /** |
| 455 private void getMatchingPaymentInstruments() { | 457 * Called when installed payment apps have been determined. Queries the inst alled payment apps |
| 456 mPendingApps = new ArrayList<>(mApps); | 458 * for their instruments that merchant supports. |
| 459 */ | |
| 460 @Override | |
| 461 public void onPaymentAppsReady(List<PaymentApp> apps) { | |
| 462 mPendingApps = new ArrayList<>(apps); | |
| 457 mPendingInstruments = new ArrayList<>(); | 463 mPendingInstruments = new ArrayList<>(); |
| 458 mPendingAutofillInstruments = new ArrayList<>(); | 464 mPendingAutofillInstruments = new ArrayList<>(); |
| 459 | 465 |
| 460 Map<PaymentApp, Map<String, JSONObject>> queryApps = new HashMap<>(); | 466 Map<PaymentApp, Map<String, JSONObject>> queryApps = new HashMap<>(); |
| 461 for (int i = 0; i < mApps.size(); i++) { | 467 for (int i = 0; i < apps.size(); i++) { |
| 462 PaymentApp app = mApps.get(i); | 468 PaymentApp app = apps.get(i); |
| 463 Map<String, JSONObject> appMethods = | 469 Map<String, JSONObject> appMethods = |
| 464 filterMerchantMethodData(mMethodData, app.getAppMethodNames( )); | 470 filterMerchantMethodData(mMethodData, app.getAppMethodNames( )); |
| 465 if (appMethods == null) { | 471 if (appMethods == null) { |
| 466 mPendingApps.remove(app); | 472 mPendingApps.remove(app); |
| 467 } else { | 473 } else { |
| 468 mArePaymentMethodsSupported = true; | 474 mArePaymentMethodsSupported = true; |
| 469 mMerchantSupportsAutofillPaymentInstruments |= app instanceof Au tofillPaymentApp; | 475 mMerchantSupportsAutofillPaymentInstruments |= app instanceof Au tofillPaymentApp; |
| 470 queryApps.put(app, appMethods); | 476 queryApps.put(app, appMethods); |
| 471 } | 477 } |
| 472 } | 478 } |
| 473 | 479 |
| 474 // Query instruments after mMerchantSupportsAutofillPaymentInstruments h as been initialized, | 480 // Query instruments after mMerchantSupportsAutofillPaymentInstruments h as been initialized, |
| 475 // so a fast response from a non-autofill payment app at the front of th e app list does not | 481 // so a fast response from a non-autofill payment app at the front of th e app list does not |
| 476 // cause NOT_SUPPORTED payment rejection. | 482 // cause NOT_SUPPORTED payment rejection. |
| 477 for (Map.Entry<PaymentApp, Map<String, JSONObject>> q : queryApps.entryS et()) { | 483 for (Map.Entry<PaymentApp, Map<String, JSONObject>> q : queryApps.entryS et()) { |
| 478 q.getKey().getInstruments(q.getValue(), this); | 484 q.getKey().getInstruments(q.getValue(), mOrigin, this); |
| 479 } | 485 } |
| 486 mWaitingForPaymentFactory = false; | |
| 480 } | 487 } |
| 481 | 488 |
| 482 /** Filter out merchant method data that's not relevant to a payment app. Ca n return null. */ | 489 /** Filter out merchant method data that's not relevant to a payment app. Ca n return null. */ |
| 483 private static Map<String, JSONObject> filterMerchantMethodData( | 490 private static Map<String, JSONObject> filterMerchantMethodData( |
| 484 Map<String, JSONObject> merchantMethodData, Set<String> appMethods) { | 491 Map<String, JSONObject> merchantMethodData, Set<String> appMethods) { |
| 485 Map<String, JSONObject> result = null; | 492 Map<String, JSONObject> result = null; |
| 486 for (String method : appMethods) { | 493 for (String method : appMethods) { |
| 487 if (merchantMethodData.containsKey(method)) { | 494 if (merchantMethodData.containsKey(method)) { |
| 488 if (result == null) result = new HashMap<>(); | 495 if (result == null) result = new HashMap<>(); |
| 489 result.put(method, merchantMethodData.get(method)); | 496 result.put(method, merchantMethodData.get(method)); |
| (...skipping 561 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1051 // UI has requested the full list of payment instruments. Provide it now . | 1058 // UI has requested the full list of payment instruments. Provide it now . |
| 1052 if (mPaymentInformationCallback != null) providePaymentInformation(); | 1059 if (mPaymentInformationCallback != null) providePaymentInformation(); |
| 1053 } | 1060 } |
| 1054 | 1061 |
| 1055 /** | 1062 /** |
| 1056 * If no payment methods are supported, disconnect from the client and retur n true. | 1063 * If no payment methods are supported, disconnect from the client and retur n true. |
| 1057 * | 1064 * |
| 1058 * @return True if no payment methods are supported | 1065 * @return True if no payment methods are supported |
| 1059 */ | 1066 */ |
| 1060 private boolean disconnectIfNoPaymentMethodsSupported() { | 1067 private boolean disconnectIfNoPaymentMethodsSupported() { |
| 1068 // Waiting for payment app factory to respond. | |
| 1069 if (mPendingApps == null || mPendingInstruments == null) return false; | |
|
please use gerrit instead
2016/11/17 20:40:08
Maybe check mWaitingForPaymentFactory here instead
| |
| 1070 | |
| 1061 boolean waitingForPaymentApps = !mPendingApps.isEmpty() || !mPendingInst ruments.isEmpty(); | 1071 boolean waitingForPaymentApps = !mPendingApps.isEmpty() || !mPendingInst ruments.isEmpty(); |
| 1062 boolean foundPaymentMethods = | 1072 boolean foundPaymentMethods = |
| 1063 mPaymentMethodsSection != null && !mPaymentMethodsSection.isEmpt y(); | 1073 mPaymentMethodsSection != null && !mPaymentMethodsSection.isEmpt y(); |
| 1064 boolean userCanAddCreditCard = mMerchantSupportsAutofillPaymentInstrumen ts | 1074 boolean userCanAddCreditCard = mMerchantSupportsAutofillPaymentInstrumen ts |
| 1065 && !ChromeFeatureList.isEnabled(ChromeFeatureList.NO_CREDIT_CARD _ABORT); | 1075 && !ChromeFeatureList.isEnabled(ChromeFeatureList.NO_CREDIT_CARD _ABORT); |
| 1066 | 1076 |
| 1067 if (!mArePaymentMethodsSupported | 1077 if (!mArePaymentMethodsSupported |
| 1068 || (mIsShowing && !waitingForPaymentApps && !foundPaymentMethods | 1078 || (mIsShowing && !waitingForPaymentApps && !foundPaymentMethods |
| 1069 && !userCanAddCreditCard)) { | 1079 && !mWaitingForPaymentFactory && !userCanAddCreditCar d)) { |
| 1070 // All payment apps have responded, but none of them have instrument s. It's possible to | 1080 // All payment apps have responded, but none of them have instrument s. It's possible to |
| 1071 // add credit cards, but the merchant does not support them either. The payment request | 1081 // add credit cards, but the merchant does not support them either. The payment request |
| 1072 // must be rejected. | 1082 // must be rejected. |
| 1073 disconnectFromClientWithDebugMessage("Requested payment methods have no instruments", | 1083 disconnectFromClientWithDebugMessage("Requested payment methods have no instruments", |
| 1074 PaymentErrorReason.NOT_SUPPORTED); | 1084 PaymentErrorReason.NOT_SUPPORTED); |
| 1075 recordAbortReasonHistogram(mArePaymentMethodsSupported | 1085 recordAbortReasonHistogram(mArePaymentMethodsSupported |
| 1076 ? PaymentRequestMetrics.ABORT_REASON_NO_MATCHING_PAY MENT_METHOD | 1086 ? PaymentRequestMetrics.ABORT_REASON_NO_MATCHING_PAY MENT_METHOD |
| 1077 : PaymentRequestMetrics.ABORT_REASON_NO_SUPPORTED_PA YMENT_METHOD); | 1087 : PaymentRequestMetrics.ABORT_REASON_NO_SUPPORTED_PA YMENT_METHOD); |
| 1078 if (sObserverForTest != null) sObserverForTest.onPaymentRequestServi ceShowFailed(); | 1088 if (sObserverForTest != null) sObserverForTest.onPaymentRequestServi ceShowFailed(); |
| 1079 return true; | 1089 return true; |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1223 "PaymentRequest.CheckoutFunnel.Aborted", abortReason, | 1233 "PaymentRequest.CheckoutFunnel.Aborted", abortReason, |
| 1224 PaymentRequestMetrics.ABORT_REASON_MAX); | 1234 PaymentRequestMetrics.ABORT_REASON_MAX); |
| 1225 | 1235 |
| 1226 if (abortReason == PaymentRequestMetrics.ABORT_REASON_ABORTED_BY_USER) { | 1236 if (abortReason == PaymentRequestMetrics.ABORT_REASON_ABORTED_BY_USER) { |
| 1227 mJourneyLogger.recordJourneyStatsHistograms("UserAborted"); | 1237 mJourneyLogger.recordJourneyStatsHistograms("UserAborted"); |
| 1228 } else { | 1238 } else { |
| 1229 mJourneyLogger.recordJourneyStatsHistograms("OtherAborted"); | 1239 mJourneyLogger.recordJourneyStatsHistograms("OtherAborted"); |
| 1230 } | 1240 } |
| 1231 } | 1241 } |
| 1232 } | 1242 } |
| OLD | NEW |