Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analytics Hooks #3951

Draft
wants to merge 7 commits into
base: feature/2.2
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Stripe3DS2/Stripe3DS2.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -393,6 +394,7 @@
3A8CD68A006F970DDC54469A /* Project-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Debug.xcconfig"; sourceTree = "<group>"; };
3A9F828840A3B361842E38DE /* STDSChallengeResponseImageObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STDSChallengeResponseImageObject.m; sourceTree = "<group>"; };
3AA2E6DA60350400C5270E08 /* STDSJSONWebSignature.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STDSJSONWebSignature.h; sourceTree = "<group>"; };
3AD384AD2C76634D009AB784 /* STDSAnalyticsDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STDSAnalyticsDelegate.h; sourceTree = "<group>"; };
3B6B71C61CF5ADC7796441F5 /* STDSCompletionEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = STDSCompletionEvent.m; sourceTree = "<group>"; };
3B9A821F35B93898A7AC1ADF /* sl-SI */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sl-SI"; path = "sl-SI.lproj/Localizable.strings"; sourceTree = "<group>"; };
3C22D410C297E2C3AFE727A6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#import "STDSDirectoryServer.h"

@class STDSChallengeResponseViewController;
@protocol STDSAnalyticsDelegate;

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -66,7 +67,10 @@ NS_ASSUME_NONNULL_BEGIN
/// Use setChallengeResponser:animated: to update this value
@property (nonatomic, strong, readonly) id<STDSChallengeResponse> 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<STDSAnalyticsDelegate>)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<STDSChallengeResponse>)response animated:(BOOL)animated;
Expand Down
22 changes: 21 additions & 1 deletion Stripe3DS2/Stripe3DS2/STDSChallengeResponseViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
#import "UIButton+CustomInitialization.h"
#import "UIFont+DefaultFonts.h"
#import "UIViewController+Stripe3DS2.h"
#import "include/STDSAnalyticsDelegate.h"

NS_ASSUME_NONNULL_BEGIN

@interface STDSChallengeResponseViewController() <WKNavigationDelegate>

@property (nonatomic, strong, nullable) id<STDSChallengeResponse> response;
@property (nonatomic) STDSDirectoryServer directoryServer;
@property (weak, nonatomic) id<STDSAnalyticsDelegate>analyticsDelegate;
/// 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;
Expand Down Expand Up @@ -67,14 +69,18 @@ @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<STDSAnalyticsDelegate>)analyticsDelegate {
self = [super initWithNibName:nil bundle:nil];

if (self) {
_uiCustomization = uiCustomization;
_imageLoader = imageLoader;
_tapOutsideKeyboardGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_didTapOutsideKeyboard:)];
_directoryServer = directoryServer;
_analyticsDelegate = analyticsDelegate;
}

return self;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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:
Expand All @@ -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.
Expand Down
6 changes: 5 additions & 1 deletion Stripe3DS2/Stripe3DS2/STDSProgressViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<STDSAnalyticsDelegate>)analyticsDelegate
didCancel:(void (^)(void))didCancel;

@end

Expand Down
7 changes: 6 additions & 1 deletion Stripe3DS2/Stripe3DS2/STDSProgressViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -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<STDSAnalyticsDelegate> 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<STDSAnalyticsDelegate>)analyticsDelegate
didCancel:(void (^)(void))didCancel {
self = [super initWithNibName:nil bundle:nil];

if (self) {
Expand Down
7 changes: 5 additions & 2 deletions Stripe3DS2/Stripe3DS2/STDSTransaction+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

@class STDSDeviceInformation;
@class STDSDirectoryServerCertificate;
@protocol STDSAnalyticsDelegate;

#import "STDSDirectoryServer.h"
#import "STDSThreeDSProtocolVersion+Private.h"
Expand All @@ -22,15 +23,17 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithDeviceInformation:(STDSDeviceInformation *)deviceInformation
directoryServer:(STDSDirectoryServer)directoryServer
protocolVersion:(STDSThreeDSProtocolVersion)protocolVersion
uiCustomization:(STDSUICustomization *)uiCustomization;
uiCustomization:(STDSUICustomization *)uiCustomization
analyticsDelegate:(nullable id<STDSAnalyticsDelegate>)analyticsDelegate;

- (instancetype)initWithDeviceInformation:(STDSDeviceInformation *)deviceInformation
directoryServerID:(NSString *)directoryServerID
serverKeyID:(nullable NSString *)serverKeyID
directoryServerCertificate:(STDSDirectoryServerCertificate *)directoryServerCertificate
rootCertificateStrings:(NSArray<NSString *> *)rootCertificateStrings
protocolVersion:(STDSThreeDSProtocolVersion)protocolVersion
uiCustomization:(STDSUICustomization *)uiCustomization;
uiCustomization:(STDSUICustomization *)uiCustomization
analyticsDelegate:(nullable id<STDSAnalyticsDelegate>)analyticsDelegate;

@property (nonatomic, strong) NSTimer *timeoutTimer;
@property (nonatomic) BOOL bypassTestModeVerification; // Should be used during internal testing ONLY
Expand Down
25 changes: 25 additions & 0 deletions Stripe3DS2/Stripe3DS2/include/STDSAnalyticsDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// STDSAnalyticsDelegate.h
// Stripe3DS2
//
// Created by Kenneth Ackerson on 8/21/24.
//

NS_ASSUME_NONNULL_BEGIN

@protocol STDSAnalyticsDelegate <NSObject>

- (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
6 changes: 6 additions & 0 deletions Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
@class STDSTransaction;
@class STDSUICustomization;
@class STDSWarning;
@protocol STDSAnalyticsDelegate;

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -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<STDSAnalyticsDelegate>)analyticsDelegate;

/**
Creates and returns an instance of `STDSTransaction`.

Expand Down
15 changes: 13 additions & 2 deletions Stripe3DS2/Stripe3DS2/include/STDSThreeDS2Service.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,19 @@ @implementation STDSThreeDS2Service
STDSDeviceInformation *_deviceInformation;
STDSUICustomization *_uiSettings;
STDSConfigParameters *_configuration;
__weak id<STDSAnalyticsDelegate> _analyticsDelegate;
}

@synthesize warnings = _warnings;

- (void)initializeWithConfig:(STDSConfigParameters *)config
locale:(nullable NSLocale *)locale
uiSettings:(nullable STDSUICustomization *)uiSettings
analyticsDelegate:(nonnull id<STDSAnalyticsDelegate>)analyticsDelegate {
_analyticsDelegate = analyticsDelegate;
[self initializeWithConfig:config locale:locale uiSettings:uiSettings];
}

- (void)initializeWithConfig:(STDSConfigParameters *)config
locale:(nullable NSLocale *)locale
uiSettings:(nullable STDSUICustomization *)uiSettings {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
Loading