Commit 059d59a5 authored by Anthony Vallee-Dubois's avatar Anthony Vallee-Dubois Committed by Commit Bot

[Web Payments] Implement modifier support

Bug: 618857
Change-Id: I2c17940cd681874a8f0582f2066069f47744a955
Reviewed-on: https://chromium-review.googlesource.com/550401
Commit-Queue: Anthony Vallee-Dubois <anthonyvd@chromium.org>
Reviewed-by: default avatarMathieu Perreault <mathp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#484926}
parent a29d2a46
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <vector>
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
namespace payments {
class PaymentRequestModifiersTest : public PaymentRequestBrowserTestBase {
protected:
PaymentRequestModifiersTest()
: PaymentRequestBrowserTestBase(
"/payment_request_bobpay_and_basic_card_with_basic_card_modifiers_"
"test.html") {}
void SetUpCommandLine(base::CommandLine* command_line) override {
PaymentRequestBrowserTestBase::SetUpCommandLine(command_line);
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
}
private:
DISALLOW_COPY_AND_ASSIGN(PaymentRequestModifiersTest);
};
IN_PROC_BROWSER_TEST_F(PaymentRequestModifiersTest,
NoModifierAppliedIfNoSelectedInstrument) {
InvokePaymentRequestUI();
OpenOrderSummaryScreen();
EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
// There's only the total line.
EXPECT_EQ(1, dialog_view()
->view_stack_for_testing()
->top()
->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
->child_count());
}
IN_PROC_BROWSER_TEST_F(
PaymentRequestModifiersTest,
ModifierAppliedIfApplicableSelectedInstrumentWithoutTypeOrNetwork) {
autofill::AutofillProfile profile(autofill::test::GetFullProfile());
AddAutofillProfile(profile);
autofill::CreditCard card(
autofill::test::GetMaskedServerCard()); // Mastercard card.
card.set_billing_address_id(profile.guid());
card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
AddCreditCard(card);
InvokePaymentRequestUI();
OpenOrderSummaryScreen();
EXPECT_EQ(base::ASCIIToUTF16("$4.00"),
GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
// A line for the discount and one for the total.
EXPECT_EQ(2, dialog_view()
->view_stack_for_testing()
->top()
->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
->child_count());
}
IN_PROC_BROWSER_TEST_F(
PaymentRequestModifiersTest,
ModifierAppliedIfApplicableSelectedInstrumentWithCreditSupportedType) {
autofill::AutofillProfile profile(autofill::test::GetFullProfile());
AddAutofillProfile(profile);
autofill::CreditCard card(
autofill::test::GetMaskedServerCard()); // Mastercard card.
card.set_billing_address_id(profile.guid());
card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
AddCreditCard(card);
ResetEventObserver(DialogEvent::DIALOG_OPENED);
content::WebContents* web_contents = GetActiveWebContents();
const std::string click_buy_button_js =
"(function() { "
"document.getElementById('credit_supported_type')."
"click(); })();";
ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
WaitForObservedEvent();
// The web-modal dialog should be open.
web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
OpenOrderSummaryScreen();
EXPECT_EQ(base::ASCIIToUTF16("$4.00"),
GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
// A line for the discount and one for the total.
EXPECT_EQ(2, dialog_view()
->view_stack_for_testing()
->top()
->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
->child_count());
}
IN_PROC_BROWSER_TEST_F(
PaymentRequestModifiersTest,
ModifierNotAppliedIfSelectedInstrumentWithDebitSupportedType) {
autofill::AutofillProfile profile(autofill::test::GetFullProfile());
AddAutofillProfile(profile);
autofill::CreditCard card(
autofill::test::GetMaskedServerCard()); // Mastercard card.
card.set_billing_address_id(profile.guid());
card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
AddCreditCard(card);
ResetEventObserver(DialogEvent::DIALOG_OPENED);
content::WebContents* web_contents = GetActiveWebContents();
const std::string click_buy_button_js =
"(function() { "
"document.getElementById('debit_supported_type').click("
"); })();";
ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
WaitForObservedEvent();
// The web-modal dialog should be open.
web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
OpenOrderSummaryScreen();
EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
// There's only the total line.
EXPECT_EQ(1, dialog_view()
->view_stack_for_testing()
->top()
->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
->child_count());
}
IN_PROC_BROWSER_TEST_F(
PaymentRequestModifiersTest,
ModifierAppliedIfApplicableSelectedInstrumentWithMatchingNetwork) {
autofill::AutofillProfile profile(autofill::test::GetFullProfile());
AddAutofillProfile(profile);
autofill::CreditCard card(
autofill::test::GetMaskedServerCard()); // Mastercard card.
card.set_billing_address_id(profile.guid());
card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
AddCreditCard(card);
ResetEventObserver(DialogEvent::DIALOG_OPENED);
content::WebContents* web_contents = GetActiveWebContents();
const std::string click_buy_button_js =
"(function() { "
"document.getElementById('mastercard_supported_network'"
").click(); })();";
ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
WaitForObservedEvent();
// The web-modal dialog should be open.
web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
OpenOrderSummaryScreen();
EXPECT_EQ(base::ASCIIToUTF16("$4.00"),
GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
// A line for the discount and one for the total.
EXPECT_EQ(2, dialog_view()
->view_stack_for_testing()
->top()
->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
->child_count());
}
IN_PROC_BROWSER_TEST_F(
PaymentRequestModifiersTest,
ModifierNotAppliedIfSelectedInstrumentWithoutMatchingNetwork) {
autofill::AutofillProfile profile(autofill::test::GetFullProfile());
AddAutofillProfile(profile);
autofill::CreditCard card(
autofill::test::GetMaskedServerCard()); // Mastercard card.
card.set_billing_address_id(profile.guid());
card.set_card_type(autofill::CreditCard::CardType::CARD_TYPE_CREDIT);
AddCreditCard(card);
ResetEventObserver(DialogEvent::DIALOG_OPENED);
content::WebContents* web_contents = GetActiveWebContents();
const std::string click_buy_button_js =
"(function() { "
"document.getElementById('visa_supported_network')."
"click(); })();";
ASSERT_TRUE(content::ExecuteScript(web_contents, click_buy_button_js));
WaitForObservedEvent();
// The web-modal dialog should be open.
web_modal::WebContentsModalDialogManager* web_contents_modal_dialog_manager =
web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
OpenOrderSummaryScreen();
EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
// There's only the total line.
EXPECT_EQ(1, dialog_view()
->view_stack_for_testing()
->top()
->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
->child_count());
}
IN_PROC_BROWSER_TEST_F(PaymentRequestModifiersTest,
ModifierNotAppliedToUnknownType) {
autofill::AutofillProfile profile(autofill::test::GetFullProfile());
AddAutofillProfile(profile);
autofill::CreditCard card(autofill::test::GetCreditCard()); // Visa card.
card.set_billing_address_id(profile.guid());
AddCreditCard(card);
InvokePaymentRequestUI();
OpenOrderSummaryScreen();
EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
// There's only the total line.
EXPECT_EQ(1, dialog_view()
->view_stack_for_testing()
->top()
->GetViewByID(static_cast<int>(DialogViewID::CONTENT_VIEW))
->child_count());
}
} // namespace payments
......@@ -170,35 +170,40 @@ void OrderSummaryViewController::FillContentView(views::View* content_view) {
DialogViewID::ORDER_SUMMARY_LINE_ITEM_1,
DialogViewID::ORDER_SUMMARY_LINE_ITEM_2,
DialogViewID::ORDER_SUMMARY_LINE_ITEM_3};
for (size_t i = 0; i < spec()->details().display_items.size(); i++) {
const auto& display_items =
spec()->GetDisplayItems(state()->selected_instrument());
for (size_t i = 0; i < display_items.size(); i++) {
DialogViewID view_id =
i < line_items.size() ? line_items[i] : DialogViewID::VIEW_ID_NONE;
base::string16 currency = base::UTF8ToUTF16("");
if (is_mixed_currency) {
currency = base::UTF8ToUTF16(
spec()->details().display_items[i]->amount->currency);
currency = base::UTF8ToUTF16((*display_items[i])->amount->currency);
}
content_view->AddChildView(
CreateLineItemView(
base::UTF8ToUTF16(spec()->details().display_items[i]->label),
currency,
spec()->GetFormattedCurrencyAmount(
spec()->details().display_items[i]->amount),
base::UTF8ToUTF16((*display_items[i])->label), currency,
spec()->GetFormattedCurrencyAmount((*display_items[i])->amount),
false, DialogViewID::VIEW_ID_NONE, 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));
base::UTF8ToUTF16(
spec()->GetTotal(state()->selected_instrument())->amount->currency),
spec()->GetFormattedCurrencyAmount(
spec()->GetTotal(state()->selected_instrument())->amount));
content_view->AddChildView(
CreateLineItemView(
base::UTF8ToUTF16(spec()->details().total->label),
base::UTF8ToUTF16(spec()->details().total->amount->currency),
spec()->GetFormattedCurrencyAmount(spec()->details().total->amount),
base::UTF8ToUTF16(
spec()->GetTotal(state()->selected_instrument())->label),
base::UTF8ToUTF16(spec()
->GetTotal(state()->selected_instrument())
->amount->currency),
spec()->GetFormattedCurrencyAmount(
spec()->GetTotal(state()->selected_instrument())->amount),
true, DialogViewID::ORDER_SUMMARY_TOTAL_CURRENCY_LABEL,
DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL)
.release());
......
......@@ -14,6 +14,7 @@ namespace payments {
enum class DialogViewID : int {
VIEW_ID_NONE = autofill::MAX_VALID_FIELD_TYPE,
CONTENT_VIEW, // The main content view filled by each sheet
// The following are views::Button (clickable).
PAYMENT_SHEET_CONTACT_INFO_SECTION,
......
......@@ -250,6 +250,7 @@ std::unique_ptr<views::View> PaymentRequestSheetController::CreateView() {
content_view_->SetPaintToLayer();
content_view_->layer()->SetFillsBoundsOpaquely(true);
content_view_->SetBackground(views::CreateSolidBackground(SK_ColorWHITE));
content_view_->set_id(static_cast<int>(DialogViewID::CONTENT_VIEW));
pane_layout->AddView(content_view_);
pane_->SizeToPreferredSize();
......
......@@ -555,8 +555,8 @@ PaymentSheetViewController::CreatePaymentSheetSummaryRow() {
views::GridLayout::FIXED, kItemSummaryPriceFixedWidth,
kItemSummaryPriceFixedWidth);
const std::vector<mojom::PaymentItemPtr>& items =
spec()->details().display_items;
const std::vector<const mojom::PaymentItemPtr*>& items =
spec()->GetDisplayItems(state()->selected_instrument());
bool is_mixed_currency = spec()->IsMixedCurrency();
// The inline items section contains the first 2 display items of the
......@@ -572,7 +572,7 @@ PaymentSheetViewController::CreatePaymentSheetSummaryRow() {
for (size_t i = 0; i < items.size() && i < displayed_items; ++i) {
layout->StartRow(0, 0);
std::unique_ptr<views::Label> summary =
base::MakeUnique<views::Label>(base::UTF8ToUTF16(items[i]->label));
base::MakeUnique<views::Label>(base::UTF8ToUTF16((*items[i])->label));
summary->SetHorizontalAlignment(gfx::ALIGN_LEFT);
layout->AddView(summary.release());
......@@ -580,9 +580,10 @@ PaymentSheetViewController::CreatePaymentSheetSummaryRow() {
CreateInlineCurrencyAmountItem(
is_mixed_currency
? base::UTF8ToUTF16(
spec()->GetFormattedCurrencyCode(items[i]->amount))
spec()->GetFormattedCurrencyCode((*items[i])->amount))
: base::string16(),
spec()->GetFormattedCurrencyAmount(items[i]->amount), true, false)
spec()->GetFormattedCurrencyAmount((*items[i])->amount), true,
false)
.release());
}
......@@ -603,14 +604,17 @@ PaymentSheetViewController::CreatePaymentSheetSummaryRow() {
layout->StartRow(0, 0);
layout->AddView(
CreateBoldLabel(base::UTF8ToUTF16(spec()->details().total->label))
CreateBoldLabel(
base::UTF8ToUTF16(
spec()->GetTotal(state()->selected_instrument())->label))
.release());
layout->AddView(
CreateInlineCurrencyAmountItem(
base::UTF8ToUTF16(spec()->GetFormattedCurrencyCode(
spec()->details().total->amount)),
spec()->GetFormattedCurrencyAmount(spec()->details().total->amount),
spec()->GetTotal(state()->selected_instrument())->amount)),
spec()->GetFormattedCurrencyAmount(
spec()->GetTotal(state()->selected_instrument())->amount),
false, true)
.release());
......@@ -849,7 +853,7 @@ PaymentSheetViewController::CreateShippingOptionRow() {
builder.Tag(PaymentSheetViewControllerTags::SHOW_SHIPPING_OPTION_BUTTON);
if (state()->selected_shipping_profile()) {
if (spec()->details().shipping_options.empty()) {
if (spec()->GetShippingOptions().empty()) {
// 1.1 No shipping options, do not display the row.
return nullptr;
}
......@@ -868,16 +872,16 @@ PaymentSheetViewController::CreateShippingOptionRow() {
} else {
// 1.3 There are options, none are selected: show the enabled Choose
// button.
const auto& shipping_options = spec()->GetShippingOptions();
return builder
.Id(DialogViewID::PAYMENT_SHEET_SHIPPING_OPTION_SECTION_BUTTON)
.CreateWithButton(
base::UTF8ToUTF16(spec()->details().shipping_options[0]->label),
l10n_util::GetPluralStringFUTF16(
IDS_PAYMENT_REQUEST_SHIPPING_OPTIONS_PREVIEW,
spec()->details().shipping_options.size() - 1),
spec()->details().shipping_options.size() - 1,
l10n_util::GetStringUTF16(IDS_CHOOSE),
/*button_enabled=*/true);
.CreateWithButton(base::UTF8ToUTF16(shipping_options[0]->label),
l10n_util::GetPluralStringFUTF16(
IDS_PAYMENT_REQUEST_SHIPPING_OPTIONS_PREVIEW,
shipping_options.size() - 1),
shipping_options.size() - 1,
l10n_util::GetStringUTF16(IDS_CHOOSE),
/*button_enabled=*/true);
}
} else {
// 2. There is no selected address: do not show the shipping option section.
......
......@@ -172,7 +172,7 @@ class ShippingProfileViewController : public ProfileListViewController,
// | Warning icon | Warning message |
// ---------------------------------------------
std::unique_ptr<views::View> CreateHeaderView() override {
if (!spec()->details().shipping_options.empty())
if (!spec()->GetShippingOptions().empty())
return nullptr;
auto header_view = base::MakeUnique<views::View>();
......
......@@ -80,7 +80,7 @@ ShippingOptionViewController::ShippingOptionViewController(
PaymentRequestDialogView* dialog)
: PaymentRequestSheetController(spec, state, dialog) {
spec->AddObserver(this);
for (const auto& option : spec->details().shipping_options) {
for (const auto& option : spec->GetShippingOptions()) {
shipping_option_list_.AddItem(base::MakeUnique<ShippingOptionItem>(
option.get(), spec, state, &shipping_option_list_, dialog,
option.get() == spec->selected_shipping_option()));
......
......@@ -48,6 +48,9 @@ class ViewStack : public views::BoundsAnimatorObserver,
void Layout() override;
void RequestFocus() override;
// Returns the top state of the stack.
views::View* top() { return stack_.back().get(); }
private:
FRIEND_TEST_ALL_PREFIXES(
ViewStackTest, TestPopStateRemovesChildViewAndCleansUpState);
......@@ -58,9 +61,6 @@ class ViewStack : public views::BoundsAnimatorObserver,
friend class ViewStackTest;
friend class payments::PaymentRequestBrowserTestBase;
// Returns the top state of the stack, used in tests.
views::View* top() { return stack_.back().get(); }
// Marks all views, except the topmost, as invisible.
void HideCoveredViews();
......
......@@ -2072,6 +2072,7 @@ test("browser_tests") {
"../browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc",
"../browser/ui/views/payments/cvc_unmask_view_controller_browsertest.cc",
"../browser/ui/views/payments/error_message_view_controller_browsertest.cc",
"../browser/ui/views/payments/modifiers_browsertest.cc",
"../browser/ui/views/payments/order_summary_view_controller_browsertest.cc",
"../browser/ui/views/payments/payment_method_view_controller_browsertest.cc",
"../browser/ui/views/payments/payment_request_blob_url_browsertest.cc",
......
/*
* Copyright 2017 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* global PaymentRequest:false */
/**
* Launches the PaymentRequest UI with Bob Pay and basic-card as payment
* methods and a modifier for basic-card
*/
function buy() { // eslint-disable-line no-unused-vars
try {
new PaymentRequest(
[{supportedMethods: ['https://bobpay.com', 'basic-card']}],
{
total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
modifiers: [{
supportedMethods: ['basic-card'],
total: {
label: 'Total',
amount: {currency: 'USD', value: '4.00'},
},
additionalDisplayItems: [{
label: 'basic-card discount',
amount: {currency: 'USD', value: '-1.00'},
}],
data: {discountProgramParticipantId: '86328764873265'},
}],
})
.show()
.then(function(resp) {
resp.complete('success')
.then(function() {
print(
resp.methodName + '<br>' +
JSON.stringify(resp.details, undefined, 2));
})
.catch(function(error) {
print(error.message);
});
})
.catch(function(error) {
print(error.message);
});
} catch (error) {
print(error.message);
}
}
/**
* Launches the PaymentRequest UI with Bob Pay and basic-card as payment
* methods and a modifier for basic-card with "credit" type
*/
function creditSupportedType() { // eslint-disable-line no-unused-vars
try {
new PaymentRequest(
[{supportedMethods: ['https://bobpay.com', 'basic-card']}],
{
total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
modifiers: [{
supportedMethods: ['basic-card'],
total: {
label: 'Total',
amount: {currency: 'USD', value: '4.00'},
},
additionalDisplayItems: [{
label: 'basic-card discount',
amount: {currency: 'USD', value: '-1.00'},
}],
data: {
discountProgramParticipantId: '86328764873265',
supportedTypes: ['credit'],
},
}],
})
.show()
.then(function(resp) {
resp.complete('success')
.then(function() {
print(
resp.methodName + '<br>' +
JSON.stringify(resp.details, undefined, 2));
})
.catch(function(error) {
print(error.message);
});
})
.catch(function(error) {
print(error.message);
});
} catch (error) {
print(error.message);
}
}
/**
* Launches the PaymentRequest UI with Bob Pay and basic-card as payment
* methods and a modifier for basic-card with "debit" type
*/
function debitSupportedType() { // eslint-disable-line no-unused-vars
try {
new PaymentRequest(
[{supportedMethods: ['https://bobpay.com', 'basic-card']}],
{
total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
modifiers: [{
supportedMethods: ['basic-card'],
total: {
label: 'Total',
amount: {currency: 'USD', value: '4.00'},
},
additionalDisplayItems: [{
label: 'basic-card discount',
amount: {currency: 'USD', value: '-1.00'},
}],
data: {
discountProgramParticipantId: '86328764873265',
supportedTypes: ['debit'],
},
}],
})
.show()
.then(function(resp) {
resp.complete('success')
.then(function() {
print(
resp.methodName + '<br>' +
JSON.stringify(resp.details, undefined, 2));
})
.catch(function(error) {
print(error.message);
});
})
.catch(function(error) {
print(error.message);
});
} catch (error) {
print(error.message);
}
}
/**
* Launches the PaymentRequest UI with Bob Pay and basic-card as payment
* methods and a modifier for basic-card with "credit" type and "visa" network
*/
function visaSupportedNetwork() { // eslint-disable-line no-unused-vars
try {
new PaymentRequest(
[{supportedMethods: ['https://bobpay.com', 'basic-card']}],
{
total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
modifiers: [{
supportedMethods: ['basic-card'],
total: {
label: 'Total',
amount: {currency: 'USD', value: '4.00'},
},
additionalDisplayItems: [{
label: 'basic-card discount',
amount: {currency: 'USD', value: '-1.00'},
}],
data: {
discountProgramParticipantId: '86328764873265',
supportedTypes: ['credit'],
supportedNetworks: ['visa'],
},
}],
})
.show()
.then(function(resp) {
resp.complete('success')
.then(function() {
print(
resp.methodName + '<br>' +
JSON.stringify(resp.details, undefined, 2));
})
.catch(function(error) {
print(error.message);
});
})
.catch(function(error) {
print(error.message);
});
} catch (error) {
print(error.message);
}
}
/**
* Launches the PaymentRequest UI with Bob Pay and basic-card as payment
* methods and a modifier for basic-card with "credit" type and " mastercard"
* network
*/
function mastercardSupportedNetwork() { // eslint-disable-line no-unused-vars
try {
new PaymentRequest(
[{supportedMethods: ['https://bobpay.com', 'basic-card']}],
{
total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
modifiers: [{
supportedMethods: ['basic-card'],
total: {
label: 'Total',
amount: {currency: 'USD', value: '4.00'},
},
additionalDisplayItems: [{
label: 'basic-card discount',
amount: {currency: 'USD', value: '-1.00'},
}],
data: {
discountProgramParticipantId: '86328764873265',
supportedTypes: ['credit'],
supportedNetworks: ['mastercard'],
},
}],
})
.show()
.then(function(resp) {
resp.complete('success')
.then(function() {
print(
resp.methodName + '<br>' +
JSON.stringify(resp.details, undefined, 2));
})
.catch(function(error) {
print(error.message);
});
})
.catch(function(error) {
print(error.message);
});
} catch (error) {
print(error.message);
}
}
<!DOCTYPE html>
<!--
Copyright 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<html>
<head>
<title>Bob Pay and basic-card with Basic-Card modifiers Test</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<button onclick="buy()" id="buy">Bob Pay and Basic-Card with Basic-Card modifiers Test</button>
<button onclick="creditSupportedType()" id="credit_supported_type">Bob Pay and Basic-Card with Basic-Card modifiers Test with credit supported type</button>
<button onclick="debitSupportedType()" id="debit_supported_type">Bob Pay and Basic-Card with Basic-Card modifiers Test with debit supported type</button>
<button onclick="visaSupportedNetwork()" id="visa_supported_network">Bob Pay and Basic-Card with Basic-Card modifiers Test with visa supported network</button>
<button onclick="mastercardSupportedNetwork()" id="mastercard_supported_network">Bob Pay and Basic-Card with Basic-Card modifiers Test with mastercard supported network</button>
<pre id="result"></pre>
<script src="util.js"></script>
<script src="bobpay_and_basic_card_with_basic_card_modifiers.js"></script>
</body>
</html>
......@@ -8,6 +8,7 @@
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "components/payments/core/payment_instrument.h"
#include "components/payments/core/payment_method_data.h"
#include "components/payments/core/payment_request_data_util.h"
#include "components/strings/grit/components_strings.h"
......@@ -57,6 +58,68 @@ autofill::CreditCard::CardType GetBasicCardType(
return autofill::CreditCard::CARD_TYPE_UNKNOWN;
}
PaymentMethodData CreatePaymentMethodData(
const mojom::PaymentMethodDataPtr& method_data_entry) {
PaymentMethodData method_data;
method_data.supported_methods = method_data_entry->supported_methods;
// Transfer the supported basic card networks (visa, amex) and types
// (credit, debit).
for (const mojom::BasicCardNetwork& network :
method_data_entry->supported_networks) {
method_data.supported_networks.push_back(GetBasicCardNetworkName(network));
}
for (const mojom::BasicCardType& type : method_data_entry->supported_types) {
autofill::CreditCard::CardType card_type = GetBasicCardType(type);
method_data.supported_types.insert(card_type);
}
return method_data;
}
// Validates the |method_data| and fills |supported_card_networks_|,
// |supported_card_networks_set_| and |basic_card_specified_networks_|.
void PopulateValidatedMethodData(
const std::vector<PaymentMethodData>& method_data_vector,
std::vector<std::string>* supported_card_networks,
std::set<std::string>* basic_card_specified_networks,
std::set<std::string>* supported_card_networks_set,
std::set<autofill::CreditCard::CardType>* supported_card_types_set,
std::map<std::string, std::set<std::string>>* stringified_method_data) {
data_util::ParseBasicCardSupportedNetworks(method_data_vector,
supported_card_networks,
basic_card_specified_networks);
supported_card_networks_set->insert(supported_card_networks->begin(),
supported_card_networks->end());
data_util::ParseSupportedCardTypes(method_data_vector,
supported_card_types_set);
}
void PopulateValidatedMethodData(
const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom,
std::vector<std::string>* supported_card_networks,
std::set<std::string>* basic_card_specified_networks,
std::set<std::string>* supported_card_networks_set,
std::set<autofill::CreditCard::CardType>* supported_card_types_set,
std::map<std::string, std::set<std::string>>* stringified_method_data) {
std::vector<PaymentMethodData> method_data_vector;
method_data_vector.reserve(method_data_mojom.size());
for (const mojom::PaymentMethodDataPtr& method_data_entry :
method_data_mojom) {
for (const std::string& method : method_data_entry->supported_methods) {
(*stringified_method_data)[method].insert(
method_data_entry->stringified_data);
}
method_data_vector.push_back(CreatePaymentMethodData(method_data_entry));
}
PopulateValidatedMethodData(
method_data_vector, supported_card_networks,
basic_card_specified_networks, supported_card_networks_set,
supported_card_types_set, stringified_method_data);
}
} // namespace
const char kBasicCardMethodName[] = "basic-card";
......@@ -74,7 +137,10 @@ PaymentRequestSpec::PaymentRequestSpec(
if (observer)
AddObserver(observer);
UpdateSelectedShippingOption(/*after_update=*/false);
PopulateValidatedMethodData(method_data);
PopulateValidatedMethodData(
method_data, &supported_card_networks_, &basic_card_specified_networks_,
&supported_card_networks_set_, &supported_card_types_set_,
&stringified_method_data_);
}
PaymentRequestSpec::~PaymentRequestSpec() {}
......@@ -161,44 +227,60 @@ bool PaymentRequestSpec::IsMixedCurrency() const {
});
}
void PaymentRequestSpec::PopulateValidatedMethodData(
const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom) {
std::vector<PaymentMethodData> method_data_vector;
method_data_vector.reserve(method_data_mojom.size());
for (const mojom::PaymentMethodDataPtr& method_data_entry :
method_data_mojom) {
for (const std::string& method : method_data_entry->supported_methods) {
stringified_method_data_[method].insert(
method_data_entry->stringified_data);
}
const mojom::PaymentItemPtr& PaymentRequestSpec::GetTotal(
PaymentInstrument* selected_instrument) const {
const mojom::PaymentDetailsModifierPtr* modifier =
GetApplicableModifier(selected_instrument);
return modifier ? (*modifier)->total : details().total;
}
PaymentMethodData method_data;
method_data.supported_methods = method_data_entry->supported_methods;
std::vector<const mojom::PaymentItemPtr*> PaymentRequestSpec::GetDisplayItems(
PaymentInstrument* selected_instrument) const {
std::vector<const mojom::PaymentItemPtr*> display_items;
const mojom::PaymentDetailsModifierPtr* modifier =
GetApplicableModifier(selected_instrument);
for (const auto& item : details().display_items) {
display_items.push_back(&item);
}
// Transfer the supported basic card networks (visa, amex) and types
// (credit, debit).
for (const mojom::BasicCardNetwork& network :
method_data_entry->supported_networks) {
method_data.supported_networks.push_back(
GetBasicCardNetworkName(network));
}
for (const mojom::BasicCardType& type :
method_data_entry->supported_types) {
autofill::CreditCard::CardType card_type = GetBasicCardType(type);
method_data.supported_types.insert(card_type);
if (modifier) {
for (const auto& additional_item : (*modifier)->additional_display_items) {
display_items.push_back(&additional_item);
}
method_data_vector.push_back(std::move(method_data));
}
return display_items;
}
data_util::ParseBasicCardSupportedNetworks(method_data_vector,
&supported_card_networks_,
&basic_card_specified_networks_);
supported_card_networks_set_.insert(supported_card_networks_.begin(),
supported_card_networks_.end());
const std::vector<mojom::PaymentShippingOptionPtr>&
PaymentRequestSpec::GetShippingOptions() const {
return details().shipping_options;
}
data_util::ParseSupportedCardTypes(method_data_vector,
&supported_card_types_set_);
const mojom::PaymentDetailsModifierPtr*
PaymentRequestSpec::GetApplicableModifier(
PaymentInstrument* selected_instrument) const {
if (!selected_instrument)
return nullptr;
for (const auto& modifier : details().modifiers) {
std::vector<std::string> supported_networks;
std::set<autofill::CreditCard::CardType> supported_types;
// The following 3 are unused but required by PopulateValidatedMethodData.
std::set<std::string> basic_card_specified_networks;
std::set<std::string> supported_card_networks_set;
std::map<std::string, std::set<std::string>> stringified_method_data;
PopulateValidatedMethodData(
{CreatePaymentMethodData(modifier->method_data)}, &supported_networks,
&basic_card_specified_networks, &supported_card_networks_set,
&supported_types, &stringified_method_data);
if (selected_instrument->IsValidForModifier(
modifier->method_data->supported_methods, supported_types,
supported_networks)) {
return &modifier;
}
}
return nullptr;
}
void PaymentRequestSpec::UpdateSelectedShippingOption(bool after_update) {
......
......@@ -20,6 +20,8 @@
namespace payments {
class PaymentInstrument;
// Identifier for the basic card payment method in the PaymentMethodData.
extern const char kBasicCardMethodName[];
......@@ -112,19 +114,30 @@ class PaymentRequestSpec : public PaymentOptionsProvider {
return selected_shipping_option_error_;
}
const mojom::PaymentDetails& details() const { return *details_.get(); }
void StartWaitingForUpdateWith(UpdateReason reason);
bool IsMixedCurrency() const;
UpdateReason current_update_reason() const { return current_update_reason_; }
// Returns the total object of this payment request, taking into account the
// applicable modifier for |selected_instrument| if any.
const mojom::PaymentItemPtr& GetTotal(
PaymentInstrument* selected_instrument) const;
// Returns the display items for this payment request, taking into account the
// applicable modifier for |selected_instrument| if any.
std::vector<const mojom::PaymentItemPtr*> GetDisplayItems(
PaymentInstrument* selected_instrument) const;
const std::vector<mojom::PaymentShippingOptionPtr>& GetShippingOptions()
const;
private:
// Validates the |method_data| and fills |supported_card_networks_|,
// |supported_card_networks_set_| and |basic_card_specified_networks_|.
void PopulateValidatedMethodData(
const std::vector<mojom::PaymentMethodDataPtr>& method_data);
// Returns the first applicable modifier in the Payment Request for the
// |selected_instrument|.
const mojom::PaymentDetailsModifierPtr* GetApplicableModifier(
PaymentInstrument* selected_instrument) const;
const mojom::PaymentDetails& details() const { return *details_.get(); }
// Updates the |selected_shipping_option| based on the data passed to this
// payment request by the website. This will set selected_shipping_option_ to
......
......@@ -4,6 +4,7 @@
#include "components/payments/core/autofill_payment_instrument.h"
#include <algorithm>
#include <memory>
#include "base/json/json_writer.h"
......@@ -122,6 +123,44 @@ base::string16 AutofillPaymentInstrument::GetSublabel() const {
autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL), app_locale_);
}
bool AutofillPaymentInstrument::IsValidForModifier(
const std::vector<std::string>& method,
const std::set<autofill::CreditCard::CardType>& supported_types,
const std::vector<std::string>& supported_networks) const {
// This instrument only matches basic-card.
if (std::find(method.begin(), method.end(), "basic-card") == method.end())
return false;
// If supported_types is not specified and this instrument matches the method,
// the modifier is applicable. If supported_types is populated, it must
// contain this card's type to be applicable. The same is true for
// supported_networks.
bool is_supported_type =
supported_types.empty() ||
std::find(supported_types.begin(), supported_types.end(),
credit_card_.card_type()) != supported_types.end();
// supported_types may contain CARD_TYPE_UNKNOWN because of the parsing
// function but the modifiers shouldn't be applied since the website can't be
// sure that the instrument is an applicable card.
if (is_supported_type &&
credit_card_.card_type() ==
autofill::CreditCard::CardType::CARD_TYPE_UNKNOWN)
return false;
bool is_supported_network = supported_networks.empty();
if (!is_supported_network) {
std::string basic_card_network =
autofill::data_util::GetPaymentRequestData(credit_card_.network())
.basic_card_issuer_network;
is_supported_network =
std::find(supported_networks.begin(), supported_networks.end(),
basic_card_network) != supported_networks.end();
}
return is_supported_type && is_supported_network;
}
void AutofillPaymentInstrument::OnFullCardRequestSucceeded(
const autofill::CreditCard& card,
const base::string16& cvc) {
......
......@@ -48,6 +48,10 @@ class AutofillPaymentInstrument
void RecordUse() override;
base::string16 GetLabel() const override;
base::string16 GetSublabel() const override;
bool IsValidForModifier(
const std::vector<std::string>& method,
const std::set<autofill::CreditCard::CardType>& supported_types,
const std::vector<std::string>& supported_networks) const override;
// autofill::payments::FullCardRequest::ResultDelegate:
void OnFullCardRequestSucceeded(const autofill::CreditCard& card,
......
......@@ -7,9 +7,11 @@
#include <set>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/strings/string16.h"
#include "components/autofill/core/browser/credit_card.h"
namespace payments {
......@@ -55,6 +57,13 @@ class PaymentInstrument {
virtual base::string16 GetLabel() const = 0;
virtual base::string16 GetSublabel() const = 0;
// Returns true if this payment instrument can be used to fulfill a request
// specifying |method| as a supported method of payment, false otherwise.
virtual bool IsValidForModifier(
const std::vector<std::string>& method,
const std::set<autofill::CreditCard::CardType>& supported_types,
const std::vector<std::string>& supported_networks) const = 0;
const std::string& method_name() const { return method_name_; }
int icon_resource_id() const { return icon_resource_id_; }
Type type() { return type_; }
......
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