BTPaymentFlowDriver+ThreeDSecure.m 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #import "BTPaymentFlowDriver+ThreeDSecure.h"
  2. #if __has_include("BraintreeCore.h")
  3. #import "BTAPIClient_Internal.h"
  4. #import "Braintree-Version.h"
  5. #else
  6. #import <BraintreeCore/BTAPIClient_Internal.h>
  7. #import <BraintreeCore/Braintree-Version.h>
  8. #endif
  9. #import "BTPaymentFlowDriver_Internal.h"
  10. #import "BTPaymentFlowDriver+ThreeDSecure_Internal.h"
  11. #import "BTThreeDSecureResult.h"
  12. #import "BTThreeDSecureRequest.h"
  13. #import "BTThreeDSecureRequest_Internal.h"
  14. #import "BTThreeDSecurePostalAddress_Internal.h"
  15. #import "BTThreeDSecureAdditionalInformation_Internal.h"
  16. @implementation BTPaymentFlowDriver (ThreeDSecure)
  17. NSString * const BTThreeDSecureFlowErrorDomain = @"com.braintreepayments.BTThreeDSecureFlowErrorDomain";
  18. NSString * const BTThreeDSecureFlowInfoKey = @"com.braintreepayments.BTThreeDSecureFlowInfoKey";
  19. NSString * const BTThreeDSecureFlowValidationErrorsKey = @"com.braintreepayments.BTThreeDSecureFlowValidationErrorsKey";
  20. #pragma mark - ThreeDSecure Lookup
  21. - (void)performThreeDSecureLookup:(BTThreeDSecureRequest *)request
  22. completion:(void (^)(BTThreeDSecureLookup *threeDSecureResult, NSError *error))completionBlock {
  23. [self.apiClient fetchOrReturnRemoteConfiguration:^(__unused BTConfiguration *configuration, NSError *error) {
  24. if (error) {
  25. completionBlock(nil, error);
  26. return;
  27. }
  28. NSMutableDictionary *customer = [[NSMutableDictionary alloc] init];
  29. NSMutableDictionary *requestParameters = [@{ @"amount": request.amount, @"customer": customer } mutableCopy];
  30. if (request.dfReferenceId) {
  31. requestParameters[@"dfReferenceId"] = request.dfReferenceId;
  32. }
  33. NSMutableDictionary *additionalInformation = [NSMutableDictionary dictionary];
  34. if (request.billingAddress) {
  35. [additionalInformation addEntriesFromDictionary:[request.billingAddress asParametersWithPrefix:@"billing"]];
  36. }
  37. if (request.mobilePhoneNumber) {
  38. additionalInformation[@"mobilePhoneNumber"] = request.mobilePhoneNumber;
  39. }
  40. if (request.email) {
  41. additionalInformation[@"email"] = request.email;
  42. }
  43. if (request.shippingMethod) {
  44. additionalInformation[@"shippingMethod"] = request.shippingMethod;
  45. }
  46. if (request.additionalInformation) {
  47. [additionalInformation addEntriesFromDictionary:[request.additionalInformation asParameters]];
  48. }
  49. if (additionalInformation.count) {
  50. requestParameters[@"additionalInfo"] = additionalInformation;
  51. }
  52. if (request.challengeRequested) {
  53. requestParameters[@"challengeRequested"] = @(request.challengeRequested);
  54. }
  55. if (request.exemptionRequested) {
  56. requestParameters[@"exemptionRequested"] = @(request.exemptionRequested);
  57. }
  58. NSString *urlSafeNonce = [request.nonce stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
  59. [self.apiClient POST:[NSString stringWithFormat:@"v1/payment_methods/%@/three_d_secure/lookup", urlSafeNonce]
  60. parameters:requestParameters
  61. completion:^(BTJSON *body, __unused NSHTTPURLResponse *response, NSError *error) {
  62. if (error) {
  63. // Provide more context for card validation error when status code 422
  64. if ([error.domain isEqualToString:BTHTTPErrorDomain] &&
  65. error.code == BTHTTPErrorCodeClientError &&
  66. ((NSHTTPURLResponse *)error.userInfo[BTHTTPURLResponseKey]).statusCode == 422) {
  67. NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
  68. BTJSON *errorBody = error.userInfo[BTHTTPJSONResponseBodyKey];
  69. if ([errorBody[@"error"][@"message"] isString]) {
  70. userInfo[NSLocalizedDescriptionKey] = [errorBody[@"error"][@"message"] asString];
  71. }
  72. if ([errorBody[@"threeDSecureInfo"] isObject]) {
  73. userInfo[BTThreeDSecureFlowInfoKey] = [errorBody[@"threeDSecureInfo"] asDictionary];
  74. }
  75. if ([errorBody[@"error"] isObject]) {
  76. userInfo[BTThreeDSecureFlowValidationErrorsKey] = [errorBody[@"error"] asDictionary];
  77. }
  78. error = [NSError errorWithDomain:BTThreeDSecureFlowErrorDomain
  79. code:BTThreeDSecureFlowErrorTypeFailedLookup
  80. userInfo:userInfo];
  81. }
  82. completionBlock(nil, error);
  83. return;
  84. }
  85. BTJSON *lookupJSON = body[@"lookup"];
  86. BTThreeDSecureLookup *lookup = [[BTThreeDSecureLookup alloc] initWithJSON:lookupJSON];
  87. lookup.threeDSecureResult = [[BTThreeDSecureResult alloc] initWithJSON:body];
  88. completionBlock(lookup, nil);
  89. }];
  90. }];
  91. }
  92. - (void)prepareLookup:(BTPaymentFlowRequest<BTPaymentFlowRequestDelegate> *)request completion:(void (^)(NSString * _Nullable, NSError * _Nullable))completionBlock {
  93. BTThreeDSecureRequest *threeDSecureRequest = (BTThreeDSecureRequest *)request;
  94. NSError *integrationError;
  95. if (self.apiClient.clientToken == nil) {
  96. integrationError = [NSError errorWithDomain:BTThreeDSecureFlowErrorDomain
  97. code:BTThreeDSecureFlowErrorTypeConfiguration
  98. userInfo:@{NSLocalizedDescriptionKey: @"A client token must be used for ThreeDSecure integrations."}];
  99. } else if (threeDSecureRequest.nonce == nil) {
  100. integrationError = [NSError errorWithDomain:BTThreeDSecureFlowErrorDomain
  101. code:BTThreeDSecureFlowErrorTypeConfiguration
  102. userInfo:@{NSLocalizedDescriptionKey: @"BTThreeDSecureRequest nonce can not be nil."}];
  103. }
  104. if (integrationError != nil) {
  105. completionBlock(nil, integrationError);
  106. return;
  107. }
  108. [threeDSecureRequest prepareLookup:self.apiClient completion:^(NSError * _Nullable error) {
  109. if (error != nil) {
  110. completionBlock(nil, error);
  111. } else {
  112. NSMutableDictionary *requestParameters = [@{} mutableCopy];
  113. if (threeDSecureRequest.dfReferenceId) {
  114. requestParameters[@"dfReferenceId"] = threeDSecureRequest.dfReferenceId;
  115. }
  116. requestParameters[@"nonce"] = threeDSecureRequest.nonce;
  117. requestParameters[@"authorizationFingerprint"] = self.apiClient.clientToken.authorizationFingerprint;
  118. requestParameters[@"braintreeLibraryVersion"] = [NSString stringWithFormat:@"iOS-%@", BRAINTREE_VERSION];
  119. NSMutableDictionary *clientMetadata = [@{} mutableCopy];
  120. clientMetadata[@"sdkVersion"] = BRAINTREE_VERSION;
  121. clientMetadata[@"requestedThreeDSecureVersion"] = @"2";
  122. requestParameters[@"clientMetadata"] = clientMetadata;
  123. NSError *jsonError;
  124. NSData *jsonData = [NSJSONSerialization dataWithJSONObject:requestParameters options:0 error:&jsonError];
  125. if (!jsonData) {
  126. completionBlock(nil, jsonError);
  127. } else {
  128. NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
  129. completionBlock(jsonString, nil);
  130. }
  131. }
  132. }];
  133. }
  134. - (void)initializeChallengeWithLookupResponse:(NSString *)lookupResponse request:(BTPaymentFlowRequest<BTPaymentFlowRequestDelegate> *)request completion:(void (^)(BTPaymentFlowResult * _Nullable, NSError * _Nullable))completionBlock {
  135. [self setupPaymentFlow:request completion:completionBlock];
  136. BTJSON *jsonResponse = [[BTJSON alloc] initWithData:[lookupResponse dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]];
  137. BTJSON *lookupJSON = jsonResponse[@"lookup"];
  138. BTThreeDSecureLookup *lookup = [[BTThreeDSecureLookup alloc] initWithJSON:lookupJSON];
  139. lookup.threeDSecureResult = [[BTThreeDSecureResult alloc] initWithJSON:jsonResponse];
  140. BTThreeDSecureRequest *threeDSecureRequest = (BTThreeDSecureRequest *)request;
  141. threeDSecureRequest.paymentFlowDriverDelegate = self;
  142. [self.apiClient fetchOrReturnRemoteConfiguration:^(BTConfiguration * _Nullable configuration, NSError * _Nullable configurationError) {
  143. if (configurationError) {
  144. [threeDSecureRequest.paymentFlowDriverDelegate onPaymentComplete:nil error:configurationError];
  145. return;
  146. }
  147. [threeDSecureRequest processLookupResult:lookup configuration:configuration];
  148. }];
  149. }
  150. @end