blob: 5b59b0d25843245f0ffc408a9f9cb0371ef7af5d [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 Solomakhin2039a342020-05-21 19:21:0417#include "components/payments/content/payment_app.h"
Rouslan Solomakhin97238b22020-05-28 00:28:3518#include "components/payments/content/payment_details_converter.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2219#include "components/payments/content/payment_request_converter.h"
rouslan908248c2017-02-27 21:30:2420#include "components/payments/content/payment_request_web_contents_manager.h"
rouslan690997682017-05-09 18:07:3921#include "components/payments/core/can_make_payment_query.h"
Rouslan Solomakhin2dc99c32020-03-24 21:25:0322#include "components/payments/core/error_message_util.h"
Rouslan Solomakhina480efa2019-05-06 15:37:2223#include "components/payments/core/error_strings.h"
Anthony Vallee-Dubois968ae4d2018-03-15 16:56:3624#include "components/payments/core/features.h"
Rouslan Solomakhin85b10da2019-11-05 20:03:1725#include "components/payments/core/method_strings.h"
Rouslan Solomakhineb06b272019-07-10 16:23:3226#include "components/payments/core/native_error_strings.h"
Mohamad Ahmadif5544bb2017-09-01 21:48:2227#include "components/payments/core/payment_details.h"
28#include "components/payments/core/payment_details_validation.h"
anthonyvd6a43b932017-05-11 18:39:2729#include "components/payments/core/payment_prefs.h"
Rouslan Solomakhin4bd19a62020-08-27 17:05:5130#include "components/payments/core/payment_request_delegate.h"
Danyao Wang50ccb9f2019-05-09 23:28:0331#include "components/payments/core/payments_experimental_features.h"
Jinho Bangbe463a22018-08-02 10:26:5032#include "components/payments/core/payments_validators.h"
Rouslan Solomakhin77a7e1a2019-05-23 17:37:5833#include "components/payments/core/url_util.h"
anthonyvd6a43b932017-05-11 18:39:2734#include "components/prefs/pref_service.h"
Steven Holte2083e8bc2018-07-16 23:50:3635#include "components/ukm/content/source_url_recorder.h"
Rouslan Solomakhin6e979ab2017-08-30 17:30:3936#include "components/url_formatter/elide_url.h"
mathpf709499d2017-01-09 20:48:3637#include "content/public/browser/browser_thread.h"
rouslan690997682017-05-09 18:07:3938#include "content/public/browser/render_frame_host.h"
Danyao Wang15840792020-05-07 23:52:1739#include "content/public/browser/render_process_host.h"
mathpf709499d2017-01-09 20:48:3640#include "content/public/browser/web_contents.h"
Rouslan Solomakhin1804ee42017-10-03 14:27:4341#include "content/public/common/content_features.h"
Sahel Sharify54f5e862020-03-26 20:02:4142#include "services/metrics/public/cpp/ukm_source_id.h"
Minggang Wangd111b3c82020-09-08 02:13:1943#include "third_party/blink/public/common/loader/network_utils.h"
mathpf709499d2017-01-09 20:48:3644
Rouslan Solomakhina480efa2019-05-06 15:37:2245namespace payments {
Danyao Wangce175bf2018-12-21 22:35:5846namespace {
47
Danyao Wang57aa0442019-01-31 04:06:4148using ::payments::mojom::CanMakePaymentQueryResult;
Danyao Wangce175bf2018-12-21 22:35:5849using ::payments::mojom::HasEnrolledInstrumentQueryResult;
50
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:3951bool IsGooglePaymentMethod(const std::string& method_name) {
Rouslan Solomakhin85b10da2019-11-05 20:03:1752 return method_name == methods::kGooglePay ||
53 method_name == methods::kAndroidPay;
Sahel Sharify26884382019-05-07 16:23:5154}
55
Sahel Sharify9d98a502019-09-30 19:58:3956// Redact shipping address before exposing it in ShippingAddressChangeEvent.
57// https://w3c.github.io/payment-request/#shipping-address-changed-algorithm
58mojom::PaymentAddressPtr RedactShippingAddress(
59 mojom::PaymentAddressPtr address) {
60 DCHECK(address);
61 if (!PaymentsExperimentalFeatures::IsEnabled(
62 features::kWebPaymentsRedactShippingAddress)) {
63 return address;
64 }
65 address->organization.clear();
66 address->phone.clear();
67 address->recipient.clear();
68 address->address_line.clear();
69 return address;
70}
71
Rouslan Solomakhina480efa2019-05-06 15:37:2272} // namespace
mathpf709499d2017-01-09 20:48:3673
74PaymentRequest::PaymentRequest(
rouslan690997682017-05-09 18:07:3975 content::RenderFrameHost* render_frame_host,
mathpf709499d2017-01-09 20:48:3676 content::WebContents* web_contents,
Rouslan Solomakhin4eea9bc22017-10-10 15:18:5177 std::unique_ptr<ContentPaymentRequestDelegate> delegate,
mathpf709499d2017-01-09 20:48:3678 PaymentRequestWebContentsManager* manager,
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0779 PaymentRequestDisplayManager* display_manager,
Gyuyoung Kim9cfbda32019-08-27 02:15:1880 mojo::PendingReceiver<mojom::PaymentRequest> receiver,
mathp300fa542017-03-27 19:29:3781 ObserverForTest* observer_for_testing)
mathpf709499d2017-01-09 20:48:3682 : web_contents_(web_contents),
Danyao Wang15840792020-05-07 23:52:1783 // TODO(crbug.com/1058840): change to WeakPtr<RenderFrameHost> once
84 // RenderFrameHost provides a WeakPtr API.
85 initiator_frame_routing_id_(content::GlobalFrameRoutingId(
86 render_frame_host->GetProcess()->GetID(),
87 render_frame_host->GetRoutingID())),
Rouslan Solomakhin27064702018-12-14 21:15:3388 log_(web_contents_),
mathpf709499d2017-01-09 20:48:3689 delegate_(std::move(delegate)),
90 manager_(manager),
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:0791 display_manager_(display_manager),
92 display_handle_(nullptr),
Rouslan Solomakhin6e979ab2017-08-30 17:30:3993 top_level_origin_(url_formatter::FormatUrlForSecurityDisplay(
94 web_contents_->GetLastCommittedURL())),
95 frame_origin_(url_formatter::FormatUrlForSecurityDisplay(
96 render_frame_host->GetLastCommittedURL())),
Rouslan Solomakhinc71cca622020-01-31 22:15:3597 frame_security_origin_(render_frame_host->GetLastCommittedOrigin()),
sebsg20b49d7b2017-05-04 20:23:1798 observer_for_testing_(observer_for_testing),
Ramin Halavatifbfcef672020-05-21 16:33:3499 journey_logger_(delegate_->IsOffTheRecord(),
Jeremy Roman5c341f6d2019-07-15 15:56:10100 ukm::GetSourceIdForWebContentsDocument(web_contents)) {
Gyuyoung Kim9cfbda32019-08-27 02:15:18101 receiver_.Bind(std::move(receiver));
mathpf4bc50e2017-01-24 05:17:50102 // OnConnectionTerminated will be called when the Mojo pipe is closed. This
103 // will happen as a result of many renderer-side events (both successful and
104 // erroneous in nature).
105 // TODO(crbug.com/683636): Investigate using
106 // set_connection_error_with_reason_handler with Binding::CloseWithReason.
Gyuyoung Kim9cfbda32019-08-27 02:15:18107 receiver_.set_disconnect_handler(base::BindOnce(
Anthony Vallee-Duboisdc1dbf1a2017-07-17 15:01:13108 &PaymentRequest::OnConnectionTerminated, weak_ptr_factory_.GetWeakPtr()));
DongJun Kim54690d52020-09-26 12:07:54109
110 payment_handler_host_ = std::make_unique<PaymentHandlerHost>(
111 web_contents_, weak_ptr_factory_.GetWeakPtr());
mathpf709499d2017-01-09 20:48:36112}
113
DongJun Kim54690d52020-09-26 12:07:54114PaymentRequest::~PaymentRequest() = default;
mathpf709499d2017-01-09 20:48:36115
Gyuyoung Kim9cfbda32019-08-27 02:15:18116void PaymentRequest::Init(
117 mojo::PendingRemote<mojom::PaymentRequestClient> client,
118 std::vector<mojom::PaymentMethodDataPtr> method_data,
119 mojom::PaymentDetailsPtr details,
120 mojom::PaymentOptionsPtr options) {
mathpf709499d2017-01-09 20:48:36121 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin27064702018-12-14 21:15:33122
123 if (is_initialized_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22124 log_.Error(errors::kAttemptedInitializationTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33125 OnConnectionTerminated();
126 return;
127 }
128
Sahel Sharify9306fe92020-08-18 20:04:58129 journey_logger_.RecordCheckoutStep(
130 JourneyLogger::CheckoutFunnelStep::kInitiated);
Rouslan Solomakhin27064702018-12-14 21:15:33131 is_initialized_ = true;
Gyuyoung Kim9cfbda32019-08-27 02:15:18132 client_.Bind(std::move(client));
rouslan6e3cf7c62017-04-17 21:23:28133
rouslanb28f4532017-05-08 15:41:47134 const GURL last_committed_url = delegate_->GetLastCommittedURL();
Minggang Wangd111b3c82020-09-08 02:13:19135 if (!blink::network_utils::IsOriginSecure(last_committed_url)) {
Rouslan Solomakhina480efa2019-05-06 15:37:22136 log_.Error(errors::kNotInASecureOrigin);
rouslan6e3cf7c62017-04-17 21:23:28137 OnConnectionTerminated();
138 return;
139 }
140
rouslanb28f4532017-05-08 15:41:47141 bool allowed_origin =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58142 UrlUtil::IsOriginAllowedToUseWebPaymentApis(last_committed_url);
rouslanb28f4532017-05-08 15:41:47143 if (!allowed_origin) {
Rouslan Solomakhin68429b72019-06-27 15:12:39144 reject_show_error_message_ = errors::kProhibitedOrigin;
rouslanb28f4532017-05-08 15:41:47145 }
146
Rouslan Solomakhin55db8272019-06-25 18:16:28147 bool invalid_ssl = false;
148 if (last_committed_url.SchemeIsCryptographic()) {
Rouslan Solomakhin68429b72019-06-27 15:12:39149 DCHECK(reject_show_error_message_.empty());
150 reject_show_error_message_ =
Rouslan Solomakhin55db8272019-06-25 18:16:28151 delegate_->GetInvalidSslCertificateErrorMessage();
Rouslan Solomakhin68429b72019-06-27 15:12:39152 invalid_ssl = !reject_show_error_message_.empty();
Rouslan Solomakhin27064702018-12-14 21:15:33153 }
rouslanb28f4532017-05-08 15:41:47154
155 if (!allowed_origin || invalid_ssl) {
Rouslan Solomakhin27064702018-12-14 21:15:33156 // Intentionally don't set |spec_| and |state_|, so the UI is never shown.
Rouslan Solomakhin68429b72019-06-27 15:12:39157 log_.Error(reject_show_error_message_);
Rouslan Solomakhina480efa2019-05-06 15:37:22158 log_.Error(errors::kProhibitedOriginOrInvalidSslExplanation);
Liquan (Max) Gu47d28352020-09-08 20:31:23159 client_->OnError(
160 mojom::PaymentErrorReason::NOT_SUPPORTED_FOR_INVALID_ORIGIN_OR_SSL,
161 reject_show_error_message_);
162 OnConnectionTerminated();
rouslan6e3cf7c62017-04-17 21:23:28163 return;
164 }
165
mathpf709499d2017-01-09 20:48:36166 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22167 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33168 log_.Error(error);
mathpf4bc50e2017-01-24 05:17:50169 OnConnectionTerminated();
mathpf709499d2017-01-09 20:48:36170 return;
171 }
rouslan6e3cf7c62017-04-17 21:23:28172
jinho.bangfcb5ec92017-03-29 08:08:02173 if (!details->total) {
Rouslan Solomakhina480efa2019-05-06 15:37:22174 log_.Error(errors::kTotalRequired);
jinho.bangfcb5ec92017-03-29 08:08:02175 OnConnectionTerminated();
176 return;
177 }
rouslan6e3cf7c62017-04-17 21:23:28178
Danyao Wang15840792020-05-07 23:52:17179 // TODO(crbug.com/1058840): change to WeakPtr<RenderFrameHost> once
180 // RenderFrameHost provides a WeakPtr API.
181 content::RenderFrameHost* initiator_frame =
182 content::RenderFrameHost::FromID(initiator_frame_routing_id_);
183 if (!initiator_frame) {
184 log_.Error(errors::kInvalidInitiatorFrame);
185 OnConnectionTerminated();
186 return;
187 }
188
sebsga70a6da2017-12-21 22:27:02189 spec_ = std::make_unique<PaymentRequestSpec>(
Rouslan Solomakhin1f95f092019-08-09 12:28:51190 std::move(options), std::move(details), std::move(method_data),
DongJun Kim54690d52020-09-26 12:07:54191 /*observer=*/weak_ptr_factory_.GetWeakPtr(),
192 delegate_->GetApplicationLocale());
sebsga70a6da2017-12-21 22:27:02193 state_ = std::make_unique<PaymentRequestState>(
Danyao Wang15840792020-05-07 23:52:17194 web_contents_, initiator_frame, top_level_origin_, frame_origin_,
DongJun Kim54690d52020-09-26 12:07:54195 frame_security_origin_, spec(),
Kyungsun Leecdbdade2020-08-25 18:06:12196 /*delegate=*/weak_ptr_factory_.GetWeakPtr(),
197 delegate_->GetApplicationLocale(), delegate_->GetPersonalDataManager(),
198 delegate_.get(), &journey_logger_);
Mathieu Perreault627b97c2017-08-12 00:44:22199
200 journey_logger_.SetRequestedInformation(
201 spec_->request_shipping(), spec_->request_payer_email(),
202 spec_->request_payer_phone(), spec_->request_payer_name());
203
204 // Log metrics around which payment methods are requested by the merchant.
Rouslan Solomakhin85b10da2019-11-05 20:03:17205 GURL google_pay_url(methods::kGooglePay);
206 GURL android_pay_url(methods::kAndroidPay);
Mathieu Perreault627b97c2017-08-12 00:44:22207 // Looking for payment methods that are NOT google-related payment methods.
208 auto non_google_it =
209 std::find_if(spec_->url_payment_method_identifiers().begin(),
210 spec_->url_payment_method_identifiers().end(),
211 [google_pay_url, android_pay_url](const GURL& url) {
212 return url != google_pay_url && url != android_pay_url;
213 });
214 journey_logger_.SetRequestedPaymentMethodTypes(
215 /*requested_basic_card=*/!spec_->supported_card_networks().empty(),
216 /*requested_method_google=*/
Jan Wilken Dörrie45d34f42019-06-08 09:40:54217 base::Contains(spec_->url_payment_method_identifiers(), google_pay_url) ||
218 base::Contains(spec_->url_payment_method_identifiers(),
219 android_pay_url),
Sahel Sharifyc8b2e0d2020-08-19 20:27:22220 /*requested_method_secure_payment_confirmation=*/
221 spec_->IsSecurePaymentConfirmationRequested(),
Mathieu Perreault627b97c2017-08-12 00:44:22222 /*requested_method_other=*/non_google_it !=
223 spec_->url_payment_method_identifiers().end());
Rouslan Solomakhin1f95f092019-08-09 12:28:51224
DongJun Kim54690d52020-09-26 12:07:54225 payment_handler_host_->set_payment_request_id_for_logs(*spec_->details().id);
Rouslan Solomakhin4bd19a62020-08-27 17:05:51226
227 if (spec_->IsSecurePaymentConfirmationRequested()) {
228 delegate_->set_dialog_type(
229 PaymentRequestDelegate::DialogType::SECURE_PAYMENT_CONFIRMATION);
230 }
mathpf709499d2017-01-09 20:48:36231}
232
Rouslan Solomakhin9788d4b2019-04-09 13:10:23233void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
Rouslan Solomakhin27064702018-12-14 21:15:33234 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22235 log_.Error(errors::kCannotShowWithoutInit);
mathpf4bc50e2017-01-24 05:17:50236 OnConnectionTerminated();
tmartino8ce922852017-01-09 22:23:10237 return;
238 }
rouslan6e3cf7c62017-04-17 21:23:28239
Rouslan Solomakhin27064702018-12-14 21:15:33240 if (is_show_called_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22241 log_.Error(errors::kCannotShowTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33242 OnConnectionTerminated();
243 return;
244 }
245
Sahel Sharify9306fe92020-08-18 20:04:58246 journey_logger_.RecordCheckoutStep(
247 JourneyLogger::CheckoutFunnelStep::kShowCalled);
Rouslan Solomakhin27064702018-12-14 21:15:33248 is_show_called_ = true;
Sahel Sharify0fadf4da2019-08-09 14:55:58249 journey_logger_.SetTriggerTime();
Rouslan Solomakhin27064702018-12-14 21:15:33250
rouslan7d433cc22017-05-08 15:18:07251 // A tab can display only one PaymentRequest UI at a time.
Anthony Vallee-Dubois8f5e7e12018-01-12 16:14:06252 display_handle_ = display_manager_->TryShow(delegate_.get());
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07253 if (!display_handle_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22254 log_.Error(errors::kAnotherUiShowing);
Sahel Sharifya50fc4c2019-05-28 14:53:22255 DCHECK(!has_recorded_completion_);
256 has_recorded_completion_ = true;
sebsg828269bc2017-06-09 19:11:12257 journey_logger_.SetNotShown(
258 JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03259 client_->OnError(mojom::PaymentErrorReason::ALREADY_SHOWING,
260 errors::kAnotherUiShowing);
rouslan7d433cc22017-05-08 15:18:07261 OnConnectionTerminated();
262 return;
263 }
264
Rouslan Solomakhin5b510432017-09-26 16:59:32265 if (!delegate_->IsBrowserWindowActive()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22266 log_.Error(errors::kCannotShowInBackgroundTab);
Sahel Sharifya50fc4c2019-05-28 14:53:22267 DCHECK(!has_recorded_completion_);
268 has_recorded_completion_ = true;
Rouslan Solomakhin5b510432017-09-26 16:59:32269 journey_logger_.SetNotShown(JourneyLogger::NOT_SHOWN_REASON_OTHER);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03270 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
271 errors::kCannotShowInBackgroundTab);
Rouslan Solomakhin5b510432017-09-26 16:59:32272 OnConnectionTerminated();
273 return;
274 }
275
Rouslan Solomakhin833f8512018-04-03 23:19:25276 is_show_user_gesture_ = is_user_gesture;
277
Rouslan Solomakhin9788d4b2019-04-09 13:10:23278 if (wait_for_updated_details) {
279 // Put |spec_| into uninitialized state, so the UI knows to show a spinner.
280 // This method does not block.
281 spec_->StartWaitingForUpdateWith(
282 PaymentRequestSpec::UpdateReason::INITIAL_PAYMENT_DETAILS);
Rouslan Solomakhin6b115fb2020-10-07 21:05:18283 spec_->AddInitializationObserver(this);
Sahel Sharify98a2c2a2019-07-12 18:57:40284 } else {
285 DCHECK(spec_->details().total);
286 journey_logger_.RecordTransactionAmount(
287 spec_->details().total->amount->currency,
288 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23289 }
Takashi Sakamoto48a29702019-04-08 05:06:32290
DongJun Kim54690d52020-09-26 12:07:54291 display_handle_->Show(weak_ptr_factory_.GetWeakPtr());
Rouslan Solomakhin1dca2a922019-09-06 22:25:07292
293 state_->set_is_show_user_gesture(is_show_user_gesture_);
gogerald0a7ee6c2017-11-13 18:23:19294 state_->AreRequestedMethodsSupported(
295 base::BindOnce(&PaymentRequest::AreRequestedMethodsSupportedCallback,
296 weak_ptr_factory_.GetWeakPtr()));
297}
298
Jinho Bangbe463a22018-08-02 10:26:50299void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
Rouslan Solomakhin27064702018-12-14 21:15:33300 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22301 log_.Error(errors::kCannotRetryWithoutInit);
Jinho Bangcac8d9a02018-08-23 19:47:22302 OnConnectionTerminated();
303 return;
304 }
305
Rouslan Solomakhin27064702018-12-14 21:15:33306 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22307 log_.Error(errors::kCannotRetryWithoutShow);
Jinho Bangcac8d9a02018-08-23 19:47:22308 OnConnectionTerminated();
309 return;
310 }
311
Jinho Bangbe463a22018-08-02 10:26:50312 std::string error;
313 if (!PaymentsValidators::IsValidPaymentValidationErrorsFormat(errors,
314 &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33315 log_.Error(error);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03316 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL, error);
Jinho Bangbe463a22018-08-02 10:26:50317 OnConnectionTerminated();
318 return;
319 }
320
Sahel Sharifycf4e2132020-01-24 23:59:08321 state()->SetAvailablePaymentAppForRetry();
Jinho Bang092e7162018-09-06 23:41:19322 spec()->Retry(std::move(errors));
Jinho Bangcac8d9a02018-08-23 19:47:22323 display_handle_->Retry();
Jinho Bangbe463a22018-08-02 10:26:50324}
325
mathp151bd312017-04-03 21:07:24326void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
Rouslan Solomakhin27064702018-12-14 21:15:33327 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22328 log_.Error(errors::kCannotUpdateWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33329 OnConnectionTerminated();
330 return;
331 }
332
333 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22334 log_.Error(errors::kCannotUpdateWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33335 OnConnectionTerminated();
336 return;
337 }
338
mathp151bd312017-04-03 21:07:24339 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22340 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33341 log_.Error(error);
mathp151bd312017-04-03 21:07:24342 OnConnectionTerminated();
343 return;
344 }
Rouslan Solomakhin4cbda822017-08-23 18:50:39345
Jinho Bang092e7162018-09-06 23:41:19346 if (details->shipping_address_errors &&
347 !PaymentsValidators::IsValidAddressErrorsFormat(
348 details->shipping_address_errors, &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33349 log_.Error(error);
Jinho Bang092e7162018-09-06 23:41:19350 OnConnectionTerminated();
351 return;
352 }
353
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39354 if (state()->selected_app() && state()->IsPaymentAppInvoked() &&
Rouslan Solomakhin218f7072020-05-21 14:26:47355 state()->selected_app()->IsWaitingForPaymentDetailsUpdate()) {
Rouslan Solomakhin97238b22020-05-28 00:28:35356 state()->selected_app()->UpdateWith(
357 PaymentDetailsConverter::ConvertToPaymentRequestDetailsUpdate(
358 details, state()->selected_app()->HandlesShippingAddress(),
359 base::BindRepeating(&PaymentApp::IsValidForPaymentMethodIdentifier,
360 state()->selected_app()->AsWeakPtr())));
Rouslan Solomakhina480efa2019-05-06 15:37:22361 }
362
Rouslan Solomakhin6ba46fd2019-04-11 23:44:01363 bool is_resolving_promise_passed_into_show_method = !spec_->IsInitialized();
364
mathp151bd312017-04-03 21:07:24365 spec_->UpdateWith(std::move(details));
Rouslan Solomakhin9788d4b2019-04-09 13:10:23366
367 if (is_resolving_promise_passed_into_show_method) {
Sahel Sharify98a2c2a2019-07-12 18:57:40368 DCHECK(spec_->details().total);
369 journey_logger_.RecordTransactionAmount(
370 spec_->details().total->amount->currency,
371 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23372 if (SatisfiesSkipUIConstraints()) {
Rouslan Solomakhin9788d4b2019-04-09 13:10:23373 Pay();
374 } else if (spec_->request_shipping()) {
375 state_->SelectDefaultShippingAddressAndNotifyObservers();
376 }
377 }
mathp151bd312017-04-03 21:07:24378}
379
Sahel Sharifye2f889542019-12-02 22:17:48380void PaymentRequest::OnPaymentDetailsNotUpdated() {
Rouslan Solomakhin27064702018-12-14 21:15:33381 // This Mojo call is triggered by the user of the API doing nothing in
382 // response to a shipping address update event, so the error messages cannot
383 // be more verbose.
384 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22385 log_.Error(errors::kNotInitialized);
Rouslan Solomakhin27064702018-12-14 21:15:33386 OnConnectionTerminated();
387 return;
388 }
389
390 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22391 log_.Error(errors::kNotShown);
Rouslan Solomakhin27064702018-12-14 21:15:33392 OnConnectionTerminated();
393 return;
394 }
395
Rouslan Solomakhina9ff9282017-10-31 21:58:05396 spec_->RecomputeSpecForDetails();
Rouslan Solomakhina480efa2019-05-06 15:37:22397
Rouslan Solomakhin218f7072020-05-21 14:26:47398 if (state()->IsPaymentAppInvoked() && state()->selected_app() &&
399 state()->selected_app()->IsWaitingForPaymentDetailsUpdate()) {
Rouslan Solomakhin2039a342020-05-21 19:21:04400 state()->selected_app()->OnPaymentDetailsNotUpdated();
Rouslan Solomakhincf9093f2019-05-20 15:32:17401 }
Rouslan Solomakhina9ff9282017-10-31 21:58:05402}
403
mathpf4bc50e2017-01-24 05:17:50404void PaymentRequest::Abort() {
Rouslan Solomakhin27064702018-12-14 21:15:33405 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22406 log_.Error(errors::kCannotAbortWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33407 OnConnectionTerminated();
408 return;
409 }
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 }
416
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56417 // The API user has decided to abort. If a successful abort message is
418 // returned to the renderer, the Mojo message pipe is closed, which triggers
mathpf4bc50e2017-01-24 05:17:50419 // PaymentRequest::OnConnectionTerminated, which destroys this object.
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56420 // Otherwise, the abort promise is rejected and the pipe is not closed.
421 // The abort is only successful if the payment app wasn't yet invoked.
422 // TODO(crbug.com/716546): Add a merchant abort metric
423
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56424 if (observer_for_testing_)
425 observer_for_testing_->OnAbortCalled();
Sahel Sharifyd3418222020-02-19 21:50:02426
Rouslan Solomakhin13947ae32020-05-22 15:34:29427 if (!state_->IsPaymentAppInvoked() || !state_->selected_app()) {
428 OnAbortResult(/*aborted=*/true);
429 return;
430 }
431
432 state_->selected_app()->AbortPaymentApp(base::BindOnce(
433 &PaymentRequest::OnAbortResult, weak_ptr_factory_.GetWeakPtr()));
mathpf4bc50e2017-01-24 05:17:50434}
435
mathp218795892017-03-29 15:15:34436void PaymentRequest::Complete(mojom::PaymentComplete result) {
Rouslan Solomakhin27064702018-12-14 21:15:33437 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22438 log_.Error(errors::kCannotCompleteWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33439 OnConnectionTerminated();
mathp4b85b582017-03-08 21:07:16440 return;
Rouslan Solomakhin27064702018-12-14 21:15:33441 }
442
443 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22444 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33445 OnConnectionTerminated();
446 return;
447 }
mathp4b85b582017-03-08 21:07:16448
Sahel Sharify2a56b5f2019-11-28 19:27:19449 if (observer_for_testing_) {
450 observer_for_testing_->OnCompleteCalled();
451 }
452
Rouslan Solomakhine3473192017-06-16 14:54:57453 // Failed transactions show an error. Successful and unknown-state
454 // transactions don't show an error.
455 if (result == mojom::PaymentComplete::FAIL) {
mathp218795892017-03-29 15:15:34456 delegate_->ShowErrorMessage();
457 } else {
sebsgfcdd13c2017-06-08 15:49:33458 DCHECK(!has_recorded_completion_);
sebsgf8272a22017-05-26 14:32:58459 journey_logger_.SetCompleted();
sebsgfcdd13c2017-06-08 15:49:33460 has_recorded_completion_ = true;
Sahel Sharify98a2c2a2019-07-12 18:57:40461 DCHECK(spec_->details().total);
462 journey_logger_.RecordTransactionAmount(
463 spec_->details().total->amount->currency,
464 spec_->details().total->amount->value, true /*completed*/);
sebsgfcdd13c2017-06-08 15:49:33465
anthonyvd6a43b932017-05-11 18:39:27466 delegate_->GetPrefService()->SetBoolean(kPaymentsFirstTransactionCompleted,
467 true);
mathp218795892017-03-29 15:15:34468 // When the renderer closes the connection,
469 // PaymentRequest::OnConnectionTerminated will be called.
470 client_->OnComplete();
sebsg8a93b272017-05-11 19:30:22471 state_->RecordUseStats();
mathp218795892017-03-29 15:15:34472 }
mathp4b85b582017-03-08 21:07:16473}
474
Danyao Wang03a4cbd2019-08-15 23:47:11475void PaymentRequest::CanMakePayment() {
Rouslan Solomakhin27064702018-12-14 21:15:33476 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22477 log_.Error(errors::kCannotCallCanMakePaymentWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33478 OnConnectionTerminated();
479 return;
480 }
481
482 // It's valid to call canMakePayment() without calling show() first.
483
gogerald8189d522017-09-15 17:52:18484 if (observer_for_testing_)
485 observer_for_testing_->OnCanMakePaymentCalled();
Mathieu Perreaultcacb85e2018-06-06 20:40:13486
Liquan (Max) Gu47d28352020-09-08 20:31:23487 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled)) {
Danyao Wang03a4cbd2019-08-15 23:47:11488 CanMakePaymentCallback(/*can_make_payment=*/false);
Mathieu Perreaultcacb85e2018-06-06 20:40:13489 } else {
Rouslan Solomakhind2cae95a2018-08-09 00:16:10490 state_->CanMakePayment(
Mathieu Perreaultcacb85e2018-06-06 20:40:13491 base::BindOnce(&PaymentRequest::CanMakePaymentCallback,
Danyao Wang03a4cbd2019-08-15 23:47:11492 weak_ptr_factory_.GetWeakPtr()));
Mathieu Perreaultcacb85e2018-06-06 20:40:13493 }
gogerald8189d522017-09-15 17:52:18494}
495
Dongjun Kim0a7565b2020-08-18 09:39:46496void PaymentRequest::HasEnrolledInstrument() {
Danyao Wangce175bf2018-12-21 22:35:58497 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22498 log_.Error(errors::kCannotCallHasEnrolledInstrumentWithoutInit);
Danyao Wangce175bf2018-12-21 22:35:58499 OnConnectionTerminated();
500 return;
501 }
502
503 // It's valid to call hasEnrolledInstrument() without calling show() first.
504
505 if (observer_for_testing_)
506 observer_for_testing_->OnHasEnrolledInstrumentCalled();
507
Liquan (Max) Gu47d28352020-09-08 20:31:23508 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled)) {
Dongjun Kim0a7565b2020-08-18 09:39:46509 HasEnrolledInstrumentCallback(/*has_enrolled_instrument=*/false);
Danyao Wangce175bf2018-12-21 22:35:58510 } else {
511 state_->HasEnrolledInstrument(
512 base::BindOnce(&PaymentRequest::HasEnrolledInstrumentCallback,
Dongjun Kim0a7565b2020-08-18 09:39:46513 weak_ptr_factory_.GetWeakPtr()));
Danyao Wangce175bf2018-12-21 22:35:58514 }
515}
516
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02517bool PaymentRequest::ChangePaymentMethod(const std::string& method_name,
518 const std::string& stringified_data) {
Rouslan Solomakhina480efa2019-05-06 15:37:22519 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02520 DCHECK(!method_name.empty());
Rouslan Solomakhina480efa2019-05-06 15:37:22521
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02522 if (!state_ || !state_->IsPaymentAppInvoked() || !client_)
523 return false;
Rouslan Solomakhina480efa2019-05-06 15:37:22524
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02525 client_->OnPaymentMethodChange(method_name, stringified_data);
526 return true;
Rouslan Solomakhina480efa2019-05-06 15:37:22527}
528
Sahel Sharify9d98a502019-09-30 19:58:39529bool PaymentRequest::ChangeShippingOption(
530 const std::string& shipping_option_id) {
531 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
532 DCHECK(!shipping_option_id.empty());
533
534 bool is_valid_id = false;
535 if (spec_->details().shipping_options) {
536 for (const auto& option : spec_->GetShippingOptions()) {
537 if (option->id == shipping_option_id) {
538 is_valid_id = true;
539 break;
540 }
541 }
542 }
543
544 if (!state_ || !state_->IsPaymentAppInvoked() || !client_ || !spec_ ||
545 !spec_->request_shipping() || !is_valid_id) {
546 return false;
547 }
548
549 client_->OnShippingOptionChange(shipping_option_id);
550 return true;
551}
552
553bool PaymentRequest::ChangeShippingAddress(
554 mojom::PaymentAddressPtr shipping_address) {
555 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
556 DCHECK(shipping_address);
557
558 if (!state_ || !state_->IsPaymentAppInvoked() || !client_ || !spec_ ||
559 !spec_->request_shipping()) {
560 return false;
561 }
562
563 client_->OnShippingAddressChange(
564 RedactShippingAddress(std::move(shipping_address)));
565 return true;
566}
567
Rouslan Solomakhin27064702018-12-14 21:15:33568void PaymentRequest::AreRequestedMethodsSupportedCallback(
Rouslan Solomakhind5dcc322019-07-11 21:47:20569 bool methods_supported,
570 const std::string& error_message) {
Rouslan Solomakhin6b115fb2020-10-07 21:05:18571 if (is_show_called_ && spec_ && spec_->IsInitialized() &&
572 observer_for_testing_) {
Sahel Sharifyfd677ff2020-07-08 19:01:52573 observer_for_testing_->OnAppListReady(weak_ptr_factory_.GetWeakPtr());
Rouslan Solomakhin6b115fb2020-10-07 21:05:18574 }
Danyao Wang25f72dc2019-10-18 05:11:32575
Rouslan Solomakhin27064702018-12-14 21:15:33576 if (methods_supported) {
Sahel Sharifyd3f1bc82019-05-21 18:48:46577 if (SatisfiesSkipUIConstraints())
Rouslan Solomakhin27064702018-12-14 21:15:33578 Pay();
Rouslan Solomakhin27064702018-12-14 21:15:33579 } else {
Sahel Sharifya50fc4c2019-05-28 14:53:22580 DCHECK(!has_recorded_completion_);
581 has_recorded_completion_ = true;
Rouslan Solomakhin27064702018-12-14 21:15:33582 journey_logger_.SetNotShown(
583 JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03584 client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED,
Rouslan Solomakhin2dc99c32020-03-24 21:25:03585 GetNotSupportedErrorMessage(
586 spec_ ? spec_->payment_method_identifiers_set()
587 : std::set<std::string>()) +
Rouslan Solomakhind5dcc322019-07-11 21:47:20588 (error_message.empty() ? "" : " " + error_message));
Rouslan Solomakhin27064702018-12-14 21:15:33589 if (observer_for_testing_)
590 observer_for_testing_->OnNotSupportedError();
591 OnConnectionTerminated();
592 }
593}
594
Rouslan Solomakhine37ecbd02020-07-29 20:35:42595base::WeakPtr<PaymentRequest> PaymentRequest::GetWeakPtr() {
596 return weak_ptr_factory_.GetWeakPtr();
597}
598
Rouslan Solomakhin6b115fb2020-10-07 21:05:18599void PaymentRequest::OnInitialized(InitializationTask* initialization_task) {
600 DCHECK_EQ(spec_.get(), initialization_task);
601 DCHECK_EQ(PaymentRequestSpec::UpdateReason::INITIAL_PAYMENT_DETAILS,
602 spec_->current_update_reason());
603 if (is_show_called_ && state_ && state_->is_get_all_apps_finished() &&
604 observer_for_testing_) {
605 observer_for_testing_->OnAppListReady(weak_ptr_factory_.GetWeakPtr());
606 }
607}
608
Rouslan Solomakhin27064702018-12-14 21:15:33609bool PaymentRequest::IsInitialized() const {
610 return is_initialized_ && client_ && client_.is_bound() &&
Liquan (Max) Gu47d28352020-09-08 20:31:23611 receiver_.is_bound() && state_ && spec_;
Rouslan Solomakhin27064702018-12-14 21:15:33612}
613
614bool PaymentRequest::IsThisPaymentRequestShowing() const {
615 return is_show_called_ && display_handle_ && spec_ && state_;
616}
617
Sahel Sharify9120fd32019-12-05 17:16:11618bool PaymentRequest::OnlySingleAppCanProvideAllRequiredInformation() const {
619 DCHECK(state()->IsInitialized());
620 DCHECK(spec()->IsInitialized());
621
622 if (!spec()->request_shipping() && !spec()->request_payer_name() &&
623 !spec()->request_payer_phone() && !spec()->request_payer_email()) {
624 return state()->available_apps().size() == 1 &&
625 state()->available_apps().at(0)->type() !=
626 PaymentApp::Type::AUTOFILL;
627 }
628
629 bool an_app_can_provide_all_info = false;
630 for (const auto& app : state()->available_apps()) {
631 if ((!spec()->request_shipping() || app->HandlesShippingAddress()) &&
632 (!spec()->request_payer_name() || app->HandlesPayerName()) &&
633 (!spec()->request_payer_phone() || app->HandlesPayerPhone()) &&
634 (!spec()->request_payer_email() || app->HandlesPayerEmail())) {
635 // There is another available app that can provide all merchant requested
636 // information information.
637 if (an_app_can_provide_all_info)
638 return false;
639
640 an_app_can_provide_all_info = true;
641 }
642 }
643 return an_app_can_provide_all_info;
644}
645
Sahel Sharifyd3f1bc82019-05-21 18:48:46646bool PaymentRequest::SatisfiesSkipUIConstraints() {
Sahel Sharify44a00992020-08-20 17:49:13647 // Only allowing URL based payment apps to skip the payment sheet.
Sahel Sharifyd3f1bc82019-05-21 18:48:46648 skipped_payment_request_ui_ =
Sahel Sharify44a00992020-08-20 17:49:13649 !spec()->IsSecurePaymentConfirmationRequested() &&
Sahel Sharify9120fd32019-12-05 17:16:11650 (spec()->url_payment_method_identifiers().size() > 0 ||
danakjdca06902019-06-27 21:41:41651 delegate_->SkipUiForBasicCard()) &&
Sahel Sharifyd3f1bc82019-05-21 18:48:46652 base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
653 base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
654 is_show_user_gesture_ && state()->IsInitialized() &&
Sahel Sharify9120fd32019-12-05 17:16:11655 spec()->IsInitialized() &&
656 OnlySingleAppCanProvideAllRequiredInformation() &&
Sahel Sharify90307872019-12-02 17:28:14657 // The available app should be preselectable.
Sahel Sharify9120fd32019-12-05 17:16:11658 state()->selected_app() != nullptr;
Sahel Sharifyd3f1bc82019-05-21 18:48:46659 if (skipped_payment_request_ui_) {
660 DCHECK(state()->IsInitialized() && spec()->IsInitialized());
661 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SKIPPED_SHOW);
662 } else if (state()->IsInitialized() && spec()->IsInitialized()) {
663 // Set EVENT_SHOWN only after state() and spec() initialization.
664 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
665 }
666 return skipped_payment_request_ui_;
Rouslan Solomakhin27064702018-12-14 21:15:33667}
668
mathpf1a7a3752017-03-15 11:23:37669void PaymentRequest::OnPaymentResponseAvailable(
670 mojom::PaymentResponsePtr response) {
Rouslan Solomakhin68429b72019-06-27 15:12:39671 DCHECK(!response->method_name.empty());
672 DCHECK(!response->stringified_details.empty());
673
mathp57c8c862017-06-16 20:15:45674 journey_logger_.SetEventOccurred(
675 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
gogerald7a2b761e2017-11-09 18:30:19676
Sahel Sharify26884382019-05-07 16:23:51677 // Log the correct "selected instrument" metric according to its type and
678 // the method name in response.
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39679 DCHECK(state_->selected_app());
Sahel Sharify26884382019-05-07 16:23:51680 JourneyLogger::Event selected_event =
681 JourneyLogger::Event::EVENT_SELECTED_OTHER;
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39682 switch (state_->selected_app()->type()) {
683 case PaymentApp::Type::AUTOFILL:
Sahel Sharify26884382019-05-07 16:23:51684 selected_event = JourneyLogger::Event::EVENT_SELECTED_CREDIT_CARD;
685 break;
Rouslan Solomakhin72fd06d2020-08-17 14:23:40686 case PaymentApp::Type::SERVICE_WORKER_APP:
687 // Intentionally fall through.
688 case PaymentApp::Type::NATIVE_MOBILE_APP: {
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39689 selected_event = IsGooglePaymentMethod(response->method_name)
690 ? JourneyLogger::Event::EVENT_SELECTED_GOOGLE
691 : JourneyLogger::Event::EVENT_SELECTED_OTHER;
Sahel Sharify26884382019-05-07 16:23:51692 break;
693 }
Sahel Sharifyc8b2e0d2020-08-19 20:27:22694 case PaymentApp::Type::INTERNAL: {
695 if (response->method_name == methods::kSecurePaymentConfirmation) {
696 selected_event =
697 JourneyLogger::Event::EVENT_SELECTED_SECURE_PAYMENT_CONFIRMATION;
698 }
699 break;
700 }
Rouslan Solomakhin1604d0bb2020-05-27 09:59:21701 case PaymentApp::Type::UNDEFINED:
Sahel Sharify26884382019-05-07 16:23:51702 NOTREACHED();
703 break;
704 }
705 journey_logger_.SetEventOccurred(selected_event);
706
Rouslan Solomakhin02d086ec2019-01-31 23:10:39707 // If currently interactive, show the processing spinner. Autofill payment
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39708 // apps request a CVC, so they are always interactive at this point. A payment
709 // handler may elect to be non-interactive by not showing a confirmation page
710 // to the user.
Rouslan Solomakhin02d086ec2019-01-31 23:10:39711 if (delegate_->IsInteractive())
712 delegate_->ShowProcessingSpinner();
713
mathpf1a7a3752017-03-15 11:23:37714 client_->OnPaymentResponse(std::move(response));
mathp4b85b582017-03-08 21:07:16715}
716
Rouslan Solomakhin68429b72019-06-27 15:12:39717void PaymentRequest::OnPaymentResponseError(const std::string& error_message) {
718 journey_logger_.SetEventOccurred(
719 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
720 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_INSTRUMENT_DETAILS_ERROR);
721
722 reject_show_error_message_ = error_message;
723 delegate_->ShowErrorMessage();
724 // When the user dismisses the error message, UserCancelled() will reject
725 // PaymentRequest.show() with |reject_show_error_message_|.
726}
727
mathp151bd312017-04-03 21:07:24728void PaymentRequest::OnShippingOptionIdSelected(
729 std::string shipping_option_id) {
730 client_->OnShippingOptionChange(shipping_option_id);
731}
732
733void PaymentRequest::OnShippingAddressSelected(
734 mojom::PaymentAddressPtr address) {
Sahel Sharify9d98a502019-09-30 19:58:39735 client_->OnShippingAddressChange(RedactShippingAddress(std::move(address)));
mathp151bd312017-04-03 21:07:24736}
737
Jinho Bangbb178152018-09-13 09:44:43738void PaymentRequest::OnPayerInfoSelected(mojom::PayerDetailPtr payer_info) {
739 client_->OnPayerDetailChange(std::move(payer_info));
740}
741
mathpf4bc50e2017-01-24 05:17:50742void PaymentRequest::UserCancelled() {
743 // If |client_| is not bound, then the object is already being destroyed as
744 // a result of a renderer event.
745 if (!client_.is_bound())
746 return;
747
sebsgfcdd13c2017-06-08 15:49:33748 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
sebsg20b49d7b2017-05-04 20:23:17749
mathpf4bc50e2017-01-24 05:17:50750 // This sends an error to the renderer, which informs the API user.
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03751 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
Rouslan Solomakhin68429b72019-06-27 15:12:39752 !reject_show_error_message_.empty()
753 ? reject_show_error_message_
754 : errors::kUserCancelled);
mathpf4bc50e2017-01-24 05:17:50755
756 // We close all bindings and ask to be destroyed.
757 client_.reset();
Gyuyoung Kim9cfbda32019-08-27 02:15:18758 receiver_.reset();
DongJun Kim54690d52020-09-26 12:07:54759 payment_handler_host_->Disconnect();
rouslanb28f4532017-05-08 15:41:47760 if (observer_for_testing_)
761 observer_for_testing_->OnConnectionTerminated();
DongJun Kim54690d52020-09-26 12:07:54762 manager_->DestroyRequest(weak_ptr_factory_.GetWeakPtr());
mathpf709499d2017-01-09 20:48:36763}
764
sebsgd56b3e422017-10-20 18:08:08765void PaymentRequest::DidStartMainFrameNavigationToDifferentDocument(
766 bool is_user_initiated) {
sebsgfcdd13c2017-06-08 15:49:33767 RecordFirstAbortReason(is_user_initiated
768 ? JourneyLogger::ABORT_REASON_USER_NAVIGATION
769 : JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION);
sebsg2c8558a2017-05-17 18:54:10770}
771
Danyao Wang5c38b302020-08-26 12:08:01772void PaymentRequest::RenderFrameDeleted(
773 content::RenderFrameHost* render_frame_host) {
774 DCHECK(render_frame_host ==
775 content::RenderFrameHost::FromID(initiator_frame_routing_id_));
776 // RenderFrameHost is usually deleted explicitly before PaymentRequest
777 // destruction if the user closes the tab or browser window without closing
778 // the payment request dialog.
779 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
780 // But don't bother sending errors to |client_| because the mojo pipe will be
781 // torn down anyways when RenderFrameHost is destroyed. It's not safe to call
782 // UserCancelled() here because it is not re-entrant.
783 // TODO(crbug.com/1121841) Make UserCancelled re-entrant.
784 OnConnectionTerminated();
785}
786
mathpf4bc50e2017-01-24 05:17:50787void PaymentRequest::OnConnectionTerminated() {
788 // We are here because of a browser-side error, or likely as a result of the
Gyuyoung Kim9cfbda32019-08-27 02:15:18789 // disconnect_handler on |receiver_|, which can mean that the renderer
mathpf4bc50e2017-01-24 05:17:50790 // has decided to close the pipe for various reasons (see all uses of
791 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close
792 // the binding and the dialog, and ask to be deleted.
793 client_.reset();
Gyuyoung Kim9cfbda32019-08-27 02:15:18794 receiver_.reset();
DongJun Kim54690d52020-09-26 12:07:54795 payment_handler_host_->Disconnect();
mathpf4bc50e2017-01-24 05:17:50796 delegate_->CloseDialog();
rouslanb28f4532017-05-08 15:41:47797 if (observer_for_testing_)
798 observer_for_testing_->OnConnectionTerminated();
sebsgfcdd13c2017-06-08 15:49:33799
800 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_MOJO_CONNECTION_ERROR);
DongJun Kim54690d52020-09-26 12:07:54801 manager_->DestroyRequest(weak_ptr_factory_.GetWeakPtr());
mathpf709499d2017-01-09 20:48:36802}
803
mathpd4be8de82017-03-01 00:51:48804void PaymentRequest::Pay() {
mathp57c8c862017-06-16 20:15:45805 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
Sahel Sharify9306fe92020-08-18 20:04:58806 journey_logger_.RecordCheckoutStep(
807 JourneyLogger::CheckoutFunnelStep::kPaymentHandlerInvoked);
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39808 DCHECK(state_->selected_app());
Rouslan Solomakhin47f109e2020-05-20 15:45:36809 state_->selected_app()->SetPaymentHandlerHost(
DongJun Kim54690d52020-09-26 12:07:54810 payment_handler_host_->AsWeakPtr());
mathpf1a7a3752017-03-15 11:23:37811 state_->GeneratePaymentResponse();
mathpd4be8de82017-03-01 00:51:48812}
813
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07814void PaymentRequest::HideIfNecessary() {
815 display_handle_.reset();
816}
817
Ramin Halavatifbfcef672020-05-21 16:33:34818bool PaymentRequest::IsOffTheRecord() const {
819 return delegate_->IsOffTheRecord();
Anthony Vallee-Dubois10d131a2018-02-22 15:41:04820}
821
Sahel Sharify54f5e862020-03-26 20:02:41822void PaymentRequest::OnPaymentHandlerOpenWindowCalled() {
823 DCHECK(state_->selected_app());
824 // UKM for payment app origin should get recorded only when the origin of the
825 // invoked payment app is shown to the user.
826 journey_logger_.SetPaymentAppUkmSourceId(
827 state_->selected_app()->UkmSourceId());
828}
829
sebsgfcdd13c2017-06-08 15:49:33830void PaymentRequest::RecordFirstAbortReason(
831 JourneyLogger::AbortReason abort_reason) {
832 if (!has_recorded_completion_) {
833 has_recorded_completion_ = true;
834 journey_logger_.SetAborted(abort_reason);
sebsg2c8558a2017-05-17 18:54:10835 }
836}
837
Danyao Wang03a4cbd2019-08-15 23:47:11838void PaymentRequest::CanMakePaymentCallback(bool can_make_payment) {
839 client_->OnCanMakePayment(
840 can_make_payment ? mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT
841 : mojom::CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT);
Danyao Wang4bc0606a2018-12-27 16:54:53842
Danyao Wang57aa0442019-01-31 04:06:41843 journey_logger_.SetCanMakePaymentValue(can_make_payment);
Rouslan Solomakhin1804ee42017-10-03 14:27:43844
845 if (observer_for_testing_)
846 observer_for_testing_->OnCanMakePaymentReturned();
847}
848
Danyao Wangce175bf2018-12-21 22:35:58849void PaymentRequest::HasEnrolledInstrumentCallback(
850 bool has_enrolled_instrument) {
Rouslan Solomakhinb26faa072019-08-19 14:42:28851 if (!spec_ || CanMakePaymentQueryFactory::GetInstance()
852 ->GetForContext(web_contents_->GetBrowserContext())
853 ->CanQuery(top_level_origin_, frame_origin_,
Dongjun Kim0a7565b2020-08-18 09:39:46854 spec_->query_for_quota())) {
Danyao Wangce175bf2018-12-21 22:35:58855 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58856 /*warn_local_development=*/false);
857 } else if (UrlUtil::IsLocalDevelopmentUrl(frame_origin_)) {
Danyao Wangce175bf2018-12-21 22:35:58858 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58859 /*warn_local_development=*/true);
Danyao Wangce175bf2018-12-21 22:35:58860 } else {
861 client_->OnHasEnrolledInstrument(
862 HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED);
863 }
864
865 if (observer_for_testing_)
866 observer_for_testing_->OnHasEnrolledInstrumentReturned();
867}
868
Danyao Wangce175bf2018-12-21 22:35:58869void PaymentRequest::RespondToHasEnrolledInstrumentQuery(
870 bool has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58871 bool warn_local_development) {
Danyao Wangce175bf2018-12-21 22:35:58872 HasEnrolledInstrumentQueryResult positive =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58873 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58874 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_ENROLLED_INSTRUMENT
875 : HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT;
876 HasEnrolledInstrumentQueryResult negative =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58877 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58878 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_NO_ENROLLED_INSTRUMENT
879 : HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT;
880
881 client_->OnHasEnrolledInstrument(has_enrolled_instrument ? positive
882 : negative);
Danyao Wang57aa0442019-01-31 04:06:41883 journey_logger_.SetHasEnrolledInstrumentValue(has_enrolled_instrument);
Danyao Wangce175bf2018-12-21 22:35:58884}
885
Rouslan Solomakhin13947ae32020-05-22 15:34:29886void PaymentRequest::OnAbortResult(bool aborted) {
887 if (client_.is_bound())
888 client_->OnAbort(aborted);
889
890 if (aborted) {
891 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_MERCHANT);
892 state_->OnAbort();
893 }
894}
895
mathpf709499d2017-01-09 20:48:36896} // namespace payments