BTPayPalIDToken.m 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #import "BTPayPalIDToken.h"
  2. #import "BTJSON.h"
  3. NSString * const BTPayPalIDTokenErrorDomain = @"com.braintreepayments.BTPayPalIDTokenErrorDomain";
  4. @implementation BTPayPalIDToken
  5. - (instancetype)init {
  6. return nil;
  7. }
  8. - (nullable instancetype)initWithIDTokenString:(NSString *)idTokenString error:(NSError **)error {
  9. self = [super init];
  10. if (self) {
  11. BTJSON *json = [self decodeIDTokenString:idTokenString error:error];
  12. if (error && *error) {
  13. return nil;
  14. }
  15. NSArray *externalIds = [json[@"external_id"] asArray];
  16. for (NSString *externalId in externalIds) {
  17. if ([externalId hasPrefix:@"Braintree:"]) {
  18. _braintreeMerchantID = [externalId componentsSeparatedByString:@":"][1];
  19. } else if ([externalId hasPrefix:@"PayPal:"]) {
  20. _paypalMerchantID = [externalId componentsSeparatedByString:@":"][1];
  21. }
  22. }
  23. if (!_braintreeMerchantID) {
  24. if (error) {
  25. *error = [NSError errorWithDomain:BTPayPalIDTokenErrorDomain
  26. code:BTPayPalIDTokenErrorUnlinkedAccount
  27. userInfo:@{NSLocalizedDescriptionKey:@"Invalid PayPal ID Token: Associated Braintree merchant ID missing."}];
  28. }
  29. return nil;
  30. } else if (!_paypalMerchantID) {
  31. if (error) {
  32. *error = [NSError errorWithDomain:BTPayPalIDTokenErrorDomain
  33. code:BTPayPalIDTokenErrorUnlinkedAccount
  34. userInfo:@{NSLocalizedDescriptionKey:@"Invalid PayPal ID Token: Associated PayPal merchant ID missing."}];
  35. }
  36. return nil;
  37. }
  38. NSString *basePayPalURL = [json[@"iss"] asString];
  39. NSString *braintreeGatewayURL;
  40. if ([basePayPalURL isEqualToString:@"https://api.paypal.com"] ) {
  41. _environment = BTPayPalIDTokenEnvironmentProd;
  42. braintreeGatewayURL = @"https://api.braintreegateway.com:443";
  43. } else if ([basePayPalURL isEqualToString:@"https://api.sandbox.paypal.com"]) {
  44. _environment = BTPayPalIDTokenEnvironmentSand;
  45. braintreeGatewayURL = @"https://api.sandbox.braintreegateway.com:443";
  46. } else if ([basePayPalURL isEqualToString:@"https://api.msmaster.qa.paypal.com"]) {
  47. _environment = BTPayPalIDTokenEnvironmentStage;
  48. braintreeGatewayURL = @"https://api.sandbox.braintreegateway.com:443";
  49. } else {
  50. if (error) {
  51. *error = [NSError errorWithDomain:BTPayPalIDTokenErrorDomain
  52. code:BTPayPalIDTokenErrorInvalid
  53. userInfo:@{NSLocalizedDescriptionKey:@"Invalid PayPal ID Token: Issuer missing or unknown."}];
  54. }
  55. return nil;
  56. }
  57. _basePayPalURL = [NSURL URLWithString:basePayPalURL];
  58. _baseBraintreeURL = [NSURL URLWithString: [NSString stringWithFormat:@"%@/merchants/%@/client_api", braintreeGatewayURL, _braintreeMerchantID]];
  59. _configURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/v1/configuration", _baseBraintreeURL]];
  60. _token = idTokenString;
  61. }
  62. return self;
  63. }
  64. - (BTJSON *)decodeIDTokenString:(NSString *)idTokenString error:(NSError * __autoreleasing *)error {
  65. NSArray *payPalIDTokenComponents = [idTokenString componentsSeparatedByString:@"."];
  66. if (payPalIDTokenComponents.count != 3) {
  67. if (error) {
  68. *error = [NSError errorWithDomain:BTPayPalIDTokenErrorDomain
  69. code:BTPayPalIDTokenErrorInvalid
  70. userInfo:@{NSLocalizedDescriptionKey:@"Invalid PayPal ID Token: Missing payload."}];
  71. }
  72. return nil;
  73. }
  74. NSString *base64EncodedBody = [self base64EncodedStringWithPadding:payPalIDTokenComponents[1]];
  75. NSData *base64DecodedPayPalIDToken = [[NSData alloc] initWithBase64EncodedString:base64EncodedBody options:0];
  76. if (!base64DecodedPayPalIDToken) {
  77. if (error) {
  78. *error = [NSError errorWithDomain:BTPayPalIDTokenErrorDomain
  79. code:BTPayPalIDTokenErrorInvalid
  80. userInfo:@{NSLocalizedDescriptionKey:@"Invalid PayPal ID Token: Unable to base-64 decode payload."}];
  81. }
  82. return nil;
  83. }
  84. NSDictionary *rawPayPalIDToken;
  85. NSError *JSONError = nil;
  86. rawPayPalIDToken = [NSJSONSerialization JSONObjectWithData:base64DecodedPayPalIDToken options:0 error:&JSONError];
  87. if (JSONError) {
  88. if (error) {
  89. *error = [NSError errorWithDomain:BTPayPalIDTokenErrorDomain
  90. code:BTPayPalIDTokenErrorInvalid
  91. userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Invalid PayPal ID Token: %@", JSONError.localizedDescription]}];
  92. }
  93. return nil;
  94. }
  95. if (![rawPayPalIDToken isKindOfClass:[NSDictionary class]]) {
  96. if (error) {
  97. *error = [NSError errorWithDomain:BTPayPalIDTokenErrorDomain
  98. code:BTPayPalIDTokenErrorInvalid
  99. userInfo:@{NSLocalizedDescriptionKey: @"Invalid PayPal ID Token: Expected to find an object at JSON root."}];
  100. }
  101. return nil;
  102. }
  103. return [[BTJSON alloc] initWithValue:rawPayPalIDToken];
  104. }
  105. - (NSString *)base64EncodedStringWithPadding:(NSString *)base64EncodedString {
  106. if (base64EncodedString.length % 4 == 2) {
  107. return [NSString stringWithFormat:@"%@==", base64EncodedString];
  108. } else if (base64EncodedString.length % 4 == 3) {
  109. return [NSString stringWithFormat:@"%@=", base64EncodedString];
  110. } else {
  111. return base64EncodedString;
  112. }
  113. }
  114. @end