Merge pull request #653 from stripe/bg-telemetry

Add STPTelemetryClient
This commit is contained in:
bg-stripe 2017-05-03 12:04:48 -04:00 committed by GitHub
commit 8ab7eaa279
12 changed files with 229 additions and 29 deletions

View File

@ -1,6 +1,7 @@
== 10.1.0 2017-05-XX
* Updates STPPaymentCardTextField's icons to match Elements on the web. When the card number is invalid, the field will now display an error icon.
* The alignment of the new brand icons has changed to match the new CVC and error icons. If you use these icons via `STPImageLibrary`, you may need to adjust your layout.
* Because collecting some basic data on tokenization helps us detect fraud, we've removed the ability to disable analytics collection using `[Stripe disableAnalytics]`.
== 10.0.1 2017-03-16
* Fixes a bug where card sources didn't include the card owner's name.

View File

@ -474,6 +474,11 @@
C18867D41E8AF8F300A77634 /* OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = C11B14961E8AE316000F760C /* OCMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C18867DB1E8B0C4100A77634 /* STPFixtures.h in Headers */ = {isa = PBXBuildFile; fileRef = C18867D91E8B0C4100A77634 /* STPFixtures.h */; };
C18867DC1E8B0C4100A77634 /* STPFixtures.m in Sources */ = {isa = PBXBuildFile; fileRef = C18867DA1E8B0C4100A77634 /* STPFixtures.m */; };
C192268A1EBA228900BED563 /* STPTelemetryClient.m in Sources */ = {isa = PBXBuildFile; fileRef = C19D098E1EAEAE4000A4AB3E /* STPTelemetryClient.m */; };
C192268B1EBA228C00BED563 /* STPTelemetryClient.h in Headers */ = {isa = PBXBuildFile; fileRef = C19D098D1EAEAE4000A4AB3E /* STPTelemetryClient.h */; };
C19D098F1EAEAE4000A4AB3E /* STPTelemetryClient.h in Headers */ = {isa = PBXBuildFile; fileRef = C19D098D1EAEAE4000A4AB3E /* STPTelemetryClient.h */; };
C19D09901EAEAE4000A4AB3E /* STPTelemetryClient.m in Sources */ = {isa = PBXBuildFile; fileRef = C19D098E1EAEAE4000A4AB3E /* STPTelemetryClient.m */; };
C19D09931EAEAE5E00A4AB3E /* STPTelemetryClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C19D09911EAEAE5200A4AB3E /* STPTelemetryClientTest.m */; };
C1A06F101E1D8A7F004DCA06 /* STPCard+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */; };
C1A06F111E1D8A7F004DCA06 /* STPCard+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */; };
C1B630BB1D1D860100A05285 /* stp_card_amex.png in Resources */ = {isa = PBXBuildFile; fileRef = 0438EF891B741C2800D506CC /* stp_card_amex.png */; };
@ -1035,6 +1040,9 @@
C18021191E3A58710089D712 /* STPSourcePoller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPSourcePoller.m; sourceTree = "<group>"; };
C18867D91E8B0C4100A77634 /* STPFixtures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPFixtures.h; sourceTree = "<group>"; };
C18867DA1E8B0C4100A77634 /* STPFixtures.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPFixtures.m; sourceTree = "<group>"; };
C19D098D1EAEAE4000A4AB3E /* STPTelemetryClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STPTelemetryClient.h; sourceTree = "<group>"; };
C19D098E1EAEAE4000A4AB3E /* STPTelemetryClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPTelemetryClient.m; sourceTree = "<group>"; };
C19D09911EAEAE5200A4AB3E /* STPTelemetryClientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STPTelemetryClientTest.m; sourceTree = "<group>"; };
C1A06F0F1E1D8A6E004DCA06 /* STPCard+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "STPCard+Private.h"; sourceTree = "<group>"; };
C1B630B31D1D817900A05285 /* Stripe.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Stripe.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
C1B630B51D1D817900A05285 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -1463,6 +1471,7 @@
C1BD9B1E1E390A2700CEE925 /* STPSourceParamsTest.m */,
C17D24ED1E37DBAC005CB188 /* STPSourceTest.m */,
F1D777BF1D81DD520076FA19 /* STPStringUtilsTest.m */,
C19D09911EAEAE5200A4AB3E /* STPTelemetryClientTest.m */,
04CDB5271A5F3A9300B854EE /* STPTokenTest.m */,
04A4C3931C4F276100B3B290 /* STPUIVCStripeParentViewControllerTests.m */,
C15B02721EA176090026E606 /* StripeErrorTest.m */,
@ -1662,6 +1671,8 @@
children = (
C124A16E1CCA968B007D42EE /* STPAnalyticsClient.h */,
C124A16F1CCA968B007D42EE /* STPAnalyticsClient.m */,
C19D098D1EAEAE4000A4AB3E /* STPTelemetryClient.h */,
C19D098E1EAEAE4000A4AB3E /* STPTelemetryClient.m */,
);
name = Analytics;
sourceTree = "<group>";
@ -1969,6 +1980,7 @@
F19491DF1E5F6B8C001E1FC2 /* STPSourceCardDetails.h in Headers */,
C12655391CAA238E006F7265 /* STPAddCardViewController.h in Headers */,
04F94DAD1D229F4E004FC826 /* STPColorUtils.h in Headers */,
C192268B1EBA228C00BED563 /* STPTelemetryClient.h in Headers */,
04F94D9B1D229E76004FC826 /* STPTheme.h in Headers */,
04F213331BCEAB61001D6F22 /* STPFormEncodable.h in Headers */,
04F94D9C1D229EAA004FC826 /* PKPayment+Stripe.h in Headers */,
@ -2116,6 +2128,7 @@
F1D96F961DC7D82400477E64 /* STPLocalizationUtils.h in Headers */,
0438EF381B7416BB00D506CC /* STPPaymentCardTextFieldViewModel.h in Headers */,
04CDE5BC1BC1F21500548833 /* STPCardParams.h in Headers */,
C19D098F1EAEAE4000A4AB3E /* STPTelemetryClient.h in Headers */,
04F213351BCECB1C001D6F22 /* STPAPIResponseDecodable.h in Headers */,
04695AD31C77F9DB00E08063 /* NSString+Stripe.h in Headers */,
C1BD9B341E3940C400CEE925 /* STPSourceVerification.h in Headers */,
@ -2558,6 +2571,7 @@
F1D3A25F1EB015B30095BFA9 /* UIImage+StripeTests.m in Sources */,
04415C6B1A6605B5001225ED /* STPBankAccountFunctionalTest.m in Sources */,
04415C6C1A6605B5001225ED /* STPBankAccountTest.m in Sources */,
C19D09931EAEAE5E00A4AB3E /* STPTelemetryClientTest.m in Sources */,
F152321B1EA92F9D00D65C67 /* STPRedirectContextTest.m in Sources */,
04415C6D1A6605B5001225ED /* STPCardFunctionalTest.m in Sources */,
F15675401DB544D3004468E3 /* STPAddCardViewControllerLocalizationTests.m in Sources */,
@ -2670,6 +2684,7 @@
049952D81BCF14990088C703 /* STPAPIRequest.m in Sources */,
04F94DBE1D229F98004FC826 /* UITableViewCell+Stripe_Borders.m in Sources */,
0426B9791CEBD001006AC8DD /* UINavigationBar+Stripe_Theme.m in Sources */,
C192268A1EBA228900BED563 /* STPTelemetryClient.m in Sources */,
045D71231CEFA57000F6CD65 /* UIViewController+Stripe_Promises.m in Sources */,
04BC29A11CD8412000318357 /* STPPaymentContext.m in Sources */,
F1D64B2C1D8767FC001CDB7C /* STPWebViewController.m in Sources */,
@ -2719,6 +2734,7 @@
C15993331D8808680047950D /* STPShippingAddressViewController.m in Sources */,
04633B051CD44F1C009D4FB5 /* STPAPIClient+ApplePay.m in Sources */,
F1C7B8D31DBECF2400D9F6F0 /* STPDispatchFunctions.m in Sources */,
C19D09901EAEAE4000A4AB3E /* STPTelemetryClient.m in Sources */,
04B31DF41D09F0A800EF1631 /* UIViewController+Stripe_NavigationItemProxy.m in Sources */,
04BC29A51CD8697900318357 /* STPTheme.m in Sources */,
C15993391D8808680047950D /* STPShippingMethodTableViewCell.m in Sources */,

View File

@ -11,6 +11,7 @@
@interface NSBundle (Stripe_AppName)
+ (nullable NSString*)stp_applicationName;
+ (nullable NSString*)stp_applicationVersion;
@end

View File

@ -14,6 +14,10 @@
return [[self mainBundle] infoDictionary][(NSString *)kCFBundleNameKey];
}
+ (nullable NSString *)stp_applicationVersion {
return [[self mainBundle] infoDictionary][@"CFBundleShortVersionString"];
}
@end
void linkNSBundleAppNameCategory(void){}

View File

@ -38,12 +38,6 @@ static NSString *const STPSDKVersion = @"10.0.1";
/// The current default publishable key.
+ (nullable NSString *)defaultPublishableKey;
/**
* By default, Stripe collects some basic information about SDK usage.
* You can call this method to turn off analytics collection.
*/
+ (void)disableAnalytics;
@end
/// A client for making connections to the Stripe API.

View File

@ -26,6 +26,7 @@
#import "STPSourceParams.h"
#import "STPSourceParams+Private.h"
#import "STPSourcePoller.h"
#import "STPTelemetryClient.h"
#import "STPToken.h"
#import "StripeError.h"
#import "UIImage+Stripe.h"
@ -59,10 +60,6 @@ static NSString *const stripeAPIVersion = @"2015-10-12";
return [STPPaymentConfiguration sharedConfiguration].publishableKey;
}
+ (void)disableAnalytics {
[STPAnalyticsClient disableAnalytics];
}
@end
#if __has_include("Fabric.h")
@ -80,6 +77,7 @@ static NSString *const stripeAPIVersion = @"2015-10-12";
+ (void)initialize {
[STPAnalyticsClient initializeIfNeeded];
[STPTelemetryClient sharedInstance];
#ifdef STP_STATIC_LIBRARY_BUILD
[STPCategoryLoader loadCategories];
#endif
@ -336,8 +334,9 @@ static NSString *const stripeAPIVersion = @"2015-10-12";
- (void)createTokenWithCard:(STPCard *)card completion:(STPTokenCompletionBlock)completion {
NSMutableDictionary *params = [[STPFormEncoder dictionaryForObject:card] mutableCopy];
params[@"muid"] = [STPAnalyticsClient muid];
[[STPTelemetryClient sharedInstance] addTelemetryFieldsToParams:params];
[self createTokenWithParameters:params completion:completion];
[[STPTelemetryClient sharedInstance] sendTelemetryData];
}
@end

View File

@ -25,10 +25,6 @@ typedef NS_ENUM(NSUInteger, STPAddCardRememberMeUsage) {
+ (void)initializeIfNeeded;
+ (void)disableAnalytics;
+ (NSString *)muid;
+ (NSString *)tokenTypeFromParameters:(NSDictionary *)parameters;
- (void)logRememberMeConversion:(STPAddCardRememberMeUsage)selected;

View File

@ -25,8 +25,6 @@
#import <UIKit/UIKit.h>
#import <sys/utsname.h>
static BOOL STPAnalyticsCollectionDisabled = NO;
@interface STPAnalyticsClient()
@property (nonatomic) NSSet *apiUsage;
@ -91,28 +89,18 @@ static BOOL STPAnalyticsCollectionDisabled = NO;
});
}
+ (void)disableAnalytics {
STPAnalyticsCollectionDisabled = YES;
}
+ (BOOL)shouldCollectAnalytics {
#if TARGET_OS_SIMULATOR
return NO;
#else
return NSClassFromString(@"XCTest") == nil;
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
return NSClassFromString(@"XCTest") == nil && !STPAnalyticsCollectionDisabled;
#pragma clang diagnostic pop
}
+ (NSNumber *)timestampWithDate:(NSDate *)date {
return @((NSInteger)([date timeIntervalSince1970]*1000));
}
+ (NSString *)muid {
return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
+ (NSString *)tokenTypeFromParameters:(NSDictionary *)parameters {
if ([parameters.allKeys count] == 1) {
NSArray *validTypes = @[@"bank_account", @"card", @"pii"];

View File

@ -11,6 +11,7 @@
#import "NSBundle+Stripe_AppName.h"
#import "STPAnalyticsClient.h"
#import "STPPaymentConfiguration+Private.h"
#import "STPTelemetryClient.h"
#import "Stripe.h"
@implementation STPPaymentConfiguration
@ -19,6 +20,7 @@
+ (void)initialize {
[STPAnalyticsClient initializeIfNeeded];
[STPTelemetryClient sharedInstance];
}
+ (instancetype)sharedConfiguration {

View File

@ -0,0 +1,17 @@
//
// STPTelemetryClient.h
// Stripe
//
// Created by Ben Guo on 4/18/17.
// Copyright © 2016 Stripe, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface STPTelemetryClient : NSObject
+ (instancetype)sharedInstance;
- (void)addTelemetryFieldsToParams:(NSMutableDictionary *)params;
- (void)sendTelemetryData;
@end

149
Stripe/STPTelemetryClient.m Normal file
View File

@ -0,0 +1,149 @@
//
// STPTelemetryClient.m
// Stripe
//
// Created by Ben Guo on 4/18/17.
// Copyright © 2016 Stripe, Inc. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <sys/utsname.h>
#import "NSBundle+Stripe_AppName.h"
#import "STPTelemetryClient.h"
#import "STPAPIClient.h"
@interface STPTelemetryClient ()
@property (nonatomic) NSDate *appOpenTime;
@property (nonatomic, readwrite) NSURLSession *urlSession;
@end
@implementation STPTelemetryClient
+ (BOOL)shouldSendTelemetry {
#if TARGET_OS_SIMULATOR
return NO;
#else
return NSClassFromString(@"XCTest") == nil;
#endif
}
+ (instancetype)sharedInstance {
static id sharedClient;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedClient = [self new];
});
return sharedClient;
}
- (instancetype)init {
self = [super init];
if (self) {
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_urlSession = [NSURLSession sessionWithConfiguration:config];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
[[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)addTelemetryFieldsToParams:(NSMutableDictionary *)params {
params[@"muid"] = [self muid];
params[@"time_on_page"] = [self timeOnPage];
}
- (void)applicationDidBecomeActive {
self.appOpenTime = [NSDate date];
}
- (NSString *)muid {
NSString *muid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
return muid ?: @"";
}
- (NSNumber *)timeOnPage {
if (!self.appOpenTime) {
return @(0);
}
NSTimeInterval seconds = [[NSDate date] timeIntervalSinceDate:self.appOpenTime];
NSInteger millis = (NSInteger)round(seconds*1000);
return @(MAX(millis, 0));
}
- (NSString *)language {
NSString *localeID = [[NSLocale currentLocale] localeIdentifier];
return localeID ?: @"";
}
- (NSString *)platform {
struct utsname systemInfo;
uname(&systemInfo);
NSString *deviceType = @(systemInfo.machine) ?: @"";
NSString *version = [UIDevice currentDevice].systemVersion ?: @"";
return [@[deviceType, version] componentsJoinedByString:@" "];
}
- (NSString *)screenSize {
UIScreen *screen = [UIScreen mainScreen];
CGRect screenRect = [screen bounds];
CGFloat width = screenRect.size.width;
CGFloat height = screenRect.size.height;
CGFloat scale = [screen scale];
return [NSString stringWithFormat:@"%.0fw_%.0fh_%.0fr", width, height, scale];
}
- (NSString *)timeZoneOffset {
NSTimeZone *timeZone = [NSTimeZone localTimeZone];
double hoursFromGMT = (double)timeZone.secondsFromGMT/(60*60);
return [NSString stringWithFormat:@"%.0f", hoursFromGMT];
}
- (NSDictionary *)encodeValue:(NSString *)value {
if (value) {
return @{@"v": value};
}
return nil;
}
- (NSDictionary *)payload {
NSMutableDictionary *payload = [NSMutableDictionary new];
NSMutableDictionary *data = [NSMutableDictionary new];
data[@"c"] = [self encodeValue:[self language]];
data[@"d"] = [self encodeValue:[self platform]];
data[@"f"] = [self encodeValue:[self screenSize]];
data[@"g"] = [self encodeValue:[self timeZoneOffset]];
payload[@"a"] = [data copy];
NSMutableDictionary *otherData = [NSMutableDictionary new];
otherData[@"d"] = [self muid];
otherData[@"k"] = [NSBundle stp_applicationName];
otherData[@"l"] = [NSBundle stp_applicationVersion];
otherData[@"m"] = @([Stripe deviceSupportsApplePay]);
payload[@"b"] = [otherData copy];
payload[@"tag"] = STPSDKVersion;
payload[@"src"] = @"ios-sdk";
payload[@"v2"] = @1;
return [payload copy];
}
- (void)sendTelemetryData {
if (![[self class] shouldSendTelemetry]) {
return;
}
NSString *path = @"ios-sdk-1";
NSURL *url = [[NSURL URLWithString:@"https://m.stripe.com"] URLByAppendingPathComponent:path];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *payload = [self payload];
NSData *data = [NSJSONSerialization dataWithJSONObject:payload options:(NSJSONWritingOptions)0 error:nil];
request.HTTPBody = data;
NSURLSessionDataTask *task = [self.urlSession dataTaskWithRequest:request];
[task resume];
}
@end

View File

@ -0,0 +1,33 @@
//
// STPTelemetryClientTest.m
// Stripe
//
// Created by Ben Guo on 4/18/17.
// Copyright © 2017 Stripe, Inc. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "STPTelemetryClient.h"
@interface STPTelemetryClientTest : XCTestCase
@end
@implementation STPTelemetryClientTest
- (void)testAddTelemetryData {
STPTelemetryClient *sut = [STPTelemetryClient sharedInstance];
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidBecomeActiveNotification object:nil];
NSMutableDictionary *params = [@{@"foo": @"bar"} mutableCopy];
XCTestExpectation *exp = [self expectationWithDescription:@"delay"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[sut addTelemetryFieldsToParams:params];
NSInteger time = [params[@"time_on_page"] integerValue];
XCTAssertTrue(time > 0);
XCTAssertNotNil(params[@"muid"]);
[exp fulfill];
});
[self waitForExpectationsWithTimeout:2 handler:nil];
}
@end