Changes to support 4.45.0 version of Braintree.#22
Changes to support 4.45.0 version of Braintree.#22swaroop-srinivasan wants to merge 3 commits intomasterfrom
Conversation
- Update all Braintree dependencies to 4.45.0 (card, data-collector, paypal, venmo) - Fix BraintreeClient initialization to use FragmentActivity (required by v4.9.0+) - Update getDeviceData and venmoRequestMultiUseAgreement methods - Includes new SSL certificates for March 2026 expiration
📝 WalkthroughWalkthroughUpgraded build tooling (AGP 7.4.2) and Braintree dependencies (4.45.0). Refactored VenmoClient and dataCollector lifecycle management on Android and iOS to initialize after BraintreeClient with improved null-checking. Added Jar packaging task and bumped version to 1.0.18. Changes
Sequence Diagram(s)sequenceDiagram
participant App
participant BraintreeClient
participant VenmoClient
participant Activity
participant Callback
App->>BraintreeClient: Initialize with token & FragmentActivity
activate BraintreeClient
BraintreeClient-->>App: BraintreeClient ready
deactivate BraintreeClient
App->>VenmoClient: Initialize after BraintreeClient
activate VenmoClient
VenmoClient->>VenmoClient: Wire VenmoListener
VenmoClient-->>App: VenmoClient ready
deactivate VenmoClient
App->>Activity: Check getCurrentActivity (non-null?)
alt Activity Available
App->>VenmoClient: tokenizeVenmoAccount
activate VenmoClient
VenmoClient->>Activity: Request on UI thread
Activity-->>VenmoClient: Token result
VenmoClient->>Callback: Invoke success callback
deactivate VenmoClient
else Activity Null
App->>Callback: Invoke error callback
end
sequenceDiagram
participant App
participant BraintreeClient
participant DataCollector
participant Callback
App->>App: setupWithClientToken(token)
activate App
App->>BraintreeClient: Initialize with token
activate BraintreeClient
BraintreeClient-->>App: BraintreeClient ready
deactivate BraintreeClient
alt braintreeClient Non-Nil
App->>DataCollector: Initialize
activate DataCollector
DataCollector-->>App: DataCollector ready
deactivate DataCollector
App->>Callback: Invoke success callback
else braintreeClient Nil
App->>Callback: Invoke failure (NO)
end
deactivate App
App->>App: getDeviceData(selector)
alt Selector "card" or "both"
App->>DataCollector: collectDeviceData (async)
activate DataCollector
DataCollector-->>App: completion block with data
deactivate DataCollector
App->>Callback: Callback with collected data
else Selector "paypal" or "venmo"
App->>DataCollector: collectPayPalDeviceData
activate DataCollector
DataCollector-->>App: device data
deactivate DataCollector
App->>Callback: Callback with device data
else Invalid Selector
App->>Callback: Callback with error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
> [!CAUTION]
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)android/src/main/java/com/pw/droplet/braintree/Braintree.java (3)
239-247:⚠️ Potential issue | 🟠 MajorPre-existing bug: both
errorCallbackandsuccessCallbackcan fire if botherrorandcardNonceare non-null.React Native callbacks can only be invoked once; a second invocation will crash the bridge. The
ifon line 243 should beelse ifto prevent double invocation.Proposed fix
public void onResult(`@androidx.annotation.Nullable` CardNonce cardNonce, `@androidx.annotation.Nullable` Exception error) { if (error != null) { errorCallback.invoke(error.toString()); - } - if (cardNonce != null) { + } else if (cardNonce != null) { successCallback.invoke(cardNonce.getString()); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/src/main/java/com/pw/droplet/braintree/Braintree.java` around lines 239 - 247, In the onResult callback of the Braintree CardNonce flow (method onResult), prevent both errorCallback.invoke(...) and successCallback.invoke(...) from firing when both error and cardNonce are non-null by changing the second conditional to an else if; update the logic around errorCallback and successCallback so that if error != null you invoke only errorCallback and skip invoking successCallback, referencing the existing onResult(CardNonce cardNonce, Exception error) implementation and the errorCallback/successCallback identifiers.
293-301:⚠️ Potential issue | 🟠 Major
shouldVaultparameter is accepted but ignored — hardcoded tofalse.Line 301 sets
request.setShouldVault(false)regardless of theshouldVaultargument passed by the caller. This appears to be a bug.Proposed fix
- request.setShouldVault(false); + request.setShouldVault(shouldVault);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/src/main/java/com/pw/droplet/braintree/Braintree.java` around lines 293 - 301, In venmoRequestMultiUseAgreement in Braintree.java the incoming shouldVault parameter is ignored because request.setShouldVault(false) is hardcoded; change this to use the method parameter by calling request.setShouldVault(shouldVault) (and remove the hardcoded false) so the VenmoRequest respects the caller's intent; ensure you update any related logic that depended on the previous hardcoded value if present.
316-342:⚠️ Potential issue | 🟠 MajorReflection on private SDK fields is fragile and appears redundant; simplify the initialization pattern.
The code uses reflection to access private fields (
braintreeClientandvenmoApi) that are not part of VenmoClient's public API in SDK 4.45.0. While these fields do currently exist, they areprivate finalimplementation details that may change names, types, or be obfuscated in future SDK versions, causing runtimeNoSuchFieldException.More critically, this reflection pattern is unnecessary. The
venmoClientis already fully initialized and configured insetup()(lines 147–158) with a proper listener. ThetokenizeVenmoAccount()method then creates a redundant temporary client and copies its internal state via reflection—an indirect and brittle approach.Refactor to use the already-initialized
venmoClientdirectly:
- Remove the temporary client creation and reflection code (lines 325–336)
- Call
this.venmoClient.tokenizeVenmoAccount(activity, request)directly- If re-initialization is required for some reason, add a public reset method to VenmoClient instead of relying on private field manipulation
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/src/main/java/com/pw/droplet/braintree/Braintree.java` around lines 316 - 342, The tokenizeVenmoAccount method currently creates a temporary VenmoClient and uses reflection to copy private fields (braintreeClient, venmoApi) into this.venmoClient which is fragile and unnecessary; remove the temporary client and all reflection (Field/getDeclaredField/setAccessible/set) and simply use the already-initialized this.venmoClient (set up in setup()) to call this.venmoClient.tokenizeVenmoAccount(activity, request), keeping the existing try/catch and invokeVenmoErrorCallback(error) behavior; if you ever need to reinitialize, add a proper public reset/init path on VenmoClient rather than manipulating private fields.android/build.gradle (2)
17-27:⚠️ Potential issue | 🟠 MajorSecurity: insecure HTTP protocol and hardcoded credentials in source control.
Two concerns in this repository block:
Line 19:
allowInsecureProtocol = trueenables dependency resolution over plain HTTP, which is vulnerable to MITM attacks. Consider migratingnexus.skillz.comto HTTPS.Line 25: A password is hardcoded in the source. Even if this is a "read-only" token for Cardinal Commerce, credentials should be externalized to environment variables or a
gradle.propertiesfile excluded from version control.Additionally,
cardinalcommerce.bintray.com(line 22) was shut down in 2021. If Cardinal Commerce artifacts are still needed, they should be sourced from an alternative repository.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/build.gradle` around lines 17 - 27, Remove the insecure HTTP maven entry and hardcoded credentials: replace the maven block that uses url 'http://nexus.skillz.com' and allowInsecureProtocol = true with an HTTPS URL (or remove it if unavailable), and remove the credentials { username 'braintree-team-sdk@cardinalcommerce' password '...'} block from the maven { url "https://cardinalcommerce.bintray.com/android" } entry; instead load username/password from an external source (environment variables or gradle.properties) and reference them (e.g., use properties like cardinalUser/cardinalPass) in the credentials block. Also update or remove the cardinalcommerce.bintray.com repository reference (since bintray was shut down) and point to the current Cardinal Commerce artifact host or an alternative repository.
30-40:⚠️ Potential issue | 🟠 Major
compileSdkVersion 28is incompatible with Braintree SDK 4.45.0 andtargetSdkVersion 28violates current Play Store requirements.Braintree Android SDK 4.45.0 is built with
compileSdkVersion = 34and requirescompileSdk 34or higher. Additionally, Google Play Store requires new apps and updates (as of February 2026) to target API 35, and existing apps must target API 34+. The current configuration withcompileSdkVersion 28andtargetSdkVersion 28will cause build failures and is below all modern Play Store requirements.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/build.gradle` around lines 30 - 40, The android block is using compileSdkVersion 28 and defaultConfig.targetSdkVersion 28 which is incompatible with Braintree SDK 4.45.0 and Play Store requirements; update compileSdkVersion in the android { ... } block to at least 34 (preferably 35 to meet new-app requirements) and set defaultConfig.targetSdkVersion to 35, then rebuild and resolve any SDK/API-level compile errors; look for the compileSdkVersion and defaultConfig { targetSdkVersion ... } entries to change and verify Gradle/AGP compatibility after bumping the SDK versions.ios/RCTBraintree/RCTBraintree.m (1)
26-33:⚠️ Potential issue | 🟡 Minor
sharedInstancesingleton is unused and should be removed or clearly documented as public API.This method is defined but has zero callers within the repository. If it's not part of the public API contract, remove it. If it is public API, document why it exists alongside React Native's module lifecycle, since calling it would return a separate, potentially uninitialized instance from the one RN manages.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ios/RCTBraintree/RCTBraintree.m` around lines 26 - 33, The added +sharedInstance on RCTBraintree is currently unused; either remove it or make it a deliberate public API and wire it to the RN-managed module instance. If you intend it to be internal/unused, delete the sharedInstance method and any references; if you intend it as public, add documentation comment above +sharedInstance explaining it returns the singleton backing the React Native module lifecycle and update the RN module bootstrap to use [RCTBraintree sharedInstance] (or rename the RN init path to call sharedInstance) so the singleton and RN-managed instance are the same; ensure method name is unique (RCTBraintree/sharedInstance) and consider marking init as NS_DESIGNATED_INITIALIZER or making -init unavailable to prevent accidental separate instances.
🧹 Nitpick comments (3)
android/src/main/java/com/pw/droplet/braintree/Braintree.java (2)
140-158:setup()unconditionally overwritesvenmoClient, making the constructor-provided client transient.Line 147 directly assigns
this.venmoClient = new VenmoClient(...), bypassing the null-guard insetVenmoClient()(line 75). This means theVenmoClientpassed into the constructor is always replaced oncesetup()is called. If this is intentional (constructor-provided client is only a placeholder before token is available), consider documenting that contract. If not, use the same guarded setter pattern here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/src/main/java/com/pw/droplet/braintree/Braintree.java` around lines 140 - 158, The setup() method is unconditionally overwriting the instance venmoClient (this.venmoClient = new VenmoClient(...)), which nullifies any VenmoClient passed into the class constructor; change setup() to use the same guarded setter pattern as setVenmoClient() (or check for an existing non-null this.venmoClient before assigning) so constructor-injected clients are preserved unless explicitly intended to be replaced; update setup() to call setVenmoClient(new VenmoClient(this.braintreeClient)) or wrap the assignment with a null-check to maintain the original contract and attach the VenmoListener after using the setter.
91-93: UnusedActivityparameter inupdateVenmoClient.The
activityparameter is accepted but never used. Since this is a public method in a published library, either remove it (if no external code depends on this signature) or document why it's part of the signature (e.g., for future use or interface conformance).Proposed fix
- public void updateVenmoClient(Activity activity, VenmoClient venmoClient) { + public void updateVenmoClient(VenmoClient venmoClient) { setVenmoClient(venmoClient); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/src/main/java/com/pw/droplet/braintree/Braintree.java` around lines 91 - 93, The public method Braintree.updateVenmoClient currently accepts an unused Activity parameter; either remove the Activity parameter from updateVenmoClient (and update all callers to call updateVenmoClient(VenmoClient) since the method only calls setVenmoClient(venmoClient)), or keep the parameter for API compatibility but document it in the method Javadoc and mark it as intentionally unused (e.g., add a brief comment and `@SuppressWarnings`("unused") or similar) so callers and linters understand it is retained on purpose.android/build.gradle (1)
67-77: JAR packaging excludes Android resources — verify this is intentional.The
createJartask packages only compiled Java classes, excludingBuildConfigandRclasses. This means no Android resources, manifest entries, or ProGuard rules are included. For a library that includes Android dependencies (Braintree SDK, React Native bridge), an AAR is typically the correct distribution format. If consumers need resources or manifest merging, they'll need the AAR instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@android/build.gradle` around lines 67 - 77, The createJar task currently (createJar, archiveBaseName, archiveVersion) only packages compiled classes and explicitly excludes BuildConfig/R which omits Android resources, manifests and ProGuard rules; replace or augment this with an AAR-based distribution: convert the module to produce an AAR (use the Android library plugin / assembleRelease or bundleReleaseAar task) or add a task that packages the library as an AAR so resources, AndroidManifest entries and ProGuard consumer rules are included, and update any publishing artifacts to reference the generated .aar instead of the .jar.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@android/build.gradle`:
- Around line 56-65: The build fails because the dependency line compileOnly
'com.facebook.react:react-native:0.61.5' references a React Native
artifact/version not available on Maven Central; update the react-native
dependency on the compileOnly line (the exact symbol:
com.facebook.react:react-native:0.61.5) to a version that actually exists on
Maven Central or remove the artifact version and rely on the host app providing
React Native (i.e., keep compileOnly but point to a valid published version like
one verified on Maven Central or change to the project's correct provided
coordinate) so the dependency can be resolved during Gradle sync.
In `@ios/RCTBraintree/RCTBraintree.m`:
- Around line 252-256: Update the collectDeviceData completion block in
RCTBraintree.m to match BTDataCollector's signature and handle errors: change
the block passed to [self.dataCollector collectDeviceData:...] to accept
(NSString * _Nullable deviceData, NSError * _Nullable error), check error and
return callback(@[errorObj]) or callback(@[[NSNull null], deviceData])
accordingly, and ensure you create a proper JS/React Native error object when
invoking the existing callback so errors are not ignored.
- Around line 257-259: The code is using PPDataCollector.collectPayPalDeviceData
while importing BTDataCollector.h, causing an SDK version mismatch; decide which
Braintree SDK version to target and make the imports/usage consistent: if
targeting v6+, replace PPDataCollector usage with the v6 BTDataCollector API
(e.g., use BTDataCollector methods to collect PayPal/Venmo device data and
update any callback handling in the method that checks dataSelector), otherwise
if targeting v5 remove the BTDataCollector.h import and add `#import`
"BraintreePayPalDataCollector.h" and keep
PPDataCollector.collectPayPalDeviceData as used; update any related symbol names
(PPDataCollector, BTDataCollector, BraintreePayPalDataCollector) accordingly so
imports and method calls match the chosen SDK version.
---
Outside diff comments:
In `@android/build.gradle`:
- Around line 17-27: Remove the insecure HTTP maven entry and hardcoded
credentials: replace the maven block that uses url 'http://nexus.skillz.com' and
allowInsecureProtocol = true with an HTTPS URL (or remove it if unavailable),
and remove the credentials { username 'braintree-team-sdk@cardinalcommerce'
password '...'} block from the maven { url
"https://cardinalcommerce.bintray.com/android" } entry; instead load
username/password from an external source (environment variables or
gradle.properties) and reference them (e.g., use properties like
cardinalUser/cardinalPass) in the credentials block. Also update or remove the
cardinalcommerce.bintray.com repository reference (since bintray was shut down)
and point to the current Cardinal Commerce artifact host or an alternative
repository.
- Around line 30-40: The android block is using compileSdkVersion 28 and
defaultConfig.targetSdkVersion 28 which is incompatible with Braintree SDK
4.45.0 and Play Store requirements; update compileSdkVersion in the android {
... } block to at least 34 (preferably 35 to meet new-app requirements) and set
defaultConfig.targetSdkVersion to 35, then rebuild and resolve any SDK/API-level
compile errors; look for the compileSdkVersion and defaultConfig {
targetSdkVersion ... } entries to change and verify Gradle/AGP compatibility
after bumping the SDK versions.
In `@android/src/main/java/com/pw/droplet/braintree/Braintree.java`:
- Around line 239-247: In the onResult callback of the Braintree CardNonce flow
(method onResult), prevent both errorCallback.invoke(...) and
successCallback.invoke(...) from firing when both error and cardNonce are
non-null by changing the second conditional to an else if; update the logic
around errorCallback and successCallback so that if error != null you invoke
only errorCallback and skip invoking successCallback, referencing the existing
onResult(CardNonce cardNonce, Exception error) implementation and the
errorCallback/successCallback identifiers.
- Around line 293-301: In venmoRequestMultiUseAgreement in Braintree.java the
incoming shouldVault parameter is ignored because request.setShouldVault(false)
is hardcoded; change this to use the method parameter by calling
request.setShouldVault(shouldVault) (and remove the hardcoded false) so the
VenmoRequest respects the caller's intent; ensure you update any related logic
that depended on the previous hardcoded value if present.
- Around line 316-342: The tokenizeVenmoAccount method currently creates a
temporary VenmoClient and uses reflection to copy private fields
(braintreeClient, venmoApi) into this.venmoClient which is fragile and
unnecessary; remove the temporary client and all reflection
(Field/getDeclaredField/setAccessible/set) and simply use the
already-initialized this.venmoClient (set up in setup()) to call
this.venmoClient.tokenizeVenmoAccount(activity, request), keeping the existing
try/catch and invokeVenmoErrorCallback(error) behavior; if you ever need to
reinitialize, add a proper public reset/init path on VenmoClient rather than
manipulating private fields.
In `@ios/RCTBraintree/RCTBraintree.m`:
- Around line 26-33: The added +sharedInstance on RCTBraintree is currently
unused; either remove it or make it a deliberate public API and wire it to the
RN-managed module instance. If you intend it to be internal/unused, delete the
sharedInstance method and any references; if you intend it as public, add
documentation comment above +sharedInstance explaining it returns the singleton
backing the React Native module lifecycle and update the RN module bootstrap to
use [RCTBraintree sharedInstance] (or rename the RN init path to call
sharedInstance) so the singleton and RN-managed instance are the same; ensure
method name is unique (RCTBraintree/sharedInstance) and consider marking init as
NS_DESIGNATED_INITIALIZER or making -init unavailable to prevent accidental
separate instances.
---
Nitpick comments:
In `@android/build.gradle`:
- Around line 67-77: The createJar task currently (createJar, archiveBaseName,
archiveVersion) only packages compiled classes and explicitly excludes
BuildConfig/R which omits Android resources, manifests and ProGuard rules;
replace or augment this with an AAR-based distribution: convert the module to
produce an AAR (use the Android library plugin / assembleRelease or
bundleReleaseAar task) or add a task that packages the library as an AAR so
resources, AndroidManifest entries and ProGuard consumer rules are included, and
update any publishing artifacts to reference the generated .aar instead of the
.jar.
In `@android/src/main/java/com/pw/droplet/braintree/Braintree.java`:
- Around line 140-158: The setup() method is unconditionally overwriting the
instance venmoClient (this.venmoClient = new VenmoClient(...)), which nullifies
any VenmoClient passed into the class constructor; change setup() to use the
same guarded setter pattern as setVenmoClient() (or check for an existing
non-null this.venmoClient before assigning) so constructor-injected clients are
preserved unless explicitly intended to be replaced; update setup() to call
setVenmoClient(new VenmoClient(this.braintreeClient)) or wrap the assignment
with a null-check to maintain the original contract and attach the VenmoListener
after using the setter.
- Around line 91-93: The public method Braintree.updateVenmoClient currently
accepts an unused Activity parameter; either remove the Activity parameter from
updateVenmoClient (and update all callers to call updateVenmoClient(VenmoClient)
since the method only calls setVenmoClient(venmoClient)), or keep the parameter
for API compatibility but document it in the method Javadoc and mark it as
intentionally unused (e.g., add a brief comment and `@SuppressWarnings`("unused")
or similar) so callers and linters understand it is retained on purpose.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
android/build.gradleandroid/src/main/java/com/pw/droplet/braintree/Braintree.javaios/RCTBraintree/RCTBraintree.mpackage.json
| dependencies { | ||
| implementation 'com.facebook.react:react-native:0.61.5' | ||
| implementation 'com.braintreepayments.api:card:4.11.0' | ||
| implementation 'com.braintreepayments.api:data-collector:4.11.0' | ||
| implementation 'com.braintreepayments.api:paypal:4.11.0' | ||
| implementation 'com.braintreepayments.api:venmo:4.11.0' | ||
| compileOnly 'com.facebook.react:react-native:0.61.5' | ||
| implementation 'com.braintreepayments.api:card:4.45.0' | ||
| implementation 'com.braintreepayments.api:data-collector:4.45.0' | ||
| implementation 'com.braintreepayments.api:paypal:4.45.0' | ||
| implementation 'com.braintreepayments.api:venmo:4.45.0' | ||
|
|
||
| api 'com.squareup.okhttp3:okhttp:3.14.4' | ||
| implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6' | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify Braintree 4.45.0 artifacts exist on Maven Central
echo "Verifying Braintree v4 modular artifacts at version 4.45.0..."
for artifact in card data-collector paypal venmo; do
url="https://repo1.maven.org/maven2/com/braintreepayments/api/${artifact}/4.45.0/${artifact}-4.45.0.pom"
http_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
echo "${artifact}: HTTP $http_code"
done
echo ""
echo "Verifying react-native 0.61.5 availability..."
react_url="https://repo1.maven.org/maven2/com/facebook/react/react-native/0.61.5/react-native-0.61.5.pom"
react_code=$(curl -s -o /dev/null -w "%{http_code}" "$react_url")
echo "react-native 0.61.5: HTTP $react_code"Repository: skillz/react-native-braintree-custom-ui
Length of output: 294
React-native 0.61.5 does not exist on Maven Central and will cause build failures.
While the Braintree v4 modular artifacts (card, data-collector, paypal, venmo) at version 4.45.0 are correctly published and accessible, react-native:0.61.5 returns HTTP 404 and cannot be resolved. This is a blocking issue that will prevent the build from succeeding. Verify the correct available version for react-native or update to a version that exists on Maven Central.
The compileOnly configuration is appropriate for a library module, but it won't help if the dependency cannot be resolved in the first place.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@android/build.gradle` around lines 56 - 65, The build fails because the
dependency line compileOnly 'com.facebook.react:react-native:0.61.5' references
a React Native artifact/version not available on Maven Central; update the
react-native dependency on the compileOnly line (the exact symbol:
com.facebook.react:react-native:0.61.5) to a version that actually exists on
Maven Central or remove the artifact version and rely on the host app providing
React Native (i.e., keep compileOnly but point to a valid published version like
one verified on Maven Central or change to the project's correct provided
coordinate) so the dependency can be resolved during Gradle sync.
| // collectDeviceData is async - callback is called in completion block | ||
| [self.dataCollector collectDeviceData:^(NSString * _Nonnull collectedData) { | ||
| callback(@[[NSNull null], collectedData]); | ||
| }]; | ||
| return; // Early return - callback is called in completion block |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Braintree iOS SDK BTDataCollector collectDeviceData completion block signature documentation
💡 Result:
In the current Braintree iOS SDK, BTDataCollector exposes this completion-based API:
-
Swift
public func collectDeviceData(_ completion: `@escaping` (String?, Error?) -> Void)
The completion receives
(deviceData: String?, error: Error?). [1] -
Objective‑C (via
@objcbridging)- (void)collectDeviceData:(void (^ _Nonnull)(NSString * _Nullable deviceData, NSError * _Nullable error))completion;
(Same semantics: either
deviceDataorerroris set.) [1][2]
Sources: Braintree-generated reference docs for BTDataCollector.collectDeviceData(_:) [1] and the SDK implementation (BTDataCollector.swift) showing the exact Swift signature and closure parameters [2].
Fix collectDeviceData: completion block signature — currently incompatible with Braintree iOS SDK.
The completion block signature is incorrect. The Braintree iOS SDK's BTDataCollector defines:
- (void)collectDeviceData:(void (^ _Nonnull)(NSString * _Nullable deviceData, NSError * _Nullable error))completion;The code only accepts one parameter (NSString * _Nonnull collectedData), but the API expects two: (NSString * _Nullable, NSError * _Nullable). This causes a compilation error or silent API mismatch.
Additionally, update the block to handle both the deviceData and error parameters. Currently, errors are completely ignored and the callback is invoked unconditionally without checking for errors.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ios/RCTBraintree/RCTBraintree.m` around lines 252 - 256, Update the
collectDeviceData completion block in RCTBraintree.m to match BTDataCollector's
signature and handle errors: change the block passed to [self.dataCollector
collectDeviceData:...] to accept (NSString * _Nullable deviceData, NSError *
_Nullable error), check error and return callback(@[errorObj]) or
callback(@[[NSNull null], deviceData]) accordingly, and ensure you create a
proper JS/React Native error object when invoking the existing callback so
errors are not ignored.
| } else if ([dataSelector isEqualToString:@"paypal"] || [dataSelector isEqualToString:@"venmo"]) { | ||
| deviceData = [PPDataCollector collectPayPalDeviceData]; | ||
| NSString *deviceData = [PPDataCollector collectPayPalDeviceData]; | ||
| callback(@[[NSNull null], deviceData]); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f \( -name "Podfile" -o -name "*.podspec" \) | head -20Repository: skillz/react-native-braintree-custom-ui
Length of output: 65
🏁 Script executed:
rg "PPDataCollector|Braintree" -g "*.m" -g "*.h" -g "Podfile" -g "*.podspec" --max-count=50Repository: skillz/react-native-braintree-custom-ui
Length of output: 1213
🏁 Script executed:
rg "import.*Braintree|#import.*Braintree" -g "*.m" -g "*.h" -A 2Repository: skillz/react-native-braintree-custom-ui
Length of output: 617
🏁 Script executed:
find . -name "Podfile" -o -name "*.podspec" | xargs cat 2>/dev/nullRepository: skillz/react-native-braintree-custom-ui
Length of output: 65
🏁 Script executed:
rg "pod.*Braintree" -A 2 -B 2Repository: skillz/react-native-braintree-custom-ui
Length of output: 65
🏁 Script executed:
ls -laRepository: skillz/react-native-braintree-custom-ui
Length of output: 906
🏁 Script executed:
git log --oneline --all -- ios/RCTBraintree/ | head -20Repository: skillz/react-native-braintree-custom-ui
Length of output: 1237
🌐 Web query:
PPDataCollector collectPayPalDeviceData Braintree iOS SDK
💡 Result:
PPDataCollector.collectPayPalDeviceData() is part of Braintree iOS SDK v5’s PayPalDataCollector module. It returns a device_data string you should forward to your server and include as the device_data parameter when creating/verifying a transaction (helps Premium Fraud Management Tools; required for PayPal Vault flow). [1]
v5 (has PPDataCollector)
Install
- CocoaPods:
pod 'Braintree/PayPalDataCollector'[1]
Collect
let deviceData = PPDataCollector.collectPayPalDeviceData()
// send deviceData to your server and pass as `device_data`[1]
Sandbox note (v5.9.0+)
Use collectDeviceData(isSandbox: Bool) to collect the appropriate sandbox/production device data. [1]
v6+ (no PPDataCollector)
The PayPalDataCollector module was removed in favor of the unified BraintreeDataCollector module, so PPDataCollector.collectPayPalDeviceData() won’t exist on v6+. [2]
Sources: [1] PayPal/Braintree Premium Fraud Management Tools – iOS v5 client-side device data collection, [2] CocoaPods Braintree changelog (6.0.0-beta1 removal note.
🌐 Web query:
BTDataCollector Braintree iOS SDK PayPal device data
💡 Result:
In the Braintree iOS SDK, BTDataCollector is the client-side API you use to collect PayPal “device data” for fraud/risk evaluation, then send that string to your server as the device_data parameter with a transaction/verification request. (developer.paypal.com)
iOS SDK v6 (current pattern)
let apiClient = BTAPIClient(authorization: "<TOKENIZATION_KEY_OR_CLIENT_TOKEN>")
let dataCollector = BTDataCollector(apiClient: apiClient)
dataCollector.collectDeviceData { deviceData, error in
guard let deviceData else { return /* handle error */ }
// Send deviceData to your server (as `device_data`)
}Notes / version differences
- v6 migration:
PayPalDataCollectorwas removed “in favor of a single” collector module (BraintreeDataCollector/BTDataCollector). (developer.paypal.com) - v5 guidance:
BTDataCollector.collectDeviceData()replaced the oldercollectCardFraudData()method; call it as early as practical (app launch or start of checkout). (developer.paypal.com) - For some PayPal integrations (e.g., Vault flow), PayPal’s docs explicitly call out that collecting device data is required. (developer.paypal.com)
Citations:
- 1: https://developer.paypal.com/braintree/docs/guides/client-sdk/migration/ios/v6/?utm_source=openai
- 2: https://developer.paypal.com/braintree/docs/guides/client-sdk/migration/ios/v6/?utm_source=openai
- 3: https://developer.paypal.com/braintree/docs/guides/client-sdk/migration/ios/v6/?utm_source=openai
- 4: https://developer.paypal.com/braintree/docs/guides/client-sdk/migration/ios/v5?utm_source=openai
- 5: https://developer.paypal.com/braintree/docs/guides/premium-fraud-management-tools/client-side/ios/v5?utm_source=openai
Resolve the API version mismatch: PPDataCollector (v5) vs BTDataCollector imports (v6).
The code uses PPDataCollector.collectPayPalDeviceData(), which is the correct PayPal data collection API for Braintree iOS SDK v5. However, the header imports BTDataCollector.h, which is the v6+ unified data collector. This mismatch indicates an incomplete SDK migration. If the target is Braintree iOS SDK v6+, PPDataCollector no longer exists—replace it with BTDataCollector. If the target is v5, remove the BTDataCollector.h import and add #import "BraintreePayPalDataCollector.h" instead. Clarify the SDK version target and complete the migration consistently.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ios/RCTBraintree/RCTBraintree.m` around lines 257 - 259, The code is using
PPDataCollector.collectPayPalDeviceData while importing BTDataCollector.h,
causing an SDK version mismatch; decide which Braintree SDK version to target
and make the imports/usage consistent: if targeting v6+, replace PPDataCollector
usage with the v6 BTDataCollector API (e.g., use BTDataCollector methods to
collect PayPal/Venmo device data and update any callback handling in the method
that checks dataSelector), otherwise if targeting v5 remove the
BTDataCollector.h import and add `#import` "BraintreePayPalDataCollector.h" and
keep PPDataCollector.collectPayPalDeviceData as used; update any related symbol
names (PPDataCollector, BTDataCollector, BraintreePayPalDataCollector)
accordingly so imports and method calls match the chosen SDK version.
Summary by CodeRabbit
Bug Fixes
Chores