Skip to content

Commit

Permalink
[Autofill local card migration] Add MigrateCardRequest class
Browse files Browse the repository at this point in the history
This CL adds MigrateCardRequest class to generate the migration request and parse the response.

According to the api, the generated request should follow the request format in the design doc:
https://docs.google.com/document/d/1J7YyBeKZZaBhjAHnMEezvuBa5x1pk_XCh_yZvV_HMmc/edit#heading=h.n05gyfppyc56

The parsed response results are a map of unique-id to save_result and a display text.

The following CLs will be:

1. Trigger the migration when main prompt is ready.

2. Pass the parsed result back to the UI for display.

Bug: 852904
Change-Id: I0ca69ebbe4603aaebaed7664ce199c45d92c9b0a
Reviewed-on: https://chromium-review.googlesource.com/1178571
Commit-Queue: Sujie Zhu <[email protected]>
Reviewed-by: Jared Saul <[email protected]>
Reviewed-by: Moe Ahmadi <[email protected]>
Reviewed-by: Evan Stade <[email protected]>
Cr-Commit-Position: refs/heads/master@{#586496}
  • Loading branch information
SujieZhu authored and Commit Bot committed Aug 27, 2018
1 parent 8a9ec36 commit e49aec0
Show file tree
Hide file tree
Showing 5 changed files with 393 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void LocalCardMigrationManager::AttemptToOfferLocalCardMigration(
app_locale_,
base::BindOnce(&LocalCardMigrationManager::OnDidGetUploadDetails,
weak_ptr_factory_.GetWeakPtr(), is_from_settings_page),
payments::kMigrateCardBillableServiceNumber);
payments::kMigrateCardsBillableServiceNumber);
}

// Callback function when user agrees to migration on the intermediate dialog.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ namespace autofill {
class CreditCard;
class PersonalDataManager;

// MigratableCreditCard class is used as a DataStructure to work as an
// MigratableCreditCard class is used as a data structure to work as an
// intermediary between the UI side and the migration manager. Besides the basic
// credit card information, it also includes a boolean that represents whether
// the card was chosen for upload.
// the card was chosen for upload. We use each card's guid to distinguish each
// credit card for upload request/response.
// TODO(crbug.com/852904): Create one Enum to represent migration status such as
// whether the card is successfully uploaded or failure on uploading.
class MigratableCreditCard {
Expand Down
160 changes: 160 additions & 0 deletions components/autofill/core/browser/payments/payments_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/local_card_migration_manager.h"
#include "components/autofill/core/browser/payments/payments_request.h"
#include "components/autofill/core/browser/payments/payments_service_url.h"
#include "components/autofill/core/common/autofill_features.h"
Expand Down Expand Up @@ -60,6 +61,12 @@ const char kUploadCardRequestFormatWithoutCvc[] =
"requestContentType=application/json; charset=utf-8&request=%s"
"&s7e_1_pan=%s";

const char kMigrateCardsRequestPath[] =
"payments/apis-secure/chromepaymentsservice/migratecards"
"?s7e_suffix=chromewallet";
const char kMigrateCardsRequestFormat[] =
"requestContentType=application/json; charset=utf-8&request=%s";

const char kTokenFetchId[] = "wallet_client";
const char kPaymentsOAuth2Scope[] =
"https://www.googleapis.com/auth/wallet.chrome";
Expand Down Expand Up @@ -176,6 +183,34 @@ std::unique_ptr<base::DictionaryValue> BuildAddressDictionary(
return address;
}

// Returns a dictionary of the credit card with the structure expected by
// Payments RPCs, containing expiration month, expiration year and cardholder
// name (if any) fields in |credit_card|, formatted according to |app_locale|.
// |pan_field_name| is the field name for the encrypted pan. We use each credit
// card's guid as the unique id.
std::unique_ptr<base::DictionaryValue> BuildCreditCardDictionary(
const CreditCard& credit_card,
const std::string& app_locale,
const std::string& pan_field_name) {
std::unique_ptr<base::DictionaryValue> card(new base::DictionaryValue());
card->SetString("unique_id", credit_card.guid());

const base::string16 exp_month =
credit_card.GetInfo(AutofillType(CREDIT_CARD_EXP_MONTH), app_locale);
const base::string16 exp_year = credit_card.GetInfo(
AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
int value = 0;
if (base::StringToInt(exp_month, &value))
card->SetInteger("expiration_month", value);
if (base::StringToInt(exp_year, &value))
card->SetInteger("expiration_year", value);
SetStringIfNotEmpty(credit_card, CREDIT_CARD_NAME_FULL, app_locale,
"cardholder_name", card.get());

card->SetString("encrypted_pan", "__param:" + pan_field_name);
return card;
}

// Populates the list of active experiments that affect either the data sent in
// payments RPCs or whether the RPCs are sent or not.
void SetActiveExperiments(const std::vector<const char*>& active_experiments,
Expand Down Expand Up @@ -452,6 +487,116 @@ class UploadCardRequest : public PaymentsRequest {
std::string server_id_;
};

class MigrateCardsRequest : public PaymentsRequest {
public:
MigrateCardsRequest(
const PaymentsClient::MigrationRequestDetails& request_details,
const std::vector<MigratableCreditCard>& migratable_credit_cards,
MigrateCardsCallback callback)
: request_details_(request_details),
migratable_credit_cards_(migratable_credit_cards),
callback_(std::move(callback)) {}
~MigrateCardsRequest() override {}

std::string GetRequestUrlPath() override { return kMigrateCardsRequestPath; }

std::string GetRequestContentType() override {
return "application/x-www-form-urlencoded";
}

// TODO(crbug.com/877281):Refactor DictionaryValue to base::Value
std::string GetRequestContent() override {
base::DictionaryValue request_dict;

request_dict.SetKey("risk_data_encoded",
BuildRiskDictionary(request_details_.risk_data));

const std::string& app_locale = request_details_.app_locale;
std::unique_ptr<base::DictionaryValue> context(new base::DictionaryValue());
context->SetString("language_code", app_locale);
context->SetInteger("billable_service", kMigrateCardsBillableServiceNumber);
request_dict.Set("context", std::move(context));

request_dict.SetString("context_token", request_details_.context_token);

std::string all_pans_data = std::string();
std::unique_ptr<base::ListValue> migrate_cards(new base::ListValue());
for (size_t index = 0; index < migratable_credit_cards_.size(); ++index) {
if (migratable_credit_cards_[index].is_chosen()) {
std::string pan_field_name = GetPanFieldName(index);
// Generate credit card dictionary.
migrate_cards->Append(BuildCreditCardDictionary(
migratable_credit_cards_[index].credit_card(), app_locale,
pan_field_name));
// Append pan data to the |all_pans_data|.
all_pans_data +=
GetAppendPan(migratable_credit_cards_[index].credit_card(),
app_locale, pan_field_name);
}
}
request_dict.Set("local_card", std::move(migrate_cards));

std::string json_request;
base::JSONWriter::Write(request_dict, &json_request);
std::string request_content = base::StringPrintf(
kMigrateCardsRequestFormat,
net::EscapeUrlEncodedData(json_request, true).c_str());
request_content += all_pans_data;
return request_content;
}

void ParseResponse(std::unique_ptr<base::DictionaryValue> response) override {
const base::ListValue* save_result_list = nullptr;
if (!response->GetList("save_result", &save_result_list))
return;
save_result_ =
std::make_unique<std::unordered_map<std::string, std::string>>();
for (size_t i = 0; i < save_result_list->GetSize(); ++i) {
const base::DictionaryValue* single_card_save_result;
if (save_result_list->GetDictionary(i, &single_card_save_result)) {
std::string unique_id;
single_card_save_result->GetString("unique_id", &unique_id);
std::string save_result;
single_card_save_result->GetString("status", &save_result);
save_result_->insert(std::make_pair(unique_id, save_result));
}
}
response->GetString("value_prop_display_text", &display_text_);
}

bool IsResponseComplete() override {
return !display_text_.empty() && save_result_;
}

void RespondToDelegate(AutofillClient::PaymentsRpcResult result) override {
std::move(callback_).Run(result, std::move(save_result_), display_text_);
}

private:
// Return the pan field name for the encrypted pan based on the |index|.
std::string GetPanFieldName(const size_t& index) {
return "s7e_1_pan" + std::to_string(index);
}

// Return the formatted pan to append to the end of the request.
std::string GetAppendPan(const CreditCard& credit_card,
const std::string& app_locale,
const std::string& pan_field_name) {
const base::string16 pan =
credit_card.GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale);
std::string pan_str =
net::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str();
std::string append_pan = "&" + pan_field_name + "=" + pan_str;
return append_pan;
}

const PaymentsClient::MigrationRequestDetails request_details_;
const std::vector<MigratableCreditCard>& migratable_credit_cards_;
MigrateCardsCallback callback_;
std::unique_ptr<std::unordered_map<std::string, std::string>> save_result_;
std::string display_text_;
};

} // namespace

const char PaymentsClient::kRecipientName[] = "recipient_name";
Expand All @@ -467,6 +612,11 @@ PaymentsClient::UploadRequestDetails::UploadRequestDetails(
const UploadRequestDetails& other) = default;
PaymentsClient::UploadRequestDetails::~UploadRequestDetails() {}

PaymentsClient::MigrationRequestDetails::MigrationRequestDetails() {}
PaymentsClient::MigrationRequestDetails::MigrationRequestDetails(
const MigrationRequestDetails& other) = default;
PaymentsClient::MigrationRequestDetails::~MigrationRequestDetails() {}

PaymentsClient::PaymentsClient(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* pref_service,
Expand Down Expand Up @@ -525,6 +675,16 @@ void PaymentsClient::UploadCard(
true);
}

void PaymentsClient::MigrateCards(
const MigrationRequestDetails& request_details,
const std::vector<MigratableCreditCard>& migratable_credit_cards,
MigrateCardsCallback callback) {
IssueRequest(
std::make_unique<MigrateCardsRequest>(
request_details, migratable_credit_cards, std::move(callback)),
/*authenticate=*/true);
}

void PaymentsClient::CancelRequest() {
request_.reset();
resource_request_.reset();
Expand Down
34 changes: 33 additions & 1 deletion components/autofill/core/browser/payments/payments_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,25 @@ class SharedURLLoaderFactory;

namespace autofill {

class MigratableCreditCard;

namespace payments {

// Callback type for MigrateCards callback. |result| is the Payments Rpc result.
// |save_result| is an unordered_map parsed from the response whose key is the
// unique id (guid) for each card and value is the server save result string.
// |display_text| is the returned tip from Payments to show on the UI.
typedef base::OnceCallback<void(
AutofillClient::PaymentsRpcResult result,
std::unique_ptr<std::unordered_map<std::string, std::string>> save_result,
const std::string& display_text)>
MigrateCardsCallback;

// Billable service number is defined in Payments server to distinguish
// different requests.
const int kUnmaskCardBillableServiceNumber = 70154;
const int kUploadCardBillableServiceNumber = 70073;
const int kMigrateCardBillableServiceNumber = 70264;
const int kMigrateCardsBillableServiceNumber = 70264;

class PaymentsRequest;

Expand Down Expand Up @@ -83,6 +95,18 @@ class PaymentsClient {
std::vector<const char*> active_experiments;
};

// A collection of the information required to make local credit cards
// migration request.
struct MigrationRequestDetails {
MigrationRequestDetails();
MigrationRequestDetails(const MigrationRequestDetails& other);
~MigrationRequestDetails();

base::string16 context_token;
std::string risk_data;
std::string app_locale;
};

// |url_loader_factory| is reference counted so it has no lifetime or
// ownership requirements. |pref_service| is used to get the registered
// preference value, |identity_manager|, |unmask_delegate| and |save_delegate|
Expand Down Expand Up @@ -142,6 +166,14 @@ class PaymentsClient {
base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
const std::string&)> callback);

// The user has indicated that they would like to migrate their local credit
// cards. This request will fail server-side if a successful call to
// GetUploadDetails has not already been made.
virtual void MigrateCards(
const MigrationRequestDetails& details,
const std::vector<MigratableCreditCard>& migratable_credit_cards,
MigrateCardsCallback callback);

// Cancels and clears the current |request_|.
void CancelRequest();

Expand Down
Loading

0 comments on commit e49aec0

Please sign in to comment.