PPOTResult.m 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. //
  2. // PPOTResult.m
  3. // PayPalOneTouch
  4. //
  5. // Copyright © 2015 PayPal, Inc. All rights reserved.
  6. //
  7. #import <Foundation/Foundation.h>
  8. #import "PPOTResult_Internal.h"
  9. #import "PPOTCore_Internal.h"
  10. #import "PPOTAppSwitchResponse.h"
  11. #import "PPOTConfiguration.h"
  12. #import "PPOTAnalyticsDefines.h"
  13. #import "PPOTAnalyticsTracker.h"
  14. #import "PPOTError.h"
  15. #import "PPOTPersistentRequestData.h"
  16. #if __has_include("PayPalUtils.h")
  17. #import "PPOTDevice.h"
  18. #import "PPOTMacros.h"
  19. #import "PPOTVersion.h"
  20. #else
  21. #import <PayPalUtils/PPOTDevice.h>
  22. #import <PayPalUtils/PPOTMacros.h>
  23. #import <PayPalUtils/PPOTVersion.h>
  24. #endif
  25. #define PP_TIMESTAMP_TIMEOUT 10*60 // 10 minutes
  26. @implementation PPOTResult
  27. + (void)parseURL:(NSURL *)url completionBlock:(PPOTCompletionBlock)completionBlock {
  28. PPAssert(completionBlock, @"parseURL:completionBlock: completionBlock is required");
  29. PPOTResult *result = nil;
  30. PPOTPersistentRequestData *persistentRequestData = [PPOTPersistentRequestData fetch];
  31. NSString *analyticsPage = kAnalyticsAppSwitchCancel;
  32. NSError *analyticsError = nil;
  33. BOOL valid = [PPOTCore isValidURLAction:url];
  34. if (valid) {
  35. PPOTConfigurationRecipe *configurationRecipe = persistentRequestData.configurationRecipe;
  36. PPOTAppSwitchResponse *response = nil;
  37. if ([configurationRecipe.protocolVersion integerValue] == 0) {
  38. // Note: Token (Hermes) validation performed inside of isValidURLAction:
  39. // TODO: consider moving here
  40. response = [[PPOTAppSwitchResponse alloc] initWithHermesURL:url
  41. environment:persistentRequestData.requestData[kPPOTRequestDataDataDictionaryEnvironmentKey]];
  42. } else {
  43. NSString *encryptionKey = persistentRequestData.requestData[kPPOTRequestDataDataDictionaryEncryptionKey];
  44. response = [[PPOTAppSwitchResponse alloc] initWithEncodedURL:url encryptionKey:encryptionKey];
  45. // TODO: make better
  46. if (response.validResponse && response.version > 2) {
  47. NSString *requestMsgID = persistentRequestData.requestData[kPPOTRequestDataDataDictionaryMsgIdKey];
  48. NSNumber *protocol = configurationRecipe.protocolVersion;
  49. if (response.version != [protocol integerValue]) {
  50. valid = NO;
  51. }
  52. if (![requestMsgID isEqualToString:response.msgID]) {
  53. if (requestMsgID != nil || response.msgID != nil) {
  54. valid = NO;
  55. }
  56. }
  57. if (valid &&
  58. [[response.timeStamp dateByAddingTimeInterval:PP_TIMESTAMP_TIMEOUT]
  59. compare:[NSDate date]] == NSOrderedAscending) {
  60. valid = NO;
  61. }
  62. }
  63. }
  64. if (valid && response.validResponse && response.action == PPAppSwitchResponseActionSuccess) {
  65. result = [self resultWithSuccess:response];
  66. analyticsPage = kAnalyticsAppSwitchReturn;
  67. } else if (response.action == PPAppSwitchResponseActionCancel) {
  68. result = [self resultWithCancel:response];
  69. } else {
  70. result = [self resultWithError];
  71. analyticsError = result.error;
  72. }
  73. result.target = configurationRecipe.target;
  74. } else {
  75. if (!persistentRequestData) {
  76. result = [self resultWithPersistedRequestDataFetchError];
  77. } else {
  78. result = [self resultWithError];
  79. }
  80. result.target = PPOTRequestTargetUnknown;
  81. analyticsError = result.error;
  82. }
  83. // Protect parameters against missing persistentRequestData:
  84. [[PPOTAnalyticsTracker sharedManager] trackPage:analyticsPage
  85. environment:persistentRequestData.environment ? persistentRequestData.environment : @""
  86. clientID:persistentRequestData.clientID ? persistentRequestData.clientID : @""
  87. error:analyticsError
  88. hermesToken:persistentRequestData.requestData[kPPOTRequestDataDataDictionaryHermesTokenKey]];
  89. completionBlock(result);
  90. }
  91. + (PPOTResult *)resultWithSuccess:(PPOTAppSwitchResponse *)response {
  92. PPOTResult *result = [PPOTResult new];
  93. result.type = PPOTResultTypeSuccess;
  94. NSMutableDictionary *resultDictionary = [@{@"client": @{@"platform": @"iOS",
  95. @"paypal_sdk_version": PayPalOTVersion(),
  96. @"environment" : FORCE_VALUE_OR_NULL(response.environment),
  97. @"product_name": PP_PRODUCT_STRING,
  98. },
  99. } mutableCopy];
  100. if (response.responseType == PPAppSwitchResponseTypeAuthorizationCode) {
  101. [resultDictionary addEntriesFromDictionary:@{@"response_type" : @"authorization_code",
  102. @"response" : @{@"code": FORCE_VALUE_OR_NULL(response.authorizationCode)},
  103. @"user": @{@"display_string" : FORCE_VALUE_OR_NULL(response.email)},
  104. }];
  105. } else if (response.responseType == PPAppSwitchResponseTypeWeb) {
  106. [resultDictionary addEntriesFromDictionary:@{@"response_type" : @"web",
  107. @"response" : @{@"webURL": FORCE_VALUE_OR_NULL(response.webURL)},
  108. }];
  109. } else {
  110. NSString *error = [NSString stringWithFormat:CARDIO_STR(@"App Switch: Unexpected response type: %@"), @(response.responseType)];
  111. PPLog(@"%@", error);
  112. result.type = PPOTResultTypeError;
  113. result.error = [self errorUserInfo:@{kPPOTAppSwitchMessageKey: error}];
  114. }
  115. result.response = resultDictionary;
  116. return result;
  117. }
  118. + (PPOTResult *)resultWithCancel:(PPOTAppSwitchResponse *)response {
  119. PPOTResult *result = [PPOTResult new];
  120. if (response.error.count) {
  121. PPLog(@"App Switch Error: %@", response.error);
  122. result.type = PPOTResultTypeError;
  123. result.error = [self errorUserInfo:response.error];
  124. } else {
  125. PPLog(@"App Switch Cancelled");
  126. result.type = PPOTResultTypeCancel;
  127. }
  128. return result;
  129. }
  130. + (PPOTResult *)resultWithError {
  131. NSError *error = [self errorUserInfo:@{kPPOTAppSwitchMessageKey:CARDIO_STR(@"App Switch Invalid URL")}];
  132. return [self resultWithSpecificError:error];
  133. }
  134. + (PPOTResult *)resultWithPersistedRequestDataFetchError {
  135. NSDictionary *userInfo = @{kPPOTAppSwitchMessageKey:CARDIO_STR(@"Could not retrieve persisted request data")};
  136. NSError *error = [PPOTError errorWithErrorCode:PPOTErrorCodePersistedDataFetchFailed userInfo:userInfo];
  137. return [self resultWithSpecificError:error];
  138. }
  139. + (PPOTResult *)resultWithSpecificError:(NSError *)error {
  140. PPOTResult *result = [PPOTResult new];
  141. result.type = PPOTResultTypeError;
  142. result.error = error;
  143. return result;
  144. }
  145. + (NSError *)errorUserInfo:(NSDictionary *)dictionary {
  146. return [PPOTError errorWithErrorCode:PPOTErrorCodeParsingFailed userInfo:dictionary];
  147. }
  148. - (NSString *)description {
  149. NSMutableString *description = [NSMutableString string];
  150. NSString *typeName;
  151. switch (self.type) {
  152. case PPOTResultTypeSuccess: typeName = @"Success"; break;
  153. case PPOTResultTypeCancel: typeName = @"Cancel"; break;
  154. case PPOTResultTypeError: typeName = @"Error"; break;
  155. }
  156. [description appendFormat:@"PPOTResult (type: %@)\n", typeName];
  157. if (self.response) {
  158. [description appendFormat:@" Result Dictionary: %@\n", [self.response description]];
  159. }
  160. if (self.error) {
  161. [description appendFormat:@" Error: %@\n", [self.error description]];
  162. }
  163. return description;
  164. }
  165. @end