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

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