Commit c614137c authored by John Z Wu's avatar John Z Wu Committed by Commit Bot

Add API to obtain risk data from client app in CWVCreditCardVerifier.

Risk data is needed during credit card verification in order to
communicate with payments API, and it can only be obtained by a
1st party app.

Bug: 895694
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: I79ee7cddf9618ee32e4050f0b3b4b61447e7ea1f
Reviewed-on: https://chromium-review.googlesource.com/c/1282388
Commit-Queue: John Wu <jzw@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarHiroshi Ichikawa <ichikawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600159}
parent cd8fbb77
......@@ -84,6 +84,8 @@ if (ios_web_view_enable_autofill) {
"public/cwv_autofill_suggestion.h",
"public/cwv_credit_card.h",
"public/cwv_credit_card_verifier.h",
"public/cwv_credit_card_verifier_data_source.h",
"public/cwv_credit_card_verifier_delegate.h",
"public/cwv_preferences_autofill.h",
"public/cwv_web_view_autofill.h",
"public/cwv_web_view_configuration_autofill.h",
......
......@@ -33,6 +33,9 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
- (void)didReceiveUnmaskVerificationResult:
(autofill::AutofillClient::PaymentsRpcResult)result;
// Bridge for AutofillClient's method |LoadRiskData|.
- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback;
@end
#endif // IOS_WEB_VIEW_INTERNAL_AUTOFILL_CWV_AUTOFILL_CLIENT_IOS_BRIDGE_H_
......@@ -368,6 +368,10 @@ showUnmaskPromptForCard:(const autofill::CreditCard&)creditCard
[_verifier didReceiveUnmaskVerificationResult:result];
}
- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback {
[_verifier loadRiskData:std::move(callback)];
}
#pragma mark - AutofillDriverIOSBridge
- (void)onFormDataFilled:(uint16_t)query_id
......
......@@ -11,12 +11,39 @@
#include "components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h"
#include "components/autofill/core/browser/ui/card_unmask_prompt_view.h"
#import "ios/web_view/internal/autofill/cwv_credit_card_internal.h"
#import "ios/web_view/public/cwv_credit_card_verifier_data_source.h"
#import "ios/web_view/public/cwv_credit_card_verifier_delegate.h"
#include "ui/base/resource/resource_bundle.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
NSErrorDomain const CWVCreditCardVerifierErrorDomain =
@"org.chromium.chromewebview.CreditCardVerifierErrorDomain";
NSString* const CWVCreditCardVerifierErrorMessageKey = @"error_message";
NSString* const CWVCreditCardVerifierRetryAllowedKey = @"retry_allowed";
namespace {
// Converts an autofill::AutofillClient::PaymentsRpcResult to a
// CWVCreditCardVerificationError.
CWVCreditCardVerificationError CWVConvertPaymentsRPCResult(
autofill::AutofillClient::PaymentsRpcResult result) {
switch (result) {
case autofill::AutofillClient::NONE:
case autofill::AutofillClient::SUCCESS:
NOTREACHED();
return CWVCreditCardVerificationErrorNone;
case autofill::AutofillClient::TRY_AGAIN_FAILURE:
return CWVCreditCardVerificationErrorTryAgainFailure;
case autofill::AutofillClient::PERMANENT_FAILURE:
return CWVCreditCardVerificationErrorPermanentFailure;
case autofill::AutofillClient::NETWORK_ERROR:
return CWVCreditCardVerificationErrorNetworkFailure;
}
}
} // namespace
@interface CWVCreditCardVerifier ()
// Used to receive |GotVerificationResult| from WebViewCardUnmaskPromptView.
......@@ -60,10 +87,10 @@ class WebViewCardUnmaskPromptView : public autofill::CardUnmaskPromptView {
_unmaskingController;
// Used to interface with |_unmaskingController|.
std::unique_ptr<ios_web_view::WebViewCardUnmaskPromptView> _unmaskingView;
// Completion handler for verification results. This is provided by the
// client and copied internally to be invoked later from
// |didReceiveVerificationResultWithErrorMessage:retryAllowed:|.
void (^_completionHandler)(NSString* errorMessage, BOOL retryAllowed);
// Data source to provide risk data.
__weak id<CWVCreditCardVerifierDataSource> _dataSource;
// Delegate to receive callbacks.
__weak id<CWVCreditCardVerifierDelegate> _delegate;
}
@synthesize creditCard = _creditCard;
......@@ -131,16 +158,17 @@ class WebViewCardUnmaskPromptView : public autofill::CardUnmaskPromptView {
}
- (void)verifyWithCVC:(NSString*)CVC
expirationMonth:(NSString*)expirationMonth
expirationYear:(NSString*)expirationYear
expirationMonth:(nullable NSString*)expirationMonth
expirationYear:(nullable NSString*)expirationYear
storeLocally:(BOOL)storeLocally
completionHandler:
(void (^)(NSString* errorMessage, BOOL retryAllowed))completionHandler {
DCHECK(!_completionHandler);
dataSource:(__weak id<CWVCreditCardVerifierDataSource>)dataSource
delegate:
(nullable __weak id<CWVCreditCardVerifierDelegate>)delegate {
_dataSource = dataSource;
_delegate = delegate;
_unmaskingController->OnUnmaskResponse(
base::SysNSStringToUTF16(CVC), base::SysNSStringToUTF16(expirationMonth),
base::SysNSStringToUTF16(expirationYear), storeLocally);
_completionHandler = completionHandler;
}
- (BOOL)isCVCValid:(NSString*)CVC {
......@@ -156,9 +184,24 @@ class WebViewCardUnmaskPromptView : public autofill::CardUnmaskPromptView {
- (void)didReceiveVerificationResultWithErrorMessage:(NSString*)errorMessage
retryAllowed:(BOOL)retryAllowed {
DCHECK(_completionHandler);
_completionHandler(errorMessage, retryAllowed);
_completionHandler = nil;
if ([_delegate respondsToSelector:@selector
(creditCardVerifier:didFinishVerificationWithError:)]) {
NSError* error;
autofill::AutofillClient::PaymentsRpcResult result =
_unmaskingController->GetVerificationResult();
if (errorMessage.length > 0 && result != autofill::AutofillClient::NONE &&
result != autofill::AutofillClient::SUCCESS) {
NSDictionary* userInfo = @{
CWVCreditCardVerifierErrorMessageKey : errorMessage,
CWVCreditCardVerifierRetryAllowedKey : @(retryAllowed),
};
error = [NSError errorWithDomain:CWVCreditCardVerifierErrorDomain
code:CWVConvertPaymentsRPCResult(result)
userInfo:userInfo];
}
[_delegate creditCardVerifier:self didFinishVerificationWithError:error];
}
}
#pragma mark - Internal Methods
......@@ -168,4 +211,13 @@ class WebViewCardUnmaskPromptView : public autofill::CardUnmaskPromptView {
_unmaskingController->OnVerificationResult(result);
}
- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback {
__block base::OnceCallback<void(const std::string&)> blockCallback =
std::move(callback);
[_dataSource creditCardVerifier:self
getRiskDataWithCompletionHandler:^(NSString* _Nonnull riskData) {
std::move(blockCallback).Run(base::SysNSStringToUTF8(riskData));
}];
}
@end
......@@ -38,6 +38,10 @@ class CreditCard;
- (void)didReceiveUnmaskVerificationResult:
(autofill::AutofillClient::PaymentsRpcResult)result;
// Use to notify CWVCreditCardVerifier that it needs to obtain risk data for
// credit card verification and to pass it back in |callback|.
- (void)loadRiskData:(base::OnceCallback<void(const std::string&)>)callback;
@end
NS_ASSUME_NONNULL_END
......
......@@ -9,6 +9,7 @@
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
......@@ -25,9 +26,12 @@
#include "ios/web/public/web_task_traits.h"
#include "ios/web/public/web_thread.h"
#import "ios/web_view/internal/autofill/cwv_credit_card_internal.h"
#import "ios/web_view/public/cwv_credit_card_verifier_data_source.h"
#import "ios/web_view/public/cwv_credit_card_verifier_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/resource/resource_bundle.h"
......@@ -171,24 +175,17 @@ TEST_F(CWVCreditCardVerifierTest, IsExpirationDateValid) {
// Tests CWVCreditCardVerifier's verification method.
TEST_F(CWVCreditCardVerifierTest, VerifyCard) {
__block bool completion_handler_called = false;
id unused_data_source =
OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
NSString* cvc = @"123";
BOOL store_locally = YES;
[credit_card_verifier_
verifyWithCVC:cvc
expirationMonth:@"" // Expiration dates are ignored here because
expirationYear:@"" // |needsUpdateForExpirationDate| is NO.
storeLocally:store_locally
completionHandler:^(NSString* errorMessage, BOOL retryAllowed) {
EXPECT_FALSE(errorMessage.length > 0);
EXPECT_TRUE(retryAllowed);
completion_handler_called = true;
}];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return completion_handler_called;
}));
verifyWithCVC:cvc
expirationMonth:@"" // Expiration dates are ignored here because
expirationYear:@"" // |needsUpdateForExpirationDate| is NO.
storeLocally:store_locally
dataSource:unused_data_source
delegate:nil];
EXPECT_TRUE(credit_card_verifier_.lastStoreLocallyValue);
const FakeCardUnmaskDelegate::UnmaskResponse& unmask_response_ =
......@@ -197,4 +194,66 @@ TEST_F(CWVCreditCardVerifierTest, VerifyCard) {
EXPECT_EQ(store_locally, unmask_response_.should_store_pan);
}
// Tests CWVCreditCardVerifier properly invokes its delegate.
TEST_F(CWVCreditCardVerifierTest, DelegateCallbacks) {
id unused_data_source =
OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
id delegate = OCMProtocolMock(@protocol(CWVCreditCardVerifierDelegate));
[credit_card_verifier_ verifyWithCVC:@"123"
expirationMonth:@""
expirationYear:@""
storeLocally:NO
dataSource:unused_data_source
delegate:delegate];
[[delegate expect]
creditCardVerifier:credit_card_verifier_
didFinishVerificationWithError:[OCMArg checkWithBlock:^BOOL(
NSError* error) {
return
[error.domain isEqualToString:CWVCreditCardVerifierErrorDomain] &&
error.code == CWVCreditCardVerificationErrorTryAgainFailure &&
error.userInfo[CWVCreditCardVerifierErrorMessageKey] != nil &&
error.userInfo[CWVCreditCardVerifierRetryAllowedKey] &&
[error.userInfo[CWVCreditCardVerifierRetryAllowedKey] boolValue];
}]];
[credit_card_verifier_ didReceiveUnmaskVerificationResult:
autofill::AutofillClient::TRY_AGAIN_FAILURE];
[delegate verify];
}
// Tests CWVCreditCardVerifier properly invokes its data source.
TEST_F(CWVCreditCardVerifierTest, DataSourceCallbacks) {
id data_source = OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource));
[credit_card_verifier_ verifyWithCVC:@"123"
expirationMonth:@""
expirationYear:@""
storeLocally:NO
dataSource:data_source
delegate:nil];
[[data_source expect]
creditCardVerifier:credit_card_verifier_
getRiskDataWithCompletionHandler:[OCMArg checkWithBlock:^BOOL(id arg) {
void (^completionHandler)(NSString*) = arg;
completionHandler(@"dummy-risk-data");
return YES;
}]];
__block bool callback_called = false;
base::OnceCallback<void(const std::string&)> callback = base::BindOnce(
[](bool* callback_called, const std::string& risk_data) -> void {
*callback_called = true;
EXPECT_EQ("dummy-risk-data", risk_data);
},
&callback_called);
[credit_card_verifier_ loadRiskData:std::move(callback)];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
base::RunLoop().RunUntilIdle();
return callback_called;
}));
[data_source verify];
}
} // namespace ios_web_view
......@@ -139,7 +139,9 @@ void WebViewAutofillClientIOS::ConfirmCreditCardFillAssist(
const base::Closure& callback) {}
void WebViewAutofillClientIOS::LoadRiskData(
base::OnceCallback<void(const std::string&)> callback) {}
base::OnceCallback<void(const std::string&)> callback) {
[bridge_ loadRiskData:std::move(callback)];
}
bool WebViewAutofillClientIOS::HasCreditCardScanFeature() {
return false;
......
......@@ -12,6 +12,31 @@
NS_ASSUME_NONNULL_BEGIN
@class CWVCreditCard;
@protocol CWVCreditCardVerifierDataSource;
@protocol CWVCreditCardVerifierDelegate;
// The error domain for credit card verification errors.
FOUNDATION_EXPORT CWV_EXPORT
NSErrorDomain const CWVCreditCardVerifierErrorDomain;
// The key for the error message value in the error's |userInfo| dictionary.
FOUNDATION_EXPORT CWV_EXPORT
NSString* const CWVCreditCardVerifierErrorMessageKey;
// The key for the retry allowed value in the error's |userInfo| dictionary.
FOUNDATION_EXPORT CWV_EXPORT
NSString* const CWVCreditCardVerifierRetryAllowedKey;
// Possible error codes during credit card verification.
typedef NS_ENUM(NSInteger, CWVCreditCardVerificationError) {
// No errors.
CWVCreditCardVerificationErrorNone = 0,
// Request failed; try again.
CWVCreditCardVerificationErrorTryAgainFailure = -100,
// Request failed; don't try again.
CWVCreditCardVerificationErrorPermanentFailure = -200,
// Unable to connect to Payments servers. Prompt user to check internet
// connection.
CWVCreditCardVerificationErrorNetworkFailure = -300,
};
CWV_EXPORT
// Helps with verifying credit cards for autofill, updating expired expiration
......@@ -53,22 +78,22 @@ CWV_EXPORT
// Attempts |creditCard| verification.
// |CVC| Card verification code. e.g. 3 digit code on the back of Visa cards or
// 4 digit code in the front of American Express cards.
// |month| 1 or 2 digit expiration month. e.g. 8 or 08 for August. Ignored if
// |month| 1 or 2 digit expiration month. e.g. 8 or 08 for August. Can be nil if
// |needsUpdateForExpirationDate| is NO.
// |year| 2 or 4 digit expiration year. e.g. 19 or 2019. Ignored if
// |year| 2 or 4 digit expiration year. e.g. 19 or 2019. Can be nil if
// |needsUpdateForExpirationDate| is NO.
// |storeLocally| Whether or not to save |creditCard| locally. If YES, user will
// not be asked again to verify this card. Ignored if |canSaveLocally| is NO.
// |completionHandler| Use to receive verification results. Must wait for
// handler to return before attempting another verification.
// |error| Contains the error message if unsuccessful. Empty if successful.
// |retryAllowed| YES if user may attempt verification again.
// |dataSource| will be asked to return risk data needed for verification.
// |delegate| will be passed the verification result. Must wait for |delegate|
// methods before attempting to verify again.
- (void)verifyWithCVC:(NSString*)CVC
expirationMonth:(NSString*)expirationMonth
expirationYear:(NSString*)expirationYear
expirationMonth:(nullable NSString*)expirationMonth
expirationYear:(nullable NSString*)expirationYear
storeLocally:(BOOL)storeLocally
completionHandler:
(void (^)(NSString* errorMessage, BOOL retryAllowed))completionHandler;
dataSource:(__weak id<CWVCreditCardVerifierDataSource>)dataSource
delegate:
(nullable __weak id<CWVCreditCardVerifierDelegate>)delegate;
// Returns YES if |CVC| is all digits and matches |expectedCVCLength|.
- (BOOL)isCVCValid:(NSString*)CVC;
......
// Copyright 2018 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.
#ifndef IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DATA_SOURCE_H_
#define IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DATA_SOURCE_H_
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class CWVCreditCardVerifier;
// Data source for CWVCreditCardVerifer.
@protocol CWVCreditCardVerifierDataSource<NSObject>
// Called when CWVCreditCardVerifier needs risk data before it can continue with
// credit card verification. The client must obtain and return the risk data
// needed for 1st party integration with the internal payments API.
// See go/risk-eng.g3doc for more details.
- (void)creditCardVerifier:(CWVCreditCardVerifier*)creditCardVerifier
getRiskDataWithCompletionHandler:
(void (^)(NSString* riskData))completionHandler;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DATA_SOURCE_H_
// Copyright 2018 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.
#ifndef IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DELEGATE_H_
#define IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DELEGATE_H_
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class CWVCreditCardVerifier;
// Delegate of CWVCreditCardVerifier.
@protocol CWVCreditCardVerifierDelegate<NSObject>
@optional
// Called when CWVCreditCardVerifier could not verify the credit card.
// |error| nil if successful, non-nil if unsuccessful. User info will contain
// key CWVCreditCardVerifierErrorMessageKey indicating the reason and
// CWVCreditCardVerifierRetryAllowedKey indicating if user can try again.
- (void)creditCardVerifier:(CWVCreditCardVerifier*)creditCardVerifier
didFinishVerificationWithError:(nullable NSError*)error;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_PUBLIC_CWV_CREDIT_CARD_VERIFIER_DELEGATE_H_
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