Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(287)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java

Issue 2413533003: [Payments] Normalize billing address before sending to the merchant. (Closed)
Patch Set: Rebase Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698