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

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: Addressed comments 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 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 * 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, PaymentInstrument.DetailsCallback,
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 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 private boolean mMerchantSupportsAutofillPaymentInstruments; 184 private boolean mMerchantSupportsAutofillPaymentInstruments;
186 private ContactEditor mContactEditor; 185 private ContactEditor mContactEditor;
187 private boolean mHasRecordedAbortReason; 186 private boolean mHasRecordedAbortReason;
188 187
189 /** True if any of the requested payment methods are supported. */ 188 /** True if any of the requested payment methods are supported. */
190 private boolean mArePaymentMethodsSupported; 189 private boolean mArePaymentMethodsSupported;
191 190
192 /** True if show() was called. */ 191 /** True if show() was called. */
193 private boolean mIsShowing; 192 private boolean mIsShowing;
194 193
195 private boolean mIsWaitingForNormalization; 194 /** The helper to create and fill the response to send to the merchant. */
196 private PaymentResponse mPendingPaymentResponse; 195 private PaymentResponseHelper mPaymentResponseHelper;
197 196
198 /** 197 /**
199 * Builds the PaymentRequest service implementation. 198 * Builds the PaymentRequest service implementation.
200 * 199 *
201 * @param context The context where PaymentRequest has been invoked. 200 * @param context The context where PaymentRequest has been invoked.
202 * @param webContents The web contents that have invoked the PaymentRequ est API. 201 * @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. 202 * @param dismissObserver The observer to notify when PaymentRequest UI has been dismissed.
204 */ 203 */
205 public PaymentRequestImpl(Activity context, WebContents webContents, 204 public PaymentRequestImpl(Activity context, WebContents webContents,
206 PaymentRequestDismissObserver dismissObserver) { 205 PaymentRequestDismissObserver dismissObserver) {
(...skipping 613 matching lines...) Expand 10 before | Expand all | Expand 10 after
820 } else if (toEdit == null) { 819 } else if (toEdit == null) {
821 mPaymentMethodsSection.addAndSelectItem(completeCard); 820 mPaymentMethodsSection.addAndSelectItem(completeCard);
822 } 821 }
823 822
824 mUI.updateSection(PaymentRequestUI.TYPE_PAYMENT_METHODS, mPaymen tMethodsSection); 823 mUI.updateSection(PaymentRequestUI.TYPE_PAYMENT_METHODS, mPaymen tMethodsSection);
825 } 824 }
826 }); 825 });
827 } 826 }
828 827
829 @Override 828 @Override
829 public void loadingInstrumentDetails() {
830 mUI.showProcessingMessage();
831 }
832
833 @Override
830 public boolean onPayClicked(PaymentOption selectedShippingAddress, 834 public boolean onPayClicked(PaymentOption selectedShippingAddress,
831 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) { 835 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) {
832 assert selectedPaymentMethod instanceof PaymentInstrument; 836 assert selectedPaymentMethod instanceof PaymentInstrument;
833 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ; 837 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ;
834 mPaymentAppRunning = true; 838 mPaymentAppRunning = true;
839
840 PaymentOption selectedContact =
841 mContactSection != null ? mContactSection.getSelectedItem() : nu ll;
842 mPaymentResponseHelper = new PaymentResponseHelper(
843 selectedShippingAddress, selectedShippingOption, selectedContact , this);
844
835 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems, 845 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems,
836 mMethodData.get(instrument.getMethodName()), this); 846 mMethodData.get(instrument.getMethodName()), this);
837 recordSuccessFunnelHistograms("PayClicked"); 847 recordSuccessFunnelHistograms("PayClicked");
838 return !(instrument instanceof AutofillPaymentInstrument); 848 return !(instrument instanceof AutofillPaymentInstrument);
839 } 849 }
840 850
841 @Override 851 @Override
842 public void onDismiss() { 852 public void onDismiss() {
843 disconnectFromClientWithDebugMessage("Dialog dismissed"); 853 disconnectFromClientWithDebugMessage("Dialog dismissed");
844 closeUI(true); 854 closeUI(true);
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
923 instrument.dismiss(); 933 instrument.dismiss();
924 } 934 }
925 } 935 }
926 } 936 }
927 937
928 // Some payment apps still have not responded. Continue waiting for them . 938 // Some payment apps still have not responded. Continue waiting for them .
929 if (!mPendingApps.isEmpty()) return; 939 if (!mPendingApps.isEmpty()) return;
930 940
931 if (disconnectIfNoPaymentMethodsSupported()) return; 941 if (disconnectIfNoPaymentMethodsSupported()) return;
932 942
943 // Load the validation rules for each unique region code in the credit c ard billing
944 // addresses.
945 Set<String> uniqueCountryCodes = new HashSet<>();
946 for (int i = 0; i < mPendingAutofillInstruments.size(); ++i) {
947 assert mPendingAutofillInstruments.get(i) instanceof AutofillPayment Instrument;
948
949 String countryCode = AutofillAddress.getCountryCode((
950 (AutofillPaymentInstrument) mPendingAutofillInstruments.get(
951 i)).getBillingAddress());
952 if (!uniqueCountryCodes.contains(countryCode)) {
953 uniqueCountryCodes.add(countryCode);
954 PersonalDataManager.getInstance().loadRulesForRegion(countryCode );
955 }
956 }
957
933 // List order: 958 // List order:
934 // > Non-autofill instruments. 959 // > Non-autofill instruments.
935 // > Complete autofill instruments. 960 // > Complete autofill instruments.
936 // > Incomplete autofill instruments. 961 // > Incomplete autofill instruments.
937 Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR); 962 Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR);
938 mPendingInstruments.addAll(mPendingAutofillInstruments); 963 mPendingInstruments.addAll(mPendingAutofillInstruments);
939 964
940 mPendingAutofillInstruments.clear(); 965 mPendingAutofillInstruments.clear();
941 mPendingAutofillInstruments = null; 966 mPendingAutofillInstruments = null;
942 967
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1008 } else { 1033 } else {
1009 mPendingInstruments.add(instrument); 1034 mPendingInstruments.add(instrument);
1010 } 1035 }
1011 } 1036 }
1012 1037
1013 /** 1038 /**
1014 * Called after retrieving instrument details. 1039 * Called after retrieving instrument details.
1015 */ 1040 */
1016 @Override 1041 @Override
1017 public void onInstrumentDetailsReady(String methodName, String stringifiedDe tails) { 1042 public void onInstrumentDetailsReady(String methodName, String stringifiedDe tails) {
1018 if (mClient == null) return; 1043 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 1044
1041 // Record the payment method used to complete the transaction. If the pa yment method was an 1045 // 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. 1046 // Autofill credit card with an identifier, record its use.
1043 PaymentOption selectedPaymentMethod = mPaymentMethodsSection.getSelected Item(); 1047 PaymentOption selectedPaymentMethod = mPaymentMethodsSection.getSelected Item();
1044 if (selectedPaymentMethod instanceof AutofillPaymentInstrument) { 1048 if (selectedPaymentMethod instanceof AutofillPaymentInstrument) {
1045 if (!selectedPaymentMethod.getIdentifier().isEmpty()) { 1049 if (!selectedPaymentMethod.getIdentifier().isEmpty()) {
1046 PersonalDataManager.getInstance().recordAndLogCreditCardUse( 1050 PersonalDataManager.getInstance().recordAndLogCreditCardUse(
1047 selectedPaymentMethod.getIdentifier()); 1051 selectedPaymentMethod.getIdentifier());
1048 } 1052 }
1049 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( 1053 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram(
1050 PaymentRequestMetrics.SELECTED_METHOD_CREDIT_CARD); 1054 PaymentRequestMetrics.SELECTED_METHOD_CREDIT_CARD);
1051 } else if (methodName.equals(ANDROID_PAY_METHOD_NAME)) { 1055 } else if (methodName.equals(ANDROID_PAY_METHOD_NAME)) {
1052 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( 1056 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram(
1053 PaymentRequestMetrics.SELECTED_METHOD_ANDROID_PAY); 1057 PaymentRequestMetrics.SELECTED_METHOD_ANDROID_PAY);
1054 } else { 1058 } else {
1055 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram( 1059 PaymentRequestMetrics.recordSelectedPaymentMethodHistogram(
1056 PaymentRequestMetrics.SELECTED_METHOD_OTHER_PAYMENT_APP); 1060 PaymentRequestMetrics.SELECTED_METHOD_OTHER_PAYMENT_APP);
1057 } 1061 }
1058 1062
1059 mUI.showProcessingMessage(); 1063 recordSuccessFunnelHistograms("ReceivedInstrumentDetails");
1060 1064
1061 if (mShippingAddressesSection != null) { 1065 mPaymentResponseHelper.onInstrumentDetailsReceived(methodName, stringifi edDetails);
1062 PaymentOption selectedShippingAddress = mShippingAddressesSection.ge tSelectedItem(); 1066 }
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 1067
1069 // Addresses to be sent to the merchant should always be complet e. 1068 @Override
1070 assert selectedAutofillAddress.isComplete(); 1069 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); 1070 mClient.onPaymentResponse(response);
1102 recordSuccessFunnelHistograms("ReceivedInstrumentDetails"); 1071 mPaymentResponseHelper = null;
1072 PersonalDataManager.getInstance().cancelPendingAddressNormalizations();
1103 } 1073 }
1104 1074
1105 /** 1075 /**
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. 1076 * Called if unable to retrieve instrument details.
1141 */ 1077 */
1142 @Override 1078 @Override
1143 public void onInstrumentDetailsError() { 1079 public void onInstrumentDetailsError() {
1144 mUI.onPayButtonProcessingCancelled(); 1080 mUI.onPayButtonProcessingCancelled();
1145 mPaymentAppRunning = false; 1081 mPaymentAppRunning = false;
1146 } 1082 }
1147 1083
1148 /** 1084 /**
1149 * Closes the UI. If the client is still connected, then it's notified of UI hiding. 1085 * 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) { 1143 private void recordAbortReasonHistogram(int abortReason) {
1208 assert abortReason < PaymentRequestMetrics.ABORT_REASON_MAX; 1144 assert abortReason < PaymentRequestMetrics.ABORT_REASON_MAX;
1209 if (mHasRecordedAbortReason) return; 1145 if (mHasRecordedAbortReason) return;
1210 1146
1211 mHasRecordedAbortReason = true; 1147 mHasRecordedAbortReason = true;
1212 RecordHistogram.recordEnumeratedHistogram( 1148 RecordHistogram.recordEnumeratedHistogram(
1213 "PaymentRequest.CheckoutFunnel.Aborted", abortReason, 1149 "PaymentRequest.CheckoutFunnel.Aborted", abortReason,
1214 PaymentRequestMetrics.ABORT_REASON_MAX); 1150 PaymentRequestMetrics.ABORT_REASON_MAX);
1215 } 1151 }
1216 } 1152 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698