From 5377609e4bbb56c23d57642b42756844d927caad Mon Sep 17 00:00:00 2001 From: Michael Mudrinic Date: Tue, 19 Jan 2021 12:10:04 +0000 Subject: [PATCH 01/24] Update README.md Modified documentation to reflect new property `paypal`. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 06eb108..2f1a160 100644 --- a/README.md +++ b/README.md @@ -252,6 +252,7 @@ BraintreeDropIn.show({ googlePay: true, applePay: true, vaultManager: true, + paypal: true, cardDisabled: false, darkTheme: true, }) @@ -284,6 +285,7 @@ BraintreeDropIn.show({ googlePay: true, applePay: true, vaultManager: true, + paypal: true, cardDisabled: false, darkTheme: true, }) From 2dbfbac1ee8b33cbd6c83eb000fb3e61819182a8 Mon Sep 17 00:00:00 2001 From: Michael Mudrinic Date: Tue, 19 Jan 2021 12:13:05 +0000 Subject: [PATCH 02/24] Update RNBraintreeDropInModule.java Add `paypal` control support to Android --- .../tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java index 8fdd484..37ad2d4 100644 --- a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java +++ b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java @@ -88,6 +88,10 @@ public void show(final ReadableMap options, final Promise promise) { .amount(String.valueOf(threeDSecureOptions.getDouble("amount"))) .requestThreeDSecureVerification(true); } + + if(!options.getBoolean("payPal")){ //disable paypal + dropInRequest.disablePayPal(); + } mPromise = promise; currentActivity.startActivityForResult(dropInRequest.getIntent(currentActivity), DROP_IN_REQUEST); From 1e7981a596b906a7c295c88f8e9a5aa02ef4a26a Mon Sep 17 00:00:00 2001 From: Michael Mudrinic Date: Tue, 19 Jan 2021 12:14:31 +0000 Subject: [PATCH 03/24] Update RNBraintreeDropIn.m Add `paypal` control support to iOS --- ios/RNBraintreeDropIn.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ios/RNBraintreeDropIn.m b/ios/RNBraintreeDropIn.m index 1eefa71..3d66be1 100644 --- a/ios/RNBraintreeDropIn.m +++ b/ios/RNBraintreeDropIn.m @@ -100,6 +100,10 @@ - (dispatch_queue_t)methodQueue }else{ request.applePayDisabled = YES; } + + if(![options[@"payPal"] boolValue]){ //disable paypal + request.paypalDisabled = YES; + } BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientToken request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) { [self.reactRoot dismissViewControllerAnimated:YES completion:nil]; From adf420944e821a3b100fa0b67d6595798c842ba4 Mon Sep 17 00:00:00 2001 From: Beau Rushton Date: Mon, 1 Mar 2021 13:52:06 +1000 Subject: [PATCH 04/24] Adds a check for invalid client token --- ios/RNBraintreeDropIn.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ios/RNBraintreeDropIn.m b/ios/RNBraintreeDropIn.m index 1eefa71..1ac9194 100644 --- a/ios/RNBraintreeDropIn.m +++ b/ios/RNBraintreeDropIn.m @@ -131,7 +131,12 @@ - (dispatch_queue_t)methodQueue } } }]; - [self.reactRoot presentViewController:dropIn animated:YES completion:nil]; + + if (dropIn != nil) { + [self.reactRoot presentViewController:dropIn animated:YES completion:nil]; + } else { + reject(@"INVALID_CLIENT_TOKEN", @"The client token seems invalid", nil); + } } - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller From 393b0fa4d2004f4b0643ce2b55aa88db0436f4d0 Mon Sep 17 00:00:00 2001 From: Mahadi Hasan Sourav Date: Sun, 11 Jul 2021 20:22:29 +0600 Subject: [PATCH 05/24] Update drop-in version and maven repository credentials - Update 'com.braintreepayments.api:drop-in' version to 5 - Change maven repository credentials to solve fetching binary store failed issue --- android/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 318eac5..58245d3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -35,7 +35,7 @@ dependencies { implementation 'com.braintreepayments.api:google-payment:3.2.0' implementation 'com.google.android.gms:play-services-wallet:16.0.1' implementation 'com.braintreepayments.api:data-collector:2.+' - implementation 'com.braintreepayments.api:drop-in:4.+' + implementation 'com.braintreepayments.api:drop-in:5.+' implementation 'com.facebook.react:react-native:+' } @@ -43,10 +43,10 @@ dependencies { rootProject.allprojects { repositories { maven { - url "https://cardinalcommerce.bintray.com/android" + url "https://cardinalcommerceprod.jfrog.io/artifactory/android" credentials { - username 'braintree-team-sdk@cardinalcommerce' - password '220cc9476025679c4e5c843666c27d97cfb0f951' + username 'braintree_team_sdk' + password 'AKCp8jQcoDy2hxSWhDAUQKXLDPDx6NYRkqrgFLRc3qDrayg6rrCbJpsKKyMwaykVL8FWusJpp' } } } From f108c6b319dae9b8b3c92d661ef90d3c3d634fef Mon Sep 17 00:00:00 2001 From: Mahadi Hasan Sourav Date: Sun, 11 Jul 2021 20:29:43 +0600 Subject: [PATCH 06/24] Added issue reference Added issue reference on read me file. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 06eb108..36e2b65 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +## This version contains updated BraintreeDropIn 5.+ + +BraintreeDropIn 5.0.2 contains some security fixes that resolves Play store rejection fix. Read more - + +Unsafe implementation of the HostnameVerifier interface - vulnerability ( https://github.com/braintree/braintree-android-drop-in/issues/208 ) + + # react-native-braintree-dropin-ui > React Native integration of Braintree Drop-in for IOS & ANDROID (Apple Pay, Google Pay, Paypal, Venmo, Credit Card) From 7191b892c1a0e3412c5f40cf12a4a51d5bfafc28 Mon Sep 17 00:00:00 2001 From: Mahadi Hasan Sourav Date: Sat, 14 Aug 2021 14:09:04 +0600 Subject: [PATCH 07/24] Update readme doc --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 36e2b65..51ba983 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,3 @@ -## This version contains updated BraintreeDropIn 5.+ - -BraintreeDropIn 5.0.2 contains some security fixes that resolves Play store rejection fix. Read more - - -Unsafe implementation of the HostnameVerifier interface - vulnerability ( https://github.com/braintree/braintree-android-drop-in/issues/208 ) - - # react-native-braintree-dropin-ui > React Native integration of Braintree Drop-in for IOS & ANDROID (Apple Pay, Google Pay, Paypal, Venmo, Credit Card) @@ -313,6 +306,13 @@ BraintreeDropIn.show({ boldFontFamily: 'Averta-Semibold', }) ``` + +### Potential Fix + +This version contains updated BraintreeDropIn 5.+. BraintreeDropIn 5.0.2 contains some security fixes that resolves Play store rejection fix. Read more - + +Unsafe implementation of the HostnameVerifier interface - vulnerability ( https://github.com/braintree/braintree-android-drop-in/issues/208 ) + [1]: http://guides.cocoapods.org/using/using-cocoapods.html [2]: https://github.com/braintree/braintree-ios-drop-in [3]: https://github.com/braintree/braintree-android-drop-in From 56e09fef5d8d72cf1b5e3d9ad11720b7dac0db8a Mon Sep 17 00:00:00 2001 From: ysudharsono Date: Tue, 26 Oct 2021 14:00:48 +0700 Subject: [PATCH 08/24] Fix paypal configuration attribute typo A typo in this PR: https://github.com/wgltony/react-native-braintree-dropin-ui/pull/62 The correct config attribute is 'payPal' and not 'paypal' This PR updates the correct usage in the README file --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d078fe1..21ae9fe 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,7 @@ BraintreeDropIn.show({ googlePay: true, applePay: true, vaultManager: true, - paypal: true, + payPal: true, cardDisabled: false, darkTheme: true, }) @@ -285,7 +285,7 @@ BraintreeDropIn.show({ googlePay: true, applePay: true, vaultManager: true, - paypal: true, + payPal: true, cardDisabled: false, darkTheme: true, }) From ceaf620b1b68be7fdb880be1944d6517b848c3da Mon Sep 17 00:00:00 2001 From: wgltony Date: Tue, 26 Oct 2021 03:46:26 -0400 Subject: [PATCH 09/24] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f039170..e0ba1ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-braintree-dropin-ui", - "version": "1.1.1", + "version": "1.1.3", "description": "> React Native integration of Braintree Drop-in IOS V4 ANDROID V2 (Apple Pay &Android Pay Enabled)", "main": "index.js", "dependencies": {}, From 96910b2ac9ef6d4ca33491cd7259da2257917c5c Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 4 Jan 2023 12:20:48 +0200 Subject: [PATCH 10/24] fix: ios autolink not working --- ios/RNBraintreeDropIn.podspec => RNBraintreeDropIn.podspec | 6 +++--- react-native.config.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename ios/RNBraintreeDropIn.podspec => RNBraintreeDropIn.podspec (85%) diff --git a/ios/RNBraintreeDropIn.podspec b/RNBraintreeDropIn.podspec similarity index 85% rename from ios/RNBraintreeDropIn.podspec rename to RNBraintreeDropIn.podspec index 8835268..cdd07e7 100644 --- a/ios/RNBraintreeDropIn.podspec +++ b/RNBraintreeDropIn.podspec @@ -1,17 +1,17 @@ Pod::Spec.new do |s| s.name = "RNBraintreeDropIn" - s.version = "1.0.0" + s.version = "1.1.3" s.summary = "RNBraintreeDropIn" s.description = <<-DESC RNBraintreeDropIn DESC s.homepage = "https://github.com/bamlab/react-native-braintree-payments-drop-in" s.license = "MIT" - # s.license = { :type => "MIT", :file => "../LICENSE" } + # s.license = { :type => "MIT", :file => "./LICENSE" } s.author = { "author" => "lagrange.louis@gmail.com" } s.platform = :ios, "9.0" s.source = { :git => "https://github.com/BradyShober/react-native-braintree-dropin-ui.git", :tag => "master" } - s.source_files = "*.{h,m}" + s.source_files = "ios/**/*.{h,m}" s.requires_arc = true s.dependency 'React' s.dependency 'Braintree' diff --git a/react-native.config.js b/react-native.config.js index f162c2b..6550b2f 100644 --- a/react-native.config.js +++ b/react-native.config.js @@ -3,7 +3,7 @@ const path = require('path'); module.exports = { dependency: { platforms: { - ios: { podspecPath: path.join(__dirname, 'ios', 'RNBraintreeDropIn.podspec') }, + ios: {}, android: { packageImportPath: 'import tech.power.RNBraintreeDropIn.RNBraintreeDropInPackage;', packageInstance: 'new RNBraintreeDropInPackage()', From 0b4977c9be8d9c5ad2512df8636ecc9ccc08c3cc Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 4 Jan 2023 15:39:16 +0200 Subject: [PATCH 11/24] chore: update Braintree iOS SDK to v5 --- README.md | 2 +- RNBraintreeDropIn.podspec | 12 ++++---- ios/RNBraintreeDropIn.h | 4 +-- ios/RNBraintreeDropIn.m | 29 ++++++++++--------- .../project.pbxproj | 8 ++--- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 21ae9fe..67a6764 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ react-native link react-native-braintree-dropin-ui #### iOS specific -You must have a iOS deployment target \>= 9.0. +You must have a iOS deployment target \>= 12.0. If you don't have a Podfile or are unsure on how to proceed, see the [CocoaPods][1] usage guide. diff --git a/RNBraintreeDropIn.podspec b/RNBraintreeDropIn.podspec index cdd07e7..f4ecf61 100644 --- a/RNBraintreeDropIn.podspec +++ b/RNBraintreeDropIn.podspec @@ -9,14 +9,14 @@ Pod::Spec.new do |s| s.license = "MIT" # s.license = { :type => "MIT", :file => "./LICENSE" } s.author = { "author" => "lagrange.louis@gmail.com" } - s.platform = :ios, "9.0" + s.platform = :ios, "12.0" s.source = { :git => "https://github.com/BradyShober/react-native-braintree-dropin-ui.git", :tag => "master" } s.source_files = "ios/**/*.{h,m}" s.requires_arc = true s.dependency 'React' - s.dependency 'Braintree' - s.dependency 'BraintreeDropIn' - s.dependency 'Braintree/DataCollector' - s.dependency 'Braintree/Apple-Pay' - s.dependency 'Braintree/Venmo' + s.dependency 'Braintree', '5.17.0' + s.dependency 'BraintreeDropIn', '9.7.0' + s.dependency 'Braintree/DataCollector', '5.17.0' + s.dependency 'Braintree/ApplePay', '5.17.0' + s.dependency 'Braintree/Venmo', '5.17.0' end diff --git a/ios/RNBraintreeDropIn.h b/ios/RNBraintreeDropIn.h index 3aa5195..43dbbff 100644 --- a/ios/RNBraintreeDropIn.h +++ b/ios/RNBraintreeDropIn.h @@ -14,7 +14,7 @@ #import "BraintreeApplePay.h" -@interface RNBraintreeDropIn : NSObject +@interface RNBraintreeDropIn : NSObject @property (nonatomic, strong) UIViewController *_Nonnull reactRoot; @@ -33,7 +33,7 @@ @property (nonatomic) RCTPromiseRejectBlock _Nonnull reject; -@property (nonatomic, assign) BOOL *_Nonnull applePayAuthorized; +@property (nonatomic, assign) BOOL applePayAuthorized; + (void)resolvePayment:(BTDropInResult* _Nullable)result deviceData:(NSString * _Nonnull)deviceDataCollector resolver:(RCTPromiseResolveBlock _Nonnull)resolve; diff --git a/ios/RNBraintreeDropIn.m b/ios/RNBraintreeDropIn.m index cdfd727..068f203 100644 --- a/ios/RNBraintreeDropIn.m +++ b/ios/RNBraintreeDropIn.m @@ -12,22 +12,25 @@ - (dispatch_queue_t)methodQueue RCT_EXPORT_METHOD(show:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - + BTDropInColorScheme colorScheme; + if([options[@"darkTheme"] boolValue]){ if (@available(iOS 13.0, *)) { - BTUIKAppearance.sharedInstance.colorScheme = BTUIKColorSchemeDynamic; + colorScheme = BTDropInColorSchemeDynamic; } else { - BTUIKAppearance.sharedInstance.colorScheme = BTUIKColorSchemeDark; + colorScheme = BTDropInColorSchemeDark; } } else { - BTUIKAppearance.sharedInstance.colorScheme = BTUIKColorSchemeLight; + colorScheme = BTDropInColorSchemeLight; } + BTDropInUICustomization *uiCustomization = [[BTDropInUICustomization alloc] initWithColorScheme:colorScheme]; + if(options[@"fontFamily"]){ - [BTUIKAppearance sharedInstance].fontFamily = options[@"fontFamily"]; + uiCustomization.fontFamily = options[@"fontFamily"]; } if(options[@"boldFontFamily"]){ - [BTUIKAppearance sharedInstance].boldFontFamily = options[@"boldFontFamily"]; + uiCustomization.boldFontFamily = options[@"boldFontFamily"]; } self.resolve = resolve; @@ -41,6 +44,7 @@ - (dispatch_queue_t)methodQueue } BTDropInRequest *request = [[BTDropInRequest alloc] init]; + request.uiCustomization = uiCustomization; NSDictionary* threeDSecureOptions = options[@"threeDSecure"]; if (threeDSecureOptions) { @@ -50,7 +54,6 @@ - (dispatch_queue_t)methodQueue return; } - request.threeDSecureVerification = YES; BTThreeDSecureRequest *threeDSecureRequest = [[BTThreeDSecureRequest alloc] init]; threeDSecureRequest.amount = [NSDecimalNumber decimalNumberWithString:threeDSecureAmount.stringValue]; request.threeDSecureRequest = threeDSecureRequest; @@ -59,7 +62,7 @@ - (dispatch_queue_t)methodQueue BTAPIClient *apiClient = [[BTAPIClient alloc] initWithAuthorization:clientToken]; self.dataCollector = [[BTDataCollector alloc] initWithAPIClient:apiClient]; - [self.dataCollector collectCardFraudData:^(NSString * _Nonnull deviceDataCollector) { + [self.dataCollector collectDeviceData:^(NSString * _Nonnull deviceDataCollector) { // Save deviceData self.deviceDataCollector = deviceDataCollector; }]; @@ -113,7 +116,7 @@ - (dispatch_queue_t)methodQueue if (error != nil) { reject(error.localizedDescription, error.localizedDescription, error); - } else if (result.cancelled) { + } else if (result.canceled) { reject(@"USER_CANCELLATION", @"The user cancelled", nil); } else { if (threeDSecureOptions && [result.paymentMethod isKindOfClass:[BTCardNonce class]]) { @@ -125,7 +128,7 @@ - (dispatch_queue_t)methodQueue } else{ [[self class] resolvePayment:result deviceData:self.deviceDataCollector resolver:resolve]; } - } else if(result.paymentMethod == nil && (result.paymentOptionType == 16 || result.paymentOptionType == 18)){ //Apple Pay + } else if(result.paymentMethod == nil && (result.paymentMethodType == 16 || result.paymentMethodType == 18)){ //Apple Pay // UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; // [ctrl presentViewController:self.viewController animated:YES completion:nil]; UIViewController *rootViewController = RCTPresentedViewController(); @@ -145,7 +148,7 @@ - (dispatch_queue_t)methodQueue - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment - completion:(void (^)(PKPaymentAuthorizationStatus))completion + handler:(nonnull void (^)(PKPaymentAuthorizationResult * _Nonnull))completion { // Example: Tokenize the Apple Pay payment @@ -159,7 +162,7 @@ - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController // If applicable, address information is accessible in `payment`. // NSLog(@"description = %@", tokenizedApplePayPayment.localizedDescription); - completion(PKPaymentAuthorizationStatusSuccess); + completion([[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusSuccess errors:nil]); self.applePayAuthorized = YES; @@ -176,7 +179,7 @@ - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController // Tokenization failed. Check `error` for the cause of the failure. // Indicate failure via the completion callback: - completion(PKPaymentAuthorizationStatusFailure); + completion([[PKPaymentAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusFailure errors:nil]); } }]; } diff --git a/ios/RNBraintreeDropIn.xcodeproj/project.pbxproj b/ios/RNBraintreeDropIn.xcodeproj/project.pbxproj index 39e2439..399ba0e 100644 --- a/ios/RNBraintreeDropIn.xcodeproj/project.pbxproj +++ b/ios/RNBraintreeDropIn.xcodeproj/project.pbxproj @@ -170,7 +170,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -207,7 +207,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; @@ -230,7 +230,7 @@ "$(PODS_ROOT)/Braintree/**", "$(PODS_ROOT)/BraintreeDropIn/**", ); - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PODS_ROOT = "${SRCROOT}/../../../ios/Pods"; @@ -255,7 +255,7 @@ "$(PODS_ROOT)/Braintree/**", "$(PODS_ROOT)/BraintreeDropIn/**", ); - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PODS_ROOT = "${SRCROOT}/../../../ios/Pods"; From 34ada90772bd4cd71c913650e9edfdd29b036922 Mon Sep 17 00:00:00 2001 From: Mihai Date: Wed, 4 Jan 2023 16:54:31 +0200 Subject: [PATCH 12/24] chore: update Braintree Android SDK to v4 --- android/build.gradle | 15 +- ...oid-react-native-braintree-dropin-ui~1.iml | 169 ---------------- .../RNBraintreeDropInModule.java | 187 +++++++++++------- 3 files changed, 126 insertions(+), 245 deletions(-) delete mode 100644 android/node_modules-react-native-braintree-dropin-ui-android-react-native-braintree-dropin-ui~1.iml diff --git a/android/build.gradle b/android/build.gradle index 58245d3..6809b44 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,23 +1,23 @@ buildscript { repositories { - jcenter() google() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:7.3.1' } } apply plugin: 'com.android.library' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 33 + buildToolsVersion '33.0.1' defaultConfig { minSdkVersion 21 - targetSdkVersion 27 + targetSdkVersion 33 versionCode 1 versionName "1.0" } @@ -32,10 +32,7 @@ repositories { } dependencies { - implementation 'com.braintreepayments.api:google-payment:3.2.0' - implementation 'com.google.android.gms:play-services-wallet:16.0.1' - implementation 'com.braintreepayments.api:data-collector:2.+' - implementation 'com.braintreepayments.api:drop-in:5.+' + implementation 'com.braintreepayments.api:drop-in:6.6.0' implementation 'com.facebook.react:react-native:+' } diff --git a/android/node_modules-react-native-braintree-dropin-ui-android-react-native-braintree-dropin-ui~1.iml b/android/node_modules-react-native-braintree-dropin-ui-android-react-native-braintree-dropin-ui~1.iml deleted file mode 100644 index 187164d..0000000 --- a/android/node_modules-react-native-braintree-dropin-ui-android-react-native-braintree-dropin-ui~1.iml +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java index 37ad2d4..00697f1 100644 --- a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java +++ b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java @@ -1,36 +1,54 @@ package tech.power.RNBraintreeDropIn; import android.app.Activity; -import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; + +import com.braintreepayments.api.DropInClient; +import com.braintreepayments.api.DropInListener; +import com.braintreepayments.api.DropInPaymentMethod; +import com.braintreepayments.api.ThreeDSecureRequest; +import com.braintreepayments.api.UserCanceledException; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.ActivityEventListener; -import com.facebook.react.bridge.BaseActivityEventListener; import com.facebook.react.bridge.Promise; -import com.braintreepayments.api.dropin.DropInActivity; -import com.braintreepayments.api.dropin.DropInRequest; -import com.braintreepayments.api.dropin.DropInResult; -import com.braintreepayments.api.models.PaymentMethodNonce; -import com.braintreepayments.api.models.CardNonce; -import com.braintreepayments.api.models.ThreeDSecureInfo; -import com.braintreepayments.api.models.GooglePaymentRequest; +import com.braintreepayments.api.DropInRequest; +import com.braintreepayments.api.DropInResult; +import com.braintreepayments.api.PaymentMethodNonce; +import com.braintreepayments.api.CardNonce; +import com.braintreepayments.api.ThreeDSecureInfo; +import com.braintreepayments.api.GooglePayRequest; import com.google.android.gms.wallet.TransactionInfo; import com.google.android.gms.wallet.WalletConstants; +import java.util.Objects; + public class RNBraintreeDropInModule extends ReactContextBaseJavaModule { private Promise mPromise; - private static final int DROP_IN_REQUEST = 0x444; private boolean isVerifyingThreeDSecure = false; + private static DropInClient dropInClient = null; + private static String clientToken = null; + + public static void initDropInClient(FragmentActivity activity) { + dropInClient = new DropInClient(activity, callback -> { + if (clientToken != null) { + callback.onSuccess(clientToken); + } else { + callback.onFailure(new Exception("Client token is null")); + } + }); + } + public RNBraintreeDropInModule(ReactApplicationContext reactContext) { super(reactContext); - reactContext.addActivityEventListener(mActivityListener); } @ReactMethod @@ -42,92 +60,106 @@ public void show(final ReadableMap options, final Promise promise) { return; } - Activity currentActivity = getCurrentActivity(); + FragmentActivity currentActivity = (FragmentActivity) getCurrentActivity(); if (currentActivity == null) { promise.reject("NO_ACTIVITY", "There is no current activity"); return; } - DropInRequest dropInRequest = new DropInRequest().clientToken(options.getString("clientToken")); + DropInRequest dropInRequest = new DropInRequest(); if(options.hasKey("vaultManager")) { - dropInRequest.vaultManager(options.getBoolean("vaultManager")); + dropInRequest.setVaultManagerEnabled(options.getBoolean("vaultManager")); } - dropInRequest.collectDeviceData(true); - - if(options.getBoolean("googlePay")){ - GooglePaymentRequest googlePaymentRequest = new GooglePaymentRequest() - .transactionInfo(TransactionInfo.newBuilder() - .setTotalPrice(options.getString("orderTotal")) + if(options.hasKey("googlePay") && options.getBoolean("googlePay")){ + GooglePayRequest googlePayRequest = new GooglePayRequest(); + googlePayRequest.setTransactionInfo(TransactionInfo.newBuilder() + .setTotalPrice(Objects.requireNonNull(options.getString("orderTotal"))) .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL) - .setCurrencyCode(options.getString("currencyCode")) - .build()) - .billingAddressRequired(true) - .googleMerchantId(options.getString("googlePayMerchantId")); + .setCurrencyCode(Objects.requireNonNull(options.getString("currencyCode"))) + .build()); + googlePayRequest.setBillingAddressRequired(true); + googlePayRequest.setGoogleMerchantId(options.getString("googlePayMerchantId")); - dropInRequest.googlePaymentRequest(googlePaymentRequest); + dropInRequest.setGooglePayDisabled(false); + dropInRequest.setGooglePayRequest(googlePayRequest); }else{ - dropInRequest.disableGooglePayment(); + dropInRequest.setGooglePayDisabled(true); } - // if(options.hasKey("cardDisabled")) { - // dropInRequest.disableCard(); - // } + if(options.hasKey("cardDisabled")) { + dropInRequest.setCardDisabled(true); + } if (options.hasKey("threeDSecure")) { final ReadableMap threeDSecureOptions = options.getMap("threeDSecure"); - if (!threeDSecureOptions.hasKey("amount")) { + if (threeDSecureOptions == null || !threeDSecureOptions.hasKey("amount")) { promise.reject("NO_3DS_AMOUNT", "You must provide an amount for 3D Secure"); return; } isVerifyingThreeDSecure = true; - dropInRequest - .amount(String.valueOf(threeDSecureOptions.getDouble("amount"))) - .requestThreeDSecureVerification(true); - } - - if(!options.getBoolean("payPal")){ //disable paypal - dropInRequest.disablePayPal(); + ThreeDSecureRequest threeDSecureRequest = new ThreeDSecureRequest(); + threeDSecureRequest.setAmount(threeDSecureOptions.getString("amount")); + + dropInRequest.setThreeDSecureRequest(threeDSecureRequest); } + dropInRequest.setPayPalDisabled(!options.hasKey("payPal") || !options.getBoolean("payPal")); + mPromise = promise; - currentActivity.startActivityForResult(dropInRequest.getIntent(currentActivity), DROP_IN_REQUEST); + + clientToken = options.getString("clientToken"); + + if (dropInClient == null) { + mPromise.reject( + "DROP_IN_CLIENT_UNINITIALIZED", + "Did you forget to call RNBraintreeDropInModule.initDropInClient(this) in MainActivity.onCreate?" + ); + mPromise = null; + return; + } + dropInClient.setListener(mDropInListener); + dropInClient.launchDropIn(dropInRequest); } - private final ActivityEventListener mActivityListener = new BaseActivityEventListener() { + private final DropInListener mDropInListener = new DropInListener() { @Override - public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode != DROP_IN_REQUEST || mPromise == null) { + public void onDropInSuccess(@NonNull DropInResult dropInResult) { + if (mPromise == null) { return; } - - if (resultCode == Activity.RESULT_OK) { - DropInResult result = data.getParcelableExtra(DropInResult.EXTRA_DROP_IN_RESULT); - PaymentMethodNonce paymentMethodNonce = result.getPaymentMethodNonce(); - String deviceData = result.getDeviceData(); - - if (isVerifyingThreeDSecure && paymentMethodNonce instanceof CardNonce) { - CardNonce cardNonce = (CardNonce) paymentMethodNonce; - ThreeDSecureInfo threeDSecureInfo = cardNonce.getThreeDSecureInfo(); - if (!threeDSecureInfo.isLiabilityShiftPossible()) { - mPromise.reject("3DSECURE_NOT_ABLE_TO_SHIFT_LIABILITY", "3D Secure liability cannot be shifted"); - } else if (!threeDSecureInfo.isLiabilityShifted()) { - mPromise.reject("3DSECURE_LIABILITY_NOT_SHIFTED", "3D Secure liability was not shifted"); - } else { - resolvePayment(paymentMethodNonce, deviceData); - } + PaymentMethodNonce paymentMethodNonce = dropInResult.getPaymentMethodNonce(); + String deviceData = dropInResult.getDeviceData(); + + if (isVerifyingThreeDSecure && paymentMethodNonce instanceof CardNonce) { + CardNonce cardNonce = (CardNonce) paymentMethodNonce; + ThreeDSecureInfo threeDSecureInfo = cardNonce.getThreeDSecureInfo(); + if (!threeDSecureInfo.isLiabilityShiftPossible()) { + mPromise.reject("3DSECURE_NOT_ABLE_TO_SHIFT_LIABILITY", "3D Secure liability cannot be shifted"); + } else if (!threeDSecureInfo.isLiabilityShifted()) { + mPromise.reject("3DSECURE_LIABILITY_NOT_SHIFTED", "3D Secure liability was not shifted"); } else { - resolvePayment(paymentMethodNonce, deviceData); + resolvePayment(dropInResult, deviceData); } - } else if (resultCode == Activity.RESULT_CANCELED) { + } else { + resolvePayment(dropInResult, deviceData); + } + + mPromise = null; + } + + @Override + public void onDropInFailure(@NonNull Exception exception) { + if (mPromise == null) { + return; + } + + if (exception instanceof UserCanceledException) { mPromise.reject("USER_CANCELLATION", "The user cancelled"); } else { - Exception exception = (Exception) data.getSerializableExtra(DropInActivity.EXTRA_ERROR); mPromise.reject(exception.getMessage(), exception.getMessage()); } @@ -135,17 +167,38 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode, } }; - private final void resolvePayment(PaymentMethodNonce paymentMethodNonce, String deviceData) { + private void resolvePayment(DropInResult dropInResult, String deviceData) { + PaymentMethodNonce paymentMethodNonce = dropInResult.getPaymentMethodNonce(); + WritableMap jsResult = Arguments.createMap(); - jsResult.putString("nonce", paymentMethodNonce.getNonce()); - jsResult.putString("type", paymentMethodNonce.getTypeLabel()); - jsResult.putString("description", paymentMethodNonce.getDescription()); + + if (paymentMethodNonce == null) { + mPromise.reject("NO_PAYMENT_METHOD_NONCE", "Payment method nonce is missing"); + return; + } + + Activity currentActivity = getCurrentActivity(); + if (currentActivity == null) { + mPromise.reject("NO_ACTIVITY", "There is no current activity"); + return; + } + + DropInPaymentMethod dropInPaymentMethod = dropInResult.getPaymentMethodType(); + if (dropInPaymentMethod == null) { + mPromise.reject("NO_PAYMENT_METHOD", "There is no payment method"); + return; + } + + jsResult.putString("nonce", paymentMethodNonce.getString()); + jsResult.putString("type", currentActivity.getString(dropInPaymentMethod.getLocalizedName())); + jsResult.putString("description", dropInResult.getPaymentDescription()); jsResult.putBoolean("isDefault", paymentMethodNonce.isDefault()); jsResult.putString("deviceData", deviceData); mPromise.resolve(jsResult); } + @NonNull @Override public String getName() { return "RNBraintreeDropIn"; From 5fd72b3696619ac940c08b45261d3aa33f0a370f Mon Sep 17 00:00:00 2001 From: Mihai Date: Fri, 6 Jan 2023 15:04:19 +0200 Subject: [PATCH 13/24] chore: update index.js.flow --- index.js.flow | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/index.js.flow b/index.js.flow index 3f86d00..18fdc4a 100644 --- a/index.js.flow +++ b/index.js.flow @@ -3,6 +3,20 @@ type ShowOptions = {| threeDSecure?: {| amount: number, |}, + vaultManager?: boolean, + cardDisabled?: boolean, + googlePay?: boolean, + orderTotal?: string, + currencyCode?: string, + googlePayMerchantId?: string, + payPal?: boolean, + applePay?: boolean, + merchantIdentifier?: string, + countryCode?: string, + merchantName?: string, + darkTheme?: boolean, + fontFamily?: string, + boldFontFamily?: string, |}; type ShowResult = {| @@ -10,6 +24,7 @@ type ShowResult = {| description: string, type: string, isDefault: boolean, + deviceData: string, |}; declare module.exports: { From e44ad0f98834be3040ef1251ae608ee6775f6475 Mon Sep 17 00:00:00 2001 From: Mihai Date: Fri, 6 Jan 2023 15:04:36 +0200 Subject: [PATCH 14/24] chore: update README --- README.md | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 67a6764..fa4062e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ npm install react-native-braintree-dropin-ui --save ## Configurate Payment Method(For ALL RN VERSIONS) See Braintree's documentation, [Apple Pay][8], [Google Pay][9], [Paypal][10], [Venmo][11] -Once you have finished setting up all the configurations, it will shows in the dropin UI. +Once you have finished setting up all the configurations, it will show in the dropin UI. For React Native versions < 0.60 @@ -58,7 +58,7 @@ pod 'Braintree' pod 'BraintreeDropIn' # comment the next line to disable Apple pay -pod 'Braintree/Apple-Pay' +pod 'Braintree/ApplePay' # comment the next line to disable PayPal pod 'Braintree/PayPal' @@ -84,18 +84,29 @@ The Drop-in will show Apple Pay as a payment option as long as you've completed #### PayPal -To enable paypal payments in iOS, you will need to add `setReturnURLScheme` to `launchOptions` of your `AppDelegate.m` +To enable paypal payments in iOS, you will need to add `setReturnURLScheme` to `launchOptions` of your `AppDelegate.m` / `AppDelegate.mm` ```objective-c - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [BTAppSwitch setReturnURLScheme:@"com.your-company-name.your-app-name.payments"]; // ADD THIS LINE + [BTAppContextSwitcher setReturnURLScheme:@"com.your-company-name.your-app-name.payments"]; // ADD THIS LINE return YES; } ``` #### Android specific -Note: Only complete these steps if using React Native versions < 0.60, autolinking will do these steps automatically. +Add in your `MainActivity.java`: +``` + import tech.power.RNBraintreeDropIn.RNBraintreeDropInModule; + + @Override + protected void onCreate(Bundle savedInstanceState) { + // ... + RNBraintreeDropInModule.initDropInClient(this); + } +``` + +Note: Only complete the next steps if using React Native versions < 0.60, autolinking will do these steps automatically. Add in your `app/build.gradle`: @@ -182,7 +193,7 @@ In your `AppDelegate.m`: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... - [BTAppSwitch setReturnURLScheme:self.paymentsURLScheme]; + [BTAppContextSwitcher setReturnURLScheme:self.paymentsURLScheme]; ... } @@ -191,7 +202,7 @@ In your `AppDelegate.m`: options:(NSDictionary *)options { if ([url.scheme localizedCaseInsensitiveCompare:self.paymentsURLScheme] == NSOrderedSame) { - return [BTAppSwitch handleOpenURL:url options:options]; + return [BTAppContextSwitcher handleOpenURL:url]; } return [RCTLinkingManager application:application openURL:url options:options]; @@ -210,13 +221,13 @@ import Braintree func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { ... - BTAppSwitch.setReturnURLScheme(self.paymentsURLScheme) + BTAppContextSwitcher.setReturnURLScheme(self.paymentsURLScheme) ... } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { if let scheme = url.scheme, scheme.localizedCaseInsensitiveCompare(self.paymentsURLScheme) == .orderedSame { - return BTAppSwitch.handleOpen(url, options: options) + return BTAppContextSwitcher.handleOpen(url) } return RCTLinkingManager.application(app, open: url, options: options) } @@ -309,20 +320,14 @@ BraintreeDropIn.show({ }) ``` -### Potential Fix - -This version contains updated BraintreeDropIn 5.+. BraintreeDropIn 5.0.2 contains some security fixes that resolves Play store rejection fix. Read more - - -Unsafe implementation of the HostnameVerifier interface - vulnerability ( https://github.com/braintree/braintree-android-drop-in/issues/208 ) - [1]: http://guides.cocoapods.org/using/using-cocoapods.html [2]: https://github.com/braintree/braintree-ios-drop-in [3]: https://github.com/braintree/braintree-android-drop-in -[4]: https://developers.braintreepayments.com/guides/client-sdk/setup/android/v2#browser-switch-setup +[4]: https://developers.braintreepayments.com/guides/client-sdk/setup/android/v4#browser-switch-setup [5]: ./index.js.flow -[6]: https://developers.braintreepayments.com/guides/apple-pay/configuration/ios/v4 +[6]: https://developers.braintreepayments.com/guides/apple-pay/configuration/ios/v5 [7]: https://articles.braintreepayments.com/guides/payment-methods/apple-pay#compatibility [8]: https://developers.braintreepayments.com/guides/apple-pay/overview [9]: https://developers.braintreepayments.com/guides/google-pay/overview -[10]: https://developers.braintreepayments.com/guides/paypal/overview/ios/v4 +[10]: https://developers.braintreepayments.com/guides/paypal/overview/ios/v5 [11]: https://developers.braintreepayments.com/guides/venmo/overview From 833b82c25bb0cbc1b349aacf528d86de1f23af7d Mon Sep 17 00:00:00 2001 From: Mihai Date: Fri, 6 Jan 2023 17:35:55 +0200 Subject: [PATCH 15/24] feat: add fetchMostRecentPaymentMethod method --- README.md | 12 ++ .../RNBraintreeDropInModule.java | 106 ++++++++++-------- index.js.flow | 1 + ios/RNBraintreeDropIn.m | 15 +++ 4 files changed, 86 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index fa4062e..2ced92d 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,18 @@ BraintreeDropIn.show({ }); ``` +### Fetch more recent payment method + +```javascript +import BraintreeDropIn from 'react-native-braintree-dropin-ui'; + +BraintreeDropIn.fetchMostRecentPaymentMethod(clientToken) +.then(result => console.log(result)) +.catch((error) => { + // Handle error +}); +``` + ### Custom Fonts ``` BraintreeDropIn.show({ diff --git a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java index 00697f1..e59b8f7 100644 --- a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java +++ b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java @@ -29,11 +29,7 @@ import java.util.Objects; public class RNBraintreeDropInModule extends ReactContextBaseJavaModule { - - private Promise mPromise; - private boolean isVerifyingThreeDSecure = false; - private static DropInClient dropInClient = null; private static String clientToken = null; @@ -109,83 +105,97 @@ public void show(final ReadableMap options, final Promise promise) { dropInRequest.setPayPalDisabled(!options.hasKey("payPal") || !options.getBoolean("payPal")); - mPromise = promise; - clientToken = options.getString("clientToken"); if (dropInClient == null) { - mPromise.reject( + promise.reject( "DROP_IN_CLIENT_UNINITIALIZED", "Did you forget to call RNBraintreeDropInModule.initDropInClient(this) in MainActivity.onCreate?" ); - mPromise = null; return; } - dropInClient.setListener(mDropInListener); - dropInClient.launchDropIn(dropInRequest); - } - - private final DropInListener mDropInListener = new DropInListener() { - @Override - public void onDropInSuccess(@NonNull DropInResult dropInResult) { - if (mPromise == null) { - return; + dropInClient.setListener(new DropInListener() { + @Override + public void onDropInSuccess(@NonNull DropInResult dropInResult) { + PaymentMethodNonce paymentMethodNonce = dropInResult.getPaymentMethodNonce(); + + if (isVerifyingThreeDSecure && paymentMethodNonce instanceof CardNonce) { + CardNonce cardNonce = (CardNonce) paymentMethodNonce; + ThreeDSecureInfo threeDSecureInfo = cardNonce.getThreeDSecureInfo(); + if (!threeDSecureInfo.isLiabilityShiftPossible()) { + promise.reject("3DSECURE_NOT_ABLE_TO_SHIFT_LIABILITY", "3D Secure liability cannot be shifted"); + } else if (!threeDSecureInfo.isLiabilityShifted()) { + promise.reject("3DSECURE_LIABILITY_NOT_SHIFTED", "3D Secure liability was not shifted"); + } else { + resolvePayment(dropInResult, promise); + } + } else { + resolvePayment(dropInResult, promise); + } } - PaymentMethodNonce paymentMethodNonce = dropInResult.getPaymentMethodNonce(); - String deviceData = dropInResult.getDeviceData(); - - if (isVerifyingThreeDSecure && paymentMethodNonce instanceof CardNonce) { - CardNonce cardNonce = (CardNonce) paymentMethodNonce; - ThreeDSecureInfo threeDSecureInfo = cardNonce.getThreeDSecureInfo(); - if (!threeDSecureInfo.isLiabilityShiftPossible()) { - mPromise.reject("3DSECURE_NOT_ABLE_TO_SHIFT_LIABILITY", "3D Secure liability cannot be shifted"); - } else if (!threeDSecureInfo.isLiabilityShifted()) { - mPromise.reject("3DSECURE_LIABILITY_NOT_SHIFTED", "3D Secure liability was not shifted"); + + @Override + public void onDropInFailure(@NonNull Exception exception) { + if (exception instanceof UserCanceledException) { + promise.reject("USER_CANCELLATION", "The user cancelled"); } else { - resolvePayment(dropInResult, deviceData); + promise.reject(exception.getMessage(), exception.getMessage()); } - } else { - resolvePayment(dropInResult, deviceData); } + }); + dropInClient.launchDropIn(dropInRequest); + } + + @ReactMethod + public void fetchMostRecentPaymentMethod(final String clientToken, final Promise promise) { + FragmentActivity currentActivity = (FragmentActivity) getCurrentActivity(); - mPromise = null; + if (currentActivity == null) { + promise.reject("NO_ACTIVITY", "There is no current activity"); + return; } - @Override - public void onDropInFailure(@NonNull Exception exception) { - if (mPromise == null) { - return; - } + if (dropInClient == null) { + promise.reject( + "DROP_IN_CLIENT_UNINITIALIZED", + "Did you forget to call RNBraintreeDropInModule.initDropInClient(this) in MainActivity.onCreate?" + ); + return; + } - if (exception instanceof UserCanceledException) { - mPromise.reject("USER_CANCELLATION", "The user cancelled"); + RNBraintreeDropInModule.clientToken = clientToken; + + dropInClient.fetchMostRecentPaymentMethod(currentActivity, (dropInResult, error) -> { + if (error != null) { + promise.reject(error.getMessage(), error.getMessage()); + } else if (dropInResult == null) { + promise.reject("NO_DROP_IN_RESULT", "dropInResult is null"); } else { - mPromise.reject(exception.getMessage(), exception.getMessage()); + resolvePayment(dropInResult, promise); } + }); + } - mPromise = null; - } - }; - - private void resolvePayment(DropInResult dropInResult, String deviceData) { + private void resolvePayment(DropInResult dropInResult, Promise promise) { + String deviceData = dropInResult.getDeviceData(); PaymentMethodNonce paymentMethodNonce = dropInResult.getPaymentMethodNonce(); WritableMap jsResult = Arguments.createMap(); if (paymentMethodNonce == null) { - mPromise.reject("NO_PAYMENT_METHOD_NONCE", "Payment method nonce is missing"); + promise.reject("NO_PAYMENT_METHOD_NONCE", "Payment method nonce is missing"); return; } Activity currentActivity = getCurrentActivity(); if (currentActivity == null) { - mPromise.reject("NO_ACTIVITY", "There is no current activity"); + promise.reject("NO_ACTIVITY", "There is no current activity"); return; } DropInPaymentMethod dropInPaymentMethod = dropInResult.getPaymentMethodType(); if (dropInPaymentMethod == null) { - mPromise.reject("NO_PAYMENT_METHOD", "There is no payment method"); + promise.reject("NO_PAYMENT_METHOD", "There is no payment method"); return; } @@ -195,7 +205,7 @@ private void resolvePayment(DropInResult dropInResult, String deviceData) { jsResult.putBoolean("isDefault", paymentMethodNonce.isDefault()); jsResult.putString("deviceData", deviceData); - mPromise.resolve(jsResult); + promise.resolve(jsResult); } @NonNull diff --git a/index.js.flow b/index.js.flow index 18fdc4a..579e6f4 100644 --- a/index.js.flow +++ b/index.js.flow @@ -29,4 +29,5 @@ type ShowResult = {| declare module.exports: { show: (options: ShowOptions) => Promise, + fetchMostRecentPaymentMethod: (clientToken: string) => Promise, }; diff --git a/ios/RNBraintreeDropIn.m b/ios/RNBraintreeDropIn.m index 068f203..17ea5a4 100644 --- a/ios/RNBraintreeDropIn.m +++ b/ios/RNBraintreeDropIn.m @@ -146,6 +146,21 @@ - (dispatch_queue_t)methodQueue } } +RCT_EXPORT_METHOD(fetchMostRecentPaymentMethod:(NSString*)clientToken + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [BTDropInResult mostRecentPaymentMethodForClientToken:clientToken completion:^(BTDropInResult * _Nullable result, NSError * _Nullable error) { + if (error != nil) { + reject(error.localizedDescription, error.localizedDescription, error); + } else if (result.canceled) { + reject(@"USER_CANCELLATION", @"The user cancelled", nil); + } else { + [[self class] resolvePayment:result deviceData:result.deviceData resolver:resolve]; + } + }]; +} + - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment handler:(nonnull void (^)(PKPaymentAuthorizationResult * _Nonnull))completion From af56ae5bfaeba158401c8dd798fceac5c8f8c39c Mon Sep 17 00:00:00 2001 From: Mihai Date: Fri, 6 Jan 2023 18:25:42 +0200 Subject: [PATCH 16/24] feat: add tokenizeCard method --- README.md | 18 +++++++ .../RNBraintreeDropInModule.java | 49 +++++++++++++++++++ index.js.flow | 9 ++++ ios/RNBraintreeDropIn.m | 35 +++++++++++++ 4 files changed, 111 insertions(+) diff --git a/README.md b/README.md index 2ced92d..6b3a01e 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,24 @@ BraintreeDropIn.fetchMostRecentPaymentMethod(clientToken) }); ``` +### Tokenize card + +```javascript +import BraintreeDropIn from 'react-native-braintree-dropin-ui'; + +BraintreeDropIn.tokenizeCard(clientToken, { + number: '4111111111111111', + expirationMonth: '10', + expirationYear: '23', + cvv: '123', + postalCode: '12345', +}) +.then(cardNonce => console.log(cardNonce)) +.catch((error) => { + // Handle error +}); +``` + ### Custom Fonts ``` BraintreeDropIn.show({ diff --git a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java index e59b8f7..bcce697 100644 --- a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java +++ b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java @@ -5,6 +5,9 @@ import androidx.annotation.NonNull; import androidx.fragment.app.FragmentActivity; +import com.braintreepayments.api.BraintreeClient; +import com.braintreepayments.api.Card; +import com.braintreepayments.api.CardClient; import com.braintreepayments.api.DropInClient; import com.braintreepayments.api.DropInListener; import com.braintreepayments.api.DropInPaymentMethod; @@ -176,6 +179,52 @@ public void fetchMostRecentPaymentMethod(final String clientToken, final Promise }); } + @ReactMethod + public void tokenizeCard(final String clientToken, final ReadableMap cardInfo, final Promise promise) { + if (clientToken == null) { + promise.reject("NO_CLIENT_TOKEN", "You must provide a client token"); + return; + } + + if ( + !cardInfo.hasKey("number") || + !cardInfo.hasKey("expirationMonth") || + !cardInfo.hasKey("expirationYear") || + !cardInfo.hasKey("cvv") || + !cardInfo.hasKey("postalCode") + ) { + promise.reject("INVALID_CARD_INFO", "Invalid card info"); + return; + } + + Activity currentActivity = getCurrentActivity(); + + if (currentActivity == null) { + promise.reject("NO_ACTIVITY", "There is no current activity"); + return; + } + + BraintreeClient braintreeClient = new BraintreeClient(getCurrentActivity(), clientToken); + CardClient cardClient = new CardClient(braintreeClient); + + Card card = new Card(); + card.setNumber(cardInfo.getString("number")); + card.setExpirationMonth(cardInfo.getString("expirationMonth")); + card.setExpirationYear(cardInfo.getString("expirationYear")); + card.setCvv(cardInfo.getString("cvv")); + card.setPostalCode(cardInfo.getString("postalCode")); + + cardClient.tokenize(card, (cardNonce, error) -> { + if (error != null) { + promise.reject(error.getMessage(), error.getMessage()); + } else if (cardNonce == null) { + promise.reject("NO_CARD_NONCE", "Card nonce is null"); + } else { + promise.resolve(cardNonce.getString()); + } + }); + } + private void resolvePayment(DropInResult dropInResult, Promise promise) { String deviceData = dropInResult.getDeviceData(); PaymentMethodNonce paymentMethodNonce = dropInResult.getPaymentMethodNonce(); diff --git a/index.js.flow b/index.js.flow index 579e6f4..a46e7b5 100644 --- a/index.js.flow +++ b/index.js.flow @@ -19,6 +19,14 @@ type ShowOptions = {| boldFontFamily?: string, |}; +type CardInfo = {| + number: string, + expirationMonth: string, + expirationYear: string, + cvv: string, + postalCode: string, +|}; + type ShowResult = {| nonce: string, description: string, @@ -30,4 +38,5 @@ type ShowResult = {| declare module.exports: { show: (options: ShowOptions) => Promise, fetchMostRecentPaymentMethod: (clientToken: string) => Promise, + tokenizeCard: (clientToken: string, cardInfo: CardInfo) => Promise, }; diff --git a/ios/RNBraintreeDropIn.m b/ios/RNBraintreeDropIn.m index 17ea5a4..9380e1c 100644 --- a/ios/RNBraintreeDropIn.m +++ b/ios/RNBraintreeDropIn.m @@ -161,6 +161,41 @@ - (dispatch_queue_t)methodQueue }]; } +RCT_EXPORT_METHOD(tokenizeCard:(NSString*)clientToken + info:(NSDictionary*)cardInfo + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + NSString *number = cardInfo[@"number"]; + NSString *expirationMonth = cardInfo[@"expirationMonth"]; + NSString *expirationYear = cardInfo[@"expirationYear"]; + NSString *cvv = cardInfo[@"cvv"]; + NSString *postalCode = cardInfo[@"postalCode"]; + + if (!number || !expirationMonth || !expirationYear || !cvv || !postalCode) { + reject(@"INVALID_CARD_INFO", @"Invalid card info", nil); + return; + } + + BTAPIClient *braintreeClient = [[BTAPIClient alloc] initWithAuthorization:clientToken]; + BTCardClient *cardClient = [[BTCardClient alloc] initWithAPIClient:braintreeClient]; + BTCard *card = [[BTCard alloc] init]; + card.number = number; + card.expirationMonth = expirationMonth; + card.expirationYear = expirationYear; + card.cvv = cvv; + card.postalCode = postalCode; + + [cardClient tokenizeCard:card + completion:^(BTCardNonce *tokenizedCard, NSError *error) { + if (error == nil) { + resolve(tokenizedCard.nonce); + } else { + reject(@"TOKENIZE_ERROR", @"Error tokenizing card.", error); + } + }]; +} + - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment handler:(nonnull void (^)(PKPaymentAuthorizationResult * _Nonnull))completion From 88175e203847589895b78af3a4fc437b80f98ed8 Mon Sep 17 00:00:00 2001 From: Mihai Date: Thu, 2 Mar 2023 16:06:17 +0200 Subject: [PATCH 17/24] fix: fetchMostRecentPaymentMethod throwing an error instead of returning null When there are no payment methods fetchMostRecentPaymentMethod should resolve to null instead of throwing an error (on Android) or crashing the app (on iOS). --- .../power/RNBraintreeDropIn/RNBraintreeDropInModule.java | 2 +- ios/RNBraintreeDropIn.m | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java index bcce697..f875e36 100644 --- a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java +++ b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java @@ -232,7 +232,7 @@ private void resolvePayment(DropInResult dropInResult, Promise promise) { WritableMap jsResult = Arguments.createMap(); if (paymentMethodNonce == null) { - promise.reject("NO_PAYMENT_METHOD_NONCE", "Payment method nonce is missing"); + promise.resolve(null); return; } diff --git a/ios/RNBraintreeDropIn.m b/ios/RNBraintreeDropIn.m index 9380e1c..2380339 100644 --- a/ios/RNBraintreeDropIn.m +++ b/ios/RNBraintreeDropIn.m @@ -245,6 +245,11 @@ - (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewC + (void)resolvePayment:(BTDropInResult* _Nullable)result deviceData:(NSString * _Nonnull)deviceDataCollector resolver:(RCTPromiseResolveBlock _Nonnull)resolve { //NSLog(@"result = %@", result); + if (!result) { + resolve(nil); + return; + } + NSMutableDictionary* jsResult = [NSMutableDictionary new]; //NSLog(@"paymentMethod = %@", result.paymentMethod); From af0ee488dbafb1e6d434ba5e65874904da272b17 Mon Sep 17 00:00:00 2001 From: Mihai Date: Thu, 16 Mar 2023 12:33:49 +0200 Subject: [PATCH 18/24] chore: update Braintree dependencies --- RNBraintreeDropIn.podspec | 10 +++++----- android/build.gradle | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/RNBraintreeDropIn.podspec b/RNBraintreeDropIn.podspec index f4ecf61..936d583 100644 --- a/RNBraintreeDropIn.podspec +++ b/RNBraintreeDropIn.podspec @@ -14,9 +14,9 @@ Pod::Spec.new do |s| s.source_files = "ios/**/*.{h,m}" s.requires_arc = true s.dependency 'React' - s.dependency 'Braintree', '5.17.0' - s.dependency 'BraintreeDropIn', '9.7.0' - s.dependency 'Braintree/DataCollector', '5.17.0' - s.dependency 'Braintree/ApplePay', '5.17.0' - s.dependency 'Braintree/Venmo', '5.17.0' + s.dependency 'Braintree', '5.20.1' + s.dependency 'BraintreeDropIn', '9.8.1' + s.dependency 'Braintree/DataCollector', '5.20.1' + s.dependency 'Braintree/ApplePay', '5.20.1' + s.dependency 'Braintree/Venmo', '5.20.1' end diff --git a/android/build.gradle b/android/build.gradle index 6809b44..5eb3e24 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -32,7 +32,7 @@ repositories { } dependencies { - implementation 'com.braintreepayments.api:drop-in:6.6.0' + implementation 'com.braintreepayments.api:drop-in:6.8.1' implementation 'com.facebook.react:react-native:+' } From 7e4f2ce3f54cb7215226b4ada199e6b072e71a3b Mon Sep 17 00:00:00 2001 From: wgltony Date: Tue, 25 Apr 2023 11:38:47 -0400 Subject: [PATCH 19/24] Update RNBraintreeDropIn.m Update Applepay Code 17 --- ios/RNBraintreeDropIn.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/RNBraintreeDropIn.m b/ios/RNBraintreeDropIn.m index 2380339..07df5d3 100644 --- a/ios/RNBraintreeDropIn.m +++ b/ios/RNBraintreeDropIn.m @@ -128,7 +128,7 @@ - (dispatch_queue_t)methodQueue } else{ [[self class] resolvePayment:result deviceData:self.deviceDataCollector resolver:resolve]; } - } else if(result.paymentMethod == nil && (result.paymentMethodType == 16 || result.paymentMethodType == 18)){ //Apple Pay + } else if(result.paymentMethod == nil && (result.paymentMethodType == 16 || result.paymentMethodType == 17 || result.paymentMethodType == 18)){ //Apple Pay // UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; // [ctrl presentViewController:self.viewController animated:YES completion:nil]; UIViewController *rootViewController = RCTPresentedViewController(); From 02154246a318435a17b3806203707fd014af6735 Mon Sep 17 00:00:00 2001 From: wgltony Date: Tue, 25 Apr 2023 11:44:59 -0400 Subject: [PATCH 20/24] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e0ba1ff..7a5da3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-braintree-dropin-ui", - "version": "1.1.3", + "version": "1.1.5", "description": "> React Native integration of Braintree Drop-in IOS V4 ANDROID V2 (Apple Pay &Android Pay Enabled)", "main": "index.js", "dependencies": {}, From 59ab74aefaf475511491bfef30fb56674d23c26b Mon Sep 17 00:00:00 2001 From: Ryan Lin Xiang Date: Thu, 18 May 2023 14:56:46 +0200 Subject: [PATCH 21/24] removed link to setup Android browser switch --- README.md | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6b3a01e..e222dac 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ npm install react-native-braintree-dropin-ui --save ``` ## Configurate Payment Method(For ALL RN VERSIONS) -See Braintree's documentation, [Apple Pay][8], [Google Pay][9], [Paypal][10], [Venmo][11] +See Braintree's documentation, [Apple Pay][7], [Google Pay][8], [Paypal][9], [Venmo][10] Once you have finished setting up all the configurations, it will show in the dropin UI. @@ -80,7 +80,7 @@ pod install #### Apple Pay -The Drop-in will show Apple Pay as a payment option as long as you've completed the [Apple Pay integration][6] and the customer's [device and card type are supported][7]. +The Drop-in will show Apple Pay as a payment option as long as you've completed the [Apple Pay integration][5] and the customer's [device and card type are supported][6]. #### PayPal @@ -238,14 +238,9 @@ private var paymentsURLScheme: String { } ``` -##### Android - -Setup [browser switch][4]. - - ## Usage -For the API, see the [Flow typings][5]. +For the API, see the [Flow typings][4]. ### Basic @@ -353,11 +348,10 @@ BraintreeDropIn.show({ [1]: http://guides.cocoapods.org/using/using-cocoapods.html [2]: https://github.com/braintree/braintree-ios-drop-in [3]: https://github.com/braintree/braintree-android-drop-in -[4]: https://developers.braintreepayments.com/guides/client-sdk/setup/android/v4#browser-switch-setup -[5]: ./index.js.flow -[6]: https://developers.braintreepayments.com/guides/apple-pay/configuration/ios/v5 -[7]: https://articles.braintreepayments.com/guides/payment-methods/apple-pay#compatibility -[8]: https://developers.braintreepayments.com/guides/apple-pay/overview -[9]: https://developers.braintreepayments.com/guides/google-pay/overview -[10]: https://developers.braintreepayments.com/guides/paypal/overview/ios/v5 -[11]: https://developers.braintreepayments.com/guides/venmo/overview +[4]: ./index.js.flow +[5]: https://developers.braintreepayments.com/guides/apple-pay/configuration/ios/v5 +[6]: https://articles.braintreepayments.com/guides/payment-methods/apple-pay#compatibility +[7]: https://developers.braintreepayments.com/guides/apple-pay/overview +[8]: https://developers.braintreepayments.com/guides/google-pay/overview +[9]: https://developers.braintreepayments.com/guides/paypal/overview/ios/v5 +[10]: https://developers.braintreepayments.com/guides/venmo/overview From 30b6b8a3b7f73612c464b954e85f0422424ab47a Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Tue, 29 Aug 2023 16:19:54 -0400 Subject: [PATCH 22/24] Update Braintree SDK --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 5eb3e24..25d0106 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -32,7 +32,7 @@ repositories { } dependencies { - implementation 'com.braintreepayments.api:drop-in:6.8.1' + implementation 'com.braintreepayments.api:drop-in:6.11.0' implementation 'com.facebook.react:react-native:+' } From 0a8bb8d237abe6f9783efe7ee528ca5950014bba Mon Sep 17 00:00:00 2001 From: wgltony Date: Tue, 29 Aug 2023 17:08:49 -0400 Subject: [PATCH 23/24] version update --- RNBraintreeDropIn.podspec | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RNBraintreeDropIn.podspec b/RNBraintreeDropIn.podspec index 936d583..3bfda25 100644 --- a/RNBraintreeDropIn.podspec +++ b/RNBraintreeDropIn.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "RNBraintreeDropIn" - s.version = "1.1.3" + s.version = "1.1.6" s.summary = "RNBraintreeDropIn" s.description = <<-DESC RNBraintreeDropIn diff --git a/package.json b/package.json index 7a5da3c..8e6b01d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-braintree-dropin-ui", - "version": "1.1.5", + "version": "1.1.6", "description": "> React Native integration of Braintree Drop-in IOS V4 ANDROID V2 (Apple Pay &Android Pay Enabled)", "main": "index.js", "dependencies": {}, From 6dc32cd4b3ec41ce08869fb056cfef59f9cf01e5 Mon Sep 17 00:00:00 2001 From: wgltony Date: Thu, 31 Aug 2023 20:42:17 -0400 Subject: [PATCH 24/24] readme update, disable credit card bug fixed --- README.md | 8 ++++++++ .../power/RNBraintreeDropIn/RNBraintreeDropInModule.java | 2 +- package.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e222dac..cd30b69 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ IOS ```bash npm install react-native-braintree-dropin-ui --save +OR + +yarn add react-native-braintree-dropin-ui + cd ./ios pod install ``` @@ -22,6 +26,10 @@ pod install Android ```bash npm install react-native-braintree-dropin-ui --save + +OR + +yarn add react-native-braintree-dropin-u ``` ## Configurate Payment Method(For ALL RN VERSIONS) diff --git a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java index f875e36..9d8a9dd 100644 --- a/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java +++ b/android/src/main/java/tech/power/RNBraintreeDropIn/RNBraintreeDropInModule.java @@ -88,7 +88,7 @@ public void show(final ReadableMap options, final Promise promise) { } if(options.hasKey("cardDisabled")) { - dropInRequest.setCardDisabled(true); + dropInRequest.setCardDisabled(options.getBoolean("cardDisabled")); } if (options.hasKey("threeDSecure")) { diff --git a/package.json b/package.json index 8e6b01d..318b936 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-braintree-dropin-ui", - "version": "1.1.6", + "version": "1.1.7", "description": "> React Native integration of Braintree Drop-in IOS V4 ANDROID V2 (Apple Pay &Android Pay Enabled)", "main": "index.js", "dependencies": {},