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; |
11 | 11 |
12 import org.json.JSONException; | 12 import org.json.JSONException; |
13 import org.json.JSONObject; | 13 import org.json.JSONObject; |
14 | 14 |
15 import org.chromium.base.Callback; | 15 import org.chromium.base.Callback; |
16 import org.chromium.base.Log; | 16 import org.chromium.base.Log; |
17 import org.chromium.base.VisibleForTesting; | 17 import org.chromium.base.VisibleForTesting; |
18 import org.chromium.base.metrics.RecordHistogram; | 18 import org.chromium.base.metrics.RecordHistogram; |
19 import org.chromium.chrome.R; | 19 import org.chromium.chrome.R; |
20 import org.chromium.chrome.browser.ChromeActivity; | 20 import org.chromium.chrome.browser.ChromeActivity; |
21 import org.chromium.chrome.browser.autofill.PersonalDataManager; | 21 import org.chromium.chrome.browser.autofill.PersonalDataManager; |
22 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; | 22 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; |
23 import org.chromium.chrome.browser.autofill.PersonalDataManager.NormalizedAddres
sRequestDelegate; | |
24 import org.chromium.chrome.browser.favicon.FaviconHelper; | 23 import org.chromium.chrome.browser.favicon.FaviconHelper; |
25 import org.chromium.chrome.browser.payments.ui.Completable; | 24 import org.chromium.chrome.browser.payments.ui.Completable; |
26 import org.chromium.chrome.browser.payments.ui.LineItem; | 25 import org.chromium.chrome.browser.payments.ui.LineItem; |
27 import org.chromium.chrome.browser.payments.ui.PaymentInformation; | 26 import org.chromium.chrome.browser.payments.ui.PaymentInformation; |
28 import org.chromium.chrome.browser.payments.ui.PaymentOption; | 27 import org.chromium.chrome.browser.payments.ui.PaymentOption; |
29 import org.chromium.chrome.browser.payments.ui.PaymentRequestUI; | 28 import org.chromium.chrome.browser.payments.ui.PaymentRequestUI; |
30 import org.chromium.chrome.browser.payments.ui.SectionInformation; | 29 import org.chromium.chrome.browser.payments.ui.SectionInformation; |
31 import org.chromium.chrome.browser.payments.ui.ShoppingCart; | 30 import org.chromium.chrome.browser.payments.ui.ShoppingCart; |
32 import org.chromium.chrome.browser.profiles.Profile; | 31 import org.chromium.chrome.browser.profiles.Profile; |
33 import org.chromium.chrome.browser.tab.Tab; | 32 import org.chromium.chrome.browser.tab.Tab; |
(...skipping 29 matching lines...) Expand all Loading... |
63 import java.util.Locale; | 62 import java.util.Locale; |
64 import java.util.Map; | 63 import java.util.Map; |
65 import java.util.Set; | 64 import java.util.Set; |
66 | 65 |
67 /** | 66 /** |
68 * Android implementation of the PaymentRequest service defined in | 67 * Android implementation of the PaymentRequest service defined in |
69 * components/payments/payment_request.mojom. | 68 * components/payments/payment_request.mojom. |
70 */ | 69 */ |
71 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
nt, | 70 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
nt, |
72 PaymentApp.InstrumentsCallback, PaymentInstrument.InstrumentDetailsCallb
ack, | 71 PaymentApp.InstrumentsCallback, PaymentInstrument.InstrumentDetailsCallb
ack, |
73 NormalizedAddressRequestDelegate { | 72 PaymentResponseHelper.PaymentResponseRequesterDelegate { |
74 /** | 73 /** |
75 * Observer to be notified when PaymentRequest UI has been dismissed. | 74 * Observer to be notified when PaymentRequest UI has been dismissed. |
76 */ | 75 */ |
77 public interface PaymentRequestDismissObserver { | 76 public interface PaymentRequestDismissObserver { |
78 /** | 77 /** |
79 * Called when PaymentRequest UI has been dismissed. | 78 * Called when PaymentRequest UI has been dismissed. |
80 */ | 79 */ |
81 void onPaymentRequestDismissed(); | 80 void onPaymentRequestDismissed(); |
82 } | 81 } |
83 | 82 |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
186 private boolean mMerchantSupportsAutofillPaymentInstruments; | 185 private boolean mMerchantSupportsAutofillPaymentInstruments; |
187 private ContactEditor mContactEditor; | 186 private ContactEditor mContactEditor; |
188 private boolean mHasRecordedAbortReason; | 187 private boolean mHasRecordedAbortReason; |
189 | 188 |
190 /** True if any of the requested payment methods are supported. */ | 189 /** True if any of the requested payment methods are supported. */ |
191 private boolean mArePaymentMethodsSupported; | 190 private boolean mArePaymentMethodsSupported; |
192 | 191 |
193 /** True if show() was called. */ | 192 /** True if show() was called. */ |
194 private boolean mIsShowing; | 193 private boolean mIsShowing; |
195 | 194 |
196 private boolean mIsWaitingForNormalization; | 195 /** The helper to create and fill the response to send to the merchant. */ |
197 private PaymentResponse mPendingPaymentResponse; | 196 private PaymentResponseHelper mPaymentResponseHelper; |
198 | 197 |
199 /** | 198 /** |
200 * Builds the PaymentRequest service implementation. | 199 * Builds the PaymentRequest service implementation. |
201 * | 200 * |
202 * @param context The context where PaymentRequest has been invoked. | 201 * @param context The context where PaymentRequest has been invoked. |
203 * @param webContents The web contents that have invoked the PaymentRequ
est API. | 202 * @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. | 203 * @param dismissObserver The observer to notify when PaymentRequest UI has
been dismissed. |
205 */ | 204 */ |
206 public PaymentRequestImpl(Activity context, WebContents webContents, | 205 public PaymentRequestImpl(Activity context, WebContents webContents, |
207 PaymentRequestDismissObserver dismissObserver) { | 206 PaymentRequestDismissObserver dismissObserver) { |
(...skipping 673 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
881 } else if (toEdit == null) { | 880 } else if (toEdit == null) { |
882 mPaymentMethodsSection.addAndSelectItem(completeCard); | 881 mPaymentMethodsSection.addAndSelectItem(completeCard); |
883 } | 882 } |
884 | 883 |
885 mUI.updateSection(PaymentRequestUI.TYPE_PAYMENT_METHODS, mPaymen
tMethodsSection); | 884 mUI.updateSection(PaymentRequestUI.TYPE_PAYMENT_METHODS, mPaymen
tMethodsSection); |
886 } | 885 } |
887 }); | 886 }); |
888 } | 887 } |
889 | 888 |
890 @Override | 889 @Override |
| 890 public void loadingInstrumentDetails() { |
| 891 mUI.showProcessingMessage(); |
| 892 mPaymentResponseHelper.onInstrumentsDetailsLoading(); |
| 893 } |
| 894 |
| 895 @Override |
891 public boolean onPayClicked(PaymentOption selectedShippingAddress, | 896 public boolean onPayClicked(PaymentOption selectedShippingAddress, |
892 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM
ethod) { | 897 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM
ethod) { |
893 assert selectedPaymentMethod instanceof PaymentInstrument; | 898 assert selectedPaymentMethod instanceof PaymentInstrument; |
894 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod
; | 899 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod
; |
895 mPaymentAppRunning = true; | 900 mPaymentAppRunning = true; |
| 901 |
| 902 PaymentOption selectedContact = |
| 903 mContactSection != null ? mContactSection.getSelectedItem() : nu
ll; |
| 904 mPaymentResponseHelper = new PaymentResponseHelper( |
| 905 selectedShippingAddress, selectedShippingOption, selectedContact
, this); |
| 906 |
896 instrument.getInstrumentDetails(mMerchantName, mOrigin, mRawTotal, mRawL
ineItems, | 907 instrument.getInstrumentDetails(mMerchantName, mOrigin, mRawTotal, mRawL
ineItems, |
897 mMethodData.get(instrument.getInstrumentMethodName()), this); | 908 mMethodData.get(instrument.getInstrumentMethodName()), this); |
898 recordSuccessFunnelHistograms("PayClicked"); | 909 recordSuccessFunnelHistograms("PayClicked"); |
899 return !(instrument instanceof AutofillPaymentInstrument); | 910 return !(instrument instanceof AutofillPaymentInstrument); |
900 } | 911 } |
901 | 912 |
902 @Override | 913 @Override |
903 public void onDismiss() { | 914 public void onDismiss() { |
904 disconnectFromClientWithDebugMessage("Dialog dismissed"); | 915 disconnectFromClientWithDebugMessage("Dialog dismissed"); |
905 closeUI(true); | 916 closeUI(true); |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
984 instrument.dismissInstrument(); | 995 instrument.dismissInstrument(); |
985 } | 996 } |
986 } | 997 } |
987 } | 998 } |
988 | 999 |
989 // Some payment apps still have not responded. Continue waiting for them
. | 1000 // Some payment apps still have not responded. Continue waiting for them
. |
990 if (!mPendingApps.isEmpty()) return; | 1001 if (!mPendingApps.isEmpty()) return; |
991 | 1002 |
992 if (disconnectIfNoPaymentMethodsSupported()) return; | 1003 if (disconnectIfNoPaymentMethodsSupported()) return; |
993 | 1004 |
| 1005 // Load the validation rules for each unique region code in the credit c
ard billing |
| 1006 // addresses. |
| 1007 Set<String> uniqueCountryCodes = new HashSet<>(); |
| 1008 for (int i = 0; i < mPendingAutofillInstruments.size(); ++i) { |
| 1009 assert mPendingAutofillInstruments.get(i) instanceof AutofillPayment
Instrument; |
| 1010 |
| 1011 String countryCode = AutofillAddress.getCountryCode(( |
| 1012 (AutofillPaymentInstrument) mPendingAutofillInstruments.get( |
| 1013 i)).getBillingAddress()); |
| 1014 if (!uniqueCountryCodes.contains(countryCode)) { |
| 1015 uniqueCountryCodes.add(countryCode); |
| 1016 PersonalDataManager.getInstance().loadRulesForRegion(countryCode
); |
| 1017 } |
| 1018 } |
| 1019 |
994 // List order: | 1020 // List order: |
995 // > Non-autofill instruments. | 1021 // > Non-autofill instruments. |
996 // > Complete autofill instruments. | 1022 // > Complete autofill instruments. |
997 // > Incomplete autofill instruments. | 1023 // > Incomplete autofill instruments. |
998 Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR); | 1024 Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR); |
999 mPendingInstruments.addAll(mPendingAutofillInstruments); | 1025 mPendingInstruments.addAll(mPendingAutofillInstruments); |
1000 | 1026 |
1001 // Log the number of suggested credit cards. | 1027 // Log the number of suggested credit cards. |
1002 mJourneyLogger.setNumberOfSuggestionsShown(PaymentRequestJourneyLogger.S
ECTION_CREDIT_CARDS, | 1028 mJourneyLogger.setNumberOfSuggestionsShown(PaymentRequestJourneyLogger.S
ECTION_CREDIT_CARDS, |
1003 mPendingAutofillInstruments.size()); | 1029 mPendingAutofillInstruments.size()); |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1073 } else { | 1099 } else { |
1074 mPendingInstruments.add(instrument); | 1100 mPendingInstruments.add(instrument); |
1075 } | 1101 } |
1076 } | 1102 } |
1077 | 1103 |
1078 /** | 1104 /** |
1079 * Called after retrieving instrument details. | 1105 * Called after retrieving instrument details. |
1080 */ | 1106 */ |
1081 @Override | 1107 @Override |
1082 public void onInstrumentDetailsReady(String methodName, String stringifiedDe
tails) { | 1108 public void onInstrumentDetailsReady(String methodName, String stringifiedDe
tails) { |
1083 if (mClient == null) return; | 1109 if (mClient == null || mPaymentResponseHelper == null) return; |
1084 | |
1085 PaymentResponse response = new PaymentResponse(); | |
1086 response.methodName = methodName; | |
1087 response.stringifiedDetails = stringifiedDetails; | |
1088 | |
1089 if (mContactSection != null) { | |
1090 PaymentOption selectedContact = mContactSection.getSelectedItem(); | |
1091 if (selectedContact != null) { | |
1092 // Contacts are created in show(). These should all be instances
of AutofillContact. | |
1093 assert selectedContact instanceof AutofillContact; | |
1094 response.payerName = ((AutofillContact) selectedContact).getPaye
rName(); | |
1095 response.payerPhone = ((AutofillContact) selectedContact).getPay
erPhone(); | |
1096 response.payerEmail = ((AutofillContact) selectedContact).getPay
erEmail(); | |
1097 } | |
1098 } | |
1099 | |
1100 if (mUiShippingOptions != null) { | |
1101 PaymentOption selectedShippingOption = mUiShippingOptions.getSelecte
dItem(); | |
1102 if (selectedShippingOption != null && selectedShippingOption.getIden
tifier() != null) { | |
1103 response.shippingOption = selectedShippingOption.getIdentifier()
; | |
1104 } | |
1105 } | |
1106 | 1110 |
1107 // Record the payment method used to complete the transaction. If the pa
yment method was an | 1111 // Record the payment method used to complete the transaction. If the pa
yment method was an |
1108 // Autofill credit card with an identifier, record its use. | 1112 // Autofill credit card with an identifier, record its use. |
1109 PaymentOption selectedPaymentMethod = mPaymentMethodsSection.getSelected
Item(); | 1113 PaymentOption selectedPaymentMethod = mPaymentMethodsSection.getSelected
Item(); |
1110 if (selectedPaymentMethod instanceof AutofillPaymentInstrument) { | 1114 if (selectedPaymentMethod instanceof AutofillPaymentInstrument) { |
1111 if (!selectedPaymentMethod.getIdentifier().isEmpty()) { | 1115 if (!selectedPaymentMethod.getIdentifier().isEmpty()) { |
1112 PersonalDataManager.getInstance().recordAndLogCreditCardUse( | 1116 PersonalDataManager.getInstance().recordAndLogCreditCardUse( |
1113 selectedPaymentMethod.getIdentifier()); | 1117 selectedPaymentMethod.getIdentifier()); |
1114 } | 1118 } |
1115 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( | 1119 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( |
1116 PaymentRequestMetrics.SELECTED_METHOD_CREDIT_CARD); | 1120 PaymentRequestMetrics.SELECTED_METHOD_CREDIT_CARD); |
1117 } else if (methodName.equals(ANDROID_PAY_METHOD_NAME)) { | 1121 } else if (methodName.equals(ANDROID_PAY_METHOD_NAME)) { |
1118 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( | 1122 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( |
1119 PaymentRequestMetrics.SELECTED_METHOD_ANDROID_PAY); | 1123 PaymentRequestMetrics.SELECTED_METHOD_ANDROID_PAY); |
1120 } else { | 1124 } else { |
1121 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( | 1125 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( |
1122 PaymentRequestMetrics.SELECTED_METHOD_OTHER_PAYMENT_APP); | 1126 PaymentRequestMetrics.SELECTED_METHOD_OTHER_PAYMENT_APP); |
1123 } | 1127 } |
1124 | 1128 |
1125 mUI.showProcessingMessage(); | 1129 recordSuccessFunnelHistograms("ReceivedInstrumentDetails"); |
1126 | 1130 |
1127 if (mShippingAddressesSection != null) { | 1131 mPaymentResponseHelper.onInstrumentDetailsReceived(methodName, stringifi
edDetails); |
1128 PaymentOption selectedShippingAddress = mShippingAddressesSection.ge
tSelectedItem(); | 1132 } |
1129 if (selectedShippingAddress != null) { | |
1130 // Shipping addresses are created in show(). These should all be
instances of | |
1131 // AutofillAddress. | |
1132 assert selectedShippingAddress instanceof AutofillAddress; | |
1133 AutofillAddress selectedAutofillAddress = (AutofillAddress) sele
ctedShippingAddress; | |
1134 | 1133 |
1135 // Addresses to be sent to the merchant should always be complet
e. | 1134 @Override |
1136 assert selectedAutofillAddress.isComplete(); | 1135 public void onPaymentResponseReady(PaymentResponse response) { |
1137 | |
1138 // Record the use of the profile. | |
1139 PersonalDataManager.getInstance().recordAndLogProfileUse( | |
1140 selectedAutofillAddress.getProfile().getGUID()); | |
1141 | |
1142 response.shippingAddress = selectedAutofillAddress.toPaymentAddr
ess(); | |
1143 | |
1144 // Create the normalization task. | |
1145 mPendingPaymentResponse = response; | |
1146 mIsWaitingForNormalization = true; | |
1147 boolean willNormalizeAsync = PersonalDataManager.getInstance().n
ormalizeAddress( | |
1148 selectedAutofillAddress.getProfile().getGUID(), | |
1149 AutofillAddress.getCountryCode(selectedAutofillAddress.g
etProfile()), this); | |
1150 | |
1151 if (willNormalizeAsync) { | |
1152 // If the normalization was not done synchronously, start a
timer to cancel the | |
1153 // asynchronous normalization if it takes too long. | |
1154 mHandler.postDelayed(new Runnable() { | |
1155 @Override | |
1156 public void run() { | |
1157 onAddressNormalized(null); | |
1158 } | |
1159 }, PersonalDataManager.getInstance().getNormalizationTimeout
MS()); | |
1160 } | |
1161 | |
1162 // The payment response will be sent to the merchant in onAddres
sNormalized instead. | |
1163 return; | |
1164 } | |
1165 } | |
1166 | |
1167 mClient.onPaymentResponse(response); | 1136 mClient.onPaymentResponse(response); |
1168 recordSuccessFunnelHistograms("ReceivedInstrumentDetails"); | 1137 mPaymentResponseHelper = null; |
| 1138 PersonalDataManager.getInstance().cancelPendingAddressNormalizations(); |
1169 } | 1139 } |
1170 | 1140 |
1171 /** | 1141 /** |
1172 * Callback method called either when the address has finished normalizing o
r when the timeout | |
1173 * triggers. Replaces the address in the response with the normalized versio
n if present and | |
1174 * sends the response to the merchant. | |
1175 * | |
1176 * @param profile The profile with the address normalized or a null profile
if the timeout | |
1177 * triggered first. | |
1178 */ | |
1179 @Override | |
1180 public void onAddressNormalized(AutofillProfile profile) { | |
1181 // Check if the other task finished first. | |
1182 if (!mIsWaitingForNormalization) return; | |
1183 mIsWaitingForNormalization = false; | |
1184 | |
1185 // Check if the response was already sent to the merchant. | |
1186 if (mClient == null || mPendingPaymentResponse == null) return; | |
1187 | |
1188 if (profile != null && !TextUtils.isEmpty(profile.getGUID())) { | |
1189 // The normalization finished first: use the normalized address. | |
1190 mPendingPaymentResponse.shippingAddress = | |
1191 new AutofillAddress(profile, true /* isComplete */).toPaymen
tAddress(); | |
1192 } else { | |
1193 // The timeout triggered first: cancel the normalization task. | |
1194 PersonalDataManager.getInstance().cancelPendingAddressNormalization(
); | |
1195 } | |
1196 | |
1197 // Send the payment response to the merchant. | |
1198 mClient.onPaymentResponse(mPendingPaymentResponse); | |
1199 | |
1200 mPendingPaymentResponse = null; | |
1201 | |
1202 recordSuccessFunnelHistograms("ReceivedInstrumentDetails"); | |
1203 } | |
1204 | |
1205 /** | |
1206 * Called if unable to retrieve instrument details. | 1142 * Called if unable to retrieve instrument details. |
1207 */ | 1143 */ |
1208 @Override | 1144 @Override |
1209 public void onInstrumentDetailsError() { | 1145 public void onInstrumentDetailsError() { |
1210 if (mClient == null) return; | 1146 if (mClient == null) return; |
1211 mUI.onPayButtonProcessingCancelled(); | 1147 mUI.onPayButtonProcessingCancelled(); |
1212 mPaymentAppRunning = false; | 1148 mPaymentAppRunning = false; |
1213 } | 1149 } |
1214 | 1150 |
1215 /** | 1151 /** |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1284 "PaymentRequest.CheckoutFunnel.Aborted", abortReason, | 1220 "PaymentRequest.CheckoutFunnel.Aborted", abortReason, |
1285 PaymentRequestMetrics.ABORT_REASON_MAX); | 1221 PaymentRequestMetrics.ABORT_REASON_MAX); |
1286 | 1222 |
1287 if (abortReason == PaymentRequestMetrics.ABORT_REASON_ABORTED_BY_USER) { | 1223 if (abortReason == PaymentRequestMetrics.ABORT_REASON_ABORTED_BY_USER) { |
1288 mJourneyLogger.recordJourneyStatsHistograms("UserAborted"); | 1224 mJourneyLogger.recordJourneyStatsHistograms("UserAborted"); |
1289 } else { | 1225 } else { |
1290 mJourneyLogger.recordJourneyStatsHistograms("OtherAborted"); | 1226 mJourneyLogger.recordJourneyStatsHistograms("OtherAborted"); |
1291 } | 1227 } |
1292 } | 1228 } |
1293 } | 1229 } |
OLD | NEW |