BTApplePayClient.m 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #import "BTApplePayClient_Internal.h"
  2. #import <PassKit/PassKit.h>
  3. #if __has_include(<Braintree/BraintreeApplePay.h>) // CocoaPods
  4. #import <Braintree/BTConfiguration+ApplePay.h>
  5. #import <Braintree/BTApplePayCardNonce.h>
  6. #import <Braintree/BraintreeCore.h>
  7. #import <Braintree/BTAPIClient_Internal.h>
  8. #import <Braintree/BTPaymentMethodNonceParser.h>
  9. #elif SWIFT_PACKAGE // SPM
  10. #import <BraintreeApplePay/BTConfiguration+ApplePay.h>
  11. #import <BraintreeApplePay/BTApplePayCardNonce.h>
  12. #import <BraintreeCore/BraintreeCore.h>
  13. #import "../BraintreeCore/BTAPIClient_Internal.h"
  14. #import "../BraintreeCore/BTPaymentMethodNonceParser.h"
  15. #else // Carthage
  16. #import <BraintreeApplePay/BTConfiguration+ApplePay.h>
  17. #import <BraintreeApplePay/BTApplePayCardNonce.h>
  18. #import <BraintreeCore/BraintreeCore.h>
  19. #import <BraintreeCore/BTAPIClient_Internal.h>
  20. #import <BraintreeCore/BTPaymentMethodNonceParser.h>
  21. #endif
  22. NSString *const BTApplePayErrorDomain = @"com.braintreepayments.BTApplePayErrorDomain";
  23. @implementation BTApplePayClient
  24. #pragma mark - Initialization
  25. + (void)load {
  26. if (self == [BTApplePayClient class]) {
  27. [[BTPaymentMethodNonceParser sharedParser] registerType:@"ApplePayCard" withParsingBlock:^BTPaymentMethodNonce * _Nullable(BTJSON * _Nonnull applePayCard) {
  28. return [[BTApplePayCardNonce alloc] initWithJSON:applePayCard];
  29. }];
  30. }
  31. }
  32. - (instancetype)initWithAPIClient:(BTAPIClient *)apiClient {
  33. if (self = [super init]) {
  34. _apiClient = apiClient;
  35. }
  36. return self;
  37. }
  38. - (instancetype)init {
  39. return nil;
  40. }
  41. #pragma mark - Public methods
  42. - (void)paymentRequest:(void (^)(PKPaymentRequest * _Nullable, NSError * _Nullable))completion {
  43. if (!self.apiClient) {
  44. NSError *error = [NSError errorWithDomain:BTApplePayErrorDomain
  45. code:BTApplePayErrorTypeIntegration
  46. userInfo:@{NSLocalizedDescriptionKey: @"BTAPIClient is nil."}];
  47. [self invokeBlock:completion onMainThreadWithPaymentRequest:nil error:error];
  48. return;
  49. }
  50. [self.apiClient fetchOrReturnRemoteConfiguration:^(BTConfiguration * _Nullable configuration, NSError * _Nullable error) {
  51. if (error) {
  52. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.error.configuration"];
  53. [self invokeBlock:completion onMainThreadWithPaymentRequest:nil error:error];
  54. return;
  55. }
  56. if (!configuration.isApplePayEnabled) {
  57. NSError *error = [NSError errorWithDomain:BTApplePayErrorDomain
  58. code:BTApplePayErrorTypeUnsupported
  59. userInfo:@{ NSLocalizedDescriptionKey: @"Apple Pay is not enabled for this merchant. Please ensure that Apple Pay is enabled in the control panel and then try saving an Apple Pay payment method again." }];
  60. [self invokeBlock:completion onMainThreadWithPaymentRequest:nil error:error];
  61. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.error.disabled"];
  62. return;
  63. }
  64. PKPaymentRequest *paymentRequest = [[PKPaymentRequest alloc] init];
  65. paymentRequest.countryCode = configuration.applePayCountryCode;
  66. paymentRequest.currencyCode = configuration.applePayCurrencyCode;
  67. paymentRequest.merchantIdentifier = configuration.applePayMerchantIdentifier;
  68. paymentRequest.supportedNetworks = configuration.applePaySupportedNetworks;
  69. [self invokeBlock:completion onMainThreadWithPaymentRequest:paymentRequest error:nil];
  70. }];
  71. }
  72. - (void)tokenizeApplePayPayment:(PKPayment *)payment completion:(void (^)(BTApplePayCardNonce *, NSError *))completionBlock {
  73. if (!self.apiClient) {
  74. NSError *error = [NSError errorWithDomain:BTApplePayErrorDomain
  75. code:BTApplePayErrorTypeIntegration
  76. userInfo:@{NSLocalizedDescriptionKey: @"BTApplePayClient tokenization failed because BTAPIClient is nil."}];
  77. completionBlock(nil, error);
  78. return;
  79. }
  80. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.start"];
  81. if (!payment) {
  82. NSError *error = [NSError errorWithDomain:BTApplePayErrorDomain
  83. code:BTApplePayErrorTypeUnsupported
  84. userInfo:@{NSLocalizedDescriptionKey: @"A valid PKPayment is required."}];
  85. completionBlock(nil, error);
  86. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.error.invalid-payment"];
  87. return;
  88. }
  89. [self.apiClient fetchOrReturnRemoteConfiguration:^(BTConfiguration *configuration, NSError *error) {
  90. if (error) {
  91. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.error.configuration"];
  92. completionBlock(nil, error);
  93. return;
  94. }
  95. if (![configuration.json[@"applePay"][@"status"] isString] ||
  96. [[configuration.json[@"applePay"][@"status"] asString] isEqualToString:@"off"]) {
  97. NSError *error = [NSError errorWithDomain:BTApplePayErrorDomain
  98. code:BTApplePayErrorTypeUnsupported
  99. userInfo:@{ NSLocalizedDescriptionKey: @"Apple Pay is not enabled for this merchant. Please ensure that Apple Pay is enabled in the control panel and then try saving an Apple Pay payment method again." }];
  100. completionBlock(nil, error);
  101. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.error.disabled"];
  102. return;
  103. }
  104. NSMutableDictionary *parameters = [NSMutableDictionary new];
  105. parameters[@"applePaymentToken"] = [self parametersForPaymentToken:payment.token];
  106. parameters[@"_meta"] = @{
  107. @"source" : self.apiClient.metadata.sourceString,
  108. @"integration" : self.apiClient.metadata.integrationString,
  109. @"sessionId" : self.apiClient.metadata.sessionID,
  110. };
  111. [self.apiClient POST:@"v1/payment_methods/apple_payment_tokens"
  112. parameters:parameters
  113. completion:^(BTJSON *body, __unused NSHTTPURLResponse *response, NSError *error) {
  114. if (error) {
  115. if (error.code == NETWORK_CONNECTION_LOST_CODE) {
  116. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.network-connection.failure"];
  117. }
  118. completionBlock(nil, error);
  119. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.error.tokenization"];
  120. return;
  121. }
  122. BTApplePayCardNonce *tokenized = [[BTApplePayCardNonce alloc] initWithJSON:body[@"applePayCards"][0]];
  123. completionBlock(tokenized, nil);
  124. [self.apiClient sendAnalyticsEvent:@"ios.apple-pay.success"];
  125. }];
  126. }];
  127. }
  128. #pragma mark - Helpers
  129. - (NSDictionary *)parametersForPaymentToken:(PKPaymentToken *)token {
  130. NSMutableDictionary *mutableParameters = [NSMutableDictionary dictionary];
  131. mutableParameters[@"paymentData"] = [token.paymentData base64EncodedStringWithOptions:0];
  132. mutableParameters[@"transactionIdentifier"] = token.transactionIdentifier;
  133. mutableParameters[@"paymentInstrumentName"] = token.paymentMethod.displayName;
  134. mutableParameters[@"paymentNetwork"] = token.paymentMethod.network;
  135. return [mutableParameters copy];
  136. }
  137. - (void)invokeBlock:(nonnull void (^)(PKPaymentRequest * _Nullable, NSError * _Nullable))completion onMainThreadWithPaymentRequest:(nullable PKPaymentRequest *)paymentRequest error:(nullable NSError *)error {
  138. dispatch_async(dispatch_get_main_queue(), ^{
  139. completion(paymentRequest, error);
  140. });
  141. }
  142. @end