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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java

Issue 2020883002: PaymentRequest: Introduce PaymentMethodData. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 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
« no previous file with comments | « no previous file | chrome/test/data/android/payments/dynamic_shipping.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
(...skipping 13 matching lines...) Expand all
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.components.safejson.JsonSanitizer; 28 import org.chromium.components.safejson.JsonSanitizer;
29 import org.chromium.content.browser.ContentViewCore; 29 import org.chromium.content.browser.ContentViewCore;
30 import org.chromium.content_public.browser.WebContents; 30 import org.chromium.content_public.browser.WebContents;
31 import org.chromium.mojo.system.MojoException; 31 import org.chromium.mojo.system.MojoException;
32 import org.chromium.mojom.payments.PaymentDetails; 32 import org.chromium.mojom.payments.PaymentDetails;
33 import org.chromium.mojom.payments.PaymentItem; 33 import org.chromium.mojom.payments.PaymentItem;
34 import org.chromium.mojom.payments.PaymentMethodData;
34 import org.chromium.mojom.payments.PaymentOptions; 35 import org.chromium.mojom.payments.PaymentOptions;
35 import org.chromium.mojom.payments.PaymentRequest; 36 import org.chromium.mojom.payments.PaymentRequest;
36 import org.chromium.mojom.payments.PaymentRequestClient; 37 import org.chromium.mojom.payments.PaymentRequestClient;
37 import org.chromium.mojom.payments.PaymentResponse; 38 import org.chromium.mojom.payments.PaymentResponse;
38 import org.chromium.mojom.payments.ShippingOption; 39 import org.chromium.mojom.payments.ShippingOption;
39 import org.chromium.ui.base.WindowAndroid; 40 import org.chromium.ui.base.WindowAndroid;
40 import org.json.JSONException; 41 import org.json.JSONException;
41 import org.json.JSONObject; 42 import org.json.JSONObject;
42 43
43 import java.io.IOException; 44 import java.io.IOException;
44 import java.util.ArrayList; 45 import java.util.ArrayList;
45 import java.util.Arrays; 46 import java.util.Arrays;
46 import java.util.HashSet; 47 import java.util.HashMap;
47 import java.util.Iterator;
48 import java.util.List; 48 import java.util.List;
49 import java.util.Locale; 49 import java.util.Locale;
50 import java.util.Set; 50 import java.util.Set;
51 import java.util.regex.Pattern; 51 import java.util.regex.Pattern;
52 52
53 /** 53 /**
54 * Android implementation of the PaymentRequest service defined in 54 * Android implementation of the PaymentRequest service defined in
55 * third_party/WebKit/public/platform/modules/payments/payment_request.mojom. 55 * third_party/WebKit/public/platform/modules/payments/payment_request.mojom.
56 */ 56 */
57 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt, 57 public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie nt,
58 PaymentApp.InstrumentsCallback, PaymentInstrument.DetailsCallback { 58 PaymentApp.InstrumentsCallback, PaymentInstrument.DetailsCallback {
59 59
60 /** 60 /**
61 * The size for the favicon in density-independent pixels. 61 * The size for the favicon in density-independent pixels.
62 */ 62 */
63 private static final int FAVICON_SIZE_DP = 24; 63 private static final int FAVICON_SIZE_DP = 24;
64 64
65 private static final String TAG = "cr_PaymentRequest"; 65 private static final String TAG = "cr_PaymentRequest";
66 66
67 private final Handler mHandler = new Handler(); 67 private final Handler mHandler = new Handler();
68 68
69 private Activity mContext; 69 private Activity mContext;
70 private String mMerchantName; 70 private String mMerchantName;
71 private String mOrigin; 71 private String mOrigin;
72 private Bitmap mFavicon; 72 private Bitmap mFavicon;
73 private List<PaymentApp> mApps; 73 private List<PaymentApp> mApps;
74 private PaymentRequestClient mClient; 74 private PaymentRequestClient mClient;
75 private Set<String> mSupportedMethods;
76 75
77 /** 76 /**
78 * The raw total amount being charged, as it was received from the website. This data is passed 77 * The raw total amount being charged, as it was received from the website. This data is passed
79 * to the payment app. 78 * to the payment app.
80 */ 79 */
81 private PaymentItem mRawTotal; 80 private PaymentItem mRawTotal;
82 81
83 /** 82 /**
84 * The raw items in the shopping cart, as they were received from the websit e. This data is 83 * The raw items in the shopping cart, as they were received from the websit e. This data is
85 * passed to the payment app. 84 * passed to the payment app.
(...skipping 12 matching lines...) Expand all
98 * due to user selecting a shipping address. 97 * due to user selecting a shipping address.
99 */ 98 */
100 private List<ShippingOption> mRawShippingOptions; 99 private List<ShippingOption> mRawShippingOptions;
101 100
102 /** 101 /**
103 * The UI model for the shipping options. Includes the label and sublabel fo r each shipping 102 * The UI model for the shipping options. Includes the label and sublabel fo r each shipping
104 * option. Also keeps track of the selected shipping option. This data is pa ssed to the UI. 103 * option. Also keeps track of the selected shipping option. This data is pa ssed to the UI.
105 */ 104 */
106 private SectionInformation mUiShippingOptions; 105 private SectionInformation mUiShippingOptions;
107 106
108 private JSONObject mData; 107 private HashMap<String, JSONObject> mMethodData;
109 private SectionInformation mShippingAddressesSection; 108 private SectionInformation mShippingAddressesSection;
110 private List<PaymentApp> mPendingApps; 109 private List<PaymentApp> mPendingApps;
111 private List<PaymentInstrument> mPendingInstruments; 110 private List<PaymentInstrument> mPendingInstruments;
112 private SectionInformation mPaymentMethodsSection; 111 private SectionInformation mPaymentMethodsSection;
113 private PaymentRequestUI mUI; 112 private PaymentRequestUI mUI;
114 private Callback<PaymentInformation> mPaymentInformationCallback; 113 private Callback<PaymentInformation> mPaymentInformationCallback;
115 private Pattern mRegionCodePattern; 114 private Pattern mRegionCodePattern;
116 private boolean mMerchantNeedsShippingAddress; 115 private boolean mMerchantNeedsShippingAddress;
117 116
118 /** 117 /**
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 169
171 if (mContext == null) { 170 if (mContext == null) {
172 disconnectFromClientWithDebugMessage("Web contents don't have associ ated activity"); 171 disconnectFromClientWithDebugMessage("Web contents don't have associ ated activity");
173 } 172 }
174 } 173 }
175 174
176 /** 175 /**
177 * Called by the merchant website to show the payment request to the user. 176 * Called by the merchant website to show the payment request to the user.
178 */ 177 */
179 @Override 178 @Override
180 public void show(String[] supportedMethods, PaymentDetails details, PaymentO ptions options, 179 public void show(PaymentMethodData[] methodData, PaymentDetails details,
181 String stringifiedData) { 180 PaymentOptions options) {
182 if (mClient == null) return; 181 if (mClient == null) return;
183 182
184 if (mSupportedMethods != null) { 183 if (mMethodData != null) {
185 disconnectFromClientWithDebugMessage("PaymentRequest.show() called m ore than once."); 184 disconnectFromClientWithDebugMessage("PaymentRequest.show() called m ore than once.");
186 return; 185 return;
187 } 186 }
188 187
189 mSupportedMethods = getValidatedSupportedMethods(supportedMethods); 188 mMethodData = getValidatedMethodData(methodData);
190 if (mSupportedMethods == null) { 189 if (mMethodData == null) {
191 disconnectFromClientWithDebugMessage("Invalid payment methods"); 190 disconnectFromClientWithDebugMessage("Invalid payment methods or dat a");
192 return; 191 return;
193 } 192 }
194 193
195 if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return; 194 if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return;
196 195
197 // If the merchant requests shipping and does not provide a selected shi pping option, then 196 // If the merchant requests shipping and does not provide a selected shi pping option, then
198 // the merchant needs the shipping address to calculate the shipping pri ce and availability. 197 // the merchant needs the shipping address to calculate the shipping pri ce and availability.
199 boolean requestShipping = options != null && options.requestShipping; 198 boolean requestShipping = options != null && options.requestShipping;
200 mMerchantNeedsShippingAddress = 199 mMerchantNeedsShippingAddress =
201 requestShipping && mUiShippingOptions.getSelectedItem() == null; 200 requestShipping && mUiShippingOptions.getSelectedItem() == null;
202 201
203 mData = getValidatedData(mSupportedMethods, stringifiedData);
204 if (mData == null) {
205 disconnectFromClientWithDebugMessage("Invalid payment method specifi c data");
206 return;
207 }
208
209 List<AutofillAddress> addresses = new ArrayList<>(); 202 List<AutofillAddress> addresses = new ArrayList<>();
210 List<AutofillProfile> profiles = PersonalDataManager.getInstance().getPr ofilesToSuggest(); 203 List<AutofillProfile> profiles = PersonalDataManager.getInstance().getPr ofilesToSuggest();
211 for (int i = 0; i < profiles.size(); i++) { 204 for (int i = 0; i < profiles.size(); i++) {
212 AutofillProfile profile = profiles.get(i); 205 AutofillProfile profile = profiles.get(i);
213 if (profile.getCountryCode() != null 206 if (profile.getCountryCode() != null
214 && mRegionCodePattern.matcher(profile.getCountryCode()).matc hes() 207 && mRegionCodePattern.matcher(profile.getCountryCode()).matc hes()
215 && profile.getStreetAddress() != null && profile.getRegion() != null 208 && profile.getStreetAddress() != null && profile.getRegion() != null
216 && profile.getLocality() != null && profile.getDependentLoca lity() != null 209 && profile.getLocality() != null && profile.getDependentLoca lity() != null
217 && profile.getPostalCode() != null && profile.getSortingCode () != null 210 && profile.getPostalCode() != null && profile.getSortingCode () != null
218 && profile.getCompanyName() != null && profile.getFullName() != null 211 && profile.getCompanyName() != null && profile.getFullName() != null
219 && profile.getPhoneNumber() != null) { 212 && profile.getPhoneNumber() != null) {
220 addresses.add(new AutofillAddress(profile)); 213 addresses.add(new AutofillAddress(profile));
221 } 214 }
222 } 215 }
223 216
224 int selectedIndex = SectionInformation.NO_SELECTION; 217 int selectedIndex = SectionInformation.NO_SELECTION;
225 if (!addresses.isEmpty() && mUiShippingOptions.getSelectedItem() != null ) { 218 if (!addresses.isEmpty() && mUiShippingOptions.getSelectedItem() != null ) {
226 selectedIndex = 0; 219 selectedIndex = 0;
227 } 220 }
228 mShippingAddressesSection = new SectionInformation( 221 mShippingAddressesSection = new SectionInformation(
229 PaymentRequestUI.TYPE_SHIPPING_ADDRESSES, selectedIndex, address es); 222 PaymentRequestUI.TYPE_SHIPPING_ADDRESSES, selectedIndex, address es);
230 223
231 mPendingApps = new ArrayList<>(mApps); 224 mPendingApps = new ArrayList<>(mApps);
232 mPendingInstruments = new ArrayList<>(); 225 mPendingInstruments = new ArrayList<>();
233 boolean isGettingInstruments = false; 226 boolean isGettingInstruments = false;
234 227
235 for (int i = 0; i < mApps.size(); i++) { 228 for (int i = 0; i < mApps.size(); i++) {
236 PaymentApp app = mApps.get(i); 229 PaymentApp app = mApps.get(i);
237 Set<String> appMethods = app.getSupportedMethodNames(); 230 Set<String> appMethods = app.getSupportedMethodNames();
238 appMethods.retainAll(mSupportedMethods); 231 appMethods.retainAll(mMethodData.keySet());
239 if (appMethods.isEmpty()) { 232 if (appMethods.isEmpty()) {
240 mPendingApps.remove(app); 233 mPendingApps.remove(app);
241 } else { 234 } else {
242 isGettingInstruments = true; 235 isGettingInstruments = true;
243 app.getInstruments(mRawTotal, mRawLineItems, this); 236 app.getInstruments(mRawTotal, mRawLineItems, this);
244 } 237 }
245 } 238 }
246 239
247 if (!isGettingInstruments) { 240 if (!isGettingInstruments) {
248 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYP E_PAYMENT_METHODS); 241 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYP E_PAYMENT_METHODS);
249 } 242 }
250 243
251 mUI = PaymentRequestUI.show(mContext, this, requestShipping, mMerchantNa me, mOrigin); 244 mUI = PaymentRequestUI.show(mContext, this, requestShipping, mMerchantNa me, mOrigin);
252 if (mFavicon != null) mUI.setTitleBitmap(mFavicon); 245 if (mFavicon != null) mUI.setTitleBitmap(mFavicon);
253 mFavicon = null; 246 mFavicon = null;
254 } 247 }
255 248
256 private HashSet<String> getValidatedSupportedMethods(String[] methods) { 249 private HashMap<String, JSONObject> getValidatedMethodData(PaymentMethodData [] methodData) {
257 // Payment methods are required. 250 // Payment methodData are required.
258 if (methods == null || methods.length == 0) return null; 251 if (methodData == null || methodData.length == 0) return null;
252 HashMap<String, JSONObject> result = new HashMap<>();
253 for (int i = 0; i < methodData.length; i++) {
254 JSONObject data = null;
255 if (!TextUtils.isEmpty(methodData[i].stringifiedData)) {
256 try {
257 data = new JSONObject(JsonSanitizer.sanitize(methodData[i].s tringifiedData));
258 } catch (JSONException | IOException | IllegalStateException e) {
259 // Payment method specific data should be a JSON object.
260 // According to the payment request spec[1], for each method data,
261 // if the data field is supplied but is not a JSON-serializa ble object,
262 // then should throw a TypeError. So, we should return null here even if
263 // only one is bad.
264 // [1] https://w3c.github.io/browser-payment-api/specs/payme ntrequest.html
265 return null;
266 }
267 }
259 268
260 HashSet<String> result = new HashSet<>(); 269 String[] methods = methodData[i].supportedMethods;
261 for (int i = 0; i < methods.length; i++) { 270
262 // Payment methods should be non-empty. 271 // Payment methods are required.
263 if (TextUtils.isEmpty(methods[i])) return null; 272 if (methods == null || methods.length == 0) return null;
264 result.add(methods[i]); 273
274 for (int j = 0; j < methods.length; j++) {
275 // Payment methods should be non-empty.
276 if (TextUtils.isEmpty(methods[j])) return null;
277 result.put(methods[j], data);
278 }
265 } 279 }
266
267 return result; 280 return result;
268 } 281 }
269 282
270 /** 283 /**
271 * Called by merchant to update the shipping options and line items after th e user has selected 284 * Called by merchant to update the shipping options and line items after th e user has selected
272 * their shipping address or shipping option. 285 * their shipping address or shipping option.
273 */ 286 */
274 @Override 287 @Override
275 public void updateWith(PaymentDetails details) { 288 public void updateWith(PaymentDetails details) {
276 if (mClient == null) return; 289 if (mClient == null) return;
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 ShippingOption option = options[i]; 450 ShippingOption option = options[i];
438 result.add(new PaymentOption(option.id, option.label, 451 result.add(new PaymentOption(option.id, option.label,
439 formatter.format(option.amount.value), PaymentOption.NO_ICON )); 452 formatter.format(option.amount.value), PaymentOption.NO_ICON ));
440 if (option.selected) selectedItemIndex = i; 453 if (option.selected) selectedItemIndex = i;
441 } 454 }
442 455
443 return new SectionInformation(PaymentRequestUI.TYPE_SHIPPING_OPTIONS, se lectedItemIndex, 456 return new SectionInformation(PaymentRequestUI.TYPE_SHIPPING_OPTIONS, se lectedItemIndex,
444 result); 457 result);
445 } 458 }
446 459
447 private JSONObject getValidatedData(Set<String> supportedMethods, String str ingifiedData) {
448 if (TextUtils.isEmpty(stringifiedData)) return new JSONObject();
449
450 JSONObject result;
451 try {
452 result = new JSONObject(JsonSanitizer.sanitize(stringifiedData));
453 } catch (JSONException | IOException | IllegalStateException e) {
454 // Payment method specific data should be a valid JSON object.
455 return null;
456 }
457
458 Iterator<String> it = result.keys();
459 while (it.hasNext()) {
460 String name = it.next();
461 // Each key should be one of the supported payment methods.
462 if (!supportedMethods.contains(name)) return null;
463 // Each value should be a JSON object.
464 if (result.optJSONObject(name) == null) return null;
465 }
466
467 return result;
468 }
469
470 /** 460 /**
471 * Called to retrieve the data to show in the initial PaymentRequest UI. 461 * Called to retrieve the data to show in the initial PaymentRequest UI.
472 */ 462 */
473 @Override 463 @Override
474 public void getDefaultPaymentInformation(Callback<PaymentInformation> callba ck) { 464 public void getDefaultPaymentInformation(Callback<PaymentInformation> callba ck) {
475 mPaymentInformationCallback = callback; 465 mPaymentInformationCallback = callback;
476 466
477 if (mPaymentMethodsSection == null) return; 467 if (mPaymentMethodsSection == null) return;
478 468
479 mHandler.post(new Runnable() { 469 mHandler.post(new Runnable() {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 mContext, AutofillCreditCardEditor.class.getName()); 540 mContext, AutofillCreditCardEditor.class.getName());
551 } 541 }
552 } 542 }
553 543
554 @Override 544 @Override
555 public void onPayClicked(PaymentOption selectedShippingAddress, 545 public void onPayClicked(PaymentOption selectedShippingAddress,
556 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) { 546 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) {
557 assert selectedPaymentMethod instanceof PaymentInstrument; 547 assert selectedPaymentMethod instanceof PaymentInstrument;
558 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ; 548 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ;
559 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems, 549 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems,
560 mData.optJSONObject(instrument.getMethodName()), this); 550 mMethodData.get(instrument.getMethodName()), this);
561 } 551 }
562 552
563 @Override 553 @Override
564 public void onDismiss() { 554 public void onDismiss() {
565 disconnectFromClientWithDebugMessage("Dialog dismissed"); 555 disconnectFromClientWithDebugMessage("Dialog dismissed");
566 closeUI(false); 556 closeUI(false);
567 } 557 }
568 558
569 @Override 559 @Override
570 public boolean merchantNeedsShippingAddress() { 560 public boolean merchantNeedsShippingAddress() {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
609 /** 599 /**
610 * Called after retrieving the list of payment instruments in an app. 600 * Called after retrieving the list of payment instruments in an app.
611 */ 601 */
612 @Override 602 @Override
613 public void onInstrumentsReady(PaymentApp app, List<PaymentInstrument> instr uments) { 603 public void onInstrumentsReady(PaymentApp app, List<PaymentInstrument> instr uments) {
614 mPendingApps.remove(app); 604 mPendingApps.remove(app);
615 605
616 if (instruments != null) { 606 if (instruments != null) {
617 for (int i = 0; i < instruments.size(); i++) { 607 for (int i = 0; i < instruments.size(); i++) {
618 PaymentInstrument instrument = instruments.get(i); 608 PaymentInstrument instrument = instruments.get(i);
619 if (mSupportedMethods.contains(instrument.getMethodName())) { 609 if (mMethodData.containsKey(instrument.getMethodName())) {
620 mPendingInstruments.add(instrument); 610 mPendingInstruments.add(instrument);
621 } else { 611 } else {
622 instrument.dismiss(); 612 instrument.dismiss();
623 } 613 }
624 } 614 }
625 } 615 }
626 616
627 if (mPendingApps.isEmpty()) { 617 if (mPendingApps.isEmpty()) {
628 mPaymentMethodsSection = new SectionInformation( 618 mPaymentMethodsSection = new SectionInformation(
629 PaymentRequestUI.TYPE_PAYMENT_METHODS, 0, mPendingInstrument s); 619 PaymentRequestUI.TYPE_PAYMENT_METHODS, 0, mPendingInstrument s);
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
697 } 687 }
698 mPaymentMethodsSection = null; 688 mPaymentMethodsSection = null;
699 } 689 }
700 } 690 }
701 691
702 private void closeClient() { 692 private void closeClient() {
703 if (mClient != null) mClient.close(); 693 if (mClient != null) mClient.close();
704 mClient = null; 694 mClient = null;
705 } 695 }
706 } 696 }
OLDNEW
« no previous file with comments | « no previous file | chrome/test/data/android/payments/dynamic_shipping.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698