blob: df695665003a90337a0149dc1acb38c39cf446ee [file] [log] [blame]
mathpf709499d2017-01-09 20:48:361// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
rouslan908248c2017-02-27 21:30:245#include "components/payments/content/payment_request.h"
6
Rouslan Solomakhin9c83b8a2019-06-24 20:50:037#include <algorithm>
anthonyvdd23ed702017-04-05 15:29:008#include <string>
rouslan908248c2017-02-27 21:30:249#include <utility>
mathpf709499d2017-01-09 20:48:3610
Sebastien Marchand53801a32019-01-25 16:26:1111#include "base/bind.h"
Rouslan Solomakhin1804ee42017-10-03 14:27:4312#include "base/feature_list.h"
Mathieu Perreault627b97c2017-08-12 00:44:2213#include "base/stl_util.h"
Rouslan Solomakhin9c83b8a2019-06-24 20:50:0314#include "base/strings/string_util.h"
rouslan690997682017-05-09 18:07:3915#include "components/payments/content/can_make_payment_query_factory.h"
Rouslan Solomakhin4eea9bc22017-10-10 15:18:5116#include "components/payments/content/content_payment_request_delegate.h"
Rouslan Solomakhincf9093f2019-05-20 15:32:1717#include "components/payments/content/payment_details_converter.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2218#include "components/payments/content/payment_request_converter.h"
rouslan908248c2017-02-27 21:30:2419#include "components/payments/content/payment_request_web_contents_manager.h"
Rouslan Solomakhina480efa2019-05-06 15:37:2220#include "components/payments/content/service_worker_payment_instrument.h"
rouslan690997682017-05-09 18:07:3921#include "components/payments/core/can_make_payment_query.h"
Rouslan Solomakhina480efa2019-05-06 15:37:2222#include "components/payments/core/error_strings.h"
Anthony Vallee-Dubois968ae4d2018-03-15 16:56:3623#include "components/payments/core/features.h"
Rouslan Solomakhineb06b272019-07-10 16:23:3224#include "components/payments/core/native_error_strings.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2225#include "components/payments/core/payment_details.h"
26#include "components/payments/core/payment_details_validation.h"
Mathieu Perreault23d25bfb82018-05-11 14:45:3727#include "components/payments/core/payment_instrument.h"
anthonyvd6a43b932017-05-11 18:39:2728#include "components/payments/core/payment_prefs.h"
Danyao Wang50ccb9f2019-05-09 23:28:0329#include "components/payments/core/payments_experimental_features.h"
Jinho Bangbe463a22018-08-02 10:26:5030#include "components/payments/core/payments_validators.h"
Rouslan Solomakhin77a7e1a2019-05-23 17:37:5831#include "components/payments/core/url_util.h"
anthonyvd6a43b932017-05-11 18:39:2732#include "components/prefs/pref_service.h"
Steven Holte2083e8bc2018-07-16 23:50:3633#include "components/ukm/content/source_url_recorder.h"
Rouslan Solomakhin6e979ab2017-08-30 17:30:3934#include "components/url_formatter/elide_url.h"
mathpf709499d2017-01-09 20:48:3635#include "content/public/browser/browser_thread.h"
rouslan690997682017-05-09 18:07:3936#include "content/public/browser/render_frame_host.h"
mathpf709499d2017-01-09 20:48:3637#include "content/public/browser/web_contents.h"
Rouslan Solomakhin1804ee42017-10-03 14:27:4338#include "content/public/common/content_features.h"
Rouslan Solomakhin77a7e1a2019-05-23 17:37:5839#include "content/public/common/origin_util.h"
mathpf709499d2017-01-09 20:48:3640
Rouslan Solomakhina480efa2019-05-06 15:37:2241namespace payments {
Danyao Wangce175bf2018-12-21 22:35:5842namespace {
43
Danyao Wang57aa0442019-01-31 04:06:4144using ::payments::mojom::CanMakePaymentQueryResult;
Danyao Wangce175bf2018-12-21 22:35:5845using ::payments::mojom::HasEnrolledInstrumentQueryResult;
46
Sahel Sharify26884382019-05-07 16:23:5147bool IsGooglePaymentMethodInstrumentSelected(const std::string& method_name) {
48 return method_name == kGooglePayMethodName ||
49 method_name == kAndroidPayMethodName;
50}
51
Rouslan Solomakhin9c83b8a2019-06-24 20:50:0352std::string GetNotSupportedErrorMessage(PaymentRequestSpec* spec) {
53 if (!spec || spec->payment_method_identifiers_set().empty())
54 return errors::kGenericPaymentMethodNotSupportedMessage;
55
56 std::vector<std::string> method_names(
57 spec->payment_method_identifiers_set().size());
58 std::transform(
59 spec->payment_method_identifiers_set().begin(),
60 spec->payment_method_identifiers_set().end(), method_names.begin(),
61 [](const std::string& method_name) { return "\"" + method_name + "\""; });
62
63 std::string output;
64 bool replaced = base::ReplaceChars(
65 method_names.size() == 1
66 ? errors::kSinglePaymentMethodNotSupportedFormat
67 : errors::kMultiplePaymentMethodsNotSupportedFormat,
68 "$", base::JoinString(method_names, ", "), &output);
69 DCHECK(replaced);
70 return output;
71}
72
Rouslan Solomakhina480efa2019-05-06 15:37:2273} // namespace
mathpf709499d2017-01-09 20:48:3674
75PaymentRequest::PaymentRequest(
rouslan690997682017-05-09 18:07:3976 content::RenderFrameHost* render_frame_host,
mathpf709499d2017-01-09 20:48:3677 content::WebContents* web_contents,
Rouslan Solomakhin4eea9bc22017-10-10 15:18:5178 std::unique_ptr<ContentPaymentRequestDelegate> delegate,
mathpf709499d2017-01-09 20:48:3679 PaymentRequestWebContentsManager* manager,
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0780 PaymentRequestDisplayManager* display_manager,
rouslan6e3cf7c62017-04-17 21:23:2881 mojo::InterfaceRequest<mojom::PaymentRequest> request,
mathp300fa542017-03-27 19:29:3782 ObserverForTest* observer_for_testing)
mathpf709499d2017-01-09 20:48:3683 : web_contents_(web_contents),
Rouslan Solomakhin27064702018-12-14 21:15:3384 log_(web_contents_),
mathpf709499d2017-01-09 20:48:3685 delegate_(std::move(delegate)),
86 manager_(manager),
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0787 display_manager_(display_manager),
88 display_handle_(nullptr),
mathp300fa542017-03-27 19:29:3789 binding_(this, std::move(request)),
Rouslan Solomakhin1f95f092019-08-09 12:28:5190 payment_handler_host_(web_contents_, this),
Rouslan Solomakhin6e979ab2017-08-30 17:30:3991 top_level_origin_(url_formatter::FormatUrlForSecurityDisplay(
92 web_contents_->GetLastCommittedURL())),
93 frame_origin_(url_formatter::FormatUrlForSecurityDisplay(
94 render_frame_host->GetLastCommittedURL())),
sebsg20b49d7b2017-05-04 20:23:1795 observer_for_testing_(observer_for_testing),
96 journey_logger_(delegate_->IsIncognito(),
Jeremy Roman5c341f6d2019-07-15 15:56:1097 ukm::GetSourceIdForWebContentsDocument(web_contents)) {
mathpf4bc50e2017-01-24 05:17:5098 // OnConnectionTerminated will be called when the Mojo pipe is closed. This
99 // will happen as a result of many renderer-side events (both successful and
100 // erroneous in nature).
101 // TODO(crbug.com/683636): Investigate using
102 // set_connection_error_with_reason_handler with Binding::CloseWithReason.
tzik2bcf8e42018-07-31 11:22:15103 binding_.set_connection_error_handler(base::BindOnce(
Anthony Vallee-Duboisdc1dbf1a2017-07-17 15:01:13104 &PaymentRequest::OnConnectionTerminated, weak_ptr_factory_.GetWeakPtr()));
mathpf709499d2017-01-09 20:48:36105}
106
107PaymentRequest::~PaymentRequest() {}
108
rouslan6e3cf7c62017-04-17 21:23:28109void PaymentRequest::Init(mojom::PaymentRequestClientPtr client,
110 std::vector<mojom::PaymentMethodDataPtr> method_data,
111 mojom::PaymentDetailsPtr details,
112 mojom::PaymentOptionsPtr options) {
mathpf709499d2017-01-09 20:48:36113 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin27064702018-12-14 21:15:33114
115 if (is_initialized_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22116 log_.Error(errors::kAttemptedInitializationTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33117 OnConnectionTerminated();
118 return;
119 }
120
121 is_initialized_ = true;
rouslan6e3cf7c62017-04-17 21:23:28122 client_ = std::move(client);
123
rouslanb28f4532017-05-08 15:41:47124 const GURL last_committed_url = delegate_->GetLastCommittedURL();
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58125 if (!content::IsOriginSecure(last_committed_url)) {
Rouslan Solomakhina480efa2019-05-06 15:37:22126 log_.Error(errors::kNotInASecureOrigin);
rouslan6e3cf7c62017-04-17 21:23:28127 OnConnectionTerminated();
128 return;
129 }
130
Rouslan Solomakhin55db8272019-06-25 18:16:28131 // TODO(crbug.com/978471): Improve architecture for handling prohibited
132 // origins and invalid SSL certificates.
rouslanb28f4532017-05-08 15:41:47133 bool allowed_origin =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58134 UrlUtil::IsOriginAllowedToUseWebPaymentApis(last_committed_url);
rouslanb28f4532017-05-08 15:41:47135 if (!allowed_origin) {
Rouslan Solomakhin68429b72019-06-27 15:12:39136 reject_show_error_message_ = errors::kProhibitedOrigin;
rouslanb28f4532017-05-08 15:41:47137 }
138
Rouslan Solomakhin55db8272019-06-25 18:16:28139 bool invalid_ssl = false;
140 if (last_committed_url.SchemeIsCryptographic()) {
Rouslan Solomakhin68429b72019-06-27 15:12:39141 DCHECK(reject_show_error_message_.empty());
142 reject_show_error_message_ =
Rouslan Solomakhin55db8272019-06-25 18:16:28143 delegate_->GetInvalidSslCertificateErrorMessage();
Rouslan Solomakhin68429b72019-06-27 15:12:39144 invalid_ssl = !reject_show_error_message_.empty();
Rouslan Solomakhin27064702018-12-14 21:15:33145 }
rouslanb28f4532017-05-08 15:41:47146
147 if (!allowed_origin || invalid_ssl) {
Rouslan Solomakhin27064702018-12-14 21:15:33148 // Intentionally don't set |spec_| and |state_|, so the UI is never shown.
Rouslan Solomakhin68429b72019-06-27 15:12:39149 log_.Error(reject_show_error_message_);
Rouslan Solomakhina480efa2019-05-06 15:37:22150 log_.Error(errors::kProhibitedOriginOrInvalidSslExplanation);
rouslan6e3cf7c62017-04-17 21:23:28151 return;
152 }
153
mathpf709499d2017-01-09 20:48:36154 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22155 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33156 log_.Error(error);
mathpf4bc50e2017-01-24 05:17:50157 OnConnectionTerminated();
mathpf709499d2017-01-09 20:48:36158 return;
159 }
rouslan6e3cf7c62017-04-17 21:23:28160
jinho.bangfcb5ec92017-03-29 08:08:02161 if (!details->total) {
Rouslan Solomakhina480efa2019-05-06 15:37:22162 log_.Error(errors::kTotalRequired);
jinho.bangfcb5ec92017-03-29 08:08:02163 OnConnectionTerminated();
164 return;
165 }
rouslan6e3cf7c62017-04-17 21:23:28166
sebsga70a6da2017-12-21 22:27:02167 spec_ = std::make_unique<PaymentRequestSpec>(
Rouslan Solomakhin1f95f092019-08-09 12:28:51168 std::move(options), std::move(details), std::move(method_data),
169 /*observer=*/this, delegate_->GetApplicationLocale());
sebsga70a6da2017-12-21 22:27:02170 state_ = std::make_unique<PaymentRequestState>(
Rouslan Solomakhin1f95f092019-08-09 12:28:51171 web_contents_, top_level_origin_, frame_origin_, spec_.get(),
172 /*delegate=*/this, delegate_->GetApplicationLocale(),
173 delegate_->GetPersonalDataManager(), delegate_.get(),
174 /*sw_identity_observer=*/this, &journey_logger_);
Mathieu Perreault627b97c2017-08-12 00:44:22175
176 journey_logger_.SetRequestedInformation(
177 spec_->request_shipping(), spec_->request_payer_email(),
178 spec_->request_payer_phone(), spec_->request_payer_name());
179
180 // Log metrics around which payment methods are requested by the merchant.
181 GURL google_pay_url(kGooglePayMethodName);
182 GURL android_pay_url(kAndroidPayMethodName);
183 // Looking for payment methods that are NOT google-related payment methods.
184 auto non_google_it =
185 std::find_if(spec_->url_payment_method_identifiers().begin(),
186 spec_->url_payment_method_identifiers().end(),
187 [google_pay_url, android_pay_url](const GURL& url) {
188 return url != google_pay_url && url != android_pay_url;
189 });
190 journey_logger_.SetRequestedPaymentMethodTypes(
191 /*requested_basic_card=*/!spec_->supported_card_networks().empty(),
192 /*requested_method_google=*/
Jan Wilken Dörrie45d34f42019-06-08 09:40:54193 base::Contains(spec_->url_payment_method_identifiers(), google_pay_url) ||
194 base::Contains(spec_->url_payment_method_identifiers(),
195 android_pay_url),
Mathieu Perreault627b97c2017-08-12 00:44:22196 /*requested_method_other=*/non_google_it !=
197 spec_->url_payment_method_identifiers().end());
Rouslan Solomakhin1f95f092019-08-09 12:28:51198
199 payment_handler_host_.set_payment_request_id_for_logs(*spec_->details().id);
mathpf709499d2017-01-09 20:48:36200}
201
Rouslan Solomakhin9788d4b2019-04-09 13:10:23202void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
Rouslan Solomakhin27064702018-12-14 21:15:33203 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22204 log_.Error(errors::kCannotShowWithoutInit);
mathpf4bc50e2017-01-24 05:17:50205 OnConnectionTerminated();
tmartino8ce922852017-01-09 22:23:10206 return;
207 }
rouslan6e3cf7c62017-04-17 21:23:28208
Rouslan Solomakhin27064702018-12-14 21:15:33209 if (is_show_called_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22210 log_.Error(errors::kCannotShowTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33211 OnConnectionTerminated();
212 return;
213 }
214
215 is_show_called_ = true;
216
rouslan7d433cc22017-05-08 15:18:07217 // A tab can display only one PaymentRequest UI at a time.
Anthony Vallee-Dubois8f5e7e12018-01-12 16:14:06218 display_handle_ = display_manager_->TryShow(delegate_.get());
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07219 if (!display_handle_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22220 log_.Error(errors::kAnotherUiShowing);
Sahel Sharifya50fc4c2019-05-28 14:53:22221 DCHECK(!has_recorded_completion_);
222 has_recorded_completion_ = true;
sebsg828269bc2017-06-09 19:11:12223 journey_logger_.SetNotShown(
224 JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03225 client_->OnError(mojom::PaymentErrorReason::ALREADY_SHOWING,
226 errors::kAnotherUiShowing);
rouslan7d433cc22017-05-08 15:18:07227 OnConnectionTerminated();
228 return;
229 }
230
Rouslan Solomakhin5b510432017-09-26 16:59:32231 if (!delegate_->IsBrowserWindowActive()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22232 log_.Error(errors::kCannotShowInBackgroundTab);
Sahel Sharifya50fc4c2019-05-28 14:53:22233 DCHECK(!has_recorded_completion_);
234 has_recorded_completion_ = true;
Rouslan Solomakhin5b510432017-09-26 16:59:32235 journey_logger_.SetNotShown(JourneyLogger::NOT_SHOWN_REASON_OTHER);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03236 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
237 errors::kCannotShowInBackgroundTab);
Rouslan Solomakhin5b510432017-09-26 16:59:32238 OnConnectionTerminated();
239 return;
240 }
241
Rouslan Solomakhind2cae95a2018-08-09 00:16:10242 if (!state_) {
Rouslan Solomakhin27064702018-12-14 21:15:33243 // SSL is not valid. Reject show with NotSupportedError, disconnect the
244 // mojo pipe, and destroy this object.
Rouslan Solomakhind5dcc322019-07-11 21:47:20245 AreRequestedMethodsSupportedCallback(false, reject_show_error_message_);
Rouslan Solomakhind2cae95a2018-08-09 00:16:10246 return;
247 }
248
Rouslan Solomakhin833f8512018-04-03 23:19:25249 is_show_user_gesture_ = is_user_gesture;
250
Rouslan Solomakhin9788d4b2019-04-09 13:10:23251 if (wait_for_updated_details) {
252 // Put |spec_| into uninitialized state, so the UI knows to show a spinner.
253 // This method does not block.
254 spec_->StartWaitingForUpdateWith(
255 PaymentRequestSpec::UpdateReason::INITIAL_PAYMENT_DETAILS);
Sahel Sharify98a2c2a2019-07-12 18:57:40256 } else {
257 DCHECK(spec_->details().total);
258 journey_logger_.RecordTransactionAmount(
259 spec_->details().total->amount->currency,
260 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23261 }
Takashi Sakamoto48a29702019-04-08 05:06:32262
Rouslan Solomakhin9788d4b2019-04-09 13:10:23263 display_handle_->Show(this);
gogerald0a7ee6c2017-11-13 18:23:19264 state_->AreRequestedMethodsSupported(
265 base::BindOnce(&PaymentRequest::AreRequestedMethodsSupportedCallback,
266 weak_ptr_factory_.GetWeakPtr()));
267}
268
Jinho Bangbe463a22018-08-02 10:26:50269void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
Rouslan Solomakhin27064702018-12-14 21:15:33270 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22271 log_.Error(errors::kCannotRetryWithoutInit);
Jinho Bangcac8d9a02018-08-23 19:47:22272 OnConnectionTerminated();
273 return;
274 }
275
Rouslan Solomakhin27064702018-12-14 21:15:33276 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22277 log_.Error(errors::kCannotRetryWithoutShow);
Jinho Bangcac8d9a02018-08-23 19:47:22278 OnConnectionTerminated();
279 return;
280 }
281
Jinho Bangbe463a22018-08-02 10:26:50282 std::string error;
283 if (!PaymentsValidators::IsValidPaymentValidationErrorsFormat(errors,
284 &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33285 log_.Error(error);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03286 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL, error);
Jinho Bangbe463a22018-08-02 10:26:50287 OnConnectionTerminated();
288 return;
289 }
290
Jinho Bang092e7162018-09-06 23:41:19291 spec()->Retry(std::move(errors));
Jinho Bangcac8d9a02018-08-23 19:47:22292 display_handle_->Retry();
Jinho Bangbe463a22018-08-02 10:26:50293}
294
mathp151bd31e2017-04-03 21:07:24295void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
Rouslan Solomakhin27064702018-12-14 21:15:33296 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22297 log_.Error(errors::kCannotUpdateWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33298 OnConnectionTerminated();
299 return;
300 }
301
302 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22303 log_.Error(errors::kCannotUpdateWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33304 OnConnectionTerminated();
305 return;
306 }
307
mathp151bd31e2017-04-03 21:07:24308 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22309 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33310 log_.Error(error);
mathp151bd31e2017-04-03 21:07:24311 OnConnectionTerminated();
312 return;
313 }
Rouslan Solomakhin4cbda822017-08-23 18:50:39314
Jinho Bang092e7162018-09-06 23:41:19315 if (details->shipping_address_errors &&
316 !PaymentsValidators::IsValidAddressErrorsFormat(
317 details->shipping_address_errors, &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33318 log_.Error(error);
Jinho Bang092e7162018-09-06 23:41:19319 OnConnectionTerminated();
320 return;
321 }
322
Rouslan Solomakhincf9093f2019-05-20 15:32:17323 if (state()->selected_instrument() && state()->IsPaymentAppInvoked() &&
324 payment_handler_host_.is_changing_payment_method()) {
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02325 payment_handler_host_.UpdateWith(
Rouslan Solomakhincf9093f2019-05-20 15:32:17326 PaymentDetailsConverter::ConvertToPaymentMethodChangeResponse(
327 details, base::BindRepeating(
328 &PaymentInstrument::IsValidForPaymentMethodIdentifier,
329 base::Unretained(state()->selected_instrument()))));
Rouslan Solomakhina480efa2019-05-06 15:37:22330 }
331
Rouslan Solomakhin6ba46fd2019-04-11 23:44:01332 bool is_resolving_promise_passed_into_show_method = !spec_->IsInitialized();
333
mathp151bd31e2017-04-03 21:07:24334 spec_->UpdateWith(std::move(details));
Rouslan Solomakhin9788d4b2019-04-09 13:10:23335
336 if (is_resolving_promise_passed_into_show_method) {
Sahel Sharify98a2c2a2019-07-12 18:57:40337 DCHECK(spec_->details().total);
338 journey_logger_.RecordTransactionAmount(
339 spec_->details().total->amount->currency,
340 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23341 if (SatisfiesSkipUIConstraints()) {
Rouslan Solomakhin9788d4b2019-04-09 13:10:23342 Pay();
343 } else if (spec_->request_shipping()) {
344 state_->SelectDefaultShippingAddressAndNotifyObservers();
345 }
346 }
mathp151bd31e2017-04-03 21:07:24347}
348
Rouslan Solomakhina9ff9282017-10-31 21:58:05349void PaymentRequest::NoUpdatedPaymentDetails() {
Rouslan Solomakhin27064702018-12-14 21:15:33350 // This Mojo call is triggered by the user of the API doing nothing in
351 // response to a shipping address update event, so the error messages cannot
352 // be more verbose.
353 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22354 log_.Error(errors::kNotInitialized);
Rouslan Solomakhin27064702018-12-14 21:15:33355 OnConnectionTerminated();
356 return;
357 }
358
359 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22360 log_.Error(errors::kNotShown);
Rouslan Solomakhin27064702018-12-14 21:15:33361 OnConnectionTerminated();
362 return;
363 }
364
Rouslan Solomakhina9ff9282017-10-31 21:58:05365 spec_->RecomputeSpecForDetails();
Rouslan Solomakhina480efa2019-05-06 15:37:22366
Rouslan Solomakhincf9093f2019-05-20 15:32:17367 if (state()->IsPaymentAppInvoked() &&
368 payment_handler_host_.is_changing_payment_method()) {
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02369 payment_handler_host_.NoUpdatedPaymentDetails();
Rouslan Solomakhincf9093f2019-05-20 15:32:17370 }
Rouslan Solomakhina9ff9282017-10-31 21:58:05371}
372
mathpf4bc50e2017-01-24 05:17:50373void PaymentRequest::Abort() {
Rouslan Solomakhin27064702018-12-14 21:15:33374 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22375 log_.Error(errors::kCannotAbortWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33376 OnConnectionTerminated();
377 return;
378 }
379
380 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22381 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33382 OnConnectionTerminated();
383 return;
384 }
385
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56386 // The API user has decided to abort. If a successful abort message is
387 // returned to the renderer, the Mojo message pipe is closed, which triggers
mathpf4bc50e2017-01-24 05:17:50388 // PaymentRequest::OnConnectionTerminated, which destroys this object.
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56389 // Otherwise, the abort promise is rejected and the pipe is not closed.
390 // The abort is only successful if the payment app wasn't yet invoked.
391 // TODO(crbug.com/716546): Add a merchant abort metric
392
393 bool accepting_abort = !state_->IsPaymentAppInvoked();
sebsgfcdd13c2017-06-08 15:49:33394 if (accepting_abort)
395 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_MERCHANT);
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56396
mathpf4bc50e2017-01-24 05:17:50397 if (client_.is_bound())
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56398 client_->OnAbort(accepting_abort);
399
400 if (observer_for_testing_)
401 observer_for_testing_->OnAbortCalled();
mathpf4bc50e2017-01-24 05:17:50402}
403
mathp218795892017-03-29 15:15:34404void PaymentRequest::Complete(mojom::PaymentComplete result) {
Rouslan Solomakhin27064702018-12-14 21:15:33405 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22406 log_.Error(errors::kCannotCompleteWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33407 OnConnectionTerminated();
mathp4b85b582017-03-08 21:07:16408 return;
Rouslan Solomakhin27064702018-12-14 21:15:33409 }
410
411 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22412 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33413 OnConnectionTerminated();
414 return;
415 }
mathp4b85b582017-03-08 21:07:16416
Rouslan Solomakhine3473192017-06-16 14:54:57417 // Failed transactions show an error. Successful and unknown-state
418 // transactions don't show an error.
419 if (result == mojom::PaymentComplete::FAIL) {
mathp218795892017-03-29 15:15:34420 delegate_->ShowErrorMessage();
421 } else {
sebsgfcdd13c2017-06-08 15:49:33422 DCHECK(!has_recorded_completion_);
sebsgf8272a22017-05-26 14:32:58423 journey_logger_.SetCompleted();
sebsgfcdd13c2017-06-08 15:49:33424 has_recorded_completion_ = true;
Sahel Sharify98a2c2a2019-07-12 18:57:40425 DCHECK(spec_->details().total);
426 journey_logger_.RecordTransactionAmount(
427 spec_->details().total->amount->currency,
428 spec_->details().total->amount->value, true /*completed*/);
sebsgfcdd13c2017-06-08 15:49:33429
anthonyvd6a43b932017-05-11 18:39:27430 delegate_->GetPrefService()->SetBoolean(kPaymentsFirstTransactionCompleted,
431 true);
mathp218795892017-03-29 15:15:34432 // When the renderer closes the connection,
433 // PaymentRequest::OnConnectionTerminated will be called.
434 client_->OnComplete();
sebsg8a93b272017-05-11 19:30:22435 state_->RecordUseStats();
mathp218795892017-03-29 15:15:34436 }
mathp4b85b582017-03-08 21:07:16437}
438
Danyao Wang57aa0442019-01-31 04:06:41439void PaymentRequest::CanMakePayment(bool legacy_mode) {
Rouslan Solomakhin27064702018-12-14 21:15:33440 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22441 log_.Error(errors::kCannotCallCanMakePaymentWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33442 OnConnectionTerminated();
443 return;
444 }
445
446 // It's valid to call canMakePayment() without calling show() first.
447
gogerald8189d522017-09-15 17:52:18448 if (observer_for_testing_)
449 observer_for_testing_->OnCanMakePaymentCalled();
Mathieu Perreaultcacb85e2018-06-06 20:40:13450
Rouslan Solomakhind2cae95a2018-08-09 00:16:10451 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
452 !state_) {
Danyao Wang57aa0442019-01-31 04:06:41453 CanMakePaymentCallback(legacy_mode, /*can_make_payment=*/false);
Mathieu Perreaultcacb85e2018-06-06 20:40:13454 } else {
Rouslan Solomakhind2cae95a2018-08-09 00:16:10455 state_->CanMakePayment(
Danyao Wang57aa0442019-01-31 04:06:41456 legacy_mode,
Mathieu Perreaultcacb85e2018-06-06 20:40:13457 base::BindOnce(&PaymentRequest::CanMakePaymentCallback,
Danyao Wang57aa0442019-01-31 04:06:41458 weak_ptr_factory_.GetWeakPtr(), legacy_mode));
Mathieu Perreaultcacb85e2018-06-06 20:40:13459 }
gogerald8189d522017-09-15 17:52:18460}
461
Rouslan Solomakhin5683eb282019-01-29 18:06:03462void PaymentRequest::HasEnrolledInstrument(bool per_method_quota) {
Danyao Wangce175bf2018-12-21 22:35:58463 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22464 log_.Error(errors::kCannotCallHasEnrolledInstrumentWithoutInit);
Danyao Wangce175bf2018-12-21 22:35:58465 OnConnectionTerminated();
466 return;
467 }
468
469 // It's valid to call hasEnrolledInstrument() without calling show() first.
470
471 if (observer_for_testing_)
472 observer_for_testing_->OnHasEnrolledInstrumentCalled();
473
474 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
475 !state_) {
Rouslan Solomakhin5683eb282019-01-29 18:06:03476 HasEnrolledInstrumentCallback(per_method_quota,
477 /*has_enrolled_instrument=*/false);
Danyao Wangce175bf2018-12-21 22:35:58478 } else {
479 state_->HasEnrolledInstrument(
480 base::BindOnce(&PaymentRequest::HasEnrolledInstrumentCallback,
Rouslan Solomakhin5683eb282019-01-29 18:06:03481 weak_ptr_factory_.GetWeakPtr(), per_method_quota));
Danyao Wangce175bf2018-12-21 22:35:58482 }
483}
484
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02485bool PaymentRequest::ChangePaymentMethod(const std::string& method_name,
486 const std::string& stringified_data) {
Rouslan Solomakhina480efa2019-05-06 15:37:22487 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02488 DCHECK(!method_name.empty());
Rouslan Solomakhina480efa2019-05-06 15:37:22489
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02490 if (!state_ || !state_->IsPaymentAppInvoked() || !client_)
491 return false;
Rouslan Solomakhina480efa2019-05-06 15:37:22492
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02493 client_->OnPaymentMethodChange(method_name, stringified_data);
494 return true;
Rouslan Solomakhina480efa2019-05-06 15:37:22495}
496
Rouslan Solomakhin27064702018-12-14 21:15:33497void PaymentRequest::AreRequestedMethodsSupportedCallback(
Rouslan Solomakhind5dcc322019-07-11 21:47:20498 bool methods_supported,
499 const std::string& error_message) {
Rouslan Solomakhin27064702018-12-14 21:15:33500 if (methods_supported) {
Sahel Sharifyd3f1bc82019-05-21 18:48:46501 if (SatisfiesSkipUIConstraints())
Rouslan Solomakhin27064702018-12-14 21:15:33502 Pay();
Rouslan Solomakhin27064702018-12-14 21:15:33503 } else {
Sahel Sharifya50fc4c2019-05-28 14:53:22504 DCHECK(!has_recorded_completion_);
505 has_recorded_completion_ = true;
Rouslan Solomakhin27064702018-12-14 21:15:33506 journey_logger_.SetNotShown(
507 JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03508 client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED,
Rouslan Solomakhind5dcc322019-07-11 21:47:20509 GetNotSupportedErrorMessage(spec_.get()) +
510 (error_message.empty() ? "" : " " + error_message));
Rouslan Solomakhin27064702018-12-14 21:15:33511 if (observer_for_testing_)
512 observer_for_testing_->OnNotSupportedError();
513 OnConnectionTerminated();
514 }
515}
516
517bool PaymentRequest::IsInitialized() const {
518 return is_initialized_ && client_ && client_.is_bound() &&
519 binding_.is_bound();
520}
521
522bool PaymentRequest::IsThisPaymentRequestShowing() const {
523 return is_show_called_ && display_handle_ && spec_ && state_;
524}
525
Sahel Sharifyd3f1bc82019-05-21 18:48:46526bool PaymentRequest::SatisfiesSkipUIConstraints() {
Rouslan Solomakhin9788d4b2019-04-09 13:10:23527 // Only allowing URL base payment apps to skip the payment sheet.
Sahel Sharifyd3f1bc82019-05-21 18:48:46528 skipped_payment_request_ui_ =
529 (spec()->url_payment_method_identifiers().size() == 1 ||
danakjdca06902019-06-27 21:41:41530 delegate_->SkipUiForBasicCard()) &&
Sahel Sharifyd3f1bc82019-05-21 18:48:46531 base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
532 base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
533 is_show_user_gesture_ && state()->IsInitialized() &&
534 spec()->IsInitialized() && state()->available_instruments().size() == 1 &&
535 spec()->stringified_method_data().size() == 1 &&
536 !spec()->request_shipping() && !spec()->request_payer_name() &&
537 !spec()->request_payer_phone() && !spec()->request_payer_email();
538 if (skipped_payment_request_ui_) {
539 DCHECK(state()->IsInitialized() && spec()->IsInitialized());
540 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SKIPPED_SHOW);
541 } else if (state()->IsInitialized() && spec()->IsInitialized()) {
542 // Set EVENT_SHOWN only after state() and spec() initialization.
543 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
544 }
545 return skipped_payment_request_ui_;
Rouslan Solomakhin27064702018-12-14 21:15:33546}
547
mathpf1a7a3752017-03-15 11:23:37548void PaymentRequest::OnPaymentResponseAvailable(
549 mojom::PaymentResponsePtr response) {
Rouslan Solomakhin68429b72019-06-27 15:12:39550 DCHECK(!response->method_name.empty());
551 DCHECK(!response->stringified_details.empty());
552
mathp57c8c862017-06-16 20:15:45553 journey_logger_.SetEventOccurred(
554 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
gogerald7a2b761e2017-11-09 18:30:19555
Sahel Sharify26884382019-05-07 16:23:51556 // Log the correct "selected instrument" metric according to its type and
557 // the method name in response.
558 DCHECK(state_->selected_instrument());
559 JourneyLogger::Event selected_event =
560 JourneyLogger::Event::EVENT_SELECTED_OTHER;
561 switch (state_->selected_instrument()->type()) {
562 case PaymentInstrument::Type::AUTOFILL:
563 selected_event = JourneyLogger::Event::EVENT_SELECTED_CREDIT_CARD;
564 break;
565 case PaymentInstrument::Type::SERVICE_WORKER_APP: {
566 selected_event =
567 IsGooglePaymentMethodInstrumentSelected(response->method_name)
568 ? JourneyLogger::Event::EVENT_SELECTED_GOOGLE
569 : JourneyLogger::Event::EVENT_SELECTED_OTHER;
570 break;
571 }
572 case PaymentInstrument::Type::NATIVE_MOBILE_APP:
573 NOTREACHED();
574 break;
575 }
576 journey_logger_.SetEventOccurred(selected_event);
577
Rouslan Solomakhin02d086ec2019-01-31 23:10:39578 // If currently interactive, show the processing spinner. Autofill payment
579 // instruments request a CVC, so they are always interactive at this point. A
580 // payment handler may elect to be non-interactive by not showing a
581 // confirmation page to the user.
582 if (delegate_->IsInteractive())
583 delegate_->ShowProcessingSpinner();
584
mathpf1a7a3752017-03-15 11:23:37585 client_->OnPaymentResponse(std::move(response));
mathp4b85b582017-03-08 21:07:16586}
587
Rouslan Solomakhin68429b72019-06-27 15:12:39588void PaymentRequest::OnPaymentResponseError(const std::string& error_message) {
589 journey_logger_.SetEventOccurred(
590 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
591 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_INSTRUMENT_DETAILS_ERROR);
592
593 reject_show_error_message_ = error_message;
594 delegate_->ShowErrorMessage();
595 // When the user dismisses the error message, UserCancelled() will reject
596 // PaymentRequest.show() with |reject_show_error_message_|.
597}
598
mathp151bd31e2017-04-03 21:07:24599void PaymentRequest::OnShippingOptionIdSelected(
600 std::string shipping_option_id) {
601 client_->OnShippingOptionChange(shipping_option_id);
602}
603
604void PaymentRequest::OnShippingAddressSelected(
605 mojom::PaymentAddressPtr address) {
Danyao Wang50ccb9f2019-05-09 23:28:03606 // Redact shipping address before exposing it in ShippingAddressChangeEvent.
607 // https://w3c.github.io/payment-request/#shipping-address-changed-algorithm
608 if (PaymentsExperimentalFeatures::IsEnabled(
609 features::kWebPaymentsRedactShippingAddress)) {
610 address->organization.clear();
611 address->phone.clear();
612 address->recipient.clear();
613 address->address_line.clear();
614 }
mathp151bd31e2017-04-03 21:07:24615 client_->OnShippingAddressChange(std::move(address));
616}
617
Jinho Bangbb178152018-09-13 09:44:43618void PaymentRequest::OnPayerInfoSelected(mojom::PayerDetailPtr payer_info) {
619 client_->OnPayerDetailChange(std::move(payer_info));
620}
621
Rouslan Solomakhin1f95f092019-08-09 12:28:51622void PaymentRequest::SetInvokedServiceWorkerIdentity(const url::Origin& origin,
623 int64_t registration_id) {
624 payment_handler_host_.set_sw_origin_for_logs(origin);
625 payment_handler_host_.set_registration_id_for_logs(registration_id);
626}
627
mathpf4bc50e2017-01-24 05:17:50628void PaymentRequest::UserCancelled() {
629 // If |client_| is not bound, then the object is already being destroyed as
630 // a result of a renderer event.
631 if (!client_.is_bound())
632 return;
633
sebsgfcdd13c2017-06-08 15:49:33634 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
sebsg20b49d7b2017-05-04 20:23:17635
mathpf4bc50e2017-01-24 05:17:50636 // This sends an error to the renderer, which informs the API user.
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03637 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
Rouslan Solomakhin68429b72019-06-27 15:12:39638 !reject_show_error_message_.empty()
639 ? reject_show_error_message_
640 : errors::kUserCancelled);
mathpf4bc50e2017-01-24 05:17:50641
642 // We close all bindings and ask to be destroyed.
643 client_.reset();
644 binding_.Close();
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02645 payment_handler_host_.Disconnect();
rouslanb28f4532017-05-08 15:41:47646 if (observer_for_testing_)
647 observer_for_testing_->OnConnectionTerminated();
mathpf4bc50e2017-01-24 05:17:50648 manager_->DestroyRequest(this);
mathpf709499d2017-01-09 20:48:36649}
650
sebsgd56b3e422017-10-20 18:08:08651void PaymentRequest::DidStartMainFrameNavigationToDifferentDocument(
652 bool is_user_initiated) {
sebsgfcdd13c2017-06-08 15:49:33653 RecordFirstAbortReason(is_user_initiated
654 ? JourneyLogger::ABORT_REASON_USER_NAVIGATION
655 : JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION);
sebsg2c8558a2017-05-17 18:54:10656}
657
mathpf4bc50e2017-01-24 05:17:50658void PaymentRequest::OnConnectionTerminated() {
659 // We are here because of a browser-side error, or likely as a result of the
660 // connection_error_handler on |binding_|, which can mean that the renderer
661 // has decided to close the pipe for various reasons (see all uses of
662 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close
663 // the binding and the dialog, and ask to be deleted.
664 client_.reset();
mathpf709499d2017-01-09 20:48:36665 binding_.Close();
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02666 payment_handler_host_.Disconnect();
mathpf4bc50e2017-01-24 05:17:50667 delegate_->CloseDialog();
rouslanb28f4532017-05-08 15:41:47668 if (observer_for_testing_)
669 observer_for_testing_->OnConnectionTerminated();
sebsgfcdd13c2017-06-08 15:49:33670
671 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_MOJO_CONNECTION_ERROR);
mathpf709499d2017-01-09 20:48:36672 manager_->DestroyRequest(this);
673}
674
mathpd4be8de82017-03-01 00:51:48675void PaymentRequest::Pay() {
mathp57c8c862017-06-16 20:15:45676 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
Mathieu Perreault23d25bfb82018-05-11 14:45:37677 DCHECK(state_->selected_instrument());
Sahel Sharify26884382019-05-07 16:23:51678 if (state_->selected_instrument()->type() ==
679 PaymentInstrument::Type::SERVICE_WORKER_APP) {
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02680 static_cast<ServiceWorkerPaymentInstrument*>(state_->selected_instrument())
681 ->set_payment_handler_host(payment_handler_host_.Bind());
Mathieu Perreault23d25bfb82018-05-11 14:45:37682 }
mathpf1a7a3752017-03-15 11:23:37683 state_->GeneratePaymentResponse();
mathpd4be8de82017-03-01 00:51:48684}
685
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07686void PaymentRequest::HideIfNecessary() {
687 display_handle_.reset();
688}
689
Anthony Vallee-Dubois10d131a2018-02-22 15:41:04690bool PaymentRequest::IsIncognito() const {
691 return delegate_->IsIncognito();
692}
693
sebsgfcdd13c2017-06-08 15:49:33694void PaymentRequest::RecordFirstAbortReason(
695 JourneyLogger::AbortReason abort_reason) {
696 if (!has_recorded_completion_) {
697 has_recorded_completion_ = true;
698 journey_logger_.SetAborted(abort_reason);
sebsg2c8558a2017-05-17 18:54:10699 }
700}
701
Danyao Wang57aa0442019-01-31 04:06:41702void PaymentRequest::CanMakePaymentCallback(bool legacy_mode,
703 bool can_make_payment) {
704 // Only need to enforce query quota in legacy mode. Per-method quota not
705 // supported.
706 if (legacy_mode && spec_ &&
707 !CanMakePaymentQueryFactory::GetInstance()
708 ->GetForContext(web_contents_->GetBrowserContext())
709 ->CanQuery(top_level_origin_, frame_origin_,
710 spec_->stringified_method_data(),
711 /*per_method_quota=*/false)) {
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58712 if (UrlUtil::IsLocalDevelopmentUrl(frame_origin_)) {
Danyao Wang57aa0442019-01-31 04:06:41713 client_->OnCanMakePayment(
714 can_make_payment
715 ? CanMakePaymentQueryResult::WARNING_CAN_MAKE_PAYMENT
716 : CanMakePaymentQueryResult::WARNING_CANNOT_MAKE_PAYMENT);
717 } else {
718 client_->OnCanMakePayment(
719 CanMakePaymentQueryResult::QUERY_QUOTA_EXCEEDED);
720 }
721 } else {
722 client_->OnCanMakePayment(
723 can_make_payment
724 ? mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT
725 : mojom::CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT);
726 }
Danyao Wang4bc0606a2018-12-27 16:54:53727
Danyao Wang57aa0442019-01-31 04:06:41728 journey_logger_.SetCanMakePaymentValue(can_make_payment);
Rouslan Solomakhin1804ee42017-10-03 14:27:43729
730 if (observer_for_testing_)
731 observer_for_testing_->OnCanMakePaymentReturned();
732}
733
Danyao Wangce175bf2018-12-21 22:35:58734void PaymentRequest::HasEnrolledInstrumentCallback(
Rouslan Solomakhin5683eb282019-01-29 18:06:03735 bool per_method_quota,
Danyao Wangce175bf2018-12-21 22:35:58736 bool has_enrolled_instrument) {
Rouslan Solomakhin5683eb282019-01-29 18:06:03737 if (!spec_ ||
738 CanMakePaymentQueryFactory::GetInstance()
739 ->GetForContext(web_contents_->GetBrowserContext())
740 ->CanQuery(top_level_origin_, frame_origin_,
741 spec_->stringified_method_data(), per_method_quota)) {
Danyao Wangce175bf2018-12-21 22:35:58742 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58743 /*warn_local_development=*/false);
744 } else if (UrlUtil::IsLocalDevelopmentUrl(frame_origin_)) {
Danyao Wangce175bf2018-12-21 22:35:58745 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58746 /*warn_local_development=*/true);
Danyao Wangce175bf2018-12-21 22:35:58747 } else {
748 client_->OnHasEnrolledInstrument(
749 HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED);
750 }
751
752 if (observer_for_testing_)
753 observer_for_testing_->OnHasEnrolledInstrumentReturned();
754}
755
Danyao Wangce175bf2018-12-21 22:35:58756void PaymentRequest::RespondToHasEnrolledInstrumentQuery(
757 bool has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58758 bool warn_local_development) {
Danyao Wangce175bf2018-12-21 22:35:58759 HasEnrolledInstrumentQueryResult positive =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58760 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58761 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_ENROLLED_INSTRUMENT
762 : HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT;
763 HasEnrolledInstrumentQueryResult negative =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58764 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58765 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_NO_ENROLLED_INSTRUMENT
766 : HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT;
767
768 client_->OnHasEnrolledInstrument(has_enrolled_instrument ? positive
769 : negative);
Danyao Wang57aa0442019-01-31 04:06:41770 journey_logger_.SetHasEnrolledInstrumentValue(has_enrolled_instrument);
Danyao Wangce175bf2018-12-21 22:35:58771}
772
mathpf709499d2017-01-09 20:48:36773} // namespace payments