blob: 4210ef3e6d7b45babc73b4f68a1ea409fc20a69d [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
anthonyvdd23ed702017-04-05 15:29:007#include <string>
rouslan908248c2017-02-27 21:30:248#include <utility>
mathpf709499d2017-01-09 20:48:369
Rouslan Solomakhin1804ee42017-10-03 14:27:4310#include "base/feature_list.h"
Mathieu Perreault627b97c2017-08-12 00:44:2211#include "base/stl_util.h"
rouslan690997682017-05-09 18:07:3912#include "components/payments/content/can_make_payment_query_factory.h"
Rouslan Solomakhin4eea9bc22017-10-10 15:18:5113#include "components/payments/content/content_payment_request_delegate.h"
rouslan6e3cf7c62017-04-17 21:23:2814#include "components/payments/content/origin_security_checker.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2215#include "components/payments/content/payment_request_converter.h"
rouslan908248c2017-02-27 21:30:2416#include "components/payments/content/payment_request_web_contents_manager.h"
rouslan690997682017-05-09 18:07:3917#include "components/payments/core/can_make_payment_query.h"
Anthony Vallee-Dubois968ae4d2018-03-15 16:56:3618#include "components/payments/core/features.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2219#include "components/payments/core/payment_details.h"
20#include "components/payments/core/payment_details_validation.h"
Mathieu Perreault23d25bfb82018-05-11 14:45:3721#include "components/payments/core/payment_instrument.h"
anthonyvd6a43b932017-05-11 18:39:2722#include "components/payments/core/payment_prefs.h"
Jinho Bangbe463a22018-08-02 10:26:5023#include "components/payments/core/payments_validators.h"
anthonyvd6a43b932017-05-11 18:39:2724#include "components/prefs/pref_service.h"
Steven Holte2083e8bc2018-07-16 23:50:3625#include "components/ukm/content/source_url_recorder.h"
Rouslan Solomakhin6e979ab2017-08-30 17:30:3926#include "components/url_formatter/elide_url.h"
mathpf709499d2017-01-09 20:48:3627#include "content/public/browser/browser_thread.h"
rouslan690997682017-05-09 18:07:3928#include "content/public/browser/render_frame_host.h"
mathpf709499d2017-01-09 20:48:3629#include "content/public/browser/web_contents.h"
Rouslan Solomakhin1804ee42017-10-03 14:27:4330#include "content/public/common/content_features.h"
mathpf709499d2017-01-09 20:48:3631
Danyao Wangce175bf2018-12-21 22:35:5832namespace {
33
34using ::payments::mojom::HasEnrolledInstrumentQueryResult;
35
36} // namespace
37
mathpf709499d2017-01-09 20:48:3638namespace payments {
39
40PaymentRequest::PaymentRequest(
rouslan690997682017-05-09 18:07:3941 content::RenderFrameHost* render_frame_host,
mathpf709499d2017-01-09 20:48:3642 content::WebContents* web_contents,
Rouslan Solomakhin4eea9bc22017-10-10 15:18:5143 std::unique_ptr<ContentPaymentRequestDelegate> delegate,
mathpf709499d2017-01-09 20:48:3644 PaymentRequestWebContentsManager* manager,
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0745 PaymentRequestDisplayManager* display_manager,
rouslan6e3cf7c62017-04-17 21:23:2846 mojo::InterfaceRequest<mojom::PaymentRequest> request,
mathp300fa542017-03-27 19:29:3747 ObserverForTest* observer_for_testing)
mathpf709499d2017-01-09 20:48:3648 : web_contents_(web_contents),
Rouslan Solomakhin27064702018-12-14 21:15:3349 log_(web_contents_),
mathpf709499d2017-01-09 20:48:3650 delegate_(std::move(delegate)),
51 manager_(manager),
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0752 display_manager_(display_manager),
53 display_handle_(nullptr),
mathp300fa542017-03-27 19:29:3754 binding_(this, std::move(request)),
Rouslan Solomakhin6e979ab2017-08-30 17:30:3955 top_level_origin_(url_formatter::FormatUrlForSecurityDisplay(
56 web_contents_->GetLastCommittedURL())),
57 frame_origin_(url_formatter::FormatUrlForSecurityDisplay(
58 render_frame_host->GetLastCommittedURL())),
sebsg20b49d7b2017-05-04 20:23:1759 observer_for_testing_(observer_for_testing),
60 journey_logger_(delegate_->IsIncognito(),
Steven Holte2083e8bc2018-07-16 23:50:3661 ukm::GetSourceIdForWebContentsDocument(web_contents)),
Anthony Vallee-Duboisdc1dbf1a2017-07-17 15:01:1362 weak_ptr_factory_(this) {
mathpf4bc50e2017-01-24 05:17:5063 // OnConnectionTerminated will be called when the Mojo pipe is closed. This
64 // will happen as a result of many renderer-side events (both successful and
65 // erroneous in nature).
66 // TODO(crbug.com/683636): Investigate using
67 // set_connection_error_with_reason_handler with Binding::CloseWithReason.
tzik2bcf8e42018-07-31 11:22:1568 binding_.set_connection_error_handler(base::BindOnce(
Anthony Vallee-Duboisdc1dbf1a2017-07-17 15:01:1369 &PaymentRequest::OnConnectionTerminated, weak_ptr_factory_.GetWeakPtr()));
mathpf709499d2017-01-09 20:48:3670}
71
72PaymentRequest::~PaymentRequest() {}
73
rouslan6e3cf7c62017-04-17 21:23:2874void PaymentRequest::Init(mojom::PaymentRequestClientPtr client,
75 std::vector<mojom::PaymentMethodDataPtr> method_data,
76 mojom::PaymentDetailsPtr details,
77 mojom::PaymentOptionsPtr options) {
mathpf709499d2017-01-09 20:48:3678 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin27064702018-12-14 21:15:3379
80 if (is_initialized_) {
81 log_.Error("Attempted initialization twice");
82 OnConnectionTerminated();
83 return;
84 }
85
86 is_initialized_ = true;
rouslan6e3cf7c62017-04-17 21:23:2887 client_ = std::move(client);
88
rouslanb28f4532017-05-08 15:41:4789 const GURL last_committed_url = delegate_->GetLastCommittedURL();
90 if (!OriginSecurityChecker::IsOriginSecure(last_committed_url)) {
Rouslan Solomakhin27064702018-12-14 21:15:3391 log_.Error("Not in a secure origin");
rouslan6e3cf7c62017-04-17 21:23:2892 OnConnectionTerminated();
93 return;
94 }
95
rouslanb28f4532017-05-08 15:41:4796 bool allowed_origin =
97 OriginSecurityChecker::IsSchemeCryptographic(last_committed_url) ||
98 OriginSecurityChecker::IsOriginLocalhostOrFile(last_committed_url);
99 if (!allowed_origin) {
Rouslan Solomakhin27064702018-12-14 21:15:33100 log_.Error(
101 "Only localhost, file://, and cryptographic scheme origins allowed");
rouslanb28f4532017-05-08 15:41:47102 }
103
104 bool invalid_ssl =
105 OriginSecurityChecker::IsSchemeCryptographic(last_committed_url) &&
106 !delegate_->IsSslCertificateValid();
Rouslan Solomakhin27064702018-12-14 21:15:33107 if (invalid_ssl) {
108 log_.Error("SSL certificate is not valid.");
109 }
rouslanb28f4532017-05-08 15:41:47110
111 if (!allowed_origin || invalid_ssl) {
Rouslan Solomakhin27064702018-12-14 21:15:33112 // Intentionally don't set |spec_| and |state_|, so the UI is never shown.
113 log_.Error(
114 "No UI will be shown. CanMakePayment will always return false. "
115 "Show will be rejected with NotSupportedError.");
rouslan6e3cf7c62017-04-17 21:23:28116 return;
117 }
118
mathpf709499d2017-01-09 20:48:36119 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22120 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33121 log_.Error(error);
mathpf4bc50e2017-01-24 05:17:50122 OnConnectionTerminated();
mathpf709499d2017-01-09 20:48:36123 return;
124 }
rouslan6e3cf7c62017-04-17 21:23:28125
jinho.bangfcb5ec92017-03-29 08:08:02126 if (!details->total) {
Rouslan Solomakhin27064702018-12-14 21:15:33127 log_.Error("Missing total");
jinho.bangfcb5ec92017-03-29 08:08:02128 OnConnectionTerminated();
129 return;
130 }
rouslan6e3cf7c62017-04-17 21:23:28131
sebsga70a6da2017-12-21 22:27:02132 spec_ = std::make_unique<PaymentRequestSpec>(
mathpc0d616a2017-03-15 14:09:33133 std::move(options), std::move(details), std::move(method_data), this,
134 delegate_->GetApplicationLocale());
sebsga70a6da2017-12-21 22:27:02135 state_ = std::make_unique<PaymentRequestState>(
Rouslan Solomakhindbf593d92017-11-21 19:20:57136 web_contents_, top_level_origin_, frame_origin_, spec_.get(), this,
137 delegate_->GetApplicationLocale(), delegate_->GetPersonalDataManager(),
138 delegate_.get(), &journey_logger_);
Mathieu Perreault627b97c2017-08-12 00:44:22139
140 journey_logger_.SetRequestedInformation(
141 spec_->request_shipping(), spec_->request_payer_email(),
142 spec_->request_payer_phone(), spec_->request_payer_name());
143
144 // Log metrics around which payment methods are requested by the merchant.
145 GURL google_pay_url(kGooglePayMethodName);
146 GURL android_pay_url(kAndroidPayMethodName);
147 // Looking for payment methods that are NOT google-related payment methods.
148 auto non_google_it =
149 std::find_if(spec_->url_payment_method_identifiers().begin(),
150 spec_->url_payment_method_identifiers().end(),
151 [google_pay_url, android_pay_url](const GURL& url) {
152 return url != google_pay_url && url != android_pay_url;
153 });
154 journey_logger_.SetRequestedPaymentMethodTypes(
155 /*requested_basic_card=*/!spec_->supported_card_networks().empty(),
156 /*requested_method_google=*/
157 base::ContainsValue(spec_->url_payment_method_identifiers(),
158 google_pay_url) ||
159 base::ContainsValue(spec_->url_payment_method_identifiers(),
160 android_pay_url),
161 /*requested_method_other=*/non_google_it !=
162 spec_->url_payment_method_identifiers().end());
mathpf709499d2017-01-09 20:48:36163}
164
Rouslan Solomakhin833f8512018-04-03 23:19:25165void PaymentRequest::Show(bool is_user_gesture) {
Rouslan Solomakhin27064702018-12-14 21:15:33166 if (!IsInitialized()) {
167 log_.Error("Attempted show without initialization");
mathpf4bc50e2017-01-24 05:17:50168 OnConnectionTerminated();
tmartino8ce922852017-01-09 22:23:10169 return;
170 }
rouslan6e3cf7c62017-04-17 21:23:28171
Rouslan Solomakhin27064702018-12-14 21:15:33172 if (is_show_called_) {
173 log_.Error("Attempted show twice");
174 OnConnectionTerminated();
175 return;
176 }
177
178 is_show_called_ = true;
179
rouslan7d433cc22017-05-08 15:18:07180 // A tab can display only one PaymentRequest UI at a time.
Anthony Vallee-Dubois8f5e7e12018-01-12 16:14:06181 display_handle_ = display_manager_->TryShow(delegate_.get());
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07182 if (!display_handle_) {
Rouslan Solomakhin27064702018-12-14 21:15:33183 log_.Error("A PaymentRequest UI is already showing");
sebsg828269bc2017-06-09 19:11:12184 journey_logger_.SetNotShown(
185 JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
Rouslan Solomakhinb2d233c42018-08-30 16:18:40186 client_->OnError(mojom::PaymentErrorReason::ALREADY_SHOWING);
rouslan7d433cc22017-05-08 15:18:07187 OnConnectionTerminated();
188 return;
189 }
190
Rouslan Solomakhin5b510432017-09-26 16:59:32191 if (!delegate_->IsBrowserWindowActive()) {
Rouslan Solomakhin27064702018-12-14 21:15:33192 log_.Error("Cannot show PaymentRequest UI in a background tab");
Rouslan Solomakhin5b510432017-09-26 16:59:32193 journey_logger_.SetNotShown(JourneyLogger::NOT_SHOWN_REASON_OTHER);
194 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL);
195 OnConnectionTerminated();
196 return;
197 }
198
Rouslan Solomakhind2cae95a2018-08-09 00:16:10199 if (!state_) {
Rouslan Solomakhin27064702018-12-14 21:15:33200 // SSL is not valid. Reject show with NotSupportedError, disconnect the
201 // mojo pipe, and destroy this object.
Rouslan Solomakhind2cae95a2018-08-09 00:16:10202 AreRequestedMethodsSupportedCallback(false);
203 return;
204 }
205
Rouslan Solomakhin833f8512018-04-03 23:19:25206 is_show_user_gesture_ = is_user_gesture;
207
Rouslan Solomakhind2cae95a2018-08-09 00:16:10208 display_handle_->Show(this);
209
gogerald0a7ee6c2017-11-13 18:23:19210 state_->AreRequestedMethodsSupported(
211 base::BindOnce(&PaymentRequest::AreRequestedMethodsSupportedCallback,
212 weak_ptr_factory_.GetWeakPtr()));
213}
214
Jinho Bangbe463a22018-08-02 10:26:50215void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
Rouslan Solomakhin27064702018-12-14 21:15:33216 if (!IsInitialized()) {
217 log_.Error("Attempted retry without initialization");
Jinho Bangcac8d9a02018-08-23 19:47:22218 OnConnectionTerminated();
219 return;
220 }
221
Rouslan Solomakhin27064702018-12-14 21:15:33222 if (!IsThisPaymentRequestShowing()) {
223 log_.Error("Attempted retry without show");
Jinho Bangcac8d9a02018-08-23 19:47:22224 OnConnectionTerminated();
225 return;
226 }
227
Jinho Bangbe463a22018-08-02 10:26:50228 std::string error;
229 if (!PaymentsValidators::IsValidPaymentValidationErrorsFormat(errors,
230 &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33231 log_.Error(error);
Jinho Bangbe463a22018-08-02 10:26:50232 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL);
233 OnConnectionTerminated();
234 return;
235 }
236
Jinho Bang092e7162018-09-06 23:41:19237 spec()->Retry(std::move(errors));
Jinho Bangcac8d9a02018-08-23 19:47:22238 display_handle_->Retry();
Jinho Bangbe463a22018-08-02 10:26:50239}
240
mathp151bd312017-04-03 21:07:24241void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
Rouslan Solomakhin27064702018-12-14 21:15:33242 if (!IsInitialized()) {
243 log_.Error("Attempted updateWith without initialization");
244 OnConnectionTerminated();
245 return;
246 }
247
248 if (!IsThisPaymentRequestShowing()) {
249 log_.Error("Attempted updateWith without show");
250 OnConnectionTerminated();
251 return;
252 }
253
mathp151bd312017-04-03 21:07:24254 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22255 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33256 log_.Error(error);
mathp151bd312017-04-03 21:07:24257 OnConnectionTerminated();
258 return;
259 }
Rouslan Solomakhin4cbda822017-08-23 18:50:39260
Jinho Bang092e7162018-09-06 23:41:19261 if (details->shipping_address_errors &&
262 !PaymentsValidators::IsValidAddressErrorsFormat(
263 details->shipping_address_errors, &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33264 log_.Error(error);
Jinho Bang092e7162018-09-06 23:41:19265 OnConnectionTerminated();
266 return;
267 }
268
Rouslan Solomakhin4cbda822017-08-23 18:50:39269 if (!details->total) {
Rouslan Solomakhin27064702018-12-14 21:15:33270 log_.Error("Missing total");
Rouslan Solomakhin4cbda822017-08-23 18:50:39271 OnConnectionTerminated();
272 return;
273 }
274
mathp151bd312017-04-03 21:07:24275 spec_->UpdateWith(std::move(details));
276}
277
Rouslan Solomakhina9ff9282017-10-31 21:58:05278void PaymentRequest::NoUpdatedPaymentDetails() {
Rouslan Solomakhin27064702018-12-14 21:15:33279 // This Mojo call is triggered by the user of the API doing nothing in
280 // response to a shipping address update event, so the error messages cannot
281 // be more verbose.
282 if (!IsInitialized()) {
283 log_.Error("Not initialized");
284 OnConnectionTerminated();
285 return;
286 }
287
288 if (!IsThisPaymentRequestShowing()) {
289 log_.Error("Not shown");
290 OnConnectionTerminated();
291 return;
292 }
293
Rouslan Solomakhina9ff9282017-10-31 21:58:05294 spec_->RecomputeSpecForDetails();
295}
296
mathpf4bc50e2017-01-24 05:17:50297void PaymentRequest::Abort() {
Rouslan Solomakhin27064702018-12-14 21:15:33298 if (!IsInitialized()) {
299 log_.Error("Attempted abort without initialization");
300 OnConnectionTerminated();
301 return;
302 }
303
304 if (!IsThisPaymentRequestShowing()) {
305 log_.Error("Attempted abort without show");
306 OnConnectionTerminated();
307 return;
308 }
309
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56310 // The API user has decided to abort. If a successful abort message is
311 // returned to the renderer, the Mojo message pipe is closed, which triggers
mathpf4bc50e2017-01-24 05:17:50312 // PaymentRequest::OnConnectionTerminated, which destroys this object.
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56313 // Otherwise, the abort promise is rejected and the pipe is not closed.
314 // The abort is only successful if the payment app wasn't yet invoked.
315 // TODO(crbug.com/716546): Add a merchant abort metric
316
317 bool accepting_abort = !state_->IsPaymentAppInvoked();
sebsgfcdd13c2017-06-08 15:49:33318 if (accepting_abort)
319 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_MERCHANT);
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56320
mathpf4bc50e2017-01-24 05:17:50321 if (client_.is_bound())
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56322 client_->OnAbort(accepting_abort);
323
324 if (observer_for_testing_)
325 observer_for_testing_->OnAbortCalled();
mathpf4bc50e2017-01-24 05:17:50326}
327
mathp218795892017-03-29 15:15:34328void PaymentRequest::Complete(mojom::PaymentComplete result) {
Rouslan Solomakhin27064702018-12-14 21:15:33329 if (!IsInitialized()) {
330 log_.Error("Attempted complete without initialization");
331 OnConnectionTerminated();
mathp4b85b582017-03-08 21:07:16332 return;
Rouslan Solomakhin27064702018-12-14 21:15:33333 }
334
335 if (!IsThisPaymentRequestShowing()) {
336 log_.Error("Attempted complete without show");
337 OnConnectionTerminated();
338 return;
339 }
mathp4b85b582017-03-08 21:07:16340
Rouslan Solomakhine3473192017-06-16 14:54:57341 // Failed transactions show an error. Successful and unknown-state
342 // transactions don't show an error.
343 if (result == mojom::PaymentComplete::FAIL) {
mathp218795892017-03-29 15:15:34344 delegate_->ShowErrorMessage();
345 } else {
sebsgfcdd13c2017-06-08 15:49:33346 DCHECK(!has_recorded_completion_);
sebsgf8272a22017-05-26 14:32:58347 journey_logger_.SetCompleted();
sebsgfcdd13c2017-06-08 15:49:33348 has_recorded_completion_ = true;
349
anthonyvd6a43b932017-05-11 18:39:27350 delegate_->GetPrefService()->SetBoolean(kPaymentsFirstTransactionCompleted,
351 true);
mathp218795892017-03-29 15:15:34352 // When the renderer closes the connection,
353 // PaymentRequest::OnConnectionTerminated will be called.
354 client_->OnComplete();
sebsg8a93b272017-05-11 19:30:22355 state_->RecordUseStats();
mathp218795892017-03-29 15:15:34356 }
mathp4b85b582017-03-08 21:07:16357}
358
359void PaymentRequest::CanMakePayment() {
Rouslan Solomakhin27064702018-12-14 21:15:33360 if (!IsInitialized()) {
361 log_.Error("Attempted canMakePayment without initialization");
362 OnConnectionTerminated();
363 return;
364 }
365
366 // It's valid to call canMakePayment() without calling show() first.
367
gogerald8189d522017-09-15 17:52:18368 if (observer_for_testing_)
369 observer_for_testing_->OnCanMakePaymentCalled();
Mathieu Perreaultcacb85e2018-06-06 20:40:13370
Rouslan Solomakhind2cae95a2018-08-09 00:16:10371 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
372 !state_) {
Mathieu Perreaultcacb85e2018-06-06 20:40:13373 CanMakePaymentCallback(/*can_make_payment=*/false);
374 } else {
Rouslan Solomakhind2cae95a2018-08-09 00:16:10375 state_->CanMakePayment(
Mathieu Perreaultcacb85e2018-06-06 20:40:13376 base::BindOnce(&PaymentRequest::CanMakePaymentCallback,
377 weak_ptr_factory_.GetWeakPtr()));
378 }
gogerald8189d522017-09-15 17:52:18379}
380
Danyao Wangce175bf2018-12-21 22:35:58381void PaymentRequest::HasEnrolledInstrument() {
382 if (!IsInitialized()) {
383 log_.Error("Attempted hasEnrolledInstrument without initialization");
384 OnConnectionTerminated();
385 return;
386 }
387
388 // It's valid to call hasEnrolledInstrument() without calling show() first.
389
390 if (observer_for_testing_)
391 observer_for_testing_->OnHasEnrolledInstrumentCalled();
392
393 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
394 !state_) {
395 HasEnrolledInstrumentCallback(/*has_enrolled_instrument=*/false);
396 } else {
397 state_->HasEnrolledInstrument(
398 base::BindOnce(&PaymentRequest::HasEnrolledInstrumentCallback,
399 weak_ptr_factory_.GetWeakPtr()));
400 }
401}
402
Rouslan Solomakhin27064702018-12-14 21:15:33403void PaymentRequest::AreRequestedMethodsSupportedCallback(
404 bool methods_supported) {
405 if (methods_supported) {
406 if (SatisfiesSkipUIConstraints()) {
407 skipped_payment_request_ui_ = true;
408 Pay();
409 }
410 } else {
411 journey_logger_.SetNotShown(
412 JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
413 client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED);
414 if (observer_for_testing_)
415 observer_for_testing_->OnNotSupportedError();
416 OnConnectionTerminated();
417 }
418}
419
420bool PaymentRequest::IsInitialized() const {
421 return is_initialized_ && client_ && client_.is_bound() &&
422 binding_.is_bound();
423}
424
425bool PaymentRequest::IsThisPaymentRequestShowing() const {
426 return is_show_called_ && display_handle_ && spec_ && state_;
427}
428
429bool PaymentRequest::SatisfiesSkipUIConstraints() const {
430 return base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
431 base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
432 is_show_user_gesture_ && state()->is_get_all_instruments_finished() &&
433 state()->available_instruments().size() == 1 &&
434 spec()->stringified_method_data().size() == 1 &&
435 !spec()->request_shipping() && !spec()->request_payer_name() &&
436 !spec()->request_payer_phone() &&
437 !spec()->request_payer_email()
438 // Only allowing URL base payment apps to skip the payment sheet.
439 && spec()->url_payment_method_identifiers().size() == 1;
440}
441
mathpf1a7a3752017-03-15 11:23:37442void PaymentRequest::OnPaymentResponseAvailable(
443 mojom::PaymentResponsePtr response) {
mathp57c8c862017-06-16 20:15:45444 journey_logger_.SetEventOccurred(
445 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
gogerald7a2b761e2017-11-09 18:30:19446
447 // Do not send invalid response to client.
448 if (response->method_name.empty() || response->stringified_details.empty()) {
449 RecordFirstAbortReason(
450 JourneyLogger::ABORT_REASON_INSTRUMENT_DETAILS_ERROR);
451 delegate_->ShowErrorMessage();
452 return;
453 }
454
mathpf1a7a3752017-03-15 11:23:37455 client_->OnPaymentResponse(std::move(response));
mathp4b85b582017-03-08 21:07:16456}
457
mathp151bd312017-04-03 21:07:24458void PaymentRequest::OnShippingOptionIdSelected(
459 std::string shipping_option_id) {
460 client_->OnShippingOptionChange(shipping_option_id);
461}
462
463void PaymentRequest::OnShippingAddressSelected(
464 mojom::PaymentAddressPtr address) {
465 client_->OnShippingAddressChange(std::move(address));
466}
467
Jinho Bangbb178152018-09-13 09:44:43468void PaymentRequest::OnPayerInfoSelected(mojom::PayerDetailPtr payer_info) {
469 client_->OnPayerDetailChange(std::move(payer_info));
470}
471
mathpf4bc50e2017-01-24 05:17:50472void PaymentRequest::UserCancelled() {
473 // If |client_| is not bound, then the object is already being destroyed as
474 // a result of a renderer event.
475 if (!client_.is_bound())
476 return;
477
sebsgfcdd13c2017-06-08 15:49:33478 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
sebsg20b49d7b2017-05-04 20:23:17479
mathpf4bc50e2017-01-24 05:17:50480 // This sends an error to the renderer, which informs the API user.
rouslan6e3cf7c62017-04-17 21:23:28481 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL);
mathpf4bc50e2017-01-24 05:17:50482
483 // We close all bindings and ask to be destroyed.
484 client_.reset();
485 binding_.Close();
rouslanb28f4532017-05-08 15:41:47486 if (observer_for_testing_)
487 observer_for_testing_->OnConnectionTerminated();
mathpf4bc50e2017-01-24 05:17:50488 manager_->DestroyRequest(this);
mathpf709499d2017-01-09 20:48:36489}
490
sebsgd56b3e422017-10-20 18:08:08491void PaymentRequest::DidStartMainFrameNavigationToDifferentDocument(
492 bool is_user_initiated) {
sebsgfcdd13c2017-06-08 15:49:33493 RecordFirstAbortReason(is_user_initiated
494 ? JourneyLogger::ABORT_REASON_USER_NAVIGATION
495 : JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION);
sebsg2c8558a2017-05-17 18:54:10496}
497
mathpf4bc50e2017-01-24 05:17:50498void PaymentRequest::OnConnectionTerminated() {
499 // We are here because of a browser-side error, or likely as a result of the
500 // connection_error_handler on |binding_|, which can mean that the renderer
501 // has decided to close the pipe for various reasons (see all uses of
502 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close
503 // the binding and the dialog, and ask to be deleted.
504 client_.reset();
mathpf709499d2017-01-09 20:48:36505 binding_.Close();
mathpf4bc50e2017-01-24 05:17:50506 delegate_->CloseDialog();
rouslanb28f4532017-05-08 15:41:47507 if (observer_for_testing_)
508 observer_for_testing_->OnConnectionTerminated();
sebsgfcdd13c2017-06-08 15:49:33509
510 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_MOJO_CONNECTION_ERROR);
mathpf709499d2017-01-09 20:48:36511 manager_->DestroyRequest(this);
512}
513
mathpd4be8de82017-03-01 00:51:48514void PaymentRequest::Pay() {
mathp57c8c862017-06-16 20:15:45515 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
Mathieu Perreault23d25bfb82018-05-11 14:45:37516
517 // Log the correct "selected instrument" metric according to type.
518 DCHECK(state_->selected_instrument());
519 JourneyLogger::Event selected_event =
520 JourneyLogger::Event::EVENT_SELECTED_OTHER;
521 switch (state_->selected_instrument()->type()) {
522 case PaymentInstrument::Type::AUTOFILL:
523 selected_event = JourneyLogger::Event::EVENT_SELECTED_CREDIT_CARD;
524 break;
525 case PaymentInstrument::Type::SERVICE_WORKER_APP:
526 selected_event = JourneyLogger::Event::EVENT_SELECTED_OTHER;
527 break;
528 case PaymentInstrument::Type::NATIVE_MOBILE_APP:
529 NOTREACHED();
530 break;
531 }
532 journey_logger_.SetEventOccurred(selected_event);
533
mathpf1a7a3752017-03-15 11:23:37534 state_->GeneratePaymentResponse();
mathpd4be8de82017-03-01 00:51:48535}
536
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07537void PaymentRequest::HideIfNecessary() {
538 display_handle_.reset();
539}
540
Rouslan Solomakhind2cae95a2018-08-09 00:16:10541void PaymentRequest::RecordDialogShownEventInJourneyLogger() {
542 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
543}
544
Anthony Vallee-Dubois10d131a2018-02-22 15:41:04545bool PaymentRequest::IsIncognito() const {
546 return delegate_->IsIncognito();
547}
548
sebsgfcdd13c2017-06-08 15:49:33549void PaymentRequest::RecordFirstAbortReason(
550 JourneyLogger::AbortReason abort_reason) {
551 if (!has_recorded_completion_) {
552 has_recorded_completion_ = true;
553 journey_logger_.SetAborted(abort_reason);
sebsg2c8558a2017-05-17 18:54:10554 }
555}
556
Rouslan Solomakhin1804ee42017-10-03 14:27:43557void PaymentRequest::CanMakePaymentCallback(bool can_make_payment) {
Rouslan Solomakhind2cae95a2018-08-09 00:16:10558 if (!spec_ || CanMakePaymentQueryFactory::GetInstance()
559 ->GetForContext(web_contents_->GetBrowserContext())
560 ->CanQuery(top_level_origin_, frame_origin_,
561 spec_->stringified_method_data())) {
Rouslan Solomakhin16ee55202017-10-03 19:04:42562 RespondToCanMakePaymentQuery(can_make_payment, false);
Rouslan Solomakhin1804ee42017-10-03 14:27:43563 } else if (OriginSecurityChecker::IsOriginLocalhostOrFile(frame_origin_)) {
Rouslan Solomakhin16ee55202017-10-03 19:04:42564 RespondToCanMakePaymentQuery(can_make_payment, true);
Rouslan Solomakhin1804ee42017-10-03 14:27:43565 } else {
566 client_->OnCanMakePayment(
567 mojom::CanMakePaymentQueryResult::QUERY_QUOTA_EXCEEDED);
568 }
569
570 if (observer_for_testing_)
571 observer_for_testing_->OnCanMakePaymentReturned();
572}
573
Danyao Wangce175bf2018-12-21 22:35:58574void PaymentRequest::HasEnrolledInstrumentCallback(
575 bool has_enrolled_instrument) {
576 if (!spec_ || CanMakePaymentQueryFactory::GetInstance()
577 ->GetForContext(web_contents_->GetBrowserContext())
578 ->CanQuery(top_level_origin_, frame_origin_,
579 spec_->stringified_method_data())) {
580 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
581 /*warn_localhost_or_file=*/false);
582 } else if (OriginSecurityChecker::IsOriginLocalhostOrFile(frame_origin_)) {
583 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
584 /*warn_localhost_or_file=*/true);
585 } else {
586 client_->OnHasEnrolledInstrument(
587 HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED);
588 }
589
590 if (observer_for_testing_)
591 observer_for_testing_->OnHasEnrolledInstrumentReturned();
592}
593
Rouslan Solomakhin16ee55202017-10-03 19:04:42594void PaymentRequest::RespondToCanMakePaymentQuery(bool can_make_payment,
595 bool warn_localhost_or_file) {
Rouslan Solomakhin16ee55202017-10-03 19:04:42596 mojom::CanMakePaymentQueryResult positive =
597 warn_localhost_or_file
598 ? mojom::CanMakePaymentQueryResult::WARNING_CAN_MAKE_PAYMENT
599 : mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT;
600 mojom::CanMakePaymentQueryResult negative =
601 warn_localhost_or_file
602 ? mojom::CanMakePaymentQueryResult::WARNING_CANNOT_MAKE_PAYMENT
603 : mojom::CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT;
604
605 client_->OnCanMakePayment(can_make_payment ? positive : negative);
Rouslan Solomakhin1804ee42017-10-03 14:27:43606 journey_logger_.SetCanMakePaymentValue(can_make_payment);
607}
608
Danyao Wangce175bf2018-12-21 22:35:58609void PaymentRequest::RespondToHasEnrolledInstrumentQuery(
610 bool has_enrolled_instrument,
611 bool warn_localhost_or_file) {
612 HasEnrolledInstrumentQueryResult positive =
613 warn_localhost_or_file
614 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_ENROLLED_INSTRUMENT
615 : HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT;
616 HasEnrolledInstrumentQueryResult negative =
617 warn_localhost_or_file
618 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_NO_ENROLLED_INSTRUMENT
619 : HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT;
620
621 client_->OnHasEnrolledInstrument(has_enrolled_instrument ? positive
622 : negative);
623 journey_logger_.SetCanMakePaymentValue(has_enrolled_instrument);
624}
625
mathpf709499d2017-01-09 20:48:36626} // namespace payments