blob: 3af09f31a51b1c6e2f4a75ead291fd39b154e983 [file] [log] [blame]
mathpf1a7a3752017-03-15 11:23:371// 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
5#include "components/payments/content/payment_request_spec.h"
6
7#include <utility>
8
9#include "base/logging.h"
mathpeb8892ff2017-05-04 18:42:5510#include "base/strings/utf_string_conversions.h"
Anthony Vallee-Dubois059d59a2017-07-07 15:05:4911#include "components/payments/core/payment_instrument.h"
mathpb65623a2017-04-06 02:01:5412#include "components/payments/core/payment_method_data.h"
13#include "components/payments/core/payment_request_data_util.h"
mathpeb8892ff2017-05-04 18:42:5514#include "components/strings/grit/components_strings.h"
15#include "ui/base/l10n/l10n_util.h"
mathpf1a7a3752017-03-15 11:23:3716
17namespace payments {
18
mathpb65623a2017-04-06 02:01:5419namespace {
20
21// Returns the card network name associated with a given BasicCardNetwork. Names
22// are inspired by https://www.w3.org/Payments/card-network-ids.
23std::string GetBasicCardNetworkName(const mojom::BasicCardNetwork& network) {
24 switch (network) {
25 case mojom::BasicCardNetwork::AMEX:
26 return "amex";
27 case mojom::BasicCardNetwork::DINERS:
28 return "diners";
29 case mojom::BasicCardNetwork::DISCOVER:
30 return "discover";
31 case mojom::BasicCardNetwork::JCB:
32 return "jcb";
33 case mojom::BasicCardNetwork::MASTERCARD:
34 return "mastercard";
35 case mojom::BasicCardNetwork::MIR:
36 return "mir";
37 case mojom::BasicCardNetwork::UNIONPAY:
38 return "unionpay";
39 case mojom::BasicCardNetwork::VISA:
40 return "visa";
41 }
42 NOTREACHED();
43 return std::string();
44}
45
Rouslan Solomakhin25d708b2017-06-23 17:12:0346// Returns the card type associated with the given BasicCardType.
47autofill::CreditCard::CardType GetBasicCardType(
48 const mojom::BasicCardType& type) {
49 switch (type) {
50 case mojom::BasicCardType::CREDIT:
51 return autofill::CreditCard::CARD_TYPE_CREDIT;
52 case mojom::BasicCardType::DEBIT:
53 return autofill::CreditCard::CARD_TYPE_DEBIT;
54 case mojom::BasicCardType::PREPAID:
55 return autofill::CreditCard::CARD_TYPE_PREPAID;
56 }
57 NOTREACHED();
58 return autofill::CreditCard::CARD_TYPE_UNKNOWN;
59}
60
Anthony Vallee-Dubois059d59a2017-07-07 15:05:4961PaymentMethodData CreatePaymentMethodData(
62 const mojom::PaymentMethodDataPtr& method_data_entry) {
63 PaymentMethodData method_data;
64 method_data.supported_methods = method_data_entry->supported_methods;
65
66 // Transfer the supported basic card networks (visa, amex) and types
67 // (credit, debit).
68 for (const mojom::BasicCardNetwork& network :
69 method_data_entry->supported_networks) {
70 method_data.supported_networks.push_back(GetBasicCardNetworkName(network));
71 }
72 for (const mojom::BasicCardType& type : method_data_entry->supported_types) {
73 autofill::CreditCard::CardType card_type = GetBasicCardType(type);
74 method_data.supported_types.insert(card_type);
75 }
76 return method_data;
77}
78
79// Validates the |method_data| and fills |supported_card_networks_|,
Randall Raymondec4e0852017-07-14 01:30:4580// |supported_card_networks_set_|, |basic_card_specified_networks_|,
81// and |url_payment_method_identifiers_|.
Anthony Vallee-Dubois059d59a2017-07-07 15:05:4982void PopulateValidatedMethodData(
83 const std::vector<PaymentMethodData>& method_data_vector,
84 std::vector<std::string>* supported_card_networks,
85 std::set<std::string>* basic_card_specified_networks,
86 std::set<std::string>* supported_card_networks_set,
87 std::set<autofill::CreditCard::CardType>* supported_card_types_set,
Randall Raymondec4e0852017-07-14 01:30:4588 std::vector<std::string>* url_payment_method_identifiers,
Anthony Vallee-Dubois059d59a2017-07-07 15:05:4989 std::map<std::string, std::set<std::string>>* stringified_method_data) {
Randall Raymondec4e0852017-07-14 01:30:4590 data_util::ParseSupportedMethods(method_data_vector, supported_card_networks,
91 basic_card_specified_networks,
92 url_payment_method_identifiers);
Anthony Vallee-Dubois059d59a2017-07-07 15:05:4993 supported_card_networks_set->insert(supported_card_networks->begin(),
94 supported_card_networks->end());
95
96 data_util::ParseSupportedCardTypes(method_data_vector,
97 supported_card_types_set);
98}
99
100void PopulateValidatedMethodData(
101 const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom,
102 std::vector<std::string>* supported_card_networks,
103 std::set<std::string>* basic_card_specified_networks,
104 std::set<std::string>* supported_card_networks_set,
105 std::set<autofill::CreditCard::CardType>* supported_card_types_set,
Randall Raymondec4e0852017-07-14 01:30:45106 std::vector<std::string>* url_payment_method_identifiers,
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49107 std::map<std::string, std::set<std::string>>* stringified_method_data) {
108 std::vector<PaymentMethodData> method_data_vector;
109 method_data_vector.reserve(method_data_mojom.size());
110 for (const mojom::PaymentMethodDataPtr& method_data_entry :
111 method_data_mojom) {
112 for (const std::string& method : method_data_entry->supported_methods) {
113 (*stringified_method_data)[method].insert(
114 method_data_entry->stringified_data);
115 }
116
117 method_data_vector.push_back(CreatePaymentMethodData(method_data_entry));
118 }
119
120 PopulateValidatedMethodData(
121 method_data_vector, supported_card_networks,
122 basic_card_specified_networks, supported_card_networks_set,
Randall Raymondec4e0852017-07-14 01:30:45123 supported_card_types_set, url_payment_method_identifiers,
124 stringified_method_data);
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49125}
126
mathpb65623a2017-04-06 02:01:54127} // namespace
128
mathp600bab52017-03-26 03:47:59129const char kBasicCardMethodName[] = "basic-card";
mathpf1a7a3752017-03-15 11:23:37130
131PaymentRequestSpec::PaymentRequestSpec(
132 mojom::PaymentOptionsPtr options,
133 mojom::PaymentDetailsPtr details,
134 std::vector<mojom::PaymentMethodDataPtr> method_data,
mathpc0d616a2017-03-15 14:09:33135 Observer* observer,
136 const std::string& app_locale)
137 : options_(std::move(options)),
138 details_(std::move(details)),
mathp151bd31e2017-04-03 21:07:24139 app_locale_(app_locale),
Anthony Vallee-Duboisdb030dd2017-05-19 18:04:51140 selected_shipping_option_(nullptr) {
mathpf1a7a3752017-03-15 11:23:37141 if (observer)
142 AddObserver(observer);
mathpb77b8732017-05-11 15:26:42143 UpdateSelectedShippingOption(/*after_update=*/false);
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49144 PopulateValidatedMethodData(
145 method_data, &supported_card_networks_, &basic_card_specified_networks_,
146 &supported_card_networks_set_, &supported_card_types_set_,
Randall Raymondec4e0852017-07-14 01:30:45147 &url_payment_method_identifiers_, &stringified_method_data_);
mathpf1a7a3752017-03-15 11:23:37148}
149PaymentRequestSpec::~PaymentRequestSpec() {}
150
mathp151bd31e2017-04-03 21:07:24151void PaymentRequestSpec::UpdateWith(mojom::PaymentDetailsPtr details) {
152 details_ = std::move(details);
153 // We reparse the |details_| and update the observers.
mathpb77b8732017-05-11 15:26:42154 UpdateSelectedShippingOption(/*after_update=*/true);
mathp151bd31e2017-04-03 21:07:24155 NotifyOnSpecUpdated();
Anthony Vallee-Duboisdb030dd2017-05-19 18:04:51156 current_update_reason_ = UpdateReason::NONE;
mathp151bd31e2017-04-03 21:07:24157}
158
mathpf1a7a3752017-03-15 11:23:37159void PaymentRequestSpec::AddObserver(Observer* observer) {
160 CHECK(observer);
161 observers_.AddObserver(observer);
162}
163
164void PaymentRequestSpec::RemoveObserver(Observer* observer) {
165 observers_.RemoveObserver(observer);
166}
167
tmartino5f0912b82017-03-30 03:20:52168bool PaymentRequestSpec::request_shipping() const {
169 return options_->request_shipping;
170}
171bool PaymentRequestSpec::request_payer_name() const {
172 return options_->request_payer_name;
173}
174bool PaymentRequestSpec::request_payer_phone() const {
175 return options_->request_payer_phone;
176}
177bool PaymentRequestSpec::request_payer_email() const {
178 return options_->request_payer_email;
179}
180
181PaymentShippingType PaymentRequestSpec::shipping_type() const {
182 // Transform Mojo-specific enum into platform-agnostic equivalent.
183 switch (options_->shipping_type) {
184 case mojom::PaymentShippingType::DELIVERY:
185 return PaymentShippingType::DELIVERY;
186 case payments::mojom::PaymentShippingType::PICKUP:
187 return PaymentShippingType::PICKUP;
188 case payments::mojom::PaymentShippingType::SHIPPING:
189 return PaymentShippingType::SHIPPING;
190 default:
191 NOTREACHED();
192 }
193 // Needed for compilation on some platforms.
194 return PaymentShippingType::SHIPPING;
195}
196
mathp600bab52017-03-26 03:47:59197bool PaymentRequestSpec::IsMethodSupportedThroughBasicCard(
198 const std::string& method_name) {
199 return basic_card_specified_networks_.count(method_name) > 0;
200}
201
mathpc0d616a2017-03-15 14:09:33202base::string16 PaymentRequestSpec::GetFormattedCurrencyAmount(
Anthony Vallee-Dubois080d5b72017-05-11 22:34:04203 const mojom::PaymentCurrencyAmountPtr& currency_amount) {
mathpc0d616a2017-03-15 14:09:33204 CurrencyFormatter* formatter = GetOrCreateCurrencyFormatter(
Anthony Vallee-Dubois080d5b72017-05-11 22:34:04205 currency_amount->currency, currency_amount->currency_system, app_locale_);
206 return formatter->Format(currency_amount->value);
mathpc0d616a2017-03-15 14:09:33207}
208
Anthony Vallee-Dubois080d5b72017-05-11 22:34:04209std::string PaymentRequestSpec::GetFormattedCurrencyCode(
210 const mojom::PaymentCurrencyAmountPtr& currency_amount) {
mathpc0d616a2017-03-15 14:09:33211 CurrencyFormatter* formatter = GetOrCreateCurrencyFormatter(
Anthony Vallee-Dubois080d5b72017-05-11 22:34:04212 currency_amount->currency, currency_amount->currency_system, app_locale_);
mathpc0d616a2017-03-15 14:09:33213
214 return formatter->formatted_currency_code();
215}
216
anthonyvd2f30baa12017-04-13 22:30:50217void PaymentRequestSpec::StartWaitingForUpdateWith(
218 PaymentRequestSpec::UpdateReason reason) {
Anthony Vallee-Duboisdb030dd2017-05-19 18:04:51219 current_update_reason_ = reason;
anthonyvd2f30baa12017-04-13 22:30:50220 for (auto& observer : observers_) {
221 observer.OnStartUpdating(reason);
222 }
223}
224
Anthony Vallee-Dubois080d5b72017-05-11 22:34:04225bool PaymentRequestSpec::IsMixedCurrency() const {
226 const std::string& total_currency = details_->total->amount->currency;
227 return std::any_of(details_->display_items.begin(),
228 details_->display_items.end(),
229 [&total_currency](const mojom::PaymentItemPtr& item) {
230 return item->amount->currency != total_currency;
231 });
232}
233
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49234const mojom::PaymentItemPtr& PaymentRequestSpec::GetTotal(
235 PaymentInstrument* selected_instrument) const {
236 const mojom::PaymentDetailsModifierPtr* modifier =
237 GetApplicableModifier(selected_instrument);
238 return modifier ? (*modifier)->total : details().total;
239}
rouslan690997682017-05-09 18:07:39240
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49241std::vector<const mojom::PaymentItemPtr*> PaymentRequestSpec::GetDisplayItems(
242 PaymentInstrument* selected_instrument) const {
243 std::vector<const mojom::PaymentItemPtr*> display_items;
244 const mojom::PaymentDetailsModifierPtr* modifier =
245 GetApplicableModifier(selected_instrument);
246 for (const auto& item : details().display_items) {
247 display_items.push_back(&item);
mathpf1a7a3752017-03-15 11:23:37248 }
mathp363735b2017-03-16 18:08:05249
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49250 if (modifier) {
251 for (const auto& additional_item : (*modifier)->additional_display_items) {
252 display_items.push_back(&additional_item);
253 }
254 }
255 return display_items;
256}
Rouslan Solomakhin25d708b2017-06-23 17:12:03257
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49258const std::vector<mojom::PaymentShippingOptionPtr>&
259PaymentRequestSpec::GetShippingOptions() const {
260 return details().shipping_options;
261}
262
263const mojom::PaymentDetailsModifierPtr*
264PaymentRequestSpec::GetApplicableModifier(
265 PaymentInstrument* selected_instrument) const {
266 if (!selected_instrument)
267 return nullptr;
268
269 for (const auto& modifier : details().modifiers) {
270 std::vector<std::string> supported_networks;
271 std::set<autofill::CreditCard::CardType> supported_types;
Randall Raymondec4e0852017-07-14 01:30:45272 // The following 4 are unused but required by PopulateValidatedMethodData.
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49273 std::set<std::string> basic_card_specified_networks;
274 std::set<std::string> supported_card_networks_set;
Randall Raymondec4e0852017-07-14 01:30:45275 std::vector<std::string> url_payment_method_identifiers;
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49276 std::map<std::string, std::set<std::string>> stringified_method_data;
277 PopulateValidatedMethodData(
278 {CreatePaymentMethodData(modifier->method_data)}, &supported_networks,
279 &basic_card_specified_networks, &supported_card_networks_set,
Randall Raymondec4e0852017-07-14 01:30:45280 &supported_types, &url_payment_method_identifiers,
281 &stringified_method_data);
Anthony Vallee-Dubois059d59a2017-07-07 15:05:49282
283 if (selected_instrument->IsValidForModifier(
284 modifier->method_data->supported_methods, supported_types,
285 supported_networks)) {
286 return &modifier;
287 }
288 }
289 return nullptr;
mathpf1a7a3752017-03-15 11:23:37290}
291
mathpb77b8732017-05-11 15:26:42292void PaymentRequestSpec::UpdateSelectedShippingOption(bool after_update) {
mathp151bd31e2017-04-03 21:07:24293 if (!request_shipping())
294 return;
295
mathpb77b8732017-05-11 15:26:42296 selected_shipping_option_ = nullptr;
mathpeb8892ff2017-05-04 18:42:55297 selected_shipping_option_error_.clear();
mathpb77b8732017-05-11 15:26:42298 if (details().shipping_options.empty()) {
299 // No options are provided by the merchant.
300 if (after_update) {
Mathieu Perreault2c1f3192017-05-18 14:45:28301 // This is after an update, which means that the selected address is not
mathpb77b8732017-05-11 15:26:42302 // supported. The merchant may have customized the error string, or a
303 // generic one is used.
304 if (!details().error.empty()) {
305 selected_shipping_option_error_ = base::UTF8ToUTF16(details().error);
306 } else {
Mathieu Perreault2c1f3192017-05-18 14:45:28307 // The generic error string depends on the shipping type.
308 switch (shipping_type()) {
309 case PaymentShippingType::DELIVERY:
310 selected_shipping_option_error_ = l10n_util::GetStringUTF16(
311 IDS_PAYMENTS_UNSUPPORTED_DELIVERY_ADDRESS);
312 break;
313 case PaymentShippingType::PICKUP:
314 selected_shipping_option_error_ = l10n_util::GetStringUTF16(
315 IDS_PAYMENTS_UNSUPPORTED_PICKUP_ADDRESS);
316 break;
317 case PaymentShippingType::SHIPPING:
318 selected_shipping_option_error_ = l10n_util::GetStringUTF16(
319 IDS_PAYMENTS_UNSUPPORTED_SHIPPING_ADDRESS);
320 break;
321 }
mathpb77b8732017-05-11 15:26:42322 }
323 }
324 return;
325 }
326
mathp151bd31e2017-04-03 21:07:24327 // As per the spec, the selected shipping option should initially be the last
mathpb77b8732017-05-11 15:26:42328 // one in the array that has its selected field set to true. If none are
329 // selected by the merchant, |selected_shipping_option_| stays nullptr.
mathp151bd31e2017-04-03 21:07:24330 auto selected_shipping_option_it = std::find_if(
331 details().shipping_options.rbegin(), details().shipping_options.rend(),
332 [](const payments::mojom::PaymentShippingOptionPtr& element) {
333 return element->selected;
334 });
335 if (selected_shipping_option_it != details().shipping_options.rend()) {
336 selected_shipping_option_ = selected_shipping_option_it->get();
337 }
338}
339
mathp151bd31e2017-04-03 21:07:24340void PaymentRequestSpec::NotifyOnSpecUpdated() {
341 for (auto& observer : observers_)
342 observer.OnSpecUpdated();
mathpf1a7a3752017-03-15 11:23:37343}
344
mathpc0d616a2017-03-15 14:09:33345CurrencyFormatter* PaymentRequestSpec::GetOrCreateCurrencyFormatter(
346 const std::string& currency_code,
347 const std::string& currency_system,
348 const std::string& locale_name) {
Anthony Vallee-Dubois080d5b72017-05-11 22:34:04349 // Create a currency formatter for |currency_code|, or if already created
350 // return the cached version.
351 std::pair<std::map<std::string, CurrencyFormatter>::iterator, bool>
352 emplace_result = currency_formatters_.emplace(
353 std::piecewise_construct, std::forward_as_tuple(currency_code),
354 std::forward_as_tuple(currency_code, currency_system, locale_name));
355
356 return &(emplace_result.first->second);
mathpc0d616a2017-03-15 14:09:33357}
358
mathpf1a7a3752017-03-15 11:23:37359} // namespace payments