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 |