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; |
(...skipping 12 matching lines...) Expand all Loading... | |
23 import org.chromium.chrome.browser.preferences.PreferencesLauncher; | 23 import org.chromium.chrome.browser.preferences.PreferencesLauncher; |
24 import org.chromium.chrome.browser.preferences.autofill.AutofillCreditCardEditor ; | 24 import org.chromium.chrome.browser.preferences.autofill.AutofillCreditCardEditor ; |
25 import org.chromium.chrome.browser.preferences.autofill.AutofillProfileEditor; | 25 import org.chromium.chrome.browser.preferences.autofill.AutofillProfileEditor; |
26 import org.chromium.chrome.browser.profiles.Profile; | 26 import org.chromium.chrome.browser.profiles.Profile; |
27 import org.chromium.chrome.browser.util.UrlUtilities; | 27 import org.chromium.chrome.browser.util.UrlUtilities; |
28 import org.chromium.content.browser.ContentViewCore; | 28 import org.chromium.content.browser.ContentViewCore; |
29 import org.chromium.content_public.browser.WebContents; | 29 import org.chromium.content_public.browser.WebContents; |
30 import org.chromium.mojo.system.MojoException; | 30 import org.chromium.mojo.system.MojoException; |
31 import org.chromium.mojom.payments.PaymentDetails; | 31 import org.chromium.mojom.payments.PaymentDetails; |
32 import org.chromium.mojom.payments.PaymentItem; | 32 import org.chromium.mojom.payments.PaymentItem; |
33 import org.chromium.mojom.payments.PaymentMethodData; | |
33 import org.chromium.mojom.payments.PaymentOptions; | 34 import org.chromium.mojom.payments.PaymentOptions; |
34 import org.chromium.mojom.payments.PaymentRequest; | 35 import org.chromium.mojom.payments.PaymentRequest; |
35 import org.chromium.mojom.payments.PaymentRequestClient; | 36 import org.chromium.mojom.payments.PaymentRequestClient; |
36 import org.chromium.mojom.payments.PaymentResponse; | 37 import org.chromium.mojom.payments.PaymentResponse; |
37 import org.chromium.mojom.payments.ShippingOption; | 38 import org.chromium.mojom.payments.ShippingOption; |
38 import org.chromium.ui.base.WindowAndroid; | 39 import org.chromium.ui.base.WindowAndroid; |
39 import org.json.JSONException; | 40 import org.json.JSONException; |
40 import org.json.JSONObject; | 41 import org.json.JSONObject; |
41 | 42 |
42 import java.util.ArrayList; | 43 import java.util.ArrayList; |
43 import java.util.Arrays; | 44 import java.util.Arrays; |
44 import java.util.HashSet; | 45 import java.util.HashMap; |
45 import java.util.Iterator; | |
46 import java.util.List; | 46 import java.util.List; |
47 import java.util.Locale; | 47 import java.util.Locale; |
48 import java.util.Set; | 48 import java.util.Set; |
49 import java.util.regex.Pattern; | 49 import java.util.regex.Pattern; |
50 | 50 |
51 /** | 51 /** |
52 * Android implementation of the PaymentRequest service defined in | 52 * Android implementation of the PaymentRequest service defined in |
53 * third_party/WebKit/public/platform/modules/payments/payment_request.mojom. | 53 * third_party/WebKit/public/platform/modules/payments/payment_request.mojom. |
54 */ | 54 */ |
55 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, | 55 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, |
56 PaymentApp.InstrumentsCallback, PaymentInstrument.DetailsCallback { | 56 PaymentApp.InstrumentsCallback, PaymentInstrument.DetailsCallback { |
57 | 57 |
58 /** | 58 /** |
59 * The size for the favicon in density-independent pixels. | 59 * The size for the favicon in density-independent pixels. |
60 */ | 60 */ |
61 private static final int FAVICON_SIZE_DP = 24; | 61 private static final int FAVICON_SIZE_DP = 24; |
62 | 62 |
63 private static final String TAG = "cr_PaymentRequest"; | 63 private static final String TAG = "cr_PaymentRequest"; |
64 | 64 |
65 private final Handler mHandler = new Handler(); | 65 private final Handler mHandler = new Handler(); |
66 | 66 |
67 private Activity mContext; | 67 private Activity mContext; |
68 private String mMerchantName; | 68 private String mMerchantName; |
69 private String mOrigin; | 69 private String mOrigin; |
70 private Bitmap mFavicon; | 70 private Bitmap mFavicon; |
71 private List<PaymentApp> mApps; | 71 private List<PaymentApp> mApps; |
72 private PaymentRequestClient mClient; | 72 private PaymentRequestClient mClient; |
73 private Set<String> mSupportedMethods; | |
74 | 73 |
75 /** | 74 /** |
76 * The raw total amount being charged, as it was received from the website. This data is passed | 75 * The raw total amount being charged, as it was received from the website. This data is passed |
77 * to the payment app. | 76 * to the payment app. |
78 */ | 77 */ |
79 private PaymentItem mRawTotal; | 78 private PaymentItem mRawTotal; |
80 | 79 |
81 /** | 80 /** |
82 * The raw items in the shopping cart, as they were received from the websit e. This data is | 81 * The raw items in the shopping cart, as they were received from the websit e. This data is |
83 * passed to the payment app. | 82 * passed to the payment app. |
(...skipping 12 matching lines...) Expand all Loading... | |
96 * due to user selecting a shipping address. | 95 * due to user selecting a shipping address. |
97 */ | 96 */ |
98 private List<ShippingOption> mRawShippingOptions; | 97 private List<ShippingOption> mRawShippingOptions; |
99 | 98 |
100 /** | 99 /** |
101 * The UI model for the shipping options. Includes the label and sublabel fo r each shipping | 100 * The UI model for the shipping options. Includes the label and sublabel fo r each shipping |
102 * option. Also keeps track of the selected shipping option. This data is pa ssed to the UI. | 101 * option. Also keeps track of the selected shipping option. This data is pa ssed to the UI. |
103 */ | 102 */ |
104 private SectionInformation mUiShippingOptions; | 103 private SectionInformation mUiShippingOptions; |
105 | 104 |
106 private JSONObject mData; | 105 private HashMap<String, JSONObject> mMethodData; |
107 private SectionInformation mShippingAddressesSection; | 106 private SectionInformation mShippingAddressesSection; |
108 private List<PaymentApp> mPendingApps; | 107 private List<PaymentApp> mPendingApps; |
109 private List<PaymentInstrument> mPendingInstruments; | 108 private List<PaymentInstrument> mPendingInstruments; |
110 private SectionInformation mPaymentMethodsSection; | 109 private SectionInformation mPaymentMethodsSection; |
111 private PaymentRequestUI mUI; | 110 private PaymentRequestUI mUI; |
112 private Callback<PaymentInformation> mPaymentInformationCallback; | 111 private Callback<PaymentInformation> mPaymentInformationCallback; |
113 private Pattern mRegionCodePattern; | 112 private Pattern mRegionCodePattern; |
114 private boolean mMerchantNeedsShippingAddress; | 113 private boolean mMerchantNeedsShippingAddress; |
115 | 114 |
116 /** | 115 /** |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
168 | 167 |
169 if (mContext == null) { | 168 if (mContext == null) { |
170 disconnectFromClientWithDebugMessage("Web contents don't have associ ated activity"); | 169 disconnectFromClientWithDebugMessage("Web contents don't have associ ated activity"); |
171 } | 170 } |
172 } | 171 } |
173 | 172 |
174 /** | 173 /** |
175 * Called by the merchant website to show the payment request to the user. | 174 * Called by the merchant website to show the payment request to the user. |
176 */ | 175 */ |
177 @Override | 176 @Override |
178 public void show(String[] supportedMethods, PaymentDetails details, PaymentO ptions options, | 177 public void show(PaymentMethodData[] methodData, PaymentDetails details, |
179 String stringifiedData) { | 178 PaymentOptions options) { |
180 if (mClient == null) return; | 179 if (mClient == null) return; |
181 | 180 |
182 if (mSupportedMethods != null) { | 181 if (mMethodData != null) { |
183 disconnectFromClientWithDebugMessage("PaymentRequest.show() called m ore than once."); | 182 disconnectFromClientWithDebugMessage("PaymentRequest.show() called m ore than once."); |
184 return; | 183 return; |
185 } | 184 } |
186 | 185 |
187 mSupportedMethods = getValidatedSupportedMethods(supportedMethods); | 186 mMethodData = getValidatedMethodData(methodData); |
188 if (mSupportedMethods == null) { | 187 if (mMethodData == null) { |
189 disconnectFromClientWithDebugMessage("Invalid payment methods"); | 188 disconnectFromClientWithDebugMessage("Invalid payment methods or dat a"); |
190 return; | 189 return; |
191 } | 190 } |
192 | 191 |
193 if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return; | 192 if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return; |
194 | 193 |
195 // If the merchant requests shipping and does not provide shipping optio ns here, then the | 194 // If the merchant requests shipping and does not provide shipping optio ns here, then the |
196 // merchant needs the shipping address to calculate shipping price and a vailability. | 195 // merchant needs the shipping address to calculate shipping price and a vailability. |
197 boolean requestShipping = options != null && options.requestShipping; | 196 boolean requestShipping = options != null && options.requestShipping; |
198 mMerchantNeedsShippingAddress = requestShipping && mUiShippingOptions.is Empty(); | 197 mMerchantNeedsShippingAddress = requestShipping && mUiShippingOptions.is Empty(); |
199 | 198 |
200 mData = getValidatedData(mSupportedMethods, stringifiedData); | |
201 if (mData == null) { | |
202 disconnectFromClientWithDebugMessage("Invalid payment method specifi c data"); | |
203 return; | |
204 } | |
205 | |
206 List<AutofillAddress> addresses = new ArrayList<>(); | 199 List<AutofillAddress> addresses = new ArrayList<>(); |
207 List<AutofillProfile> profiles = PersonalDataManager.getInstance().getPr ofilesToSuggest(); | 200 List<AutofillProfile> profiles = PersonalDataManager.getInstance().getPr ofilesToSuggest(); |
208 for (int i = 0; i < profiles.size(); i++) { | 201 for (int i = 0; i < profiles.size(); i++) { |
209 AutofillProfile profile = profiles.get(i); | 202 AutofillProfile profile = profiles.get(i); |
210 if (profile.getCountryCode() != null | 203 if (profile.getCountryCode() != null |
211 && mRegionCodePattern.matcher(profile.getCountryCode()).matc hes() | 204 && mRegionCodePattern.matcher(profile.getCountryCode()).matc hes() |
212 && profile.getStreetAddress() != null && profile.getRegion() != null | 205 && profile.getStreetAddress() != null && profile.getRegion() != null |
213 && profile.getLocality() != null && profile.getDependentLoca lity() != null | 206 && profile.getLocality() != null && profile.getDependentLoca lity() != null |
214 && profile.getPostalCode() != null && profile.getSortingCode () != null | 207 && profile.getPostalCode() != null && profile.getSortingCode () != null |
215 && profile.getCompanyName() != null && profile.getFullName() != null) { | 208 && profile.getCompanyName() != null && profile.getFullName() != null) { |
216 addresses.add(new AutofillAddress(profile)); | 209 addresses.add(new AutofillAddress(profile)); |
217 } | 210 } |
218 } | 211 } |
219 | 212 |
220 int selectedIndex = SectionInformation.NO_SELECTION; | 213 int selectedIndex = SectionInformation.NO_SELECTION; |
221 if (!addresses.isEmpty() && mUiShippingOptions.getSelectedItem() != null ) { | 214 if (!addresses.isEmpty() && mUiShippingOptions.getSelectedItem() != null ) { |
222 selectedIndex = 0; | 215 selectedIndex = 0; |
223 } | 216 } |
224 mShippingAddressesSection = new SectionInformation( | 217 mShippingAddressesSection = new SectionInformation( |
225 PaymentRequestUI.TYPE_SHIPPING_ADDRESSES, selectedIndex, address es); | 218 PaymentRequestUI.TYPE_SHIPPING_ADDRESSES, selectedIndex, address es); |
226 | 219 |
227 mPendingApps = new ArrayList<>(mApps); | 220 mPendingApps = new ArrayList<>(mApps); |
228 mPendingInstruments = new ArrayList<>(); | 221 mPendingInstruments = new ArrayList<>(); |
229 boolean isGettingInstruments = false; | 222 boolean isGettingInstruments = false; |
230 | 223 |
231 for (int i = 0; i < mApps.size(); i++) { | 224 for (int i = 0; i < mApps.size(); i++) { |
232 PaymentApp app = mApps.get(i); | 225 PaymentApp app = mApps.get(i); |
233 Set<String> appMethods = app.getSupportedMethodNames(); | 226 Set<String> appMethods = app.getSupportedMethodNames(); |
234 appMethods.retainAll(mSupportedMethods); | 227 appMethods.retainAll(mMethodData.keySet()); |
235 if (appMethods.isEmpty()) { | 228 if (appMethods.isEmpty()) { |
236 mPendingApps.remove(app); | 229 mPendingApps.remove(app); |
237 } else { | 230 } else { |
238 isGettingInstruments = true; | 231 isGettingInstruments = true; |
239 app.getInstruments(mRawTotal, mRawLineItems, this); | 232 app.getInstruments(mRawTotal, mRawLineItems, this); |
240 } | 233 } |
241 } | 234 } |
242 | 235 |
243 if (!isGettingInstruments) { | 236 if (!isGettingInstruments) { |
244 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYP E_PAYMENT_METHODS); | 237 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYP E_PAYMENT_METHODS); |
245 } | 238 } |
246 | 239 |
247 mUI = PaymentRequestUI.show(mContext, this, requestShipping, mMerchantNa me, mOrigin); | 240 mUI = PaymentRequestUI.show(mContext, this, requestShipping, mMerchantNa me, mOrigin); |
248 if (mFavicon != null) mUI.setTitleBitmap(mFavicon); | 241 if (mFavicon != null) mUI.setTitleBitmap(mFavicon); |
249 mFavicon = null; | 242 mFavicon = null; |
250 } | 243 } |
251 | 244 |
252 private HashSet<String> getValidatedSupportedMethods(String[] methods) { | 245 private HashMap<String, JSONObject> getValidatedMethodData(PaymentMethodData [] methodData) { |
253 // Payment methods are required. | 246 // Payment methodData are required. |
254 if (methods == null || methods.length == 0) return null; | 247 if (methodData == null || methodData.length == 0) return null; |
248 HashMap<String, JSONObject> result = new HashMap<>(); | |
249 for (int i = 0; i < methodData.length; i++) { | |
250 JSONObject data = null; | |
251 if (!TextUtils.isEmpty(methodData[i].data)) { | |
252 try { | |
253 data = new JSONObject(methodData[i].data); | |
254 } catch (JSONException e) { | |
255 // Payment method specific data should be a JSON object. | |
please use gerrit instead
2016/06/09 02:44:13
Looks like your rebase has removed that very usefu
| |
256 return null; | |
257 } | |
258 } | |
255 | 259 |
256 HashSet<String> result = new HashSet<>(); | 260 String[] methods = methodData[i].supportedMethods; |
257 for (int i = 0; i < methods.length; i++) { | 261 |
258 // Payment methods should be non-empty. | 262 // Payment methods are required. |
259 if (TextUtils.isEmpty(methods[i])) return null; | 263 if (methods == null || methods.length == 0) return null; |
260 result.add(methods[i]); | 264 |
265 for (int j = 0; j < methods.length; j++) { | |
266 // Payment methods should be non-empty. | |
267 if (TextUtils.isEmpty(methods[j])) return null; | |
268 result.put(methods[j], data); | |
269 } | |
261 } | 270 } |
262 | |
263 return result; | 271 return result; |
264 } | 272 } |
265 | 273 |
266 /** | 274 /** |
267 * Called by merchant to update the shipping options and line items after th e user has selected | 275 * Called by merchant to update the shipping options and line items after th e user has selected |
268 * their shipping address or shipping option. | 276 * their shipping address or shipping option. |
269 */ | 277 */ |
270 @Override | 278 @Override |
271 public void updateWith(PaymentDetails details) { | 279 public void updateWith(PaymentDetails details) { |
272 if (mClient == null) return; | 280 if (mClient == null) return; |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
454 for (int i = 0; i < options.length; i++) { | 462 for (int i = 0; i < options.length; i++) { |
455 ShippingOption option = options[i]; | 463 ShippingOption option = options[i]; |
456 result.add(new PaymentOption(option.id, option.label, | 464 result.add(new PaymentOption(option.id, option.label, |
457 formatter.format(option.amount.value), PaymentOption.NO_ICON )); | 465 formatter.format(option.amount.value), PaymentOption.NO_ICON )); |
458 } | 466 } |
459 | 467 |
460 return new SectionInformation(PaymentRequestUI.TYPE_SHIPPING_OPTIONS, | 468 return new SectionInformation(PaymentRequestUI.TYPE_SHIPPING_OPTIONS, |
461 result.size() == 1 ? 0 : SectionInformation.NO_SELECTION, result ); | 469 result.size() == 1 ? 0 : SectionInformation.NO_SELECTION, result ); |
462 } | 470 } |
463 | 471 |
464 private JSONObject getValidatedData(Set<String> supportedMethods, String str ingifiedData) { | |
465 if (TextUtils.isEmpty(stringifiedData)) return new JSONObject(); | |
466 | |
467 JSONObject result; | |
468 try { | |
469 result = new JSONObject(stringifiedData); | |
470 } catch (JSONException e) { | |
471 // Payment method specific data should be a JSON object. | |
472 return null; | |
473 } | |
474 | |
475 Iterator<String> it = result.keys(); | |
476 while (it.hasNext()) { | |
477 String name = it.next(); | |
478 // Each key should be one of the supported payment methods. | |
479 if (!supportedMethods.contains(name)) return null; | |
480 // Each value should be a JSON object. | |
481 if (result.optJSONObject(name) == null) return null; | |
482 } | |
483 | |
484 return result; | |
485 } | |
486 | |
487 /** | 472 /** |
488 * Called to retrieve the data to show in the initial PaymentRequest UI. | 473 * Called to retrieve the data to show in the initial PaymentRequest UI. |
489 */ | 474 */ |
490 @Override | 475 @Override |
491 public void getDefaultPaymentInformation(Callback<PaymentInformation> callba ck) { | 476 public void getDefaultPaymentInformation(Callback<PaymentInformation> callba ck) { |
492 mPaymentInformationCallback = callback; | 477 mPaymentInformationCallback = callback; |
493 | 478 |
494 if (mPaymentMethodsSection == null) return; | 479 if (mPaymentMethodsSection == null) return; |
495 | 480 |
496 mHandler.post(new Runnable() { | 481 mHandler.post(new Runnable() { |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
567 mContext, AutofillCreditCardEditor.class.getName()); | 552 mContext, AutofillCreditCardEditor.class.getName()); |
568 } | 553 } |
569 } | 554 } |
570 | 555 |
571 @Override | 556 @Override |
572 public void onPayClicked(PaymentOption selectedShippingAddress, | 557 public void onPayClicked(PaymentOption selectedShippingAddress, |
573 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) { | 558 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) { |
574 assert selectedPaymentMethod instanceof PaymentInstrument; | 559 assert selectedPaymentMethod instanceof PaymentInstrument; |
575 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ; | 560 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ; |
576 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems, | 561 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems, |
577 mData.optJSONObject(instrument.getMethodName()), this); | 562 mMethodData.get(instrument.getMethodName()), this); |
578 } | 563 } |
579 | 564 |
580 @Override | 565 @Override |
581 public void onDismiss() { | 566 public void onDismiss() { |
582 disconnectFromClientWithDebugMessage("Dialog dismissed"); | 567 disconnectFromClientWithDebugMessage("Dialog dismissed"); |
583 closeUI(false); | 568 closeUI(false); |
584 } | 569 } |
585 | 570 |
586 @Override | 571 @Override |
587 public boolean merchantNeedsShippingAddress() { | 572 public boolean merchantNeedsShippingAddress() { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
626 /** | 611 /** |
627 * Called after retrieving the list of payment instruments in an app. | 612 * Called after retrieving the list of payment instruments in an app. |
628 */ | 613 */ |
629 @Override | 614 @Override |
630 public void onInstrumentsReady(PaymentApp app, List<PaymentInstrument> instr uments) { | 615 public void onInstrumentsReady(PaymentApp app, List<PaymentInstrument> instr uments) { |
631 mPendingApps.remove(app); | 616 mPendingApps.remove(app); |
632 | 617 |
633 if (instruments != null) { | 618 if (instruments != null) { |
634 for (int i = 0; i < instruments.size(); i++) { | 619 for (int i = 0; i < instruments.size(); i++) { |
635 PaymentInstrument instrument = instruments.get(i); | 620 PaymentInstrument instrument = instruments.get(i); |
636 if (mSupportedMethods.contains(instrument.getMethodName())) { | 621 if (mMethodData.containsKey(instrument.getMethodName())) { |
637 mPendingInstruments.add(instrument); | 622 mPendingInstruments.add(instrument); |
638 } else { | 623 } else { |
639 instrument.dismiss(); | 624 instrument.dismiss(); |
640 } | 625 } |
641 } | 626 } |
642 } | 627 } |
643 | 628 |
644 if (mPendingApps.isEmpty()) { | 629 if (mPendingApps.isEmpty()) { |
645 mPaymentMethodsSection = new SectionInformation( | 630 mPaymentMethodsSection = new SectionInformation( |
646 PaymentRequestUI.TYPE_PAYMENT_METHODS, 0, mPendingInstrument s); | 631 PaymentRequestUI.TYPE_PAYMENT_METHODS, 0, mPendingInstrument s); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
714 } | 699 } |
715 mPaymentMethodsSection = null; | 700 mPaymentMethodsSection = null; |
716 } | 701 } |
717 } | 702 } |
718 | 703 |
719 private void closeClient() { | 704 private void closeClient() { |
720 if (mClient != null) mClient.close(); | 705 if (mClient != null) mClient.close(); |
721 mClient = null; | 706 mClient = null; |
722 } | 707 } |
723 } | 708 } |
OLD | NEW |