@xbenjii/react-native-braintree-dropin-ui 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,280 +1,309 @@
1
- #import "RNBraintreeDropIn.h"
2
- #import <React/RCTUtils.h>
3
- #import "BTThreeDSecureRequest.h"
4
-
5
- @implementation RNBraintreeDropIn
6
-
7
- - (dispatch_queue_t)methodQueue
8
- {
9
- return dispatch_get_main_queue();
10
- }
11
- RCT_EXPORT_MODULE(RNBraintreeDropIn)
12
-
13
- RCT_EXPORT_METHOD(show:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
14
- {
15
- BTDropInColorScheme colorScheme;
16
-
17
- if([options[@"darkTheme"] boolValue]){
18
- if (@available(iOS 13.0, *)) {
19
- colorScheme = BTDropInColorSchemeDynamic;
20
- } else {
21
- colorScheme = BTDropInColorSchemeDark;
22
- }
23
- } else {
24
- colorScheme = BTDropInColorSchemeLight;
25
- }
26
-
27
- BTDropInUICustomization *uiCustomization = [[BTDropInUICustomization alloc] initWithColorScheme:colorScheme];
28
-
29
- if(options[@"fontFamily"]){
30
- uiCustomization.fontFamily = options[@"fontFamily"];
31
- }
32
- if(options[@"boldFontFamily"]){
33
- uiCustomization.boldFontFamily = options[@"boldFontFamily"];
34
- }
35
-
36
- self.resolve = resolve;
37
- self.reject = reject;
38
- self.applePayAuthorized = NO;
39
-
40
- NSString* clientToken = options[@"clientToken"];
41
- if (!clientToken) {
42
- reject(@"NO_CLIENT_TOKEN", @"You must provide a client token", nil);
43
- return;
44
- }
45
-
46
- BTDropInRequest *request = [[BTDropInRequest alloc] init];
47
- request.uiCustomization = uiCustomization;
48
-
49
- NSDictionary* threeDSecureOptions = options[@"threeDSecure"];
50
- if (threeDSecureOptions) {
51
- NSNumber* threeDSecureAmount = threeDSecureOptions[@"amount"];
52
- if (!threeDSecureAmount) {
53
- reject(@"NO_3DS_AMOUNT", @"You must provide an amount for 3D Secure", nil);
54
- return;
55
- }
56
-
57
- BTThreeDSecureRequest *threeDSecureRequest = [[BTThreeDSecureRequest alloc] init];
58
- threeDSecureRequest.amount = [NSDecimalNumber decimalNumberWithString:threeDSecureAmount.stringValue];
59
- request.threeDSecureRequest = threeDSecureRequest;
60
-
61
- }
62
-
63
- BTAPIClient *apiClient = [[BTAPIClient alloc] initWithAuthorization:clientToken];
64
- self.dataCollector = [[BTDataCollector alloc] initWithAPIClient:apiClient];
65
- [self.dataCollector collectDeviceData:^(NSString * _Nonnull deviceDataCollector) {
66
- // Save deviceData
67
- self.deviceDataCollector = deviceDataCollector;
68
- }];
69
-
70
- if([options[@"vaultManager"] boolValue]){
71
- request.vaultManager = YES;
72
- }
73
-
74
- if([options[@"cardDisabled"] boolValue]){
75
- request.cardDisabled = YES;
76
- }
77
-
78
- if([options[@"applePay"] boolValue]){
79
- NSString* merchantIdentifier = options[@"merchantIdentifier"];
80
- NSString* countryCode = options[@"countryCode"];
81
- NSString* currencyCode = options[@"currencyCode"];
82
- NSString* merchantName = options[@"merchantName"];
83
- NSDecimalNumber* orderTotal = [NSDecimalNumber decimalNumberWithDecimal:[options[@"orderTotal"] decimalValue]];
84
- if(!merchantIdentifier || !countryCode || !currencyCode || !merchantName || !orderTotal){
85
- reject(@"MISSING_OPTIONS", @"Not all required Apple Pay options were provided", nil);
86
- return;
87
- }
88
- self.braintreeClient = [[BTAPIClient alloc] initWithAuthorization:clientToken];
89
-
90
- self.paymentRequest = [[PKPaymentRequest alloc] init];
91
- self.paymentRequest.merchantIdentifier = merchantIdentifier;
92
- self.paymentRequest.merchantCapabilities = PKMerchantCapability3DS;
93
- self.paymentRequest.countryCode = countryCode;
94
- self.paymentRequest.currencyCode = currencyCode;
95
- self.paymentRequest.supportedNetworks = @[PKPaymentNetworkAmex, PKPaymentNetworkVisa, PKPaymentNetworkMasterCard, PKPaymentNetworkDiscover, PKPaymentNetworkChinaUnionPay];
96
- self.paymentRequest.paymentSummaryItems =
97
- @[
98
- [PKPaymentSummaryItem summaryItemWithLabel:merchantName amount:orderTotal]
99
- ];
100
-
101
- self.viewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest: self.paymentRequest];
102
- self.viewController.delegate = self;
103
- }else{
104
- request.applePayDisabled = YES;
105
- }
106
-
107
- if(![options[@"payPal"] boolValue]){ //disable paypal
108
- request.paypalDisabled = YES;
109
- }
110
-
111
- BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientToken request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) {
112
- [self.reactRoot dismissViewControllerAnimated:YES completion:nil];
113
-
114
- //result.paymentOptionType == .ApplePay
115
- //NSLog(@"paymentOptionType = %ld", result.paymentOptionType);
116
-
117
- if (error != nil) {
118
- reject(error.localizedDescription, error.localizedDescription, error);
119
- } else if (result.canceled) {
120
- reject(@"USER_CANCELLATION", @"The user cancelled", nil);
121
- } else {
122
- if (threeDSecureOptions && [result.paymentMethod isKindOfClass:[BTCardNonce class]]) {
123
- BTCardNonce *cardNonce = (BTCardNonce *)result.paymentMethod;
124
- if (!cardNonce.threeDSecureInfo.liabilityShiftPossible && cardNonce.threeDSecureInfo.wasVerified) {
125
- reject(@"3DSECURE_NOT_ABLE_TO_SHIFT_LIABILITY", @"3D Secure liability cannot be shifted", nil);
126
- } else if (!cardNonce.threeDSecureInfo.liabilityShifted && cardNonce.threeDSecureInfo.wasVerified) {
127
- reject(@"3DSECURE_LIABILITY_NOT_SHIFTED", @"3D Secure liability was not shifted", nil);
128
- } else{
129
- [[self class] resolvePayment:result deviceData:self.deviceDataCollector resolver:resolve];
130
- }
131
- } else if(result.paymentMethod == nil && (result.paymentMethodType == 16 || result.paymentMethodType == 17 || result.paymentMethodType == 18)){ //Apple Pay
132
- // UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
133
- // [ctrl presentViewController:self.viewController animated:YES completion:nil];
134
- UIViewController *rootViewController = RCTPresentedViewController();
135
- [rootViewController presentViewController:self.viewController animated:YES completion:nil];
136
- } else{
137
- [[self class] resolvePayment:result deviceData:self.deviceDataCollector resolver:resolve];
138
- }
139
- }
140
- }];
141
-
142
- if (dropIn != nil) {
143
- [self.reactRoot presentViewController:dropIn animated:YES completion:nil];
144
- } else {
145
- reject(@"INVALID_CLIENT_TOKEN", @"The client token seems invalid", nil);
146
- }
147
- }
148
-
149
- RCT_EXPORT_METHOD(fetchMostRecentPaymentMethod:(NSString*)clientToken
150
- resolver:(RCTPromiseResolveBlock)resolve
151
- rejecter:(RCTPromiseRejectBlock)reject)
152
- {
153
- [BTDropInResult mostRecentPaymentMethodForClientToken:clientToken completion:^(BTDropInResult * _Nullable result, NSError * _Nullable error) {
154
- if (error != nil) {
155
- reject(error.localizedDescription, error.localizedDescription, error);
156
- } else if (result.canceled) {
157
- reject(@"USER_CANCELLATION", @"The user cancelled", nil);
158
- } else {
159
- [[self class] resolvePayment:result deviceData:result.deviceData resolver:resolve];
160
- }
161
- }];
162
- }
163
-
164
- RCT_EXPORT_METHOD(tokenizeCard:(NSString*)clientToken
165
- info:(NSDictionary*)cardInfo
166
- resolver:(RCTPromiseResolveBlock)resolve
167
- rejecter:(RCTPromiseRejectBlock)reject)
168
- {
169
- NSString *number = cardInfo[@"number"];
170
- NSString *expirationMonth = cardInfo[@"expirationMonth"];
171
- NSString *expirationYear = cardInfo[@"expirationYear"];
172
- NSString *cvv = cardInfo[@"cvv"];
173
- NSString *postalCode = cardInfo[@"postalCode"];
174
-
175
- if (!number || !expirationMonth || !expirationYear || !cvv || !postalCode) {
176
- reject(@"INVALID_CARD_INFO", @"Invalid card info", nil);
177
- return;
178
- }
179
-
180
- BTAPIClient *braintreeClient = [[BTAPIClient alloc] initWithAuthorization:clientToken];
181
- BTCardClient *cardClient = [[BTCardClient alloc] initWithAPIClient:braintreeClient];
182
- BTCard *card = [[BTCard alloc] init];
183
- card.number = number;
184
- card.expirationMonth = expirationMonth;
185
- card.expirationYear = expirationYear;
186
- card.cvv = cvv;
187
- card.postalCode = postalCode;
188
-
189
- [cardClient tokenizeCard:card
190
- completion:^(BTCardNonce *tokenizedCard, NSError *error) {
191
- if (error == nil) {
192
- resolve(tokenizedCard.nonce);
193
- } else {
194
- reject(@"TOKENIZE_ERROR", @"Error tokenizing card.", error);
195
- }
196
- }];
197
- }
198
-
199
- - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
200
- didAuthorizePayment:(PKPayment *)payment
201
- handler:(nonnull void (^)(PKPaymentAuthorizationResult * _Nonnull))completion
202
- {
203
-
204
- // Example: Tokenize the Apple Pay payment
205
- BTApplePayClient *applePayClient = [[BTApplePayClient alloc]
206
- initWithAPIClient:self.braintreeClient];
207
- [applePayClient tokenizeApplePayPayment:payment
208
- completion:^(BTApplePayCardNonce *tokenizedApplePayPayment,
209
- NSError *error) {
210
- if (tokenizedApplePayPayment) {
211
- // On success, send nonce to your server for processing.
212
- // If applicable, address information is accessible in `payment`.
213
- // NSLog(@"description = %@", tokenizedApplePayPayment.localizedDescription);
214
-
215
- completion([[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusSuccess errors:nil]);
216
- self.applePayAuthorized = YES;
217
-
218
-
219
- NSMutableDictionary* result = [NSMutableDictionary new];
220
- [result setObject:tokenizedApplePayPayment.nonce forKey:@"nonce"];
221
- [result setObject:@"Apple Pay" forKey:@"type"];
222
- [result setObject:[NSString stringWithFormat: @"%@ %@", @"", tokenizedApplePayPayment.type] forKey:@"description"];
223
- [result setObject:[NSNumber numberWithBool:false] forKey:@"isDefault"];
224
- [result setObject:self.deviceDataCollector forKey:@"deviceData"];
225
-
226
- self.resolve(result);
227
-
228
- } else {
229
- // Tokenization failed. Check `error` for the cause of the failure.
230
-
231
- // Indicate failure via the completion callback:
232
- completion([[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusFailure errors:nil]);
233
- }
234
- }];
235
- }
236
-
237
- // Be sure to implement -paymentAuthorizationViewControllerDidFinish:
238
- - (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller{
239
- [self.reactRoot dismissViewControllerAnimated:YES completion:nil];
240
- if(self.applePayAuthorized == NO){
241
- self.reject(@"USER_CANCELLATION", @"The user cancelled", nil);
242
- }
243
- }
244
-
245
- + (void)resolvePayment:(BTDropInResult* _Nullable)result deviceData:(NSString * _Nonnull)deviceDataCollector resolver:(RCTPromiseResolveBlock _Nonnull)resolve {
246
- //NSLog(@"result = %@", result);
247
-
248
- if (!result) {
249
- resolve(nil);
250
- return;
251
- }
252
-
253
- NSMutableDictionary* jsResult = [NSMutableDictionary new];
254
-
255
- //NSLog(@"paymentMethod = %@", result.paymentMethod);
256
- //NSLog(@"paymentIcon = %@", result.paymentIcon);
257
-
258
- [jsResult setObject:result.paymentMethod.nonce forKey:@"nonce"];
259
- [jsResult setObject:result.paymentMethod.type forKey:@"type"];
260
- [jsResult setObject:result.paymentDescription forKey:@"description"];
261
- [jsResult setObject:[NSNumber numberWithBool:result.paymentMethod.isDefault] forKey:@"isDefault"];
262
- [jsResult setObject:deviceDataCollector forKey:@"deviceData"];
263
-
264
- resolve(jsResult);
265
- }
266
-
267
- - (UIViewController*)reactRoot {
268
- UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
269
- UIViewController *maybeModal = root.presentedViewController;
270
-
271
- UIViewController *modalRoot = root;
272
-
273
- if (maybeModal != nil) {
274
- modalRoot = maybeModal;
275
- }
276
-
277
- return modalRoot;
278
- }
279
-
280
- @end
1
+ #import "RNBraintreeDropIn.h"
2
+ #import <React/RCTUtils.h>
3
+ #import "BTThreeDSecureRequest.h"
4
+
5
+ @implementation RNBraintreeDropIn
6
+
7
+ - (dispatch_queue_t)methodQueue
8
+ {
9
+ return dispatch_get_main_queue();
10
+ }
11
+ RCT_EXPORT_MODULE(RNBraintreeDropIn)
12
+
13
+ RCT_EXPORT_METHOD(show:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
14
+ {
15
+ BTDropInColorScheme colorScheme;
16
+
17
+ if([options[@"darkTheme"] boolValue]){
18
+ if (@available(iOS 13.0, *)) {
19
+ colorScheme = BTDropInColorSchemeDynamic;
20
+ } else {
21
+ colorScheme = BTDropInColorSchemeDark;
22
+ }
23
+ } else {
24
+ colorScheme = BTDropInColorSchemeLight;
25
+ }
26
+
27
+ BTDropInUICustomization *uiCustomization = [[BTDropInUICustomization alloc] initWithColorScheme:colorScheme];
28
+
29
+ if(options[@"fontFamily"]){
30
+ uiCustomization.fontFamily = options[@"fontFamily"];
31
+ }
32
+ if(options[@"boldFontFamily"]){
33
+ uiCustomization.boldFontFamily = options[@"boldFontFamily"];
34
+ }
35
+
36
+ self.resolve = resolve;
37
+ self.reject = reject;
38
+ self.applePayAuthorized = NO;
39
+
40
+ NSString* clientToken = options[@"clientToken"];
41
+ if (!clientToken) {
42
+ reject(@"NO_CLIENT_TOKEN", @"You must provide a client token", nil);
43
+ return;
44
+ }
45
+
46
+ BTDropInRequest *request = [[BTDropInRequest alloc] init];
47
+ request.uiCustomization = uiCustomization;
48
+
49
+ NSDictionary* threeDSecureOptions = options[@"threeDSecure"];
50
+ if (threeDSecureOptions) {
51
+ NSNumber* threeDSecureAmount = threeDSecureOptions[@"amount"];
52
+ if (!threeDSecureAmount) {
53
+ reject(@"NO_3DS_AMOUNT", @"You must provide an amount for 3D Secure", nil);
54
+ return;
55
+ }
56
+
57
+ BTThreeDSecureRequest *threeDSecureRequest = [[BTThreeDSecureRequest alloc] init];
58
+ threeDSecureRequest.amount = [NSDecimalNumber decimalNumberWithString:threeDSecureAmount.stringValue];
59
+ request.threeDSecureRequest = threeDSecureRequest;
60
+
61
+ }
62
+
63
+ BTAPIClient *apiClient = [[BTAPIClient alloc] initWithAuthorization:clientToken];
64
+ self.dataCollector = [[BTDataCollector alloc] initWithAPIClient:apiClient];
65
+ [self.dataCollector collectDeviceData:^(NSString * _Nonnull deviceDataCollector) {
66
+ // Save deviceData
67
+ self.deviceDataCollector = deviceDataCollector;
68
+ }];
69
+
70
+ if([options[@"vaultManager"] boolValue]){
71
+ request.vaultManager = YES;
72
+ }
73
+
74
+ if([options[@"cardDisabled"] boolValue]){
75
+ request.cardDisabled = YES;
76
+ }
77
+
78
+ if([options[@"applePay"] boolValue]){
79
+ NSString* merchantIdentifier = options[@"merchantIdentifier"];
80
+ NSString* countryCode = options[@"countryCode"];
81
+ NSString* currencyCode = options[@"currencyCode"];
82
+ NSString* merchantName = options[@"merchantName"];
83
+ NSDecimalNumber* orderTotal = [NSDecimalNumber decimalNumberWithDecimal:[options[@"orderTotal"] decimalValue]];
84
+ if(!merchantIdentifier || !countryCode || !currencyCode || !merchantName || !orderTotal){
85
+ reject(@"MISSING_OPTIONS", @"Not all required Apple Pay options were provided", nil);
86
+ return;
87
+ }
88
+ self.braintreeClient = [[BTAPIClient alloc] initWithAuthorization:clientToken];
89
+
90
+ self.paymentRequest = [[PKPaymentRequest alloc] init];
91
+ self.paymentRequest.merchantIdentifier = merchantIdentifier;
92
+ self.paymentRequest.merchantCapabilities = PKMerchantCapability3DS;
93
+ self.paymentRequest.countryCode = countryCode;
94
+ self.paymentRequest.currencyCode = currencyCode;
95
+ self.paymentRequest.supportedNetworks = @[PKPaymentNetworkAmex, PKPaymentNetworkVisa, PKPaymentNetworkMasterCard, PKPaymentNetworkDiscover, PKPaymentNetworkChinaUnionPay];
96
+ self.paymentRequest.paymentSummaryItems =
97
+ @[
98
+ [PKPaymentSummaryItem summaryItemWithLabel:merchantName amount:orderTotal]
99
+ ];
100
+ if (@available(iOS 11.0, *)) {
101
+ self.paymentRequest.requiredBillingContactFields = [[NSSet<PKContactField> alloc] initWithObjects: PKContactFieldPostalAddress, PKContactFieldEmailAddress, PKContactFieldName, nil];
102
+ } else {
103
+ reject(@"MISSING_OPTIONS", @"Not all required Apple Pay options were provided", nil);
104
+ }
105
+ self.viewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest: self.paymentRequest];
106
+ self.viewController.delegate = self;
107
+ }else{
108
+ request.applePayDisabled = YES;
109
+ }
110
+
111
+ if(![options[@"payPal"] boolValue]){ //disable paypal
112
+ request.paypalDisabled = YES;
113
+ }
114
+
115
+ BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientToken request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) {
116
+ [self.reactRoot dismissViewControllerAnimated:YES completion:nil];
117
+
118
+ //result.paymentOptionType == .ApplePay
119
+ //NSLog(@"paymentOptionType = %ld", result.paymentOptionType);
120
+
121
+ if (error != nil) {
122
+ reject(error.localizedDescription, error.localizedDescription, error);
123
+ } else if (result.canceled) {
124
+ reject(@"USER_CANCELLATION", @"The user cancelled", nil);
125
+ } else {
126
+ if (threeDSecureOptions && [result.paymentMethod isKindOfClass:[BTCardNonce class]]) {
127
+ BTCardNonce *cardNonce = (BTCardNonce *)result.paymentMethod;
128
+ if (!cardNonce.threeDSecureInfo.liabilityShiftPossible && cardNonce.threeDSecureInfo.wasVerified) {
129
+ reject(@"3DSECURE_NOT_ABLE_TO_SHIFT_LIABILITY", @"3D Secure liability cannot be shifted", nil);
130
+ } else if (!cardNonce.threeDSecureInfo.liabilityShifted && cardNonce.threeDSecureInfo.wasVerified) {
131
+ reject(@"3DSECURE_LIABILITY_NOT_SHIFTED", @"3D Secure liability was not shifted", nil);
132
+ } else{
133
+ [[self class] resolvePayment:result deviceData:self.deviceDataCollector resolver:resolve];
134
+ }
135
+ } else if(result.paymentMethod == nil && (result.paymentMethodType == 16 || result.paymentMethodType == 17 || result.paymentMethodType == 18)){ //Apple Pay
136
+ // UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
137
+ // [ctrl presentViewController:self.viewController animated:YES completion:nil];
138
+ UIViewController *rootViewController = RCTPresentedViewController();
139
+ [rootViewController presentViewController:self.viewController animated:YES completion:nil];
140
+ } else{
141
+ [[self class] resolvePayment:result deviceData:self.deviceDataCollector resolver:resolve];
142
+ }
143
+ }
144
+ }];
145
+
146
+ if (dropIn != nil) {
147
+ [self.reactRoot presentViewController:dropIn animated:YES completion:nil];
148
+ } else {
149
+ reject(@"INVALID_CLIENT_TOKEN", @"The client token seems invalid", nil);
150
+ }
151
+ }
152
+
153
+ RCT_EXPORT_METHOD(getDeviceData:(NSString*)clientToken resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
154
+ {
155
+ BTAPIClient *braintreeClient = [[BTAPIClient alloc] initWithAuthorization:clientToken];
156
+ BTDataCollector *dataCollector = [[BTDataCollector alloc] initWithAPIClient:braintreeClient];
157
+ [dataCollector collectDeviceData:^(NSString * _Nonnull deviceData) {
158
+ resolve(deviceData);
159
+ }];
160
+ }
161
+
162
+ RCT_EXPORT_METHOD(fetchMostRecentPaymentMethod:(NSString*)clientToken
163
+ resolver:(RCTPromiseResolveBlock)resolve
164
+ rejecter:(RCTPromiseRejectBlock)reject)
165
+ {
166
+ [BTDropInResult mostRecentPaymentMethodForClientToken:clientToken completion:^(BTDropInResult * _Nullable result, NSError * _Nullable error) {
167
+ if (error != nil) {
168
+ reject(error.localizedDescription, error.localizedDescription, error);
169
+ } else if (result.canceled) {
170
+ reject(@"USER_CANCELLATION", @"The user cancelled", nil);
171
+ } else {
172
+ [[self class] resolvePayment:result deviceData:result.deviceData resolver:resolve];
173
+ }
174
+ }];
175
+ }
176
+
177
+ RCT_EXPORT_METHOD(tokenizeCard:(NSString*)clientToken
178
+ info:(NSDictionary*)cardInfo
179
+ resolver:(RCTPromiseResolveBlock)resolve
180
+ rejecter:(RCTPromiseRejectBlock)reject)
181
+ {
182
+ NSString *number = cardInfo[@"number"];
183
+ NSString *expirationMonth = cardInfo[@"expirationMonth"];
184
+ NSString *expirationYear = cardInfo[@"expirationYear"];
185
+ NSString *cvv = cardInfo[@"cvv"];
186
+ NSString *postalCode = cardInfo[@"postalCode"];
187
+
188
+ if (!number || !expirationMonth || !expirationYear || !cvv || !postalCode) {
189
+ reject(@"INVALID_CARD_INFO", @"Invalid card info", nil);
190
+ return;
191
+ }
192
+
193
+ BTAPIClient *braintreeClient = [[BTAPIClient alloc] initWithAuthorization:clientToken];
194
+ BTCardClient *cardClient = [[BTCardClient alloc] initWithAPIClient:braintreeClient];
195
+ BTCard *card = [[BTCard alloc] init];
196
+ card.number = number;
197
+ card.expirationMonth = expirationMonth;
198
+ card.expirationYear = expirationYear;
199
+ card.cvv = cvv;
200
+ card.postalCode = postalCode;
201
+
202
+ [cardClient tokenizeCard:card
203
+ completion:^(BTCardNonce *tokenizedCard, NSError *error) {
204
+ if (error == nil) {
205
+ resolve(tokenizedCard.nonce);
206
+ } else {
207
+ reject(@"TOKENIZE_ERROR", @"Error tokenizing card.", error);
208
+ }
209
+ }];
210
+ }
211
+
212
+ - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
213
+ didAuthorizePayment:(PKPayment *)payment
214
+ handler:(nonnull void (^)(PKPaymentAuthorizationResult * _Nonnull))completion
215
+ {
216
+
217
+ // Example: Tokenize the Apple Pay payment
218
+ BTApplePayClient *applePayClient = [[BTApplePayClient alloc]
219
+ initWithAPIClient:self.braintreeClient];
220
+ [applePayClient tokenizeApplePayPayment:payment
221
+ completion:^(BTApplePayCardNonce *tokenizedApplePayPayment,
222
+ NSError *error) {
223
+ if (tokenizedApplePayPayment) {
224
+ // On success, send nonce to your server for processing.
225
+ // If applicable, address information is accessible in `payment`.
226
+ // NSLog(@"description = %@", tokenizedApplePayPayment.localizedDescription);
227
+
228
+ completion([[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusSuccess errors:nil]);
229
+ self.applePayAuthorized = YES;
230
+
231
+
232
+ NSMutableDictionary* result = [NSMutableDictionary new];
233
+ [result setObject:tokenizedApplePayPayment.nonce forKey:@"nonce"];
234
+ [result setObject:@"Apple Pay" forKey:@"type"];
235
+ [result setObject:[NSString stringWithFormat: @"%@ %@", @"", tokenizedApplePayPayment.type] forKey:@"description"];
236
+ [result setObject:[NSNumber numberWithBool:false] forKey:@"isDefault"];
237
+ [result setObject:self.deviceDataCollector forKey:@"deviceData"];
238
+ if(payment.billingContact && payment.billingContact.postalAddress) {
239
+ [result setObject:payment.billingContact.name.givenName forKey:@"firstName"];
240
+ [result setObject:payment.billingContact.name.familyName forKey:@"lastName"];
241
+ if(payment.billingContact.emailAddress) {
242
+ [result setObject:payment.billingContact.emailAddress forKey:@"email"];
243
+ }
244
+ NSString *street = payment.billingContact.postalAddress.street;
245
+ NSArray *splitArray = [street componentsSeparatedByString:@"\n"];
246
+ [result setObject:splitArray[0] forKey:@"addressLine1"];
247
+ if([splitArray count] > 1 && splitArray[1]) {
248
+ [result setObject:splitArray[1] forKey:@"addressLine2"];
249
+ }
250
+ [result setObject:payment.billingContact.postalAddress.city forKey:@"city"];
251
+ [result setObject:payment.billingContact.postalAddress.state forKey:@"state"];
252
+ [result setObject:payment.billingContact.postalAddress.ISOCountryCode forKey:@"country"];
253
+ [result setObject:payment.billingContact.postalAddress.postalCode forKey:@"zip1"];
254
+ }
255
+ self.resolve(result);
256
+
257
+ } else {
258
+ // Tokenization failed. Check `error` for the cause of the failure.
259
+
260
+ // Indicate failure via the completion callback:
261
+ completion([[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusFailure errors:nil]);
262
+ }
263
+ }];
264
+ }
265
+
266
+ // Be sure to implement -paymentAuthorizationViewControllerDidFinish:
267
+ - (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller{
268
+ [self.reactRoot dismissViewControllerAnimated:YES completion:nil];
269
+ if(self.applePayAuthorized == NO){
270
+ self.reject(@"USER_CANCELLATION", @"The user cancelled", nil);
271
+ }
272
+ }
273
+
274
+ + (void)resolvePayment:(BTDropInResult* _Nullable)result deviceData:(NSString * _Nonnull)deviceDataCollector resolver:(RCTPromiseResolveBlock _Nonnull)resolve {
275
+ //NSLog(@"result = %@", result);
276
+
277
+ if (!result) {
278
+ resolve(nil);
279
+ return;
280
+ }
281
+
282
+ NSMutableDictionary* jsResult = [NSMutableDictionary new];
283
+
284
+ //NSLog(@"paymentMethod = %@", result.paymentMethod);
285
+ //NSLog(@"paymentIcon = %@", result.paymentIcon);
286
+
287
+ [jsResult setObject:result.paymentMethod.nonce forKey:@"nonce"];
288
+ [jsResult setObject:result.paymentMethod.type forKey:@"type"];
289
+ [jsResult setObject:result.paymentDescription forKey:@"description"];
290
+ [jsResult setObject:[NSNumber numberWithBool:result.paymentMethod.isDefault] forKey:@"isDefault"];
291
+ [jsResult setObject:deviceDataCollector forKey:@"deviceData"];
292
+
293
+ resolve(jsResult);
294
+ }
295
+
296
+ - (UIViewController*)reactRoot {
297
+ UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
298
+ UIViewController *maybeModal = root.presentedViewController;
299
+
300
+ UIViewController *modalRoot = root;
301
+
302
+ if (maybeModal != nil) {
303
+ modalRoot = maybeModal;
304
+ }
305
+
306
+ return modalRoot;
307
+ }
308
+
309
+ @end