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 11 matching lines...) Expand all Loading... | |
22 import org.chromium.chrome.browser.preferences.PreferencesLauncher; | 22 import org.chromium.chrome.browser.preferences.PreferencesLauncher; |
23 import org.chromium.chrome.browser.preferences.autofill.AutofillCreditCardEditor ; | 23 import org.chromium.chrome.browser.preferences.autofill.AutofillCreditCardEditor ; |
24 import org.chromium.chrome.browser.preferences.autofill.AutofillProfileEditor; | 24 import org.chromium.chrome.browser.preferences.autofill.AutofillProfileEditor; |
25 import org.chromium.chrome.browser.profiles.Profile; | 25 import org.chromium.chrome.browser.profiles.Profile; |
26 import org.chromium.chrome.browser.util.UrlUtilities; | 26 import org.chromium.chrome.browser.util.UrlUtilities; |
27 import org.chromium.content.browser.ContentViewCore; | 27 import org.chromium.content.browser.ContentViewCore; |
28 import org.chromium.content_public.browser.WebContents; | 28 import org.chromium.content_public.browser.WebContents; |
29 import org.chromium.mojo.system.MojoException; | 29 import org.chromium.mojo.system.MojoException; |
30 import org.chromium.mojom.payments.PaymentDetails; | 30 import org.chromium.mojom.payments.PaymentDetails; |
31 import org.chromium.mojom.payments.PaymentItem; | 31 import org.chromium.mojom.payments.PaymentItem; |
32 import org.chromium.mojom.payments.PaymentMethodData; | |
32 import org.chromium.mojom.payments.PaymentOptions; | 33 import org.chromium.mojom.payments.PaymentOptions; |
33 import org.chromium.mojom.payments.PaymentRequest; | 34 import org.chromium.mojom.payments.PaymentRequest; |
34 import org.chromium.mojom.payments.PaymentRequestClient; | 35 import org.chromium.mojom.payments.PaymentRequestClient; |
35 import org.chromium.mojom.payments.PaymentResponse; | 36 import org.chromium.mojom.payments.PaymentResponse; |
36 import org.chromium.mojom.payments.ShippingOption; | 37 import org.chromium.mojom.payments.ShippingOption; |
37 import org.chromium.ui.base.WindowAndroid; | 38 import org.chromium.ui.base.WindowAndroid; |
38 import org.json.JSONException; | 39 import org.json.JSONException; |
39 import org.json.JSONObject; | 40 import org.json.JSONObject; |
40 | 41 |
41 import java.util.ArrayList; | 42 import java.util.ArrayList; |
42 import java.util.Arrays; | 43 import java.util.Arrays; |
43 import java.util.HashSet; | 44 import java.util.HashMap; |
44 import java.util.Iterator; | |
45 import java.util.List; | 45 import java.util.List; |
46 import java.util.Locale; | 46 import java.util.Locale; |
47 import java.util.Set; | 47 import java.util.Set; |
48 import java.util.regex.Pattern; | 48 import java.util.regex.Pattern; |
49 | 49 |
50 /** | 50 /** |
51 * Android implementation of the PaymentRequest service defined in | 51 * Android implementation of the PaymentRequest service defined in |
52 * third_party/WebKit/public/platform/modules/payments/payment_request.mojom. | 52 * third_party/WebKit/public/platform/modules/payments/payment_request.mojom. |
53 */ | 53 */ |
54 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, | 54 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, |
55 PaymentApp.InstrumentsCallback, PaymentInstrument.DetailsCallback { | 55 PaymentApp.InstrumentsCallback, PaymentInstrument.DetailsCallback { |
56 /** | 56 /** |
57 * The size for the favicon in density-independent pixels. | 57 * The size for the favicon in density-independent pixels. |
58 */ | 58 */ |
59 private static final int FAVICON_SIZE_DP = 24; | 59 private static final int FAVICON_SIZE_DP = 24; |
60 | 60 |
61 private static final String TAG = "cr_PaymentRequest"; | 61 private static final String TAG = "cr_PaymentRequest"; |
62 | 62 |
63 private final Handler mHandler = new Handler(); | 63 private final Handler mHandler = new Handler(); |
64 | 64 |
65 private Activity mContext; | 65 private Activity mContext; |
66 private String mMerchantName; | 66 private String mMerchantName; |
67 private String mOrigin; | 67 private String mOrigin; |
68 private Bitmap mFavicon; | 68 private Bitmap mFavicon; |
69 private List<PaymentApp> mApps; | 69 private List<PaymentApp> mApps; |
70 private PaymentRequestClient mClient; | 70 private PaymentRequestClient mClient; |
71 private Set<String> mSupportedMethods; | 71 private HashMap<String, JSONObject> mMethodData; |
72 private List<LineItem> mLineItems; | 72 private List<LineItem> mLineItems; |
73 private List<PaymentItem> mPaymentItems; | 73 private List<PaymentItem> mPaymentItems; |
74 private List<ShippingOption> mShippingOptions; | 74 private List<ShippingOption> mShippingOptions; |
75 private SectionInformation mShippingOptionsSection; | 75 private SectionInformation mShippingOptionsSection; |
76 private JSONObject mData; | |
77 private SectionInformation mShippingAddressesSection; | 76 private SectionInformation mShippingAddressesSection; |
78 private List<PaymentApp> mPendingApps; | 77 private List<PaymentApp> mPendingApps; |
79 private List<PaymentInstrument> mPendingInstruments; | 78 private List<PaymentInstrument> mPendingInstruments; |
80 private SectionInformation mPaymentMethodsSection; | 79 private SectionInformation mPaymentMethodsSection; |
81 private PaymentRequestUI mUI; | 80 private PaymentRequestUI mUI; |
82 private Callback<PaymentInformation> mPaymentInformationCallback; | 81 private Callback<PaymentInformation> mPaymentInformationCallback; |
83 private Pattern mRegionCodePattern; | 82 private Pattern mRegionCodePattern; |
84 private boolean mMerchantNeedsShippingAddress; | 83 private boolean mMerchantNeedsShippingAddress; |
85 | 84 |
86 /** | 85 /** |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
138 | 137 |
139 if (mContext == null) { | 138 if (mContext == null) { |
140 disconnectFromClientWithDebugMessage("Web contents don't have associ ated activity"); | 139 disconnectFromClientWithDebugMessage("Web contents don't have associ ated activity"); |
141 } | 140 } |
142 } | 141 } |
143 | 142 |
144 /** | 143 /** |
145 * Called by the merchant website to show the payment request to the user. | 144 * Called by the merchant website to show the payment request to the user. |
146 */ | 145 */ |
147 @Override | 146 @Override |
148 public void show(String[] supportedMethods, PaymentDetails details, PaymentO ptions options, | 147 public void show(PaymentMethodData[] methodData, PaymentDetails details, |
149 String stringifiedData) { | 148 PaymentOptions options) { |
150 if (mClient == null) return; | 149 if (mClient == null) return; |
151 | 150 |
152 if (mSupportedMethods != null) { | 151 if (mMethodData != null) { |
153 disconnectFromClientWithDebugMessage("PaymentRequest.show() called m ore than once."); | 152 disconnectFromClientWithDebugMessage("PaymentRequest.show() called m ore than once."); |
154 return; | 153 return; |
155 } | 154 } |
156 | 155 |
157 mSupportedMethods = getValidatedSupportedMethods(supportedMethods); | 156 mMethodData = getValidatedMethodData(methodData); |
158 if (mSupportedMethods == null) { | 157 if (mMethodData == null) { |
159 disconnectFromClientWithDebugMessage("Invalid payment methods"); | 158 disconnectFromClientWithDebugMessage("Invalid payment methods or dat a"); |
160 return; | 159 return; |
161 } | 160 } |
162 | 161 |
163 if (!setLineItemsAndShippingOptionsOrDisconnectFromClient(details)) retu rn; | 162 if (!setLineItemsAndShippingOptionsOrDisconnectFromClient(details)) retu rn; |
164 | 163 |
165 // If the merchant requests shipping and does not provide shipping optio ns here, then the | 164 // If the merchant requests shipping and does not provide shipping optio ns here, then the |
166 // merchant needs the shipping address to calculate shipping price and a vailability. | 165 // merchant needs the shipping address to calculate shipping price and a vailability. |
167 boolean requestShipping = options != null && options.requestShipping; | 166 boolean requestShipping = options != null && options.requestShipping; |
168 mMerchantNeedsShippingAddress = requestShipping && mShippingOptionsSecti on.isEmpty(); | 167 mMerchantNeedsShippingAddress = requestShipping && mShippingOptionsSecti on.isEmpty(); |
169 | 168 |
170 mData = getValidatedData(mSupportedMethods, stringifiedData); | |
171 if (mData == null) { | |
172 disconnectFromClientWithDebugMessage("Invalid payment method specifi c data"); | |
173 return; | |
174 } | |
175 | |
176 List<AutofillAddress> addresses = new ArrayList<>(); | 169 List<AutofillAddress> addresses = new ArrayList<>(); |
177 List<AutofillProfile> profiles = PersonalDataManager.getInstance().getAd dressOnlyProfiles(); | 170 List<AutofillProfile> profiles = PersonalDataManager.getInstance().getAd dressOnlyProfiles(); |
178 for (int i = 0; i < profiles.size(); i++) { | 171 for (int i = 0; i < profiles.size(); i++) { |
179 AutofillProfile profile = profiles.get(i); | 172 AutofillProfile profile = profiles.get(i); |
180 if (profile.getCountryCode() != null | 173 if (profile.getCountryCode() != null |
181 && mRegionCodePattern.matcher(profile.getCountryCode()).matc hes() | 174 && mRegionCodePattern.matcher(profile.getCountryCode()).matc hes() |
182 && profile.getStreetAddress() != null && profile.getRegion() != null | 175 && profile.getStreetAddress() != null && profile.getRegion() != null |
183 && profile.getLocality() != null && profile.getDependentLoca lity() != null | 176 && profile.getLocality() != null && profile.getDependentLoca lity() != null |
184 && profile.getPostalCode() != null && profile.getSortingCode () != null | 177 && profile.getPostalCode() != null && profile.getSortingCode () != null |
185 && profile.getCompanyName() != null && profile.getFullName() != null) { | 178 && profile.getCompanyName() != null && profile.getFullName() != null) { |
186 addresses.add(new AutofillAddress(profile)); | 179 addresses.add(new AutofillAddress(profile)); |
187 } | 180 } |
188 } | 181 } |
189 | 182 |
190 int selectedIndex = SectionInformation.NO_SELECTION; | 183 int selectedIndex = SectionInformation.NO_SELECTION; |
191 if (!addresses.isEmpty() && mShippingOptionsSection.getSelectedItem() != null) { | 184 if (!addresses.isEmpty() && mShippingOptionsSection.getSelectedItem() != null) { |
192 selectedIndex = 0; | 185 selectedIndex = 0; |
193 } | 186 } |
194 mShippingAddressesSection = new SectionInformation( | 187 mShippingAddressesSection = new SectionInformation( |
195 PaymentRequestUI.TYPE_SHIPPING_ADDRESSES, selectedIndex, address es); | 188 PaymentRequestUI.TYPE_SHIPPING_ADDRESSES, selectedIndex, address es); |
196 | 189 |
197 mPendingApps = new ArrayList<>(mApps); | 190 mPendingApps = new ArrayList<>(mApps); |
198 mPendingInstruments = new ArrayList<>(); | 191 mPendingInstruments = new ArrayList<>(); |
199 boolean isGettingInstruments = false; | 192 boolean isGettingInstruments = false; |
200 | 193 |
201 for (int i = 0; i < mApps.size(); i++) { | 194 for (int i = 0; i < mApps.size(); i++) { |
202 PaymentApp app = mApps.get(i); | 195 PaymentApp app = mApps.get(i); |
203 Set<String> appMethods = app.getSupportedMethodNames(); | 196 Set<String> appMethods = app.getSupportedMethodNames(); |
204 appMethods.retainAll(mSupportedMethods); | 197 appMethods.retainAll(mMethodData.keySet()); |
205 if (appMethods.isEmpty()) { | 198 if (appMethods.isEmpty()) { |
206 mPendingApps.remove(app); | 199 mPendingApps.remove(app); |
207 } else { | 200 } else { |
208 isGettingInstruments = true; | 201 isGettingInstruments = true; |
209 app.getInstruments(mPaymentItems, this); | 202 app.getInstruments(mPaymentItems, this); |
210 } | 203 } |
211 } | 204 } |
212 | 205 |
213 if (!isGettingInstruments) { | 206 if (!isGettingInstruments) { |
214 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYP E_PAYMENT_METHODS); | 207 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYP E_PAYMENT_METHODS); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
258 getValidatedShippingOptions(details.items[0].amount.currencyCode , details); | 251 getValidatedShippingOptions(details.items[0].amount.currencyCode , details); |
259 if (mShippingOptionsSection == null) { | 252 if (mShippingOptionsSection == null) { |
260 disconnectFromClientWithDebugMessage("Invalid shipping options"); | 253 disconnectFromClientWithDebugMessage("Invalid shipping options"); |
261 return false; | 254 return false; |
262 } | 255 } |
263 mShippingOptions = Arrays.asList(details.shippingOptions); | 256 mShippingOptions = Arrays.asList(details.shippingOptions); |
264 | 257 |
265 return true; | 258 return true; |
266 } | 259 } |
267 | 260 |
268 private HashSet<String> getValidatedSupportedMethods(String[] methods) { | 261 private HashMap<String, JSONObject> getValidatedMethodData(PaymentMethodData [] methodData) { |
please use gerrit instead
2016/05/29 19:57:11
Check that methodData is not null and not empty.
zino
2016/06/01 17:43:46
Done.
| |
269 // Payment methods are required. | 262 HashMap<String, JSONObject> result = new HashMap<>(); |
270 if (methods == null || methods.length == 0) return null; | 263 for (int i = 0; i < methodData.length; i++) { |
264 String[] methods = methodData[i].supportedMethods; | |
please use gerrit instead
2016/05/29 19:57:11
Move |methods| down to line 278-ish, where you're
zino
2016/06/01 17:43:46
Done.
| |
271 | 265 |
272 HashSet<String> result = new HashSet<>(); | 266 JSONObject data; |
273 for (int i = 0; i < methods.length; i++) { | 267 if (TextUtils.isEmpty(methodData[i].data)) { |
274 // Payment methods should be non-empty. | 268 data = new JSONObject(); |
please use gerrit instead
2016/05/29 19:57:11
Let's use |null| to represent empty data. Object c
zino
2016/06/01 17:43:46
Done.
| |
275 if (TextUtils.isEmpty(methods[i])) return null; | 269 } else { |
276 result.add(methods[i]); | 270 try { |
271 data = new JSONObject(methodData[i].data); | |
272 } catch (JSONException e) { | |
273 // Payment method specific data should be a JSON object. | |
274 return null; | |
275 } | |
276 } | |
277 | |
278 // Payment methods are required. | |
279 if (methods == null || methods.length == 0) return null; | |
280 | |
281 for (int j = 0; j < methods.length; j++) { | |
282 // Payment methods should be non-empty. | |
283 if (TextUtils.isEmpty(methods[j])) return null; | |
284 result.put(methods[j], data); | |
285 } | |
277 } | 286 } |
278 | 287 |
279 return result; | 288 return result; |
280 } | 289 } |
281 | 290 |
282 private List<LineItem> getValidatedLineItems(PaymentDetails details) { | 291 private List<LineItem> getValidatedLineItems(PaymentDetails details) { |
283 // Line items are required. | 292 // Line items are required. |
284 if (details == null || details.items == null || details.items.length == 0) return null; | 293 if (details == null || details.items == null || details.items.length == 0) return null; |
285 | 294 |
286 for (int i = 0; i < details.items.length; i++) { | 295 for (int i = 0; i < details.items.length; i++) { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
364 for (int i = 0; i < details.shippingOptions.length; i++) { | 373 for (int i = 0; i < details.shippingOptions.length; i++) { |
365 ShippingOption option = details.shippingOptions[i]; | 374 ShippingOption option = details.shippingOptions[i]; |
366 result.add(new PaymentOption(option.id, option.label, | 375 result.add(new PaymentOption(option.id, option.label, |
367 formatter.format(option.amount.value), PaymentOption.NO_ICON )); | 376 formatter.format(option.amount.value), PaymentOption.NO_ICON )); |
368 } | 377 } |
369 | 378 |
370 return new SectionInformation(PaymentRequestUI.TYPE_SHIPPING_OPTIONS, | 379 return new SectionInformation(PaymentRequestUI.TYPE_SHIPPING_OPTIONS, |
371 result.size() == 1 ? 0 : SectionInformation.NO_SELECTION, result ); | 380 result.size() == 1 ? 0 : SectionInformation.NO_SELECTION, result ); |
372 } | 381 } |
373 | 382 |
374 private JSONObject getValidatedData(Set<String> supportedMethods, String str ingifiedData) { | |
375 if (TextUtils.isEmpty(stringifiedData)) return new JSONObject(); | |
376 | |
377 JSONObject result; | |
378 try { | |
379 result = new JSONObject(stringifiedData); | |
380 } catch (JSONException e) { | |
381 // Payment method specific data should be a JSON object. | |
382 return null; | |
383 } | |
384 | |
385 Iterator<String> it = result.keys(); | |
386 while (it.hasNext()) { | |
387 String name = it.next(); | |
388 // Each key should be one of the supported payment methods. | |
389 if (!supportedMethods.contains(name)) return null; | |
390 // Each value should be a JSON object. | |
391 if (result.optJSONObject(name) == null) return null; | |
392 } | |
393 | |
394 return result; | |
395 } | |
396 | |
397 /** | 383 /** |
398 * Called to retrieve the data to show in the initial PaymentRequest UI. | 384 * Called to retrieve the data to show in the initial PaymentRequest UI. |
399 */ | 385 */ |
400 @Override | 386 @Override |
401 public void getDefaultPaymentInformation(Callback<PaymentInformation> callba ck) { | 387 public void getDefaultPaymentInformation(Callback<PaymentInformation> callba ck) { |
402 mPaymentInformationCallback = callback; | 388 mPaymentInformationCallback = callback; |
403 | 389 |
404 if (mPaymentMethodsSection == null) return; | 390 if (mPaymentMethodsSection == null) return; |
405 | 391 |
406 mHandler.post(new Runnable() { | 392 mHandler.post(new Runnable() { |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
478 mContext, AutofillCreditCardEditor.class.getName()); | 464 mContext, AutofillCreditCardEditor.class.getName()); |
479 } | 465 } |
480 } | 466 } |
481 | 467 |
482 @Override | 468 @Override |
483 public void onPayClicked(PaymentOption selectedShippingAddress, | 469 public void onPayClicked(PaymentOption selectedShippingAddress, |
484 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) { | 470 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) { |
485 assert selectedPaymentMethod instanceof PaymentInstrument; | 471 assert selectedPaymentMethod instanceof PaymentInstrument; |
486 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ; | 472 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ; |
487 instrument.getDetails(mMerchantName, mOrigin, mPaymentItems, | 473 instrument.getDetails(mMerchantName, mOrigin, mPaymentItems, |
488 mData.optJSONObject(instrument.getMethodName()), this); | 474 mMethodData.get(instrument.getMethodName()), this); |
489 } | 475 } |
490 | 476 |
491 @Override | 477 @Override |
492 public void onDismiss() { | 478 public void onDismiss() { |
493 disconnectFromClientWithDebugMessage("Dialog dismissed"); | 479 disconnectFromClientWithDebugMessage("Dialog dismissed"); |
494 closeUI(false); | 480 closeUI(false); |
495 } | 481 } |
496 | 482 |
497 /** | 483 /** |
498 * Called by the merchant website to abort the payment. | 484 * Called by the merchant website to abort the payment. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
532 /** | 518 /** |
533 * Called after retrieving the list of payment instruments in an app. | 519 * Called after retrieving the list of payment instruments in an app. |
534 */ | 520 */ |
535 @Override | 521 @Override |
536 public void onInstrumentsReady(PaymentApp app, List<PaymentInstrument> instr uments) { | 522 public void onInstrumentsReady(PaymentApp app, List<PaymentInstrument> instr uments) { |
537 mPendingApps.remove(app); | 523 mPendingApps.remove(app); |
538 | 524 |
539 if (instruments != null) { | 525 if (instruments != null) { |
540 for (int i = 0; i < instruments.size(); i++) { | 526 for (int i = 0; i < instruments.size(); i++) { |
541 PaymentInstrument instrument = instruments.get(i); | 527 PaymentInstrument instrument = instruments.get(i); |
542 if (mSupportedMethods.contains(instrument.getMethodName())) { | 528 if (mMethodData.containsKey(instrument.getMethodName())) { |
543 mPendingInstruments.add(instrument); | 529 mPendingInstruments.add(instrument); |
544 } else { | 530 } else { |
545 instrument.dismiss(); | 531 instrument.dismiss(); |
546 } | 532 } |
547 } | 533 } |
548 } | 534 } |
549 | 535 |
550 if (mPendingApps.isEmpty()) { | 536 if (mPendingApps.isEmpty()) { |
551 mPaymentMethodsSection = new SectionInformation( | 537 mPaymentMethodsSection = new SectionInformation( |
552 PaymentRequestUI.TYPE_PAYMENT_METHODS, 0, mPendingInstrument s); | 538 PaymentRequestUI.TYPE_PAYMENT_METHODS, 0, mPendingInstrument s); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
620 } | 606 } |
621 mPaymentMethodsSection = null; | 607 mPaymentMethodsSection = null; |
622 } | 608 } |
623 } | 609 } |
624 | 610 |
625 private void closeClient() { | 611 private void closeClient() { |
626 if (mClient != null) mClient.close(); | 612 if (mClient != null) mClient.close(); |
627 mClient = null; | 613 mClient = null; |
628 } | 614 } |
629 } | 615 } |
OLD | NEW |