| Index: ios/chrome/browser/ui/payments/payment_request_coordinator.mm
|
| diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
|
| index f29242285850881bed195c29d376f48e4d27d368..5288255e1f4ef96d525d57bed87cfd6087c3cf74 100644
|
| --- a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
|
| +++ b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
|
| @@ -19,16 +19,20 @@
|
| #include "components/autofill/core/browser/payments/full_card_request.h"
|
| #include "components/autofill/core/browser/personal_data_manager.h"
|
| #include "components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h"
|
| +#include "components/payments/core/address_normalizer_impl.h"
|
| #include "components/payments/core/payment_address.h"
|
| #include "components/payments/core/payment_request_data_util.h"
|
| #include "components/signin/core/browser/signin_manager.h"
|
| #include "components/strings/grit/components_strings.h"
|
| #include "ios/chrome/browser/application_context.h"
|
| +#include "ios/chrome/browser/autofill/validation_rules_storage_factory.h"
|
| #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
|
| #include "ios/chrome/browser/payments/payment_request.h"
|
| #include "ios/chrome/browser/payments/payment_request_util.h"
|
| #include "ios/chrome/browser/signin/signin_manager_factory.h"
|
| #include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
|
| +#include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
|
| +#include "third_party/libaddressinput/chromium/chrome_storage_impl.h"
|
| #include "ui/base/l10n/l10n_util.h"
|
|
|
| #if !defined(__has_feature) || !__has_feature(objc_arc)
|
| @@ -38,15 +42,51 @@
|
| namespace {
|
| using ::payments::data_util::GetBasicCardResponseFromAutofillCreditCard;
|
| using ::payments::data_util::GetPaymentAddressFromAutofillProfile;
|
| -} // namespace
|
| +
|
| +constexpr int kAddressNormalizationTimeoutSeconds = 5;
|
| +
|
| +// Receives notifications from the AddressNormalizer, and forwards the details
|
| +// to a callback. This is needed as there is currently no way to know, when
|
| +// the |payments::AddressNormalizer::Delegate| methods are called, which address
|
| +// has been normalized (shipping or contact). The callback mechanism is needed
|
| +// in order to keep track of which address this delegate was created for.
|
| +class AddressNormalizerDelegate : public payments::AddressNormalizer::Delegate {
|
| + public:
|
| + // Block to be called with the profile information, once normalized.
|
| + typedef void (^Callback)(const autofill::AutofillProfile&);
|
| +
|
| + AddressNormalizerDelegate() {}
|
| + ~AddressNormalizerDelegate() override {}
|
| +
|
| + void SetCallback(Callback callback) { callback_ = callback; }
|
| +
|
| + // payments::AddressNormalizer::Delegate:
|
| + void OnAddressNormalized(
|
| + const autofill::AutofillProfile& normalizedProfile) override {
|
| + DCHECK(callback_);
|
| + callback_(normalizedProfile);
|
| + }
|
| +
|
| + // payments::AddressNormalizer::Delegate:
|
| + void OnCouldNotNormalize(const autofill::AutofillProfile& profile) override {
|
| + // Since the phone number is formatted in either case, this profile should
|
| + // be used.
|
| + DCHECK(callback_);
|
| + callback_(profile);
|
| + }
|
| +
|
| + private:
|
| + Callback callback_ = nullptr;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(AddressNormalizerDelegate);
|
| +};
|
|
|
| // The unmask prompt UI for Payment Request.
|
| class PRCardUnmaskPromptViewBridge
|
| : public autofill::CardUnmaskPromptViewBridge {
|
| public:
|
| - explicit PRCardUnmaskPromptViewBridge(
|
| - autofill::CardUnmaskPromptController* controller,
|
| - UIViewController* base_view_controller)
|
| + PRCardUnmaskPromptViewBridge(autofill::CardUnmaskPromptController* controller,
|
| + UIViewController* base_view_controller)
|
| : autofill::CardUnmaskPromptViewBridge(controller),
|
| base_view_controller_(base_view_controller) {}
|
|
|
| @@ -70,9 +110,9 @@ class FullCardRequester
|
| public autofill::payments::FullCardRequest::UIDelegate,
|
| public base::SupportsWeakPtr<FullCardRequester> {
|
| public:
|
| - explicit FullCardRequester(PaymentRequestCoordinator* owner,
|
| - UIViewController* base_view_controller,
|
| - ios::ChromeBrowserState* browser_state)
|
| + FullCardRequester(PaymentRequestCoordinator* owner,
|
| + UIViewController* base_view_controller,
|
| + ios::ChromeBrowserState* browser_state)
|
| : owner_(owner),
|
| base_view_controller_(base_view_controller),
|
| unmask_controller_(browser_state->GetPrefs(),
|
| @@ -127,6 +167,26 @@ class FullCardRequester
|
| DISALLOW_COPY_AND_ASSIGN(FullCardRequester);
|
| };
|
|
|
| +struct FullCard {
|
| + autofill::CreditCard creditCard;
|
| + base::string16 cvc;
|
| + autofill::AutofillProfile billingAddress;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +@interface PaymentRequestCoordinator ()
|
| +
|
| +// Starts normalization of |profileToNormalize| (usually asynchronous). The
|
| +// |delegate| will be notified on success/failure, and
|
| +// |normalizationRequestPendingFlag| will be updated to NO when normalization
|
| +// compleptes.
|
| +- (void)normalizeProfile:(autofill::AutofillProfile*)profileToNormalize
|
| + delegate:(AddressNormalizerDelegate*)delegate
|
| + flag:(BOOL*)normalizationRequestPendingFlag;
|
| +
|
| +@end
|
| +
|
| @implementation PaymentRequestCoordinator {
|
| UINavigationController* _navigationController;
|
| PaymentRequestViewController* _viewController;
|
| @@ -142,6 +202,22 @@ class FullCardRequester
|
|
|
| // The selected shipping address, pending approval from the page.
|
| autofill::AutofillProfile* _pendingShippingAddress;
|
| +
|
| + // The address normalizer, to normalize contact & shipping addresses. Also,
|
| + // delegates to handle callbacks for normalization of each address.
|
| + std::unique_ptr<payments::AddressNormalizer> _addressNormalizer;
|
| + AddressNormalizerDelegate _contactAddressNormalizerDelegate;
|
| + AddressNormalizerDelegate _shippingAddressNormalizerDelegate;
|
| + AddressNormalizerDelegate _billingAddressNormalizerDelegate;
|
| +
|
| + // Member variables to keep track of which operations are still pending.
|
| + BOOL _contactAddressNormalizationPending;
|
| + BOOL _shippingAddressNormalizationPending;
|
| + BOOL _fullCardRequestPending;
|
| + BOOL _billingAddressNormalizationPending;
|
| +
|
| + // Storage for the full card.
|
| + FullCard _fullCard;
|
| }
|
|
|
| @synthesize paymentRequest = _paymentRequest;
|
| @@ -152,6 +228,33 @@ class FullCardRequester
|
| @synthesize pageHost = _pageHost;
|
| @synthesize delegate = _delegate;
|
|
|
| +- (void)startAddressNormalizer {
|
| + autofill::PersonalDataManager* personalDataManager =
|
| + _paymentRequest->GetPersonalDataManager();
|
| +
|
| + std::unique_ptr<i18n::addressinput::Source> addressNormalizerSource =
|
| + base::MakeUnique<autofill::ChromeMetadataSource>(
|
| + I18N_ADDRESS_VALIDATION_DATA_URL,
|
| + personalDataManager->GetURLRequestContextGetter());
|
| +
|
| + std::unique_ptr<i18n::addressinput::Storage> addressNormalizerStorage =
|
| + autofill::ValidationRulesStorageFactory::CreateStorage();
|
| +
|
| + _addressNormalizer.reset(new payments::AddressNormalizerImpl(
|
| + std::move(addressNormalizerSource), std::move(addressNormalizerStorage)));
|
| +
|
| + // Kickoff the process of loading the rules (which is asynchronous) for each
|
| + // profile's country, to get faster address normalization later.
|
| + for (const autofill::AutofillProfile* profile :
|
| + personalDataManager->GetProfilesToSuggest()) {
|
| + std::string countryCode =
|
| + base::UTF16ToUTF8(profile->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
|
| + if (autofill::data_util::IsValidCountryCode(countryCode)) {
|
| + _addressNormalizer->LoadRulesForRegion(countryCode);
|
| + }
|
| + }
|
| +}
|
| +
|
| - (void)start {
|
| _viewController = [[PaymentRequestViewController alloc]
|
| initWithPaymentRequest:_paymentRequest];
|
| @@ -176,6 +279,8 @@ class FullCardRequester
|
| [[self baseViewController] presentViewController:_navigationController
|
| animated:YES
|
| completion:nil];
|
| +
|
| + [self startAddressNormalizer];
|
| }
|
|
|
| - (void)stop {
|
| @@ -196,102 +301,166 @@ class FullCardRequester
|
| _navigationController = nil;
|
| }
|
|
|
| -- (void)sendPaymentResponse {
|
| +- (void)normalizeProfile:(autofill::AutofillProfile*)profileToNormalize
|
| + delegate:(AddressNormalizerDelegate*)delegate
|
| + flag:(BOOL*)normalizationRequestPendingFlag {
|
| + DCHECK(normalizationRequestPendingFlag);
|
| +
|
| + __weak PaymentRequestCoordinator* weakSelf = self;
|
| +
|
| + delegate->SetCallback(^(const autofill::AutofillProfile& normalizedProfile) {
|
| + // Check that PaymentRequestCoordinator still exists as |profileToNormalize|
|
| + // and |normalizationRequestPendingFlag| are member variables of that
|
| + // object.
|
| + if (!weakSelf)
|
| + return;
|
| +
|
| + *profileToNormalize = normalizedProfile;
|
| + *normalizationRequestPendingFlag = NO;
|
| + [weakSelf sendPaymentResponseIfAllAsyncCallsCompleted];
|
| + });
|
| +
|
| + std::string country_code = base::UTF16ToUTF8(
|
| + profileToNormalize->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
|
| + if (!autofill::data_util::IsValidCountryCode(country_code)) {
|
| + country_code = base::SysNSStringToUTF8(
|
| + [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]);
|
| + DCHECK(autofill::data_util::IsValidCountryCode(country_code));
|
| + }
|
| + _addressNormalizer->StartAddressNormalization(
|
| + *profileToNormalize, country_code, kAddressNormalizationTimeoutSeconds,
|
| + delegate);
|
| +}
|
| +
|
| +- (void)requestFullCardAndNormalizeProfiles {
|
| + // Set these before starting any potentially asynchronous calls, to ensure we
|
| + // wait for completion of all operations before proceeding.
|
| + _contactAddressNormalizationPending = YES;
|
| + _shippingAddressNormalizationPending = _paymentRequest->request_shipping();
|
| + _fullCardRequestPending = YES;
|
| +
|
| DCHECK(_paymentRequest->selected_credit_card());
|
| autofill::CreditCard* card = _paymentRequest->selected_credit_card();
|
| _fullCardRequester = base::MakeUnique<FullCardRequester>(
|
| self, _navigationController, _browserState);
|
| _fullCardRequester->GetFullCard(card, _autofillManager);
|
| +
|
| + if (_paymentRequest->request_shipping()) {
|
| + [self normalizeProfile:_paymentRequest->selected_shipping_profile()
|
| + delegate:&_shippingAddressNormalizerDelegate
|
| + flag:&_shippingAddressNormalizationPending];
|
| + }
|
| +
|
| + [self normalizeProfile:_paymentRequest->selected_contact_profile()
|
| + delegate:&_contactAddressNormalizerDelegate
|
| + flag:&_contactAddressNormalizationPending];
|
| }
|
|
|
| - (void)fullCardRequestDidSucceedWithCard:(const autofill::CreditCard&)card
|
| CVC:(const base::string16&)cvc {
|
| - web::PaymentResponse paymentResponse;
|
| -
|
| - // If the merchant specified the card network as part of the "basic-card"
|
| - // payment method, return "basic-card" as the method_name. Otherwise, return
|
| - // the name of the network directly.
|
| - std::string issuer_network =
|
| - autofill::data_util::GetPaymentRequestData(card.network())
|
| - .basic_card_issuer_network;
|
| - paymentResponse.method_name =
|
| - _paymentRequest->basic_card_specified_networks().find(issuer_network) !=
|
| - _paymentRequest->basic_card_specified_networks().end()
|
| - ? base::ASCIIToUTF16("basic-card")
|
| - : base::ASCIIToUTF16(issuer_network);
|
| -
|
| - // Get the billing address
|
| - autofill::AutofillProfile billingAddress;
|
| -
|
| // TODO(crbug.com/714768): Make sure the billing address is set and valid
|
| // before getting here. Once the bug is addressed, there will be no need to
|
| // copy the address, *billing_address_ptr can be used to get the basic card
|
| // response.
|
| if (!card.billing_address_id().empty()) {
|
| - autofill::AutofillProfile* billingAddressPtr =
|
| + autofill::AutofillProfile* billingAddress =
|
| autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
|
| card.billing_address_id(), _paymentRequest->billing_profiles());
|
| - if (billingAddressPtr)
|
| - billingAddress = *billingAddressPtr;
|
| - }
|
| -
|
| - paymentResponse.details = GetBasicCardResponseFromAutofillCreditCard(
|
| - card, cvc, billingAddress,
|
| - GetApplicationContext()->GetApplicationLocale());
|
| -
|
| - if (_paymentRequest->request_shipping()) {
|
| - autofill::AutofillProfile* shippingAddress =
|
| - _paymentRequest->selected_shipping_profile();
|
| - // TODO(crbug.com/602666): User should get here only if they have selected
|
| - // a shipping address.
|
| - DCHECK(shippingAddress);
|
| - paymentResponse.shipping_address = GetPaymentAddressFromAutofillProfile(
|
| - *shippingAddress, GetApplicationContext()->GetApplicationLocale());
|
| -
|
| - web::PaymentShippingOption* shippingOption =
|
| - _paymentRequest->selected_shipping_option();
|
| - DCHECK(shippingOption);
|
| - paymentResponse.shipping_option = shippingOption->id;
|
| - }
|
| -
|
| - if (_paymentRequest->request_payer_name()) {
|
| - autofill::AutofillProfile* contactInfo =
|
| - _paymentRequest->selected_contact_profile();
|
| - // TODO(crbug.com/602666): User should get here only if they have selected
|
| - // a contact info.
|
| - DCHECK(contactInfo);
|
| - paymentResponse.payer_name =
|
| - contactInfo->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
|
| - GetApplicationContext()->GetApplicationLocale());
|
| - }
|
| + if (billingAddress) {
|
| + _fullCard.billingAddress = *billingAddress;
|
| + _billingAddressNormalizationPending = YES;
|
|
|
| - if (_paymentRequest->request_payer_email()) {
|
| - autofill::AutofillProfile* contactInfo =
|
| - _paymentRequest->selected_contact_profile();
|
| - // TODO(crbug.com/602666): User should get here only if they have selected
|
| - // a contact info.
|
| - DCHECK(contactInfo);
|
| - paymentResponse.payer_email =
|
| - contactInfo->GetRawInfo(autofill::EMAIL_ADDRESS);
|
| + [self normalizeProfile:&_fullCard.billingAddress
|
| + delegate:&_billingAddressNormalizerDelegate
|
| + flag:&_billingAddressNormalizationPending];
|
| + }
|
| }
|
|
|
| - if (_paymentRequest->request_payer_phone()) {
|
| - autofill::AutofillProfile* contactInfo =
|
| - _paymentRequest->selected_contact_profile();
|
| - // TODO(crbug.com/602666): User should get here only if they have selected
|
| - // a contact info.
|
| - DCHECK(contactInfo);
|
| - paymentResponse.payer_phone =
|
| - contactInfo->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER);
|
| - }
|
| + _fullCard.creditCard = card;
|
| + _fullCard.cvc = cvc;
|
| + _fullCardRequestPending = NO;
|
|
|
| _viewController.view.userInteractionEnabled = NO;
|
| [_viewController setPending:YES];
|
| [_viewController loadModel];
|
| [[_viewController collectionView] reloadData];
|
|
|
| - [_delegate paymentRequestCoordinator:self
|
| - didConfirmWithPaymentResponse:paymentResponse];
|
| + [self sendPaymentResponseIfAllAsyncCallsCompleted];
|
| +}
|
| +
|
| +- (void)sendPaymentResponseIfAllAsyncCallsCompleted {
|
| + if (_contactAddressNormalizationPending == NO &&
|
| + _shippingAddressNormalizationPending == NO &&
|
| + _billingAddressNormalizationPending == NO &&
|
| + _fullCardRequestPending == NO) {
|
| + web::PaymentResponse paymentResponse;
|
| +
|
| + // If the merchant specified the card network as part of the "basic-card"
|
| + // payment method, return "basic-card" as the method_name. Otherwise, return
|
| + // the name of the network directly.
|
| + const std::string issuer_network =
|
| + autofill::data_util::GetPaymentRequestData(
|
| + _fullCard.creditCard.network())
|
| + .basic_card_issuer_network;
|
| + paymentResponse.method_name =
|
| + _paymentRequest->basic_card_specified_networks().find(issuer_network) !=
|
| + _paymentRequest->basic_card_specified_networks().end()
|
| + ? base::ASCIIToUTF16("basic-card")
|
| + : base::ASCIIToUTF16(issuer_network);
|
| +
|
| + paymentResponse.details = GetBasicCardResponseFromAutofillCreditCard(
|
| + _fullCard.creditCard, _fullCard.cvc, _fullCard.billingAddress,
|
| + GetApplicationContext()->GetApplicationLocale());
|
| +
|
| + if (_paymentRequest->request_shipping()) {
|
| + autofill::AutofillProfile* shippingAddress =
|
| + _paymentRequest->selected_shipping_profile();
|
| + // TODO(crbug.com/602666): User should get here only if they have selected
|
| + // a shipping address.
|
| + DCHECK(shippingAddress);
|
| + paymentResponse.shipping_address = GetPaymentAddressFromAutofillProfile(
|
| + *shippingAddress, GetApplicationContext()->GetApplicationLocale());
|
| +
|
| + web::PaymentShippingOption* shippingOption =
|
| + _paymentRequest->selected_shipping_option();
|
| + DCHECK(shippingOption);
|
| + paymentResponse.shipping_option = shippingOption->id;
|
| + }
|
| +
|
| + if (_paymentRequest->request_payer_name()) {
|
| + autofill::AutofillProfile* contactInfo =
|
| + _paymentRequest->selected_contact_profile();
|
| + // TODO(crbug.com/602666): User should get here only if they have selected
|
| + // a contact info.
|
| + DCHECK(contactInfo);
|
| + paymentResponse.payer_name =
|
| + contactInfo->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
|
| + GetApplicationContext()->GetApplicationLocale());
|
| + }
|
| +
|
| + if (_paymentRequest->request_payer_email()) {
|
| + autofill::AutofillProfile* contactInfo =
|
| + _paymentRequest->selected_contact_profile();
|
| + // TODO(crbug.com/602666): User should get here only if they have selected
|
| + // a contact info.
|
| + DCHECK(contactInfo);
|
| + paymentResponse.payer_email =
|
| + contactInfo->GetRawInfo(autofill::EMAIL_ADDRESS);
|
| + }
|
| +
|
| + if (_paymentRequest->request_payer_phone()) {
|
| + autofill::AutofillProfile* contactInfo =
|
| + _paymentRequest->selected_contact_profile();
|
| + // TODO(crbug.com/602666): User should get here only if they have selected
|
| + // a contact info.
|
| + DCHECK(contactInfo);
|
| + paymentResponse.payer_phone =
|
| + contactInfo->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER);
|
| + }
|
| +
|
| + [_delegate paymentRequestCoordinator:self
|
| + didConfirmWithPaymentResponse:paymentResponse];
|
| + }
|
| }
|
|
|
| - (void)updatePaymentDetails:(web::PaymentDetails)paymentDetails {
|
| @@ -355,7 +524,7 @@ class FullCardRequester
|
|
|
| - (void)paymentRequestViewControllerDidConfirm:
|
| (PaymentRequestViewController*)controller {
|
| - [self sendPaymentResponse];
|
| + [self requestFullCardAndNormalizeProfiles];
|
| }
|
|
|
| - (void)paymentRequestViewControllerDidSelectSettings:
|
| @@ -431,7 +600,7 @@ class FullCardRequester
|
|
|
| - (void)paymentItemsDisplayCoordinatorDidConfirm:
|
| (PaymentItemsDisplayCoordinator*)coordinator {
|
| - [self sendPaymentResponse];
|
| + [self requestFullCardAndNormalizeProfiles];
|
| }
|
|
|
| #pragma mark - ShippingAddressSelectionCoordinatorDelegate
|
|
|