blob: 15a37367310c705400196a2ab0ad5130deffcfff [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"
Rouslan Solomakhin77a7e1a2019-05-23 17:37:5842#include "content/public/common/origin_util.h"
Sahel Sharify54f5e862020-03-26 20:02:4143#include "services/metrics/public/cpp/ukm_source_id.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 Solomakhin1f95f092019-08-09 12:28:5193 payment_handler_host_(web_contents_, this),
Rouslan Solomakhin6e979ab2017-08-30 17:30:3994 top_level_origin_(url_formatter::FormatUrlForSecurityDisplay(
95 web_contents_->GetLastCommittedURL())),
96 frame_origin_(url_formatter::FormatUrlForSecurityDisplay(
97 render_frame_host->GetLastCommittedURL())),
Rouslan Solomakhinc71cca622020-01-31 22:15:3598 frame_security_origin_(render_frame_host->GetLastCommittedOrigin()),
sebsg20b49d7b2017-05-04 20:23:1799 observer_for_testing_(observer_for_testing),
Ramin Halavatifbfcef672020-05-21 16:33:34100 journey_logger_(delegate_->IsOffTheRecord(),
Jeremy Roman5c341f6d2019-07-15 15:56:10101 ukm::GetSourceIdForWebContentsDocument(web_contents)) {
Gyuyoung Kim9cfbda32019-08-27 02:15:18102 receiver_.Bind(std::move(receiver));
mathpf4bc50e2017-01-24 05:17:50103 // OnConnectionTerminated will be called when the Mojo pipe is closed. This
104 // will happen as a result of many renderer-side events (both successful and
105 // erroneous in nature).
106 // TODO(crbug.com/683636): Investigate using
107 // set_connection_error_with_reason_handler with Binding::CloseWithReason.
Gyuyoung Kim9cfbda32019-08-27 02:15:18108 receiver_.set_disconnect_handler(base::BindOnce(
Anthony Vallee-Duboisdc1dbf1a2017-07-17 15:01:13109 &PaymentRequest::OnConnectionTerminated, weak_ptr_factory_.GetWeakPtr()));
mathpf709499d2017-01-09 20:48:36110}
111
112PaymentRequest::~PaymentRequest() {}
113
Gyuyoung Kim9cfbda32019-08-27 02:15:18114void PaymentRequest::Init(
115 mojo::PendingRemote<mojom::PaymentRequestClient> client,
116 std::vector<mojom::PaymentMethodDataPtr> method_data,
117 mojom::PaymentDetailsPtr details,
118 mojom::PaymentOptionsPtr options) {
mathpf709499d2017-01-09 20:48:36119 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin27064702018-12-14 21:15:33120
121 if (is_initialized_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22122 log_.Error(errors::kAttemptedInitializationTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33123 OnConnectionTerminated();
124 return;
125 }
126
Sahel Sharify9306fe92020-08-18 20:04:58127 journey_logger_.RecordCheckoutStep(
128 JourneyLogger::CheckoutFunnelStep::kInitiated);
Rouslan Solomakhin27064702018-12-14 21:15:33129 is_initialized_ = true;
Gyuyoung Kim9cfbda32019-08-27 02:15:18130 client_.Bind(std::move(client));
rouslan6e3cf7c62017-04-17 21:23:28131
rouslanb28f4532017-05-08 15:41:47132 const GURL last_committed_url = delegate_->GetLastCommittedURL();
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58133 if (!content::IsOriginSecure(last_committed_url)) {
Rouslan Solomakhina480efa2019-05-06 15:37:22134 log_.Error(errors::kNotInASecureOrigin);
rouslan6e3cf7c62017-04-17 21:23:28135 OnConnectionTerminated();
136 return;
137 }
138
Liquan (Max) Gu4a073e12020-09-02 23:34:29139 // TODO(crbug.com/978471): Improve architecture for handling prohibited
140 // origins and invalid SSL certificates.
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);
rouslan6e3cf7c62017-04-17 21:23:28159 return;
160 }
161
mathpf709499d2017-01-09 20:48:36162 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22163 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33164 log_.Error(error);
mathpf4bc50e2017-01-24 05:17:50165 OnConnectionTerminated();
mathpf709499d2017-01-09 20:48:36166 return;
167 }
rouslan6e3cf7c62017-04-17 21:23:28168
jinho.bangfcb5ec92017-03-29 08:08:02169 if (!details->total) {
Rouslan Solomakhina480efa2019-05-06 15:37:22170 log_.Error(errors::kTotalRequired);
jinho.bangfcb5ec92017-03-29 08:08:02171 OnConnectionTerminated();
172 return;
173 }
rouslan6e3cf7c62017-04-17 21:23:28174
Danyao Wang15840792020-05-07 23:52:17175 // TODO(crbug.com/1058840): change to WeakPtr<RenderFrameHost> once
176 // RenderFrameHost provides a WeakPtr API.
177 content::RenderFrameHost* initiator_frame =
178 content::RenderFrameHost::FromID(initiator_frame_routing_id_);
179 if (!initiator_frame) {
180 log_.Error(errors::kInvalidInitiatorFrame);
181 OnConnectionTerminated();
182 return;
183 }
184
sebsga70a6da2017-12-21 22:27:02185 spec_ = std::make_unique<PaymentRequestSpec>(
Rouslan Solomakhin1f95f092019-08-09 12:28:51186 std::move(options), std::move(details), std::move(method_data),
187 /*observer=*/this, delegate_->GetApplicationLocale());
sebsga70a6da2017-12-21 22:27:02188 state_ = std::make_unique<PaymentRequestState>(
Danyao Wang15840792020-05-07 23:52:17189 web_contents_, initiator_frame, top_level_origin_, frame_origin_,
190 frame_security_origin_, spec_.get(),
Kyungsun Leecdbdade2020-08-25 18:06:12191 /*delegate=*/weak_ptr_factory_.GetWeakPtr(),
192 delegate_->GetApplicationLocale(), delegate_->GetPersonalDataManager(),
193 delegate_.get(), &journey_logger_);
Mathieu Perreault627b97c2017-08-12 00:44:22194
195 journey_logger_.SetRequestedInformation(
196 spec_->request_shipping(), spec_->request_payer_email(),
197 spec_->request_payer_phone(), spec_->request_payer_name());
198
199 // Log metrics around which payment methods are requested by the merchant.
Rouslan Solomakhin85b10da2019-11-05 20:03:17200 GURL google_pay_url(methods::kGooglePay);
201 GURL android_pay_url(methods::kAndroidPay);
Mathieu Perreault627b97c2017-08-12 00:44:22202 // Looking for payment methods that are NOT google-related payment methods.
203 auto non_google_it =
204 std::find_if(spec_->url_payment_method_identifiers().begin(),
205 spec_->url_payment_method_identifiers().end(),
206 [google_pay_url, android_pay_url](const GURL& url) {
207 return url != google_pay_url && url != android_pay_url;
208 });
209 journey_logger_.SetRequestedPaymentMethodTypes(
210 /*requested_basic_card=*/!spec_->supported_card_networks().empty(),
211 /*requested_method_google=*/
Jan Wilken Dörrie45d34f42019-06-08 09:40:54212 base::Contains(spec_->url_payment_method_identifiers(), google_pay_url) ||
213 base::Contains(spec_->url_payment_method_identifiers(),
214 android_pay_url),
Sahel Sharifyc8b2e0d2020-08-19 20:27:22215 /*requested_method_secure_payment_confirmation=*/
216 spec_->IsSecurePaymentConfirmationRequested(),
Mathieu Perreault627b97c2017-08-12 00:44:22217 /*requested_method_other=*/non_google_it !=
218 spec_->url_payment_method_identifiers().end());
Rouslan Solomakhin1f95f092019-08-09 12:28:51219
220 payment_handler_host_.set_payment_request_id_for_logs(*spec_->details().id);
Rouslan Solomakhin4bd19a62020-08-27 17:05:51221
222 if (spec_->IsSecurePaymentConfirmationRequested()) {
223 delegate_->set_dialog_type(
224 PaymentRequestDelegate::DialogType::SECURE_PAYMENT_CONFIRMATION);
225 }
mathpf709499d2017-01-09 20:48:36226}
227
Rouslan Solomakhin9788d4b2019-04-09 13:10:23228void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
Rouslan Solomakhin27064702018-12-14 21:15:33229 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22230 log_.Error(errors::kCannotShowWithoutInit);
mathpf4bc50e2017-01-24 05:17:50231 OnConnectionTerminated();
tmartino8ce922852017-01-09 22:23:10232 return;
233 }
rouslan6e3cf7c62017-04-17 21:23:28234
Rouslan Solomakhin27064702018-12-14 21:15:33235 if (is_show_called_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22236 log_.Error(errors::kCannotShowTwice);
Rouslan Solomakhin27064702018-12-14 21:15:33237 OnConnectionTerminated();
238 return;
239 }
240
Sahel Sharify9306fe92020-08-18 20:04:58241 journey_logger_.RecordCheckoutStep(
242 JourneyLogger::CheckoutFunnelStep::kShowCalled);
Rouslan Solomakhin27064702018-12-14 21:15:33243 is_show_called_ = true;
Sahel Sharify0fadf4da2019-08-09 14:55:58244 journey_logger_.SetTriggerTime();
Rouslan Solomakhin27064702018-12-14 21:15:33245
rouslan7d433cc22017-05-08 15:18:07246 // A tab can display only one PaymentRequest UI at a time.
Anthony Vallee-Dubois8f5e7e12018-01-12 16:14:06247 display_handle_ = display_manager_->TryShow(delegate_.get());
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07248 if (!display_handle_) {
Rouslan Solomakhina480efa2019-05-06 15:37:22249 log_.Error(errors::kAnotherUiShowing);
Sahel Sharifya50fc4c2019-05-28 14:53:22250 DCHECK(!has_recorded_completion_);
251 has_recorded_completion_ = true;
sebsg828269bc2017-06-09 19:11:12252 journey_logger_.SetNotShown(
253 JourneyLogger::NOT_SHOWN_REASON_CONCURRENT_REQUESTS);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03254 client_->OnError(mojom::PaymentErrorReason::ALREADY_SHOWING,
255 errors::kAnotherUiShowing);
rouslan7d433cc22017-05-08 15:18:07256 OnConnectionTerminated();
257 return;
258 }
259
Rouslan Solomakhin5b510432017-09-26 16:59:32260 if (!delegate_->IsBrowserWindowActive()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22261 log_.Error(errors::kCannotShowInBackgroundTab);
Sahel Sharifya50fc4c2019-05-28 14:53:22262 DCHECK(!has_recorded_completion_);
263 has_recorded_completion_ = true;
Rouslan Solomakhin5b510432017-09-26 16:59:32264 journey_logger_.SetNotShown(JourneyLogger::NOT_SHOWN_REASON_OTHER);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03265 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
266 errors::kCannotShowInBackgroundTab);
Rouslan Solomakhin5b510432017-09-26 16:59:32267 OnConnectionTerminated();
268 return;
269 }
270
Liquan (Max) Gu4a073e12020-09-02 23:34:29271 if (!state_) {
272 // SSL is not valid. Reject show with NotSupportedError, disconnect the
273 // mojo pipe, and destroy this object.
274 AreRequestedMethodsSupportedCallback(false, reject_show_error_message_);
275 return;
276 }
277
Rouslan Solomakhin833f8512018-04-03 23:19:25278 is_show_user_gesture_ = is_user_gesture;
279
Rouslan Solomakhin9788d4b2019-04-09 13:10:23280 if (wait_for_updated_details) {
281 // Put |spec_| into uninitialized state, so the UI knows to show a spinner.
282 // This method does not block.
283 spec_->StartWaitingForUpdateWith(
284 PaymentRequestSpec::UpdateReason::INITIAL_PAYMENT_DETAILS);
Sahel Sharify98a2c2a2019-07-12 18:57:40285 } else {
286 DCHECK(spec_->details().total);
287 journey_logger_.RecordTransactionAmount(
288 spec_->details().total->amount->currency,
289 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23290 }
Takashi Sakamoto48a29702019-04-08 05:06:32291
Rouslan Solomakhin9788d4b2019-04-09 13:10:23292 display_handle_->Show(this);
Rouslan Solomakhin1dca2a922019-09-06 22:25:07293
294 state_->set_is_show_user_gesture(is_show_user_gesture_);
gogerald0a7ee6c2017-11-13 18:23:19295 state_->AreRequestedMethodsSupported(
296 base::BindOnce(&PaymentRequest::AreRequestedMethodsSupportedCallback,
297 weak_ptr_factory_.GetWeakPtr()));
298}
299
Jinho Bangbe463a22018-08-02 10:26:50300void PaymentRequest::Retry(mojom::PaymentValidationErrorsPtr errors) {
Rouslan Solomakhin27064702018-12-14 21:15:33301 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22302 log_.Error(errors::kCannotRetryWithoutInit);
Jinho Bangcac8d9a02018-08-23 19:47:22303 OnConnectionTerminated();
304 return;
305 }
306
Rouslan Solomakhin27064702018-12-14 21:15:33307 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22308 log_.Error(errors::kCannotRetryWithoutShow);
Jinho Bangcac8d9a02018-08-23 19:47:22309 OnConnectionTerminated();
310 return;
311 }
312
Jinho Bangbe463a22018-08-02 10:26:50313 std::string error;
314 if (!PaymentsValidators::IsValidPaymentValidationErrorsFormat(errors,
315 &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33316 log_.Error(error);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03317 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL, error);
Jinho Bangbe463a22018-08-02 10:26:50318 OnConnectionTerminated();
319 return;
320 }
321
Sahel Sharifycf4e2132020-01-24 23:59:08322 state()->SetAvailablePaymentAppForRetry();
Jinho Bang092e7162018-09-06 23:41:19323 spec()->Retry(std::move(errors));
Jinho Bangcac8d9a02018-08-23 19:47:22324 display_handle_->Retry();
Jinho Bangbe463a22018-08-02 10:26:50325}
326
mathp151bd312017-04-03 21:07:24327void PaymentRequest::UpdateWith(mojom::PaymentDetailsPtr details) {
Rouslan Solomakhin27064702018-12-14 21:15:33328 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22329 log_.Error(errors::kCannotUpdateWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33330 OnConnectionTerminated();
331 return;
332 }
333
334 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22335 log_.Error(errors::kCannotUpdateWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33336 OnConnectionTerminated();
337 return;
338 }
339
mathp151bd312017-04-03 21:07:24340 std::string error;
Mohamad Ahmadif5544bb2017-09-01 21:48:22341 if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33342 log_.Error(error);
mathp151bd312017-04-03 21:07:24343 OnConnectionTerminated();
344 return;
345 }
Rouslan Solomakhin4cbda822017-08-23 18:50:39346
Jinho Bang092e7162018-09-06 23:41:19347 if (details->shipping_address_errors &&
348 !PaymentsValidators::IsValidAddressErrorsFormat(
349 details->shipping_address_errors, &error)) {
Rouslan Solomakhin27064702018-12-14 21:15:33350 log_.Error(error);
Jinho Bang092e7162018-09-06 23:41:19351 OnConnectionTerminated();
352 return;
353 }
354
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39355 if (state()->selected_app() && state()->IsPaymentAppInvoked() &&
Rouslan Solomakhin218f7072020-05-21 14:26:47356 state()->selected_app()->IsWaitingForPaymentDetailsUpdate()) {
Rouslan Solomakhin97238b22020-05-28 00:28:35357 state()->selected_app()->UpdateWith(
358 PaymentDetailsConverter::ConvertToPaymentRequestDetailsUpdate(
359 details, state()->selected_app()->HandlesShippingAddress(),
360 base::BindRepeating(&PaymentApp::IsValidForPaymentMethodIdentifier,
361 state()->selected_app()->AsWeakPtr())));
Rouslan Solomakhina480efa2019-05-06 15:37:22362 }
363
Rouslan Solomakhin6ba46fd2019-04-11 23:44:01364 bool is_resolving_promise_passed_into_show_method = !spec_->IsInitialized();
365
mathp151bd312017-04-03 21:07:24366 spec_->UpdateWith(std::move(details));
Rouslan Solomakhin9788d4b2019-04-09 13:10:23367
368 if (is_resolving_promise_passed_into_show_method) {
Sahel Sharify98a2c2a2019-07-12 18:57:40369 DCHECK(spec_->details().total);
370 journey_logger_.RecordTransactionAmount(
371 spec_->details().total->amount->currency,
372 spec_->details().total->amount->value, false /*completed*/);
Rouslan Solomakhin9788d4b2019-04-09 13:10:23373 if (SatisfiesSkipUIConstraints()) {
Rouslan Solomakhin9788d4b2019-04-09 13:10:23374 Pay();
375 } else if (spec_->request_shipping()) {
376 state_->SelectDefaultShippingAddressAndNotifyObservers();
377 }
378 }
mathp151bd312017-04-03 21:07:24379}
380
Sahel Sharifye2f889542019-12-02 22:17:48381void PaymentRequest::OnPaymentDetailsNotUpdated() {
Rouslan Solomakhin27064702018-12-14 21:15:33382 // This Mojo call is triggered by the user of the API doing nothing in
383 // response to a shipping address update event, so the error messages cannot
384 // be more verbose.
385 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22386 log_.Error(errors::kNotInitialized);
Rouslan Solomakhin27064702018-12-14 21:15:33387 OnConnectionTerminated();
388 return;
389 }
390
391 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22392 log_.Error(errors::kNotShown);
Rouslan Solomakhin27064702018-12-14 21:15:33393 OnConnectionTerminated();
394 return;
395 }
396
Rouslan Solomakhina9ff9282017-10-31 21:58:05397 spec_->RecomputeSpecForDetails();
Rouslan Solomakhina480efa2019-05-06 15:37:22398
Rouslan Solomakhin218f7072020-05-21 14:26:47399 if (state()->IsPaymentAppInvoked() && state()->selected_app() &&
400 state()->selected_app()->IsWaitingForPaymentDetailsUpdate()) {
Rouslan Solomakhin2039a342020-05-21 19:21:04401 state()->selected_app()->OnPaymentDetailsNotUpdated();
Rouslan Solomakhincf9093f2019-05-20 15:32:17402 }
Rouslan Solomakhina9ff9282017-10-31 21:58:05403}
404
mathpf4bc50e2017-01-24 05:17:50405void PaymentRequest::Abort() {
Rouslan Solomakhin27064702018-12-14 21:15:33406 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22407 log_.Error(errors::kCannotAbortWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33408 OnConnectionTerminated();
409 return;
410 }
411
412 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22413 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33414 OnConnectionTerminated();
415 return;
416 }
417
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56418 // The API user has decided to abort. If a successful abort message is
419 // returned to the renderer, the Mojo message pipe is closed, which triggers
mathpf4bc50e2017-01-24 05:17:50420 // PaymentRequest::OnConnectionTerminated, which destroys this object.
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56421 // Otherwise, the abort promise is rejected and the pipe is not closed.
422 // The abort is only successful if the payment app wasn't yet invoked.
423 // TODO(crbug.com/716546): Add a merchant abort metric
424
Anthony Vallee-Dubois6813c1442017-05-17 19:32:56425 if (observer_for_testing_)
426 observer_for_testing_->OnAbortCalled();
Sahel Sharifyd3418222020-02-19 21:50:02427
Rouslan Solomakhin13947ae32020-05-22 15:34:29428 if (!state_->IsPaymentAppInvoked() || !state_->selected_app()) {
429 OnAbortResult(/*aborted=*/true);
430 return;
431 }
432
433 state_->selected_app()->AbortPaymentApp(base::BindOnce(
434 &PaymentRequest::OnAbortResult, weak_ptr_factory_.GetWeakPtr()));
mathpf4bc50e2017-01-24 05:17:50435}
436
mathp218795892017-03-29 15:15:34437void PaymentRequest::Complete(mojom::PaymentComplete result) {
Rouslan Solomakhin27064702018-12-14 21:15:33438 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22439 log_.Error(errors::kCannotCompleteWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33440 OnConnectionTerminated();
mathp4b85b582017-03-08 21:07:16441 return;
Rouslan Solomakhin27064702018-12-14 21:15:33442 }
443
444 if (!IsThisPaymentRequestShowing()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22445 log_.Error(errors::kCannotAbortWithoutShow);
Rouslan Solomakhin27064702018-12-14 21:15:33446 OnConnectionTerminated();
447 return;
448 }
mathp4b85b582017-03-08 21:07:16449
Sahel Sharify2a56b5f2019-11-28 19:27:19450 if (observer_for_testing_) {
451 observer_for_testing_->OnCompleteCalled();
452 }
453
Rouslan Solomakhine3473192017-06-16 14:54:57454 // Failed transactions show an error. Successful and unknown-state
455 // transactions don't show an error.
456 if (result == mojom::PaymentComplete::FAIL) {
mathp218795892017-03-29 15:15:34457 delegate_->ShowErrorMessage();
458 } else {
sebsgfcdd13c2017-06-08 15:49:33459 DCHECK(!has_recorded_completion_);
sebsgf8272a22017-05-26 14:32:58460 journey_logger_.SetCompleted();
sebsgfcdd13c2017-06-08 15:49:33461 has_recorded_completion_ = true;
Sahel Sharify98a2c2a2019-07-12 18:57:40462 DCHECK(spec_->details().total);
463 journey_logger_.RecordTransactionAmount(
464 spec_->details().total->amount->currency,
465 spec_->details().total->amount->value, true /*completed*/);
sebsgfcdd13c2017-06-08 15:49:33466
anthonyvd6a43b932017-05-11 18:39:27467 delegate_->GetPrefService()->SetBoolean(kPaymentsFirstTransactionCompleted,
468 true);
mathp218795892017-03-29 15:15:34469 // When the renderer closes the connection,
470 // PaymentRequest::OnConnectionTerminated will be called.
471 client_->OnComplete();
sebsg8a93b272017-05-11 19:30:22472 state_->RecordUseStats();
mathp218795892017-03-29 15:15:34473 }
mathp4b85b582017-03-08 21:07:16474}
475
Danyao Wang03a4cbd2019-08-15 23:47:11476void PaymentRequest::CanMakePayment() {
Rouslan Solomakhin27064702018-12-14 21:15:33477 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22478 log_.Error(errors::kCannotCallCanMakePaymentWithoutInit);
Rouslan Solomakhin27064702018-12-14 21:15:33479 OnConnectionTerminated();
480 return;
481 }
482
483 // It's valid to call canMakePayment() without calling show() first.
484
gogerald8189d522017-09-15 17:52:18485 if (observer_for_testing_)
486 observer_for_testing_->OnCanMakePaymentCalled();
Mathieu Perreaultcacb85e2018-06-06 20:40:13487
Liquan (Max) Gu4a073e12020-09-02 23:34:29488 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
489 !state_) {
Danyao Wang03a4cbd2019-08-15 23:47:11490 CanMakePaymentCallback(/*can_make_payment=*/false);
Mathieu Perreaultcacb85e2018-06-06 20:40:13491 } else {
Rouslan Solomakhind2cae95a2018-08-09 00:16:10492 state_->CanMakePayment(
Mathieu Perreaultcacb85e2018-06-06 20:40:13493 base::BindOnce(&PaymentRequest::CanMakePaymentCallback,
Danyao Wang03a4cbd2019-08-15 23:47:11494 weak_ptr_factory_.GetWeakPtr()));
Mathieu Perreaultcacb85e2018-06-06 20:40:13495 }
gogerald8189d522017-09-15 17:52:18496}
497
Dongjun Kim0a7565b2020-08-18 09:39:46498void PaymentRequest::HasEnrolledInstrument() {
Danyao Wangce175bf2018-12-21 22:35:58499 if (!IsInitialized()) {
Rouslan Solomakhina480efa2019-05-06 15:37:22500 log_.Error(errors::kCannotCallHasEnrolledInstrumentWithoutInit);
Danyao Wangce175bf2018-12-21 22:35:58501 OnConnectionTerminated();
502 return;
503 }
504
505 // It's valid to call hasEnrolledInstrument() without calling show() first.
506
507 if (observer_for_testing_)
508 observer_for_testing_->OnHasEnrolledInstrumentCalled();
509
Liquan (Max) Gu4a073e12020-09-02 23:34:29510 if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) ||
511 !state_) {
Dongjun Kim0a7565b2020-08-18 09:39:46512 HasEnrolledInstrumentCallback(/*has_enrolled_instrument=*/false);
Danyao Wangce175bf2018-12-21 22:35:58513 } else {
514 state_->HasEnrolledInstrument(
515 base::BindOnce(&PaymentRequest::HasEnrolledInstrumentCallback,
Dongjun Kim0a7565b2020-08-18 09:39:46516 weak_ptr_factory_.GetWeakPtr()));
Danyao Wangce175bf2018-12-21 22:35:58517 }
518}
519
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02520bool PaymentRequest::ChangePaymentMethod(const std::string& method_name,
521 const std::string& stringified_data) {
Rouslan Solomakhina480efa2019-05-06 15:37:22522 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02523 DCHECK(!method_name.empty());
Rouslan Solomakhina480efa2019-05-06 15:37:22524
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02525 if (!state_ || !state_->IsPaymentAppInvoked() || !client_)
526 return false;
Rouslan Solomakhina480efa2019-05-06 15:37:22527
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02528 client_->OnPaymentMethodChange(method_name, stringified_data);
529 return true;
Rouslan Solomakhina480efa2019-05-06 15:37:22530}
531
Sahel Sharify9d98a502019-09-30 19:58:39532bool PaymentRequest::ChangeShippingOption(
533 const std::string& shipping_option_id) {
534 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
535 DCHECK(!shipping_option_id.empty());
536
537 bool is_valid_id = false;
538 if (spec_->details().shipping_options) {
539 for (const auto& option : spec_->GetShippingOptions()) {
540 if (option->id == shipping_option_id) {
541 is_valid_id = true;
542 break;
543 }
544 }
545 }
546
547 if (!state_ || !state_->IsPaymentAppInvoked() || !client_ || !spec_ ||
548 !spec_->request_shipping() || !is_valid_id) {
549 return false;
550 }
551
552 client_->OnShippingOptionChange(shipping_option_id);
553 return true;
554}
555
556bool PaymentRequest::ChangeShippingAddress(
557 mojom::PaymentAddressPtr shipping_address) {
558 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
559 DCHECK(shipping_address);
560
561 if (!state_ || !state_->IsPaymentAppInvoked() || !client_ || !spec_ ||
562 !spec_->request_shipping()) {
563 return false;
564 }
565
566 client_->OnShippingAddressChange(
567 RedactShippingAddress(std::move(shipping_address)));
568 return true;
569}
570
Rouslan Solomakhin27064702018-12-14 21:15:33571void PaymentRequest::AreRequestedMethodsSupportedCallback(
Rouslan Solomakhind5dcc322019-07-11 21:47:20572 bool methods_supported,
573 const std::string& error_message) {
Danyao Wang25f72dc2019-10-18 05:11:32574 if (is_show_called_ && observer_for_testing_)
Sahel Sharifyfd677ff2020-07-08 19:01:52575 observer_for_testing_->OnAppListReady(weak_ptr_factory_.GetWeakPtr());
Danyao Wang25f72dc2019-10-18 05:11:32576
Rouslan Solomakhin27064702018-12-14 21:15:33577 if (methods_supported) {
Sahel Sharifyd3f1bc82019-05-21 18:48:46578 if (SatisfiesSkipUIConstraints())
Rouslan Solomakhin27064702018-12-14 21:15:33579 Pay();
Rouslan Solomakhin27064702018-12-14 21:15:33580 } else {
Sahel Sharifya50fc4c2019-05-28 14:53:22581 DCHECK(!has_recorded_completion_);
582 has_recorded_completion_ = true;
Rouslan Solomakhin27064702018-12-14 21:15:33583 journey_logger_.SetNotShown(
584 JourneyLogger::NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD);
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03585 client_->OnError(mojom::PaymentErrorReason::NOT_SUPPORTED,
Rouslan Solomakhin2dc99c32020-03-24 21:25:03586 GetNotSupportedErrorMessage(
587 spec_ ? spec_->payment_method_identifiers_set()
588 : std::set<std::string>()) +
Rouslan Solomakhind5dcc322019-07-11 21:47:20589 (error_message.empty() ? "" : " " + error_message));
Rouslan Solomakhin27064702018-12-14 21:15:33590 if (observer_for_testing_)
591 observer_for_testing_->OnNotSupportedError();
592 OnConnectionTerminated();
593 }
594}
595
Rouslan Solomakhine37ecbd02020-07-29 20:35:42596base::WeakPtr<PaymentRequest> PaymentRequest::GetWeakPtr() {
597 return weak_ptr_factory_.GetWeakPtr();
598}
599
Rouslan Solomakhin27064702018-12-14 21:15:33600bool PaymentRequest::IsInitialized() const {
601 return is_initialized_ && client_ && client_.is_bound() &&
Liquan (Max) Gu4a073e12020-09-02 23:34:29602 receiver_.is_bound();
Rouslan Solomakhin27064702018-12-14 21:15:33603}
604
605bool PaymentRequest::IsThisPaymentRequestShowing() const {
606 return is_show_called_ && display_handle_ && spec_ && state_;
607}
608
Sahel Sharify9120fd32019-12-05 17:16:11609bool PaymentRequest::OnlySingleAppCanProvideAllRequiredInformation() const {
610 DCHECK(state()->IsInitialized());
611 DCHECK(spec()->IsInitialized());
612
613 if (!spec()->request_shipping() && !spec()->request_payer_name() &&
614 !spec()->request_payer_phone() && !spec()->request_payer_email()) {
615 return state()->available_apps().size() == 1 &&
616 state()->available_apps().at(0)->type() !=
617 PaymentApp::Type::AUTOFILL;
618 }
619
620 bool an_app_can_provide_all_info = false;
621 for (const auto& app : state()->available_apps()) {
622 if ((!spec()->request_shipping() || app->HandlesShippingAddress()) &&
623 (!spec()->request_payer_name() || app->HandlesPayerName()) &&
624 (!spec()->request_payer_phone() || app->HandlesPayerPhone()) &&
625 (!spec()->request_payer_email() || app->HandlesPayerEmail())) {
626 // There is another available app that can provide all merchant requested
627 // information information.
628 if (an_app_can_provide_all_info)
629 return false;
630
631 an_app_can_provide_all_info = true;
632 }
633 }
634 return an_app_can_provide_all_info;
635}
636
Sahel Sharifyd3f1bc82019-05-21 18:48:46637bool PaymentRequest::SatisfiesSkipUIConstraints() {
Sahel Sharify44a00992020-08-20 17:49:13638 // Only allowing URL based payment apps to skip the payment sheet.
Sahel Sharifyd3f1bc82019-05-21 18:48:46639 skipped_payment_request_ui_ =
Sahel Sharify44a00992020-08-20 17:49:13640 !spec()->IsSecurePaymentConfirmationRequested() &&
Sahel Sharify9120fd32019-12-05 17:16:11641 (spec()->url_payment_method_identifiers().size() > 0 ||
danakjdca06902019-06-27 21:41:41642 delegate_->SkipUiForBasicCard()) &&
Sahel Sharifyd3f1bc82019-05-21 18:48:46643 base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
644 base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
645 is_show_user_gesture_ && state()->IsInitialized() &&
Sahel Sharify9120fd32019-12-05 17:16:11646 spec()->IsInitialized() &&
647 OnlySingleAppCanProvideAllRequiredInformation() &&
Sahel Sharify90307872019-12-02 17:28:14648 // The available app should be preselectable.
Sahel Sharify9120fd32019-12-05 17:16:11649 state()->selected_app() != nullptr;
Sahel Sharifyd3f1bc82019-05-21 18:48:46650 if (skipped_payment_request_ui_) {
651 DCHECK(state()->IsInitialized() && spec()->IsInitialized());
652 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SKIPPED_SHOW);
653 } else if (state()->IsInitialized() && spec()->IsInitialized()) {
654 // Set EVENT_SHOWN only after state() and spec() initialization.
655 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
656 }
657 return skipped_payment_request_ui_;
Rouslan Solomakhin27064702018-12-14 21:15:33658}
659
mathpf1a7a3752017-03-15 11:23:37660void PaymentRequest::OnPaymentResponseAvailable(
661 mojom::PaymentResponsePtr response) {
Rouslan Solomakhin68429b72019-06-27 15:12:39662 DCHECK(!response->method_name.empty());
663 DCHECK(!response->stringified_details.empty());
664
mathp57c8c862017-06-16 20:15:45665 journey_logger_.SetEventOccurred(
666 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
gogerald7a2b761e2017-11-09 18:30:19667
Sahel Sharify26884382019-05-07 16:23:51668 // Log the correct "selected instrument" metric according to its type and
669 // the method name in response.
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39670 DCHECK(state_->selected_app());
Sahel Sharify26884382019-05-07 16:23:51671 JourneyLogger::Event selected_event =
672 JourneyLogger::Event::EVENT_SELECTED_OTHER;
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39673 switch (state_->selected_app()->type()) {
674 case PaymentApp::Type::AUTOFILL:
Sahel Sharify26884382019-05-07 16:23:51675 selected_event = JourneyLogger::Event::EVENT_SELECTED_CREDIT_CARD;
676 break;
Rouslan Solomakhin72fd06d2020-08-17 14:23:40677 case PaymentApp::Type::SERVICE_WORKER_APP:
678 // Intentionally fall through.
679 case PaymentApp::Type::NATIVE_MOBILE_APP: {
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39680 selected_event = IsGooglePaymentMethod(response->method_name)
681 ? JourneyLogger::Event::EVENT_SELECTED_GOOGLE
682 : JourneyLogger::Event::EVENT_SELECTED_OTHER;
Sahel Sharify26884382019-05-07 16:23:51683 break;
684 }
Sahel Sharifyc8b2e0d2020-08-19 20:27:22685 case PaymentApp::Type::INTERNAL: {
686 if (response->method_name == methods::kSecurePaymentConfirmation) {
687 selected_event =
688 JourneyLogger::Event::EVENT_SELECTED_SECURE_PAYMENT_CONFIRMATION;
689 }
690 break;
691 }
Rouslan Solomakhin1604d0bb2020-05-27 09:59:21692 case PaymentApp::Type::UNDEFINED:
Sahel Sharify26884382019-05-07 16:23:51693 NOTREACHED();
694 break;
695 }
696 journey_logger_.SetEventOccurred(selected_event);
697
Rouslan Solomakhin02d086ec2019-01-31 23:10:39698 // If currently interactive, show the processing spinner. Autofill payment
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39699 // apps request a CVC, so they are always interactive at this point. A payment
700 // handler may elect to be non-interactive by not showing a confirmation page
701 // to the user.
Rouslan Solomakhin02d086ec2019-01-31 23:10:39702 if (delegate_->IsInteractive())
703 delegate_->ShowProcessingSpinner();
704
mathpf1a7a3752017-03-15 11:23:37705 client_->OnPaymentResponse(std::move(response));
mathp4b85b582017-03-08 21:07:16706}
707
Rouslan Solomakhin68429b72019-06-27 15:12:39708void PaymentRequest::OnPaymentResponseError(const std::string& error_message) {
709 journey_logger_.SetEventOccurred(
710 JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
711 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_INSTRUMENT_DETAILS_ERROR);
712
713 reject_show_error_message_ = error_message;
714 delegate_->ShowErrorMessage();
715 // When the user dismisses the error message, UserCancelled() will reject
716 // PaymentRequest.show() with |reject_show_error_message_|.
717}
718
mathp151bd312017-04-03 21:07:24719void PaymentRequest::OnShippingOptionIdSelected(
720 std::string shipping_option_id) {
721 client_->OnShippingOptionChange(shipping_option_id);
722}
723
724void PaymentRequest::OnShippingAddressSelected(
725 mojom::PaymentAddressPtr address) {
Sahel Sharify9d98a502019-09-30 19:58:39726 client_->OnShippingAddressChange(RedactShippingAddress(std::move(address)));
mathp151bd312017-04-03 21:07:24727}
728
Jinho Bangbb178152018-09-13 09:44:43729void PaymentRequest::OnPayerInfoSelected(mojom::PayerDetailPtr payer_info) {
730 client_->OnPayerDetailChange(std::move(payer_info));
731}
732
mathpf4bc50e2017-01-24 05:17:50733void PaymentRequest::UserCancelled() {
734 // If |client_| is not bound, then the object is already being destroyed as
735 // a result of a renderer event.
736 if (!client_.is_bound())
737 return;
738
sebsgfcdd13c2017-06-08 15:49:33739 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
sebsg20b49d7b2017-05-04 20:23:17740
mathpf4bc50e2017-01-24 05:17:50741 // This sends an error to the renderer, which informs the API user.
Rouslan Solomakhin9c83b8a2019-06-24 20:50:03742 client_->OnError(mojom::PaymentErrorReason::USER_CANCEL,
Rouslan Solomakhin68429b72019-06-27 15:12:39743 !reject_show_error_message_.empty()
744 ? reject_show_error_message_
745 : errors::kUserCancelled);
mathpf4bc50e2017-01-24 05:17:50746
747 // We close all bindings and ask to be destroyed.
748 client_.reset();
Gyuyoung Kim9cfbda32019-08-27 02:15:18749 receiver_.reset();
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02750 payment_handler_host_.Disconnect();
rouslanb28f4532017-05-08 15:41:47751 if (observer_for_testing_)
752 observer_for_testing_->OnConnectionTerminated();
mathpf4bc50e2017-01-24 05:17:50753 manager_->DestroyRequest(this);
mathpf709499d2017-01-09 20:48:36754}
755
sebsgd56b3e422017-10-20 18:08:08756void PaymentRequest::DidStartMainFrameNavigationToDifferentDocument(
757 bool is_user_initiated) {
sebsgfcdd13c2017-06-08 15:49:33758 RecordFirstAbortReason(is_user_initiated
759 ? JourneyLogger::ABORT_REASON_USER_NAVIGATION
760 : JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION);
sebsg2c8558a2017-05-17 18:54:10761}
762
Danyao Wang5c38b302020-08-26 12:08:01763void PaymentRequest::RenderFrameDeleted(
764 content::RenderFrameHost* render_frame_host) {
765 DCHECK(render_frame_host ==
766 content::RenderFrameHost::FromID(initiator_frame_routing_id_));
767 // RenderFrameHost is usually deleted explicitly before PaymentRequest
768 // destruction if the user closes the tab or browser window without closing
769 // the payment request dialog.
770 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
771 // But don't bother sending errors to |client_| because the mojo pipe will be
772 // torn down anyways when RenderFrameHost is destroyed. It's not safe to call
773 // UserCancelled() here because it is not re-entrant.
774 // TODO(crbug.com/1121841) Make UserCancelled re-entrant.
775 OnConnectionTerminated();
776}
777
mathpf4bc50e2017-01-24 05:17:50778void PaymentRequest::OnConnectionTerminated() {
779 // We are here because of a browser-side error, or likely as a result of the
Gyuyoung Kim9cfbda32019-08-27 02:15:18780 // disconnect_handler on |receiver_|, which can mean that the renderer
mathpf4bc50e2017-01-24 05:17:50781 // has decided to close the pipe for various reasons (see all uses of
782 // PaymentRequest::clearResolversAndCloseMojoConnection() in Blink). We close
783 // the binding and the dialog, and ask to be deleted.
784 client_.reset();
Gyuyoung Kim9cfbda32019-08-27 02:15:18785 receiver_.reset();
Rouslan Solomakhin8e9f149b22019-05-10 17:43:02786 payment_handler_host_.Disconnect();
mathpf4bc50e2017-01-24 05:17:50787 delegate_->CloseDialog();
rouslanb28f4532017-05-08 15:41:47788 if (observer_for_testing_)
789 observer_for_testing_->OnConnectionTerminated();
sebsgfcdd13c2017-06-08 15:49:33790
791 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_MOJO_CONNECTION_ERROR);
mathpf709499d2017-01-09 20:48:36792 manager_->DestroyRequest(this);
793}
794
mathpd4be8de82017-03-01 00:51:48795void PaymentRequest::Pay() {
mathp57c8c862017-06-16 20:15:45796 journey_logger_.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
Sahel Sharify9306fe92020-08-18 20:04:58797 journey_logger_.RecordCheckoutStep(
798 JourneyLogger::CheckoutFunnelStep::kPaymentHandlerInvoked);
Rouslan Solomakhin5d15cb1f2019-11-11 18:11:39799 DCHECK(state_->selected_app());
Rouslan Solomakhin47f109e2020-05-20 15:45:36800 state_->selected_app()->SetPaymentHandlerHost(
801 payment_handler_host_.AsWeakPtr());
mathpf1a7a3752017-03-15 11:23:37802 state_->GeneratePaymentResponse();
mathpd4be8de82017-03-01 00:51:48803}
804
Anthony Vallee-Duboisc7ae7332017-12-19 20:44:07805void PaymentRequest::HideIfNecessary() {
806 display_handle_.reset();
807}
808
Ramin Halavatifbfcef672020-05-21 16:33:34809bool PaymentRequest::IsOffTheRecord() const {
810 return delegate_->IsOffTheRecord();
Anthony Vallee-Dubois10d131a2018-02-22 15:41:04811}
812
Sahel Sharify54f5e862020-03-26 20:02:41813void PaymentRequest::OnPaymentHandlerOpenWindowCalled() {
814 DCHECK(state_->selected_app());
815 // UKM for payment app origin should get recorded only when the origin of the
816 // invoked payment app is shown to the user.
817 journey_logger_.SetPaymentAppUkmSourceId(
818 state_->selected_app()->UkmSourceId());
819}
820
sebsgfcdd13c2017-06-08 15:49:33821void PaymentRequest::RecordFirstAbortReason(
822 JourneyLogger::AbortReason abort_reason) {
823 if (!has_recorded_completion_) {
824 has_recorded_completion_ = true;
825 journey_logger_.SetAborted(abort_reason);
sebsg2c8558a2017-05-17 18:54:10826 }
827}
828
Danyao Wang03a4cbd2019-08-15 23:47:11829void PaymentRequest::CanMakePaymentCallback(bool can_make_payment) {
830 client_->OnCanMakePayment(
831 can_make_payment ? mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT
832 : mojom::CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT);
Danyao Wang4bc0606a2018-12-27 16:54:53833
Danyao Wang57aa0442019-01-31 04:06:41834 journey_logger_.SetCanMakePaymentValue(can_make_payment);
Rouslan Solomakhin1804ee42017-10-03 14:27:43835
836 if (observer_for_testing_)
837 observer_for_testing_->OnCanMakePaymentReturned();
838}
839
Danyao Wangce175bf2018-12-21 22:35:58840void PaymentRequest::HasEnrolledInstrumentCallback(
Danyao Wangce175bf2018-12-21 22:35:58841 bool has_enrolled_instrument) {
Rouslan Solomakhinb26faa072019-08-19 14:42:28842 if (!spec_ || CanMakePaymentQueryFactory::GetInstance()
843 ->GetForContext(web_contents_->GetBrowserContext())
844 ->CanQuery(top_level_origin_, frame_origin_,
Dongjun Kim0a7565b2020-08-18 09:39:46845 spec_->query_for_quota())) {
Danyao Wangce175bf2018-12-21 22:35:58846 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58847 /*warn_local_development=*/false);
848 } else if (UrlUtil::IsLocalDevelopmentUrl(frame_origin_)) {
Danyao Wangce175bf2018-12-21 22:35:58849 RespondToHasEnrolledInstrumentQuery(has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58850 /*warn_local_development=*/true);
Danyao Wangce175bf2018-12-21 22:35:58851 } else {
852 client_->OnHasEnrolledInstrument(
853 HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED);
854 }
855
856 if (observer_for_testing_)
857 observer_for_testing_->OnHasEnrolledInstrumentReturned();
858}
859
Danyao Wangce175bf2018-12-21 22:35:58860void PaymentRequest::RespondToHasEnrolledInstrumentQuery(
861 bool has_enrolled_instrument,
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58862 bool warn_local_development) {
Danyao Wangce175bf2018-12-21 22:35:58863 HasEnrolledInstrumentQueryResult positive =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58864 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58865 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_ENROLLED_INSTRUMENT
866 : HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT;
867 HasEnrolledInstrumentQueryResult negative =
Rouslan Solomakhin77a7e1a2019-05-23 17:37:58868 warn_local_development
Danyao Wangce175bf2018-12-21 22:35:58869 ? HasEnrolledInstrumentQueryResult::WARNING_HAS_NO_ENROLLED_INSTRUMENT
870 : HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT;
871
872 client_->OnHasEnrolledInstrument(has_enrolled_instrument ? positive
873 : negative);
Danyao Wang57aa0442019-01-31 04:06:41874 journey_logger_.SetHasEnrolledInstrumentValue(has_enrolled_instrument);
Danyao Wangce175bf2018-12-21 22:35:58875}
876
Rouslan Solomakhin13947ae32020-05-22 15:34:29877void PaymentRequest::OnAbortResult(bool aborted) {
878 if (client_.is_bound())
879 client_->OnAbort(aborted);
880
881 if (aborted) {
882 RecordFirstAbortReason(JourneyLogger::ABORT_REASON_ABORTED_BY_MERCHANT);
883 state_->OnAbort();
884 }
885}
886
mathpf709499d2017-01-09 20:48:36887} // namespace payments