Commit 080d5b71 authored by Anthony Vallee-Dubois's avatar Anthony Vallee-Dubois Committed by Commit Bot

[Web Payments] Handle mixed currencies in UI.

Bug: 709296
Change-Id: I79d50b9faaab080f79b95d7da334b500aa37a5b4
Reviewed-on: https://chromium-review.googlesource.com/503490Reviewed-by: default avatarMathieu Perreault <mathp@chromium.org>
Commit-Queue: Mathieu Perreault <mathp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#471179}
parent a08630cb
......@@ -134,6 +134,7 @@ void OrderSummaryViewController::FillContentView(views::View* content_view) {
views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
content_view->SetLayoutManager(layout);
bool is_mixed_currency = spec()->IsMixedCurrency();
// Set the ID for the first few line items labels, for testing.
const std::vector<DialogViewID> line_items{
DialogViewID::ORDER_SUMMARY_LINE_ITEM_1,
......@@ -142,20 +143,30 @@ void OrderSummaryViewController::FillContentView(views::View* content_view) {
for (size_t i = 0; i < spec()->details().display_items.size(); i++) {
DialogViewID view_id =
i < line_items.size() ? line_items[i] : DialogViewID::VIEW_ID_NONE;
base::string16 value_string;
if (is_mixed_currency) {
value_string = l10n_util::GetStringFUTF16(
IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SHEET_TOTAL_FORMAT,
base::UTF8ToUTF16(
spec()->details().display_items[i]->amount->currency),
spec()->GetFormattedCurrencyAmount(
spec()->details().display_items[i]->amount));
} else {
value_string = spec()->GetFormattedCurrencyAmount(
spec()->details().display_items[i]->amount);
}
content_view->AddChildView(
CreateLineItemView(
base::UTF8ToUTF16(spec()->details().display_items[i]->label),
spec()->GetFormattedCurrencyAmount(
spec()->details().display_items[i]->amount->value),
false, view_id)
value_string, false, view_id)
.release());
}
base::string16 total_label_value = l10n_util::GetStringFUTF16(
IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SHEET_TOTAL_FORMAT,
base::UTF8ToUTF16(spec()->details().total->amount->currency),
spec()->GetFormattedCurrencyAmount(
spec()->details().total->amount->value));
spec()->GetFormattedCurrencyAmount(spec()->details().total->amount));
content_view->AddChildView(
CreateLineItemView(base::UTF8ToUTF16(spec()->details().total->label),
......
......@@ -576,6 +576,8 @@ PaymentSheetViewController::CreatePaymentSheetSummaryRow() {
const std::vector<mojom::PaymentItemPtr>& items =
spec()->details().display_items;
bool is_mixed_currency = spec()->IsMixedCurrency();
// The inline items section contains the first 2 display items of the
// request's details, followed by a label indicating "N more items..." if
// there are more than 2 items in the details. The total label and amount
......@@ -588,8 +590,18 @@ PaymentSheetViewController::CreatePaymentSheetSummaryRow() {
summary->SetHorizontalAlignment(gfx::ALIGN_LEFT);
layout->AddView(summary.release());
layout->AddView(new views::Label(
spec()->GetFormattedCurrencyAmount(items[i]->amount->value)));
base::string16 item_amount;
if (is_mixed_currency) {
// If the payment request has items in different currencies, always
// display the currency code.
item_amount = l10n_util::GetStringFUTF16(
IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SHEET_TOTAL_FORMAT,
base::UTF8ToUTF16(spec()->GetFormattedCurrencyCode(items[i]->amount)),
spec()->GetFormattedCurrencyAmount(items[i]->amount));
} else {
item_amount = spec()->GetFormattedCurrencyAmount(items[i]->amount);
}
layout->AddView(new views::Label(item_amount));
}
int hidden_item_count = items.size() - kMaxNumberOfItemsShown;
......@@ -612,9 +624,10 @@ PaymentSheetViewController::CreatePaymentSheetSummaryRow() {
layout->AddView(
CreateBoldLabel(l10n_util::GetStringFUTF16(
IDS_PAYMENT_REQUEST_ORDER_SUMMARY_SHEET_TOTAL_FORMAT,
base::UTF8ToUTF16(spec()->GetFormattedCurrencyCode()),
base::UTF8ToUTF16(spec()->GetFormattedCurrencyCode(
spec()->details().total->amount)),
spec()->GetFormattedCurrencyAmount(
spec()->details().total->amount->value)))
spec()->details().total->amount)))
.release());
inline_summary->SetLayoutManager(layout.release());
......@@ -887,10 +900,10 @@ PaymentSheetViewController::CreateShippingOptionRow() {
current_update_reason_ ==
PaymentRequestSpec::UpdateReason::SHIPPING_OPTION
? CreateCheckingSpinnerView()
: CreateShippingOptionLabel(selected_option,
spec()->GetFormattedCurrencyAmount(
selected_option->amount->value),
/*emphasize_label=*/false);
: CreateShippingOptionLabel(
selected_option,
spec()->GetFormattedCurrencyAmount(selected_option->amount),
/*emphasize_label=*/false);
return builder.Id(DialogViewID::PAYMENT_SHEET_SHIPPING_OPTION_SECTION)
.CreateWithChevron(std::move(option_row_content), nullptr);
} else {
......
......@@ -33,7 +33,7 @@ class ShippingOptionItem : public PaymentRequestItemList::Item {
std::unique_ptr<views::View> CreateContentView() override {
return CreateShippingOptionLabel(
shipping_option_,
spec()->GetFormattedCurrencyAmount(shipping_option_->amount->value),
spec()->GetFormattedCurrencyAmount(shipping_option_->amount),
/*emphasize_label=*/true);
}
......
......@@ -115,17 +115,16 @@ bool PaymentRequestSpec::IsMethodSupportedThroughBasicCard(
}
base::string16 PaymentRequestSpec::GetFormattedCurrencyAmount(
const std::string& amount) {
const mojom::PaymentCurrencyAmountPtr& currency_amount) {
CurrencyFormatter* formatter = GetOrCreateCurrencyFormatter(
details_->total->amount->currency,
details_->total->amount->currency_system, app_locale_);
return formatter->Format(amount);
currency_amount->currency, currency_amount->currency_system, app_locale_);
return formatter->Format(currency_amount->value);
}
std::string PaymentRequestSpec::GetFormattedCurrencyCode() {
std::string PaymentRequestSpec::GetFormattedCurrencyCode(
const mojom::PaymentCurrencyAmountPtr& currency_amount) {
CurrencyFormatter* formatter = GetOrCreateCurrencyFormatter(
details_->total->amount->currency,
details_->total->amount->currency_system, app_locale_);
currency_amount->currency, currency_amount->currency_system, app_locale_);
return formatter->formatted_currency_code();
}
......@@ -137,6 +136,15 @@ void PaymentRequestSpec::StartWaitingForUpdateWith(
}
}
bool PaymentRequestSpec::IsMixedCurrency() const {
const std::string& total_currency = details_->total->amount->currency;
return std::any_of(details_->display_items.begin(),
details_->display_items.end(),
[&total_currency](const mojom::PaymentItemPtr& item) {
return item->amount->currency != total_currency;
});
}
void PaymentRequestSpec::PopulateValidatedMethodData(
const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom) {
std::vector<PaymentMethodData> method_data_vector;
......@@ -216,11 +224,14 @@ CurrencyFormatter* PaymentRequestSpec::GetOrCreateCurrencyFormatter(
const std::string& currency_code,
const std::string& currency_system,
const std::string& locale_name) {
if (!currency_formatter_) {
currency_formatter_.reset(
new CurrencyFormatter(currency_code, currency_system, locale_name));
}
return currency_formatter_.get();
// Create a currency formatter for |currency_code|, or if already created
// return the cached version.
std::pair<std::map<std::string, CurrencyFormatter>::iterator, bool>
emplace_result = currency_formatters_.emplace(
std::piecewise_construct, std::forward_as_tuple(currency_code),
std::forward_as_tuple(currency_code, currency_system, locale_name));
return &(emplace_result.first->second);
}
} // namespace payments
......@@ -87,15 +87,15 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
// not supported at all, or specified directly in supportedMethods.
bool IsMethodSupportedThroughBasicCard(const std::string& method_name);
// Uses CurrencyFormatter to format |amount| with the currency symbol for this
// request's currency. Will use currency of the "total" display item, because
// all items are supposed to have the same currency in a given request.
base::string16 GetFormattedCurrencyAmount(const std::string& amount);
// Uses CurrencyFormatter to format the value of |currency_amount| with the
// currency symbol for its currency.
base::string16 GetFormattedCurrencyAmount(
const mojom::PaymentCurrencyAmountPtr& currency_amount);
// Uses CurrencyFormatter to get the formatted currency code for this
// request's currency. Will use currency of the "total" display item, because
// all items are supposed to have the same currency in a given request.
std::string GetFormattedCurrencyCode();
// Uses CurrencyFormatter to get the formatted currency code for
// |currency_amount|'s currency.
std::string GetFormattedCurrencyCode(
const mojom::PaymentCurrencyAmountPtr& currency_amount);
mojom::PaymentShippingOption* selected_shipping_option() const {
return selected_shipping_option_;
......@@ -110,6 +110,8 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
void StartWaitingForUpdateWith(UpdateReason reason);
bool IsMixedCurrency() const;
private:
friend class PaymentRequestDialogView;
void add_observer_for_testing(Observer* observer_for_testing) {
......@@ -148,7 +150,8 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
mojom::PaymentShippingOption* selected_shipping_option_;
base::string16 selected_shipping_option_error_;
std::unique_ptr<CurrencyFormatter> currency_formatter_;
// One currency formatter is instantiated and cached per currency code.
std::map<std::string, CurrencyFormatter> currency_formatters_;
// A list/set of supported basic card networks. The list is used to keep the
// order in which they were specified by the merchant. The set is used for
......
......@@ -341,4 +341,93 @@ TEST_F(PaymentRequestSpecTest, ShippingOptionsSelection_NoOptionsAtAll) {
spec()->selected_shipping_option_error());
}
TEST_F(PaymentRequestSpecTest, SingleCurrencyWithoutDisplayItems) {
mojom::PaymentDetailsPtr details = mojom::PaymentDetails::New();
mojom::PaymentItemPtr total = mojom::PaymentItem::New();
mojom::PaymentCurrencyAmountPtr amount = mojom::PaymentCurrencyAmount::New();
amount->currency = "USD";
total->amount = std::move(amount);
details->total = std::move(total);
RecreateSpecWithOptionsAndDetails(mojom::PaymentOptions::New(),
std::move(details));
// If the request only has a total, it must not have mixed currencies.
EXPECT_FALSE(spec()->IsMixedCurrency());
}
TEST_F(PaymentRequestSpecTest, SingleCurrencyWithDisplayItems) {
mojom::PaymentDetailsPtr details = mojom::PaymentDetails::New();
mojom::PaymentItemPtr total = mojom::PaymentItem::New();
mojom::PaymentCurrencyAmountPtr amount = mojom::PaymentCurrencyAmount::New();
amount->currency = "USD";
total->amount = std::move(amount);
details->total = std::move(total);
mojom::PaymentItemPtr display_item = mojom::PaymentItem::New();
mojom::PaymentCurrencyAmountPtr display_amount =
mojom::PaymentCurrencyAmount::New();
display_amount->currency = "USD";
display_item->amount = std::move(display_amount);
details->display_items.push_back(std::move(display_item));
RecreateSpecWithOptionsAndDetails(mojom::PaymentOptions::New(),
std::move(details));
// Both the total and the display item have matching currency codes, this
// isn't a mixed currency case.
EXPECT_FALSE(spec()->IsMixedCurrency());
}
TEST_F(PaymentRequestSpecTest, MultipleCurrenciesWithOneDisplayItem) {
mojom::PaymentDetailsPtr details = mojom::PaymentDetails::New();
mojom::PaymentItemPtr total = mojom::PaymentItem::New();
mojom::PaymentCurrencyAmountPtr amount = mojom::PaymentCurrencyAmount::New();
amount->currency = "USD";
total->amount = std::move(amount);
details->total = std::move(total);
mojom::PaymentItemPtr display_item = mojom::PaymentItem::New();
mojom::PaymentCurrencyAmountPtr display_amount =
mojom::PaymentCurrencyAmount::New();
display_amount->currency = "CAD";
display_item->amount = std::move(display_amount);
details->display_items.push_back(std::move(display_item));
RecreateSpecWithOptionsAndDetails(mojom::PaymentOptions::New(),
std::move(details));
// The display item currency and the total's currency don't match, this is a
// mixed currencies case.
EXPECT_TRUE(spec()->IsMixedCurrency());
}
TEST_F(PaymentRequestSpecTest, MultipleCurrenciesWithTwoDisplayItem) {
mojom::PaymentDetailsPtr details = mojom::PaymentDetails::New();
mojom::PaymentItemPtr total = mojom::PaymentItem::New();
mojom::PaymentCurrencyAmountPtr amount = mojom::PaymentCurrencyAmount::New();
amount->currency = "USD";
total->amount = std::move(amount);
details->total = std::move(total);
mojom::PaymentItemPtr display_item1 = mojom::PaymentItem::New();
mojom::PaymentCurrencyAmountPtr display_amount1 =
mojom::PaymentCurrencyAmount::New();
display_amount1->currency = "CAD";
display_item1->amount = std::move(display_amount1);
details->display_items.push_back(std::move(display_item1));
mojom::PaymentItemPtr display_item2 = mojom::PaymentItem::New();
mojom::PaymentCurrencyAmountPtr display_amount2 =
mojom::PaymentCurrencyAmount::New();
display_amount2->currency = "USD";
display_item2->amount = std::move(display_amount2);
details->display_items.push_back(std::move(display_item2));
RecreateSpecWithOptionsAndDetails(mojom::PaymentOptions::New(),
std::move(details));
// At least one of the display items has a different currency, this is a mixed
// currency case.
EXPECT_TRUE(spec()->IsMixedCurrency());
}
} // namespace payments
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment