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 28 matching lines...) Expand all Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |