diff --git a/Stripe3DS2/Stripe3DS2.xcodeproj/project.pbxproj b/Stripe3DS2/Stripe3DS2.xcodeproj/project.pbxproj index 5f91c512d6c..ab3405e51c8 100644 --- a/Stripe3DS2/Stripe3DS2.xcodeproj/project.pbxproj +++ b/Stripe3DS2/Stripe3DS2.xcodeproj/project.pbxproj @@ -71,6 +71,7 @@ 3844F0E21742F43BCB32499A /* STDSDeviceInformationParameter+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4869D6E14F01EDB960CB3065 /* STDSDeviceInformationParameter+Private.h */; }; 390691CA0EAF81418B0C65DB /* UIView+LayoutSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0F0485B7048DDAA7DA8F9B /* UIView+LayoutSupport.m */; }; 3909D8028AC485BCCF17B48B /* STDSStackView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D6269F8B91341C387A911DB /* STDSStackView.m */; }; + 3AD384AE2C76634D009AB784 /* STDSAnalyticsDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AD384AD2C76634D009AB784 /* STDSAnalyticsDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B430F172A3AD6AA9AFDFC04 /* STDSNavigationBarCustomization.h in Headers */ = {isa = PBXBuildFile; fileRef = E1630D876436E43547FF69CE /* STDSNavigationBarCustomization.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3C6D16D8E7B5BC865D956A0B /* iOSSnapshotTestCase in Frameworks */ = {isa = PBXBuildFile; productRef = 0118969F6608A92583CC6C98 /* iOSSnapshotTestCase */; }; 3C88AE37A760B91E93D423CF /* STDSException.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F030410FDABFF833CD8FE46 /* STDSException.m */; }; @@ -393,6 +394,7 @@ 3A8CD68A006F970DDC54469A /* Project-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Debug.xcconfig"; sourceTree = ""; }; 3A9F828840A3B361842E38DE /* STDSChallengeResponseImageObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STDSChallengeResponseImageObject.m; sourceTree = ""; }; 3AA2E6DA60350400C5270E08 /* STDSJSONWebSignature.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STDSJSONWebSignature.h; sourceTree = ""; }; + 3AD384AD2C76634D009AB784 /* STDSAnalyticsDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STDSAnalyticsDelegate.h; sourceTree = ""; }; 3B6B71C61CF5ADC7796441F5 /* STDSCompletionEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STDSCompletionEvent.m; sourceTree = ""; }; 3B9A821F35B93898A7AC1ADF /* sl-SI */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sl-SI"; path = "sl-SI.lproj/Localizable.strings"; sourceTree = ""; }; 3C22D410C297E2C3AFE727A6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -988,6 +990,7 @@ 97163B5E9598CA3A298920B9 /* STDSTextFieldCustomization.m */, 5EAA444FA7C82BDA4AD907BF /* STDSThreeDS2Service.h */, 77646CEC7EE754F47EDBDA4F /* STDSThreeDS2Service.m */, + 3AD384AD2C76634D009AB784 /* STDSAnalyticsDelegate.h */, 5E6A9565E97DF2544254AA94 /* STDSThreeDSProtocolVersion.h */, F40CC91AA69F5296B0AA985C /* STDSTransaction.h */, 5FDE2481364C454058BF2377 /* STDSTransaction.m */, @@ -1051,6 +1054,7 @@ 0615DD02C0B022AE207DADF0 /* STDSUICustomization.h in Headers */, 9F0F6085FBD892BF5F14CF86 /* STDSWarning.h in Headers */, 893713F4464A88FAB92BC2C6 /* Stripe3DS2.h in Headers */, + 3AD384AE2C76634D009AB784 /* STDSAnalyticsDelegate.h in Headers */, 425849564519D6454DDA3424 /* NSData+JWEHelpers.h in Headers */, 1134F81C7D015BA5EA52BFD1 /* NSDictionary+DecodingHelpers.h in Headers */, 3617B07ABD719F1A5F8302FA /* NSError+Stripe3DS2.h in Headers */, diff --git a/Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.h b/Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.h index e1ace0cce9a..600ed7df92b 100644 --- a/Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.h +++ b/Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.h @@ -13,6 +13,7 @@ #import "STDSDirectoryServer.h" @class STDSChallengeResponseViewController; +@protocol STDSAnalyticsDelegate; NS_ASSUME_NONNULL_BEGIN @@ -66,7 +67,10 @@ NS_ASSUME_NONNULL_BEGIN /// Use setChallengeResponser:animated: to update this value @property (nonatomic, strong, readonly) id response; -- (instancetype)initWithUICustomization:(STDSUICustomization * _Nullable)uiCustomization imageLoader:(STDSImageLoader *)imageLoader directoryServer:(STDSDirectoryServer)directoryServer; +- (instancetype)initWithUICustomization:(STDSUICustomization * _Nullable)uiCustomization + imageLoader:(STDSImageLoader *)imageLoader + directoryServer:(STDSDirectoryServer)directoryServer + analyticsDelegate:(nullable id)analyticsDelegate; /// If `setLoading` was called beforehand, this waits until the loading spinner has been shown for at least 1 second before displaying the challenge responseself.processingView.isHidden. - (void)setChallengeResponse:(id)response animated:(BOOL)animated; diff --git a/Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.m b/Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.m index 8a0bfcd36f2..d9ceeb7101a 100644 --- a/Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.m +++ b/Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.m @@ -28,6 +28,7 @@ #import "UIButton+CustomInitialization.h" #import "UIFont+DefaultFonts.h" #import "UIViewController+Stripe3DS2.h" +#import "include/STDSAnalyticsDelegate.h" NS_ASSUME_NONNULL_BEGIN @@ -35,6 +36,7 @@ @interface STDSChallengeResponseViewController() @property (nonatomic, strong, nullable) id response; @property (nonatomic) STDSDirectoryServer directoryServer; +@property (weak, nonatomic) idanalyticsDelegate; /// Used to track how long we've been showing a loading spinner. Nil if we are not showing a spinner. @property (nonatomic, strong, nullable) NSDate *loadingStartDate; @property (nonatomic, strong, nullable) STDSUICustomization *uiCustomization; @@ -67,7 +69,10 @@ @implementation STDSChallengeResponseViewController static NSString * const kHTMLStringLoadingURL = @"about:blank"; -- (instancetype)initWithUICustomization:(STDSUICustomization * _Nullable)uiCustomization imageLoader:(STDSImageLoader *)imageLoader directoryServer:(STDSDirectoryServer)directoryServer { +- (instancetype)initWithUICustomization:(STDSUICustomization * _Nullable)uiCustomization + imageLoader:(STDSImageLoader *)imageLoader + directoryServer:(STDSDirectoryServer)directoryServer + analyticsDelegate:(nullable id)analyticsDelegate { self = [super initWithNibName:nil bundle:nil]; if (self) { @@ -75,6 +80,7 @@ - (instancetype)initWithUICustomization:(STDSUICustomization * _Nullable)uiCusto _imageLoader = imageLoader; _tapOutsideKeyboardGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_didTapOutsideKeyboard:)]; _directoryServer = directoryServer; + _analyticsDelegate = analyticsDelegate; } return self; @@ -88,6 +94,7 @@ - (void)viewDidLoad { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; NSString *imageName = STDSDirectoryServerImageName(self.directoryServer); UIImage *dsImage = imageName ? [UIImage imageNamed:imageName inBundle:[STDSBundleLocator stdsResourcesBundle] compatibleWithTraitCollection:nil] : nil; @@ -505,8 +512,17 @@ - (void)_keyboardWillHide:(NSNotification *)notification { self.scrollView.scrollIndicatorInsets = contentInsets; } +- (void)_applicationDidEnterBackground { + if (self.response.acsUIType == STDSACSUITypeOOB) { + [self.analyticsDelegate OOBDidEnterBackground:self.response.threeDSServerTransactionID]; + } +} + - (void)_applicationWillEnterForeground:(NSNotification *)notification { if (self.response.acsUIType == STDSACSUITypeOOB) { + + [self.analyticsDelegate OOBWillEnterForeground:self.response.threeDSServerTransactionID]; + if (self.response.challengeAdditionalInfoText) { // [Req 316] When Challenge Additional Information Text is present, the SDK would replace the Challenge Information Text and Challenge Information Text Indicator with the Challenge Additional Information Text when the 3DS Requestor App is moved to the foreground. self.challengeInformationView.challengeInformationText = self.response.challengeAdditionalInfoText; @@ -531,6 +547,7 @@ - (void)_didTapOutsideKeyboard:(UIGestureRecognizer *)gestureRecognizer { - (void)_cancelButtonTapped:(UIButton *)sender { [self.textChallengeView endEditing:NO]; [self.delegate challengeResponseViewControllerDidCancel:self]; + [self.analyticsDelegate cancelButtonTappedWithTransactionID:self.response.threeDSServerTransactionID]; } - (void)_resendButtonTapped:(UIButton *)sender { @@ -548,6 +565,8 @@ - (void)submit:(STDSACSUIType)type { [self.delegate challengeResponseViewController:self didSubmitInput:self.textChallengeView.inputText whitelistSelection:self.whitelistView.selectedResponse]; + + [self.analyticsDelegate OTPSubmitButtonTappedWithTransactionID:self.response.threeDSServerTransactionID]; break; } case STDSACSUITypeSingleSelect: @@ -560,6 +579,7 @@ - (void)submit:(STDSACSUIType)type { case STDSACSUITypeOOB: [self.delegate challengeResponseViewControllerDidOOBContinue:self whitelistSelection:self.whitelistView.selectedResponse]; + [self.analyticsDelegate OOBContinueButtonTappedWithTransactionID:self.response.threeDSServerTransactionID]; break; case STDSACSUITypeHTML: // No action button in this case, see WKNavigationDelegate. diff --git a/Stripe3DS2/Stripe3DS2/STDSProgressViewController.h b/Stripe3DS2/Stripe3DS2/STDSProgressViewController.h index 147fa4e4851..791e286c771 100644 --- a/Stripe3DS2/Stripe3DS2/STDSProgressViewController.h +++ b/Stripe3DS2/Stripe3DS2/STDSProgressViewController.h @@ -11,12 +11,16 @@ #import "STDSDirectoryServer.h" @class STDSImageLoader, STDSUICustomization; +@protocol STDSAnalyticsDelegate; NS_ASSUME_NONNULL_BEGIN @interface STDSProgressViewController : UIViewController -- (instancetype)initWithDirectoryServer:(STDSDirectoryServer)directoryServer uiCustomization:(STDSUICustomization * _Nullable)uiCustomization didCancel:(void (^)(void))didCancel; +- (instancetype)initWithDirectoryServer:(STDSDirectoryServer)directoryServer + uiCustomization:(STDSUICustomization * _Nullable)uiCustomization + analyticsDelegate:(nullable id)analyticsDelegate + didCancel:(void (^)(void))didCancel; @end diff --git a/Stripe3DS2/Stripe3DS2/STDSProgressViewController.m b/Stripe3DS2/Stripe3DS2/STDSProgressViewController.m index a6de17cd8a2..e7dce7001a4 100644 --- a/Stripe3DS2/Stripe3DS2/STDSProgressViewController.m +++ b/Stripe3DS2/Stripe3DS2/STDSProgressViewController.m @@ -13,16 +13,21 @@ #import "UIViewController+Stripe3DS2.h" #import "STDSProcessingView.h" #import "STDSVisionSupport.h" +#import "include/STDSAnalyticsDelegate.h" @interface STDSProgressViewController() @property (nonatomic, strong, nullable) STDSUICustomization *uiCustomization; @property (nonatomic, strong) void (^didCancel)(void); @property (nonatomic) STDSDirectoryServer directoryServer; +@property (nonatomic, weak) id analyticsDelegate; @end @implementation STDSProgressViewController -- (instancetype)initWithDirectoryServer:(STDSDirectoryServer)directoryServer uiCustomization:(STDSUICustomization * _Nullable)uiCustomization didCancel:(void (^)(void))didCancel { +- (instancetype)initWithDirectoryServer:(STDSDirectoryServer)directoryServer + uiCustomization:(STDSUICustomization * _Nullable)uiCustomization + analyticsDelegate:(id)analyticsDelegate + didCancel:(void (^)(void))didCancel { self = [super initWithNibName:nil bundle:nil]; if (self) { diff --git a/Stripe3DS2/Stripe3DS2/STDSTransaction+Private.h b/Stripe3DS2/Stripe3DS2/STDSTransaction+Private.h index 2c35e956202..e744c38de8f 100644 --- a/Stripe3DS2/Stripe3DS2/STDSTransaction+Private.h +++ b/Stripe3DS2/Stripe3DS2/STDSTransaction+Private.h @@ -10,6 +10,7 @@ @class STDSDeviceInformation; @class STDSDirectoryServerCertificate; +@protocol STDSAnalyticsDelegate; #import "STDSDirectoryServer.h" #import "STDSThreeDSProtocolVersion+Private.h" @@ -22,7 +23,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithDeviceInformation:(STDSDeviceInformation *)deviceInformation directoryServer:(STDSDirectoryServer)directoryServer protocolVersion:(STDSThreeDSProtocolVersion)protocolVersion - uiCustomization:(STDSUICustomization *)uiCustomization; + uiCustomization:(STDSUICustomization *)uiCustomization + analyticsDelegate:(nullable id)analyticsDelegate; - (instancetype)initWithDeviceInformation:(STDSDeviceInformation *)deviceInformation directoryServerID:(NSString *)directoryServerID @@ -30,7 +32,8 @@ NS_ASSUME_NONNULL_BEGIN directoryServerCertificate:(STDSDirectoryServerCertificate *)directoryServerCertificate rootCertificateStrings:(NSArray *)rootCertificateStrings protocolVersion:(STDSThreeDSProtocolVersion)protocolVersion - uiCustomization:(STDSUICustomization *)uiCustomization; + uiCustomization:(STDSUICustomization *)uiCustomization + analyticsDelegate:(nullable id)analyticsDelegate; @property (nonatomic, strong) NSTimer *timeoutTimer; @property (nonatomic) BOOL bypassTestModeVerification; // Should be used during internal testing ONLY diff --git a/Stripe3DS2/Stripe3DS2/include/STDSAnalyticsDelegate.h b/Stripe3DS2/Stripe3DS2/include/STDSAnalyticsDelegate.h new file mode 100644 index 00000000000..206a2623be2 --- /dev/null +++ b/Stripe3DS2/Stripe3DS2/include/STDSAnalyticsDelegate.h @@ -0,0 +1,25 @@ +// +// STDSAnalyticsDelegate.h +// Stripe3DS2 +// +// Created by Kenneth Ackerson on 8/21/24. +// + +NS_ASSUME_NONNULL_BEGIN + +@protocol STDSAnalyticsDelegate + +- (void)didReceiveChallengeResponseWithTransactionID:(NSString *)transactionID flow:(NSString *)type; + +- (void)cancelButtonTappedWithTransactionID:(NSString *)transactionID; + +- (void)OTPSubmitButtonTappedWithTransactionID:(NSString *)transactionID; + +- (void)OOBContinueButtonTappedWithTransactionID:(NSString *)transactionID; + +- (void)OOBDidEnterBackground:(NSString *)transactionID; +- (void)OOBWillEnterForeground:(NSString *)transactionID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.h b/Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.h index c0baa55fef5..0b38b6fe20b 100644 --- a/Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.h +++ b/Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.h @@ -12,6 +12,7 @@ @class STDSTransaction; @class STDSUICustomization; @class STDSWarning; +@protocol STDSAnalyticsDelegate; NS_ASSUME_NONNULL_BEGIN @@ -47,6 +48,11 @@ NS_ASSUME_NONNULL_BEGIN locale:(nullable NSLocale *)locale uiSettings:(nullable STDSUICustomization *)uiSettings; +- (void)initializeWithConfig:(STDSConfigParameters *)config + locale:(nullable NSLocale *)locale + uiSettings:(nullable STDSUICustomization *)uiSettings + analyticsDelegate:(nonnull id)analyticsDelegate; + /** Creates and returns an instance of `STDSTransaction`. diff --git a/Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.m b/Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.m index 2c45de663a7..4e3cccb3308 100644 --- a/Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.m +++ b/Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.m @@ -42,10 +42,19 @@ @implementation STDSThreeDS2Service STDSDeviceInformation *_deviceInformation; STDSUICustomization *_uiSettings; STDSConfigParameters *_configuration; + __weak id _analyticsDelegate; } @synthesize warnings = _warnings; +- (void)initializeWithConfig:(STDSConfigParameters *)config + locale:(nullable NSLocale *)locale + uiSettings:(nullable STDSUICustomization *)uiSettings + analyticsDelegate:(nonnull id)analyticsDelegate { + _analyticsDelegate = analyticsDelegate; + [self initializeWithConfig:config locale:locale uiSettings:uiSettings]; +} + - (void)initializeWithConfig:(STDSConfigParameters *)config locale:(nullable NSLocale *)locale uiSettings:(nullable STDSUICustomization *)uiSettings { @@ -122,7 +131,8 @@ - (STDSTransaction *)createTransactionForDirectoryServer:(NSString *)directorySe STDSTransaction *transaction = [[STDSTransaction alloc] initWithDeviceInformation:_deviceInformation directoryServer:directoryServer protocolVersion:(protocolVersion != nil) ? STDSThreeDSProtocolVersionForString(protocolVersion) : STDSThreeDSProtocolVersion2_1_0 - uiCustomization:_uiSettings]; + uiCustomization:_uiSettings + analyticsDelegate:_analyticsDelegate]; transaction.bypassTestModeVerification = [[_configuration parameterValue:kInternalStripeTestingConfigParam] isEqualToString:@"Y"]; transaction.useULTestLOA = [[_configuration parameterValue:kUseULTestLOAParam] isEqualToString:@"Y"]; return transaction; @@ -153,7 +163,8 @@ - (nullable STDSTransaction *)createTransactionForDirectoryServer:(NSString *)di directoryServerCertificate:certificate rootCertificateStrings:rootCertificateStrings protocolVersion:(protocolVersion != nil) ? STDSThreeDSProtocolVersionForString(protocolVersion) : STDSThreeDSProtocolVersion2_1_0 - uiCustomization:_uiSettings]; + uiCustomization:_uiSettings + analyticsDelegate:_analyticsDelegate]; transaction.bypassTestModeVerification = [_configuration parameterValue:kInternalStripeTestingConfigParam] != nil; } diff --git a/Stripe3DS2/Stripe3DS2/include/STDSTransaction.m b/Stripe3DS2/Stripe3DS2/include/STDSTransaction.m index 654efa64b84..c4ef2900d30 100644 --- a/Stripe3DS2/Stripe3DS2/include/STDSTransaction.m +++ b/Stripe3DS2/Stripe3DS2/include/STDSTransaction.m @@ -36,6 +36,7 @@ #import "STDSSecTypeUtilities.h" #import "STDSStripe3DS2Error.h" #import "STDSDeviceInformationParameter.h" +#import "STDSAnalyticsDelegate.h" static const NSTimeInterval kMinimumTimeout = 5 * 60; static NSString * const kStripeLOA = @"3DS_LOA_SDK_STIN_020100_00162"; @@ -69,12 +70,15 @@ @implementation STDSTransaction STDSACSNetworkingManager *_networkingManager; STDSUICustomization *_uiCustomization; + + __weak id _analyticsDelegate; } - (instancetype)initWithDeviceInformation:(STDSDeviceInformation *)deviceInformation directoryServer:(STDSDirectoryServer)directoryServer protocolVersion:(STDSThreeDSProtocolVersion)protocolVersion - uiCustomization:(nonnull STDSUICustomization *)uiCustomization { + uiCustomization:(nonnull STDSUICustomization *)uiCustomization + analyticsDelegate:(nullable id)analyticsDelegate { self = [super init]; if (self) { _deviceInformation = deviceInformation; @@ -98,7 +102,8 @@ - (instancetype)initWithDeviceInformation:(STDSDeviceInformation *)deviceInforma directoryServerCertificate:(STDSDirectoryServerCertificate *)directoryServerCertificate rootCertificateStrings:(NSArray *)rootCertificateStrings protocolVersion:(STDSThreeDSProtocolVersion)protocolVersion - uiCustomization:(STDSUICustomization *)uiCustomization { + uiCustomization:(STDSUICustomization *)uiCustomization + analyticsDelegate:(nullable id)analyticsDelegate { self = [super init]; if (self) { _deviceInformation = deviceInformation; @@ -125,7 +130,11 @@ - (NSString *)sdkVersion { } - (NSString *)presentedChallengeUIType { - switch (self.challengeResponseViewController.response.acsUIType) { + return [self UIType:self.challengeResponseViewController.response.acsUIType]; +} + +- (NSString *)UIType:(STDSACSUIType)uiType { + switch (uiType) { case STDSACSUITypeNone: return @"none"; @@ -175,7 +184,10 @@ - (STDSAuthenticationRequestParameters *)createAuthenticationRequestParameters { } - (UIViewController *)createProgressViewControllerWithDidCancel:(void (^)(void))didCancel { - return [[STDSProgressViewController alloc] initWithDirectoryServer:[self _directoryServerForUI] uiCustomization:_uiCustomization didCancel:didCancel]; + return [[STDSProgressViewController alloc] initWithDirectoryServer:[self _directoryServerForUI] + uiCustomization:_uiCustomization + analyticsDelegate:_analyticsDelegate + didCancel:didCancel]; } - (void)doChallengeWithViewController:(UIViewController *)presentingViewController @@ -276,7 +288,10 @@ - (void)doChallengeWithChallengeParameters:(STDSChallengeParameters *)challengeP acsTransactionIdentifier:self.challengeRequestParameters.acsTransactionIdentifier]; // Start the Challenge flow STDSImageLoader *imageLoader = [[STDSImageLoader alloc] initWithURLSession:NSURLSession.sharedSession]; - self.challengeResponseViewController = [[STDSChallengeResponseViewController alloc] initWithUICustomization:_uiCustomization imageLoader:imageLoader directoryServer:[self _directoryServerForUI]]; + self.challengeResponseViewController = [[STDSChallengeResponseViewController alloc] initWithUICustomization:_uiCustomization + imageLoader:imageLoader + directoryServer:[self _directoryServerForUI] + analyticsDelegate:_analyticsDelegate]; self.challengeResponseViewController.delegate = self; presentationBlock(self.challengeResponseViewController, ^{ [self _makeChallengeRequest:self.challengeRequestParameters didCancel:NO]; }); @@ -418,6 +433,7 @@ - (void) _handleError:(NSError *)error { } - (void)_handleChallengeResponse:(id)challengeResponse didCancel:(BOOL)didCancel { + if (challengeResponse.challengeCompletionIndicator) { // Final CRes // We need to pass didCancel to here because we can't distinguish between cancellation and auth failure from the CRes @@ -440,6 +456,8 @@ - (void)_handleChallengeResponse:(id)challengeResponse di [self.challengeStatusReceiver transactionDidPresentChallengeScreen:self]; } } + + [_analyticsDelegate didReceiveChallengeResponseWithTransactionID:challengeResponse.threeDSServerTransactionID flow:[self UIType:challengeResponse.acsUIType]]; } - (void)_cleanUp { diff --git a/Stripe3DS2/Stripe3DS2/include/Stripe3DS2.h b/Stripe3DS2/Stripe3DS2/include/Stripe3DS2.h index 9e9f3eba405..bf2a2ae94b7 100644 --- a/Stripe3DS2/Stripe3DS2/include/Stripe3DS2.h +++ b/Stripe3DS2/Stripe3DS2/include/Stripe3DS2.h @@ -18,6 +18,7 @@ FOUNDATION_EXPORT const unsigned char Stripe3DS2VersionString[]; #import "STDSThreeDS2Service.h" #import "STDSUICustomization.h" #import "STDSWarning.h" +#import "STDSAnalyticsDelegate.h" #import "STDSAlreadyInitializedException.h" #import "STDSInvalidInputException.h" diff --git a/Stripe3DS2/Stripe3DS2DemoUI/Sources/STDSDemoViewController.m b/Stripe3DS2/Stripe3DS2DemoUI/Sources/STDSDemoViewController.m index fa4ddeee388..de02310d9ff 100644 --- a/Stripe3DS2/Stripe3DS2DemoUI/Sources/STDSDemoViewController.m +++ b/Stripe3DS2/Stripe3DS2DemoUI/Sources/STDSDemoViewController.m @@ -138,7 +138,7 @@ - (void)presentTextChallengeWithResendCodeAndWhitelist { - (void)presentTextChallengeLoadsSlowly { self.shouldLoadSlowly = YES; - STDSProgressViewController *progressVC = [[STDSProgressViewController alloc] initWithDirectoryServer:STDSDirectoryServerULTestEC uiCustomization:self.customization didCancel:^{}]; + STDSProgressViewController *progressVC = [[STDSProgressViewController alloc] initWithDirectoryServer:STDSDirectoryServerULTestEC uiCustomization:self.customization analyticsDelegate:nil didCancel:^{}]; UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:progressVC]; [self.navigationController presentViewController:navigationController animated:YES completion:nil]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ @@ -166,7 +166,10 @@ - (void)presentHTMLChallenge { - (void)presentProgressView { __weak typeof(self) weakSelf = self; - UIViewController *vc = [[STDSProgressViewController alloc] initWithDirectoryServer:STDSDirectoryServerULTestEC uiCustomization:self.customization didCancel:^{ + UIViewController *vc = [[STDSProgressViewController alloc] initWithDirectoryServer:STDSDirectoryServerULTestEC + uiCustomization:self.customization + analyticsDelegate:nil + didCancel:^{ [weakSelf dismissViewControllerAnimated:YES completion:nil]; }]; UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:vc]; @@ -174,9 +177,12 @@ - (void)presentProgressView { } - (void)presentChallengeForChallengeResponse:(id)challengeResponse { - STDSChallengeResponseViewController *challengeResponseViewController = [[STDSChallengeResponseViewController alloc] initWithUICustomization:self.customization imageLoader:self.imageLoader directoryServer:STDSDirectoryServerULTestEC]; + STDSChallengeResponseViewController *challengeResponseViewController = [[STDSChallengeResponseViewController alloc] initWithUICustomization:self.customization + imageLoader:self.imageLoader + directoryServer:STDSDirectoryServerULTestEC + analyticsDelegate:nil]; challengeResponseViewController.delegate = self; - + UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:challengeResponseViewController]; [self.navigationController presentViewController:navigationController animated:YES completion:nil]; // Simulate what `STDSTransaction` does