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

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: Created 4 years, 2 months 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 28 matching lines...) Expand all
62 import java.util.List; 61 import java.util.List;
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 * third_party/WebKit/public/platform/modules/payments/payment_request.mojom. 68 * third_party/WebKit/public/platform/modules/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.DetailsCallback, 71 PaymentApp.InstrumentsCallback,
73 NormalizedAddressRequestDelegate { 72 PaymentInstrument.DetailsCallback,
73 PaymentResponseHelper.PaymentResponse RequesterDelegate {
please use gerrit instead 2016/10/17 20:44:18 Eeek. Don't let Dan see this. Android engineers do
sebsg 2016/10/18 14:38:19 Done.
74 /** 74 /**
75 * Observer to be notified when PaymentRequest UI has been dismissed. 75 * Observer to be notified when PaymentRequest UI has been dismissed.
76 */ 76 */
77 public interface PaymentRequestDismissObserver { 77 public interface PaymentRequestDismissObserver {
78 /** 78 /**
79 * Called when PaymentRequest UI has been dismissed. 79 * Called when PaymentRequest UI has been dismissed.
80 */ 80 */
81 void onPaymentRequestDismissed(); 81 void onPaymentRequestDismissed();
82 } 82 }
83 83
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 private boolean mMerchantSupportsAutofillPaymentInstruments; 185 private boolean mMerchantSupportsAutofillPaymentInstruments;
186 private ContactEditor mContactEditor; 186 private ContactEditor mContactEditor;
187 private boolean mHasRecordedAbortReason; 187 private boolean mHasRecordedAbortReason;
188 188
189 /** True if any of the requested payment methods are supported. */ 189 /** True if any of the requested payment methods are supported. */
190 private boolean mArePaymentMethodsSupported; 190 private boolean mArePaymentMethodsSupported;
191 191
192 /** True if show() was called. */ 192 /** True if show() was called. */
193 private boolean mIsShowing; 193 private boolean mIsShowing;
194 194
195 private boolean mIsWaitingForNormalization; 195 /** The helper to create and fill the response to send to the merchant. */
196 private PaymentResponse mPendingPaymentResponse; 196 private PaymentResponseHelper mPaymentResponseHelper;
197 197
198 /** 198 /**
199 * Builds the PaymentRequest service implementation. 199 * Builds the PaymentRequest service implementation.
200 * 200 *
201 * @param context The context where PaymentRequest has been invoked. 201 * @param context The context where PaymentRequest has been invoked.
202 * @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.
203 * @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.
204 */ 204 */
205 public PaymentRequestImpl(Activity context, WebContents webContents, 205 public PaymentRequestImpl(Activity context, WebContents webContents,
206 PaymentRequestDismissObserver dismissObserver) { 206 PaymentRequestDismissObserver dismissObserver) {
(...skipping 613 matching lines...) Expand 10 before | Expand all | Expand 10 after
820 } else if (toEdit == null) { 820 } else if (toEdit == null) {
821 mPaymentMethodsSection.addAndSelectItem(completeCard); 821 mPaymentMethodsSection.addAndSelectItem(completeCard);
822 } 822 }
823 823
824 mUI.updateSection(PaymentRequestUI.TYPE_PAYMENT_METHODS, mPaymen tMethodsSection); 824 mUI.updateSection(PaymentRequestUI.TYPE_PAYMENT_METHODS, mPaymen tMethodsSection);
825 } 825 }
826 }); 826 });
827 } 827 }
828 828
829 @Override 829 @Override
830 public void loadingInstrumentDetails() {
831 mUI.showProcessingMessage();
832 }
833
834 @Override
830 public boolean onPayClicked(PaymentOption selectedShippingAddress, 835 public boolean onPayClicked(PaymentOption selectedShippingAddress,
831 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) { 836 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) {
832 assert selectedPaymentMethod instanceof PaymentInstrument; 837 assert selectedPaymentMethod instanceof PaymentInstrument;
833 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ; 838 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ;
834 mPaymentAppRunning = true; 839 mPaymentAppRunning = true;
840
841 PaymentOption selectedContact =
842 mContactSection != null ? mContactSection.getSelectedItem() : nu ll;
843 mPaymentResponseHelper = new PaymentResponseHelper(
844 selectedShippingAddress, selectedShippingOption, selectedContact , this);
845
835 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems, 846 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems,
836 mMethodData.get(instrument.getMethodName()), this); 847 mMethodData.get(instrument.getMethodName()), this);
837 recordSuccessFunnelHistograms("PayClicked"); 848 recordSuccessFunnelHistograms("PayClicked");
838 return !(instrument instanceof AutofillPaymentInstrument); 849 return !(instrument instanceof AutofillPaymentInstrument);
839 } 850 }
840 851
841 @Override 852 @Override
842 public void onDismiss() { 853 public void onDismiss() {
843 disconnectFromClientWithDebugMessage("Dialog dismissed"); 854 disconnectFromClientWithDebugMessage("Dialog dismissed");
844 closeUI(true); 855 closeUI(true);
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
923 instrument.dismiss(); 934 instrument.dismiss();
924 } 935 }
925 } 936 }
926 } 937 }
927 938
928 // Some payment apps still have not responded. Continue waiting for them . 939 // Some payment apps still have not responded. Continue waiting for them .
929 if (!mPendingApps.isEmpty()) return; 940 if (!mPendingApps.isEmpty()) return;
930 941
931 if (disconnectIfNoPaymentMethodsSupported()) return; 942 if (disconnectIfNoPaymentMethodsSupported()) return;
932 943
944 // Load the validation rules for each unique region code in the credit c ard billing
945 // addresses.
946 Set<String> uniqueCountryCodes = new HashSet<>();
947 for (int i = 0; i < mPendingAutofillInstruments.size(); ++i) {
948 assert mPendingAutofillInstruments.get(i) instanceof AutofillPayment Instrument;
949
950 String countryCode = AutofillAddress.getCountryCode((
951 (AutofillPaymentInstrument) mPendingAutofillInstruments.get(
952 i)).getBillingAddress());
953 if (!uniqueCountryCodes.contains(countryCode)) {
954 uniqueCountryCodes.add(countryCode);
955 PersonalDataManager.getInstance().loadRulesForRegion(countryCode );
956 }
957 }
958
933 // List order: 959 // List order:
934 // > Non-autofill instruments. 960 // > Non-autofill instruments.
935 // > Complete autofill instruments. 961 // > Complete autofill instruments.
936 // > Incomplete autofill instruments. 962 // > Incomplete autofill instruments.
937 Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR); 963 Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR);
938 mPendingInstruments.addAll(mPendingAutofillInstruments); 964 mPendingInstruments.addAll(mPendingAutofillInstruments);
939 965
940 mPendingAutofillInstruments.clear(); 966 mPendingAutofillInstruments.clear();
941 mPendingAutofillInstruments = null; 967 mPendingAutofillInstruments = null;
942 968
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1008 } else { 1034 } else {
1009 mPendingInstruments.add(instrument); 1035 mPendingInstruments.add(instrument);
1010 } 1036 }
1011 } 1037 }
1012 1038
1013 /** 1039 /**
1014 * Called after retrieving instrument details. 1040 * Called after retrieving instrument details.
1015 */ 1041 */
1016 @Override 1042 @Override
1017 public void onInstrumentDetailsReady(String methodName, String stringifiedDe tails) { 1043 public void onInstrumentDetailsReady(String methodName, String stringifiedDe tails) {
1018 if (mClient == null) return; 1044 if (mClient == null || mPaymentResponseHelper == null) return;
1019
1020 PaymentResponse response = new PaymentResponse();
1021 response.methodName = methodName;
1022 response.stringifiedDetails = stringifiedDetails;
1023
1024 if (mContactSection != null) {
1025 PaymentOption selectedContact = mContactSection.getSelectedItem();
1026 if (selectedContact != null) {
1027 // Contacts are created in show(). These should all be instances of AutofillContact.
1028 assert selectedContact instanceof AutofillContact;
1029 response.payerPhone = ((AutofillContact) selectedContact).getPay erPhone();
1030 response.payerEmail = ((AutofillContact) selectedContact).getPay erEmail();
1031 }
1032 }
1033
1034 if (mUiShippingOptions != null) {
1035 PaymentOption selectedShippingOption = mUiShippingOptions.getSelecte dItem();
1036 if (selectedShippingOption != null && selectedShippingOption.getIden tifier() != null) {
1037 response.shippingOption = selectedShippingOption.getIdentifier() ;
1038 }
1039 }
1040 1045
1041 // Record the payment method used to complete the transaction. If the pa yment method was an 1046 // Record the payment method used to complete the transaction. If the pa yment method was an
1042 // Autofill credit card with an identifier, record its use. 1047 // Autofill credit card with an identifier, record its use.
1043 PaymentOption selectedPaymentMethod = mPaymentMethodsSection.getSelected Item(); 1048 PaymentOption selectedPaymentMethod = mPaymentMethodsSection.getSelected Item();
1044 if (selectedPaymentMethod instanceof AutofillPaymentInstrument) { 1049 if (selectedPaymentMethod instanceof AutofillPaymentInstrument) {
1045 if (!selectedPaymentMethod.getIdentifier().isEmpty()) { 1050 if (!selectedPaymentMethod.getIdentifier().isEmpty()) {
1046 PersonalDataManager.getInstance().recordAndLogCreditCardUse( 1051 PersonalDataManager.getInstance().recordAndLogCreditCardUse(
1047 selectedPaymentMethod.getIdentifier()); 1052 selectedPaymentMethod.getIdentifier());
1048 } 1053 }
1049 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( 1054 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram(
1050 PaymentRequestMetrics.SELECTED_METHOD_CREDIT_CARD); 1055 PaymentRequestMetrics.SELECTED_METHOD_CREDIT_CARD);
1051 } else if (methodName.equals(ANDROID_PAY_METHOD_NAME)) { 1056 } else if (methodName.equals(ANDROID_PAY_METHOD_NAME)) {
1052 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( 1057 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram(
1053 PaymentRequestMetrics.SELECTED_METHOD_ANDROID_PAY); 1058 PaymentRequestMetrics.SELECTED_METHOD_ANDROID_PAY);
1054 } else { 1059 } else {
1055 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( 1060 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram(
1056 PaymentRequestMetrics.SELECTED_METHOD_OTHER_PAYMENT_APP); 1061 PaymentRequestMetrics.SELECTED_METHOD_OTHER_PAYMENT_APP);
1057 } 1062 }
1058 1063
1059 mUI.showProcessingMessage(); 1064 recordSuccessFunnelHistograms("ReceivedInstrumentDetails");
1060 1065
1061 if (mShippingAddressesSection != null) { 1066 mPaymentResponseHelper.onInstrumentDetailsReceived(methodName, stringifi edDetails);
1062 PaymentOption selectedShippingAddress = mShippingAddressesSection.ge tSelectedItem(); 1067 }
1063 if (selectedShippingAddress != null) {
1064 // Shipping addresses are created in show(). These should all be instances of
1065 // AutofillAddress.
1066 assert selectedShippingAddress instanceof AutofillAddress;
1067 AutofillAddress selectedAutofillAddress = (AutofillAddress) sele ctedShippingAddress;
1068 1068
1069 // Addresses to be sent to the merchant should always be complet e. 1069 @Override
1070 assert selectedAutofillAddress.isComplete(); 1070 public void onPaymentResponseReady(PaymentResponse response) {
1071
1072 // Record the use of the profile.
1073 PersonalDataManager.getInstance().recordAndLogProfileUse(
1074 selectedAutofillAddress.getProfile().getGUID());
1075
1076 response.shippingAddress = selectedAutofillAddress.toPaymentAddr ess();
1077
1078 // Create the normalization task.
1079 mPendingPaymentResponse = response;
1080 mIsWaitingForNormalization = true;
1081 boolean willNormalizeAsync = PersonalDataManager.getInstance().n ormalizeAddress(
1082 selectedAutofillAddress.getProfile().getGUID(),
1083 AutofillAddress.getCountryCode(selectedAutofillAddress.g etProfile()), this);
1084
1085 if (willNormalizeAsync) {
1086 // If the normalization was not done synchronously, start a timer to cancel the
1087 // asynchronous normalization if it takes too long.
1088 mHandler.postDelayed(new Runnable() {
1089 @Override
1090 public void run() {
1091 onAddressNormalized(null);
1092 }
1093 }, PersonalDataManager.getInstance().getNormalizationTimeout MS());
1094 }
1095
1096 // The payment response will be sent to the merchant in onAddres sNormalized instead.
1097 return;
1098 }
1099 }
1100
1101 mClient.onPaymentResponse(response); 1071 mClient.onPaymentResponse(response);
1102 recordSuccessFunnelHistograms("ReceivedInstrumentDetails"); 1072 mPaymentResponseHelper = null;
1073 PersonalDataManager.getInstance().cancelPendingAddressNormalizations();
1103 } 1074 }
1104 1075
1105 /** 1076 /**
1106 * Callback method called either when the address has finished normalizing o r when the timeout
1107 * triggers. Replaces the address in the response with the normalized versio n if present and
1108 * sends the response to the merchant.
1109 *
1110 * @param profile The profile with the address normalized or a null profile if the timeout
1111 * triggered first.
1112 */
1113 @Override
1114 public void onAddressNormalized(AutofillProfile profile) {
1115 // Check if the other task finished first.
1116 if (!mIsWaitingForNormalization) return;
1117 mIsWaitingForNormalization = false;
1118
1119 // Check if the response was already sent to the merchant.
1120 if (mClient == null || mPendingPaymentResponse == null) return;
1121
1122 if (profile != null && !TextUtils.isEmpty(profile.getGUID())) {
1123 // The normalization finished first: use the normalized address.
1124 mPendingPaymentResponse.shippingAddress =
1125 new AutofillAddress(profile, true /* isComplete */).toPaymen tAddress();
1126 } else {
1127 // The timeout triggered first: cancel the normalization task.
1128 PersonalDataManager.getInstance().cancelPendingAddressNormalization( );
1129 }
1130
1131 // Send the payment response to the merchant.
1132 mClient.onPaymentResponse(mPendingPaymentResponse);
1133
1134 mPendingPaymentResponse = null;
1135
1136 recordSuccessFunnelHistograms("ReceivedInstrumentDetails");
1137 }
1138
1139 /**
1140 * Called if unable to retrieve instrument details. 1077 * Called if unable to retrieve instrument details.
1141 */ 1078 */
1142 @Override 1079 @Override
1143 public void onInstrumentDetailsError() { 1080 public void onInstrumentDetailsError() {
1144 mUI.onPayButtonProcessingCancelled(); 1081 mUI.onPayButtonProcessingCancelled();
1145 mPaymentAppRunning = false; 1082 mPaymentAppRunning = false;
1146 } 1083 }
1147 1084
1148 /** 1085 /**
1149 * Closes the UI. If the client is still connected, then it's notified of UI hiding. 1086 * Closes the UI. If the client is still connected, then it's notified of UI hiding.
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
1207 private void recordAbortReasonHistogram(int abortReason) { 1144 private void recordAbortReasonHistogram(int abortReason) {
1208 assert abortReason < PaymentRequestMetrics.ABORT_REASON_MAX; 1145 assert abortReason < PaymentRequestMetrics.ABORT_REASON_MAX;
1209 if (mHasRecordedAbortReason) return; 1146 if (mHasRecordedAbortReason) return;
1210 1147
1211 mHasRecordedAbortReason = true; 1148 mHasRecordedAbortReason = true;
1212 RecordHistogram.recordEnumeratedHistogram( 1149 RecordHistogram.recordEnumeratedHistogram(
1213 "PaymentRequest.CheckoutFunnel.Aborted", abortReason, 1150 "PaymentRequest.CheckoutFunnel.Aborted", abortReason,
1214 PaymentRequestMetrics.ABORT_REASON_MAX); 1151 PaymentRequestMetrics.ABORT_REASON_MAX);
1215 } 1152 }
1216 } 1153 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698