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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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