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 * 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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |