PPOTAppSwitchResponse.m 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. //
  2. // PPOTAppSwitchResponse.m
  3. // PayPalOneTouch
  4. //
  5. // Copyright © 2014 PayPal, Inc. All rights reserved.
  6. //
  7. #import "PPOTAppSwitchResponse.h"
  8. #if __has_include("PayPalUtils.h")
  9. #import "PPOTString.h"
  10. #import "PPOTTime.h"
  11. #import "PPOTEncryptionHelper.h"
  12. #import "PPOTJSONHelper.h"
  13. #else
  14. #import <PayPalUtils/PPOTString.h>
  15. #import <PayPalUtils/PPOTTime.h>
  16. #import <PayPalUtils/PPOTEncryptionHelper.h>
  17. #import <PayPalUtils/PPOTJSONHelper.h>
  18. #endif
  19. @implementation PPOTAppSwitchResponse
  20. - (instancetype)initWithEncodedURL:(NSURL *)url encryptionKey:(NSString *)encryptionKey {
  21. if (!url) {
  22. return nil;
  23. }
  24. self = [self initWithURL:url];
  25. if (self) {
  26. _encryptionKey = encryptionKey;
  27. // convert query string to dictionary, handles URLEncoding
  28. NSDictionary *query = [PPOTAppSwitchUtil parseQueryString:[url query]];
  29. // base64 encoded payload
  30. NSString *encodedPayload = [PPOTJSONHelper stringFromDictionary:query withKey:kPPOTAppSwitchPayloadKey];
  31. if (encodedPayload.length) {
  32. // parse payload
  33. _decodedPayload = [PPOTJSONHelper dictionaryWithBase64EncodedJSONString:encodedPayload];
  34. }
  35. if (_encryptionKey.length) {
  36. // base64 encoded encrypted payload
  37. NSString *encodedEncryptedPayload = [PPOTJSONHelper stringFromDictionary:query withKey:kPPOTAppSwitchEncryptedPayloadKey];
  38. if (encodedEncryptedPayload.length) {
  39. // parse encrypted payload
  40. NSData *encryptedData = [[NSData alloc] initWithBase64EncodedString:encodedEncryptedPayload
  41. options:NSDataBase64DecodingIgnoreUnknownCharacters];
  42. [self parseEncryptedPayload:encryptedData];
  43. }
  44. }
  45. [self parsePayload];
  46. }
  47. return self;
  48. }
  49. - (instancetype)initWithHermesURL:(NSURL *)url environment:(NSString *)environment {
  50. if (!url) {
  51. return nil;
  52. }
  53. self = [self initWithURL:url];
  54. if (self) {
  55. // TODO: add error parsing since hermes may pass a error or abroted flag.
  56. _responseType = PPAppSwitchResponseTypeWeb;
  57. _webURL = [url absoluteString];
  58. _environment = environment;
  59. }
  60. return self;
  61. }
  62. - (instancetype)initWithURL:(NSURL *)url {
  63. self = [self init];
  64. if (self) {
  65. // extract action from URL
  66. if ([[PPOTAppSwitchUtil actionFromURLAction:url] isEqualToString:kPPOTAppSwitchCancelAction]) {
  67. _action = PPAppSwitchResponseActionCancel;
  68. } else if ([[PPOTAppSwitchUtil actionFromURLAction:url] isEqualToString:kPPOTAppSwitchSuccessAction]) {
  69. _action = PPAppSwitchResponseActionSuccess;
  70. } else {
  71. _action = PPAppSwitchResponseActionUnknown;
  72. }
  73. }
  74. return self;
  75. }
  76. - (void)parsePayload {
  77. NSNumber *version = [PPOTJSONHelper numberFromDictionary:_decodedPayload withKey:kPPOTAppSwitchProtocolVersionKey];
  78. // Wallet not always sends version, default to 1
  79. _version = (version != nil) ? [version integerValue] : 1;
  80. // in version 3+ the response_type is no longer sent
  81. NSString *responseType = nil;
  82. if (_version >= 3) {
  83. responseType = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchPaymentCodeTypeKey];
  84. if (responseType == nil) {
  85. responseType = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchResponseTypeKey];
  86. }
  87. } else {
  88. responseType = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchResponseTypeKey];
  89. }
  90. if ([responseType isEqualToString:kPPOTAppSwitchResponseTypeCode] || [responseType isEqualToString:kPPOTAppSwitchResposneAuthCodeKey]) {
  91. _responseType = PPAppSwitchResponseTypeAuthorizationCode;
  92. } else if ([responseType isEqualToString:kPPOTAppSwitchResponseTypeToken]) {
  93. _responseType = PPAppSwitchResponseTypeToken;
  94. } else if ([responseType isEqualToString:kPPOTAppSwitchResponseTypeWeb]) {
  95. _responseType = PPAppSwitchResponseTypeWeb;
  96. } else {
  97. _responseType = PPAppSwitchResponseTypeUnknown;
  98. }
  99. _displayName = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchDisplayNameKey];
  100. _email = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchEmailKey];
  101. // TODO: remove check until we fix with BT
  102. if (!_email.length) {
  103. _email = [_displayName copy];
  104. }
  105. _accessToken = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchAccessTokenKey];
  106. if (_version >= 3) {
  107. _authorizationCode = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchPaymentCodeKey];;
  108. if (![_authorizationCode length]) {
  109. _authorizationCode = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchAuthorizationCodeKey];
  110. }
  111. } else {
  112. _authorizationCode = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchAuthorizationCodeKey];
  113. }
  114. NSNumber *expiresIn = [PPOTJSONHelper numberFromDictionary:_decodedPayload withKey:kPPOTAppSwitchExpiresInKey];
  115. if (expiresIn != nil) {
  116. _expiresIn = [expiresIn integerValue];
  117. }
  118. NSString *scope = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchScopesKey];
  119. if (scope.length) {
  120. _scope = [scope componentsSeparatedByString:@" "];
  121. }
  122. _photoURL = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchPhotoURLKey];
  123. _webURL = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchWebURLKey];
  124. // The decoded payload may have been JSON decoded where the error value is already a dictionary
  125. NSDictionary *errorDict = [PPOTJSONHelper dictionaryFromDictionary:_decodedPayload withKey:kPPOTAppSwitchErrorKey];
  126. if (errorDict) {
  127. _error = errorDict;
  128. } else {
  129. NSString *error = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchErrorKey];
  130. if (error.length) {
  131. _error = [NSJSONSerialization JSONObjectWithData:[error dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
  132. }
  133. }
  134. NSString *environment = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchEnvironmentKey];
  135. if (environment.length) {
  136. _environment = [environment lowercaseString];
  137. }
  138. // TODO: BT not sending at a string and in weird format
  139. NSString *strTimetamp = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchTimestampKey];
  140. if (strTimetamp) {
  141. _timeStamp = [PPOTTime dateFromRFC3339LikeString:strTimetamp];
  142. } else {
  143. NSNumber *timestamp = [PPOTJSONHelper numberFromDictionary:_decodedPayload withKey:kPPOTAppSwitchTimestampKey];
  144. if (timestamp != nil) {
  145. _timeStamp = [NSDate dateWithTimeIntervalSince1970:[timestamp doubleValue]];
  146. }
  147. }
  148. _msgID = [PPOTJSONHelper stringFromDictionary:_decodedPayload withKey:kPPOTAppSwitchMsgGUIDKey];
  149. }
  150. - (void)parseEncryptedPayload:(NSData *)encryptedPayload {
  151. NSData *encryptionKey = [PPOTString dataWithHexString:_encryptionKey];
  152. NSData *decryptedPayload = [PPOTEncryptionHelper decryptAESCTRData:encryptedPayload encryptionKey:encryptionKey];
  153. if (decryptedPayload.length && encryptionKey.length) {
  154. NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:decryptedPayload options:0 error:nil];
  155. if (jsonDictionary) {
  156. // merge with existing payload
  157. NSMutableDictionary *mergedDictionary = [NSMutableDictionary dictionaryWithCapacity:_decodedPayload.count + jsonDictionary.count];
  158. [mergedDictionary addEntriesFromDictionary:_decodedPayload];
  159. [mergedDictionary addEntriesFromDictionary:jsonDictionary];
  160. _decodedPayload = mergedDictionary;
  161. }
  162. }
  163. }
  164. - (BOOL)validResponse {
  165. if (self.action == PPAppSwitchResponseActionCancel) {
  166. return YES;
  167. }
  168. if (self.responseType == PPAppSwitchResponseTypeUnknown
  169. || self.action == PPAppSwitchResponseActionUnknown
  170. || (self.version < 0 || self.version > kPPOTAppSwitchCurrentVersionNumber)
  171. ) {
  172. return NO;
  173. }
  174. // TOKEN not supported
  175. if (self.responseType == PPAppSwitchResponseTypeAuthorizationCode) {
  176. if (!self.authorizationCode.length
  177. || !self.email.length
  178. || !self.displayName.length) {
  179. return NO;
  180. }
  181. }
  182. if (self.responseType == PPAppSwitchResponseTypeWeb) {
  183. if (!self.webURL.length) {
  184. return NO;
  185. }
  186. }
  187. return YES;
  188. }
  189. - (NSString *)description {
  190. return [_decodedPayload description];
  191. }
  192. @end