BTCardClient+UnionPay.m 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #import "BTCardClient+UnionPay.h"
  2. #import "BTCardCapabilities.h"
  3. #import "BTConfiguration+UnionPay.h"
  4. #if __has_include("BraintreeCore.h")
  5. #import "BTAPIClient_Internal.h"
  6. #import "BTCardClient_Internal.h"
  7. #import "BTCardRequest.h"
  8. #else
  9. #import <BraintreeCore/BTAPIClient_Internal.h>
  10. #import <BraintreeCard/BTCardClient_Internal.h>
  11. #import <BraintreeCard/BTCardRequest_Internal.h>
  12. #endif
  13. @implementation BTCardClient (UnionPay)
  14. + (void)load {
  15. if (self == [BTCardClient class]) {
  16. [[BTTokenizationService sharedService] registerType:@"UnionPayCard" withTokenizationBlock:^(BTAPIClient * _Nonnull apiClient, NSDictionary * _Nullable options, void (^completion)(BTPaymentMethodNonce *paymentMethodNonce, NSError *error)) {
  17. BTCardClient *client = [[BTCardClient alloc] initWithAPIClient:apiClient];
  18. BTCard *card = [[BTCard alloc] initWithParameters:options];
  19. BTCardRequest *request = [[BTCardRequest alloc] initWithCard:card];
  20. request.mobileCountryCode = options[@"mobileCountryCode"];
  21. request.smsCode = options[@"smsCode"];
  22. [client tokenizeCard:request options:nil completion:completion];
  23. }];
  24. }
  25. }
  26. #pragma mark - Public methods
  27. - (void)fetchCapabilities:(NSString *)cardNumber
  28. completion:(void (^)(BTCardCapabilities * _Nullable, NSError * _Nullable))completion
  29. {
  30. [self.apiClient fetchOrReturnRemoteConfiguration:^(BTConfiguration * _Nullable configuration, NSError * _Nullable error) {
  31. if (error) {
  32. completion(nil, error);
  33. return;
  34. }
  35. if (!configuration.isUnionPayEnabled) {
  36. NSError *error = [NSError errorWithDomain:BTCardClientErrorDomain code:BTCardClientErrorTypePaymentOptionNotEnabled userInfo:@{NSLocalizedDescriptionKey: @"UnionPay is not enabled for this merchant"}];
  37. completion(nil, error);
  38. return;
  39. }
  40. [self.apiClient GET:@"v1/payment_methods/credit_cards/capabilities"
  41. parameters:@{@"credit_card[number]" : cardNumber}
  42. completion:^(BTJSON * _Nullable body, __unused NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)
  43. {
  44. if (error) {
  45. [self sendUnionPayEvent:@"capabilities-failed"];
  46. completion(nil, error);
  47. } else {
  48. [self sendUnionPayEvent:@"capabilities-received"];
  49. BTCardCapabilities *cardCapabilities = [[BTCardCapabilities alloc] init];
  50. cardCapabilities.isUnionPay = [body[@"isUnionPay"] isTrue];
  51. cardCapabilities.isDebit = [body[@"isDebit"] isTrue];
  52. cardCapabilities.supportsTwoStepAuthAndCapture = [body[@"unionPay"][@"supportsTwoStepAuthAndCapture"] isTrue];
  53. cardCapabilities.isSupported = [body[@"unionPay"][@"isSupported"] isTrue];
  54. completion(cardCapabilities, nil);
  55. }
  56. }];
  57. }];
  58. }
  59. - (void)enrollCard:(BTCardRequest *)request
  60. completion:(nonnull void (^)(NSString * _Nullable, BOOL, NSError * _Nullable))completion
  61. {
  62. [self.apiClient fetchOrReturnRemoteConfiguration:^(BTConfiguration * _Nullable configuration, NSError * _Nullable error) {
  63. if (error) {
  64. [self invokeBlock:completion onMainThreadWithEnrollmentID:nil smsCodeRequired:NO error:error];
  65. return;
  66. }
  67. if (!configuration.isUnionPayEnabled) {
  68. NSError *error = [NSError errorWithDomain:BTCardClientErrorDomain code:BTCardClientErrorTypePaymentOptionNotEnabled userInfo:@{NSLocalizedDescriptionKey: @"UnionPay is not enabled for this merchant"}];
  69. [self invokeBlock:completion onMainThreadWithEnrollmentID:nil smsCodeRequired:NO error:error];
  70. return;
  71. }
  72. NSMutableDictionary *enrollmentParameters = [NSMutableDictionary dictionary];
  73. BTCard *card = request.card;
  74. if (card.number) {
  75. enrollmentParameters[@"number"] = card.number;
  76. }
  77. if (card.expirationMonth) {
  78. enrollmentParameters[@"expiration_month"] = card.expirationMonth;
  79. }
  80. if (card.expirationYear) {
  81. enrollmentParameters[@"expiration_year"] = card.expirationYear;
  82. }
  83. if (request.mobileCountryCode) {
  84. enrollmentParameters[@"mobile_country_code"] = request.mobileCountryCode;
  85. }
  86. if (request.mobilePhoneNumber) {
  87. enrollmentParameters[@"mobile_number"] = request.mobilePhoneNumber;
  88. }
  89. [self.apiClient POST:@"v1/union_pay_enrollments"
  90. parameters:@{ @"union_pay_enrollment": enrollmentParameters }
  91. completion:^(BTJSON * _Nullable body, __unused NSHTTPURLResponse * _Nullable response, NSError * _Nullable error)
  92. {
  93. if (error) {
  94. [self sendUnionPayEvent:@"enrollment-failed"];
  95. NSError *callbackError = error;
  96. NSHTTPURLResponse *response = error.userInfo[BTHTTPURLResponseKey];
  97. if (response.statusCode == 422) {
  98. callbackError = [NSError errorWithDomain:BTCardClientErrorDomain
  99. code:BTCardClientErrorTypeCustomerInputInvalid
  100. userInfo:[self.class validationErrorUserInfo:error.userInfo]];
  101. }
  102. [self invokeBlock:completion onMainThreadWithEnrollmentID:nil smsCodeRequired:NO error:callbackError];
  103. return;
  104. }
  105. [self sendUnionPayEvent:@"enrollment-succeeded"];
  106. BOOL smsCodeRequired = ![body[@"smsCodeRequired"] isNull] && [body[@"smsCodeRequired"] isTrue];
  107. [self invokeBlock:completion onMainThreadWithEnrollmentID:[body[@"unionPayEnrollmentId"] asString] smsCodeRequired:smsCodeRequired error:nil];
  108. }];
  109. }];
  110. }
  111. #pragma mark - Helper methods
  112. - (void)invokeBlock:(nonnull void (^)(NSString * _Nullable, BOOL, NSError * _Nullable))completion onMainThreadWithEnrollmentID:(nullable NSString *)enrollmentID smsCodeRequired:(BOOL)smsCodeRequired error:(nullable NSError *)error {
  113. dispatch_async(dispatch_get_main_queue(), ^{
  114. completion(enrollmentID, smsCodeRequired, error);
  115. });
  116. }
  117. - (void)sendUnionPayEvent:(nonnull NSString *)event {
  118. NSString *fullEvent = [NSString stringWithFormat:@"ios.%@.unionpay.%@", self.apiClient.metadata.integrationString, event];
  119. [self.apiClient sendAnalyticsEvent:fullEvent];
  120. }
  121. @end