Skip to content

Changes to support 4.45.0 version of Braintree.#22

Open
swaroop-srinivasan wants to merge 3 commits intomasterfrom
ft/upgrade-4.45.0
Open

Changes to support 4.45.0 version of Braintree.#22
swaroop-srinivasan wants to merge 3 commits intomasterfrom
ft/upgrade-4.45.0

Conversation

@swaroop-srinivasan
Copy link

@swaroop-srinivasan swaroop-srinivasan commented Feb 23, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced error handling and null-safety checks for improved stability across Android and iOS platforms.
    • Improved asynchronous device data collection flow on iOS.
  • Chores

    • Updated core dependencies to newer compatible versions for enhanced stability and security.
    • Bumped package version to 1.0.18.
    • Improved platform tooling compatibility with modern build systems.

- 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
@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

📝 Walkthrough

Walkthrough

Upgraded 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

Cohort / File(s) Summary
Build Configuration
android/build.gradle, package.json
Upgraded Android Gradle Plugin to 7.4.2, updated Braintree and React Native dependencies, added namespace declaration, introduced Java 8 compile options, and added createJar task for distribution artifact packaging. Version bumped to 1.0.18.
Android Braintree Implementation
android/src/main/java/com/pw/droplet/braintree/Braintree.java
Refactored VenmoClient lifecycle to depend on BraintreeClient initialization; added guarded assignment in setVenmoClient, updated setup() to use FragmentActivity with VenmoListener, added null-checks in venmoRequestMultiUseAgreement and tokenizeVenmoAccount with explicit error callbacks, and improved error handling throughout.
iOS Braintree Implementation
ios/RCTBraintree/RCTBraintree.m
Added requiresMainQueueSetup and sharedInstance class methods; moved dataCollector initialization from init to setupWithClientToken (conditional on braintreeClient readiness); refactored getDeviceData to use async completion blocks and direct per-path callbacks instead of legacy combined-args pattern.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A jar is packed, and order's set—
BraintreeClient first, then Venmo yet!
With null checks guarding every way,
Our lifecycle flows more sound today. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly matches the main objective of the pull request: upgrading Braintree dependencies to version 4.45.0 and making corresponding code changes across Android and iOS platforms.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ft/upgrade-4.45.0

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟠 Major

Pre-existing bug: both errorCallback and successCallback can fire if both error and cardNonce are non-null.

React Native callbacks can only be invoked once; a second invocation will crash the bridge. The if on line 243 should be else if to 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

shouldVault parameter is accepted but ignored — hardcoded to false.

Line 301 sets request.setShouldVault(false) regardless of the shouldVault argument 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 | 🟠 Major

Reflection on private SDK fields is fragile and appears redundant; simplify the initialization pattern.

The code uses reflection to access private fields (braintreeClient and venmoApi) that are not part of VenmoClient's public API in SDK 4.45.0. While these fields do currently exist, they are private final implementation details that may change names, types, or be obfuscated in future SDK versions, causing runtime NoSuchFieldException.

More critically, this reflection pattern is unnecessary. The venmoClient is already fully initialized and configured in setup() (lines 147–158) with a proper listener. The tokenizeVenmoAccount() 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 venmoClient directly:

  • 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 | 🟠 Major

Security: insecure HTTP protocol and hardcoded credentials in source control.

Two concerns in this repository block:

  1. Line 19: allowInsecureProtocol = true enables dependency resolution over plain HTTP, which is vulnerable to MITM attacks. Consider migrating nexus.skillz.com to HTTPS.

  2. 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.properties file 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 28 is incompatible with Braintree SDK 4.45.0 and targetSdkVersion 28 violates current Play Store requirements.

Braintree Android SDK 4.45.0 is built with compileSdkVersion = 34 and requires compileSdk 34 or 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 with compileSdkVersion 28 and targetSdkVersion 28 will 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

sharedInstance singleton 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 overwrites venmoClient, making the constructor-provided client transient.

Line 147 directly assigns this.venmoClient = new VenmoClient(...), bypassing the null-guard in setVenmoClient() (line 75). This means the VenmoClient passed into the constructor is always replaced once setup() 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: Unused Activity parameter in updateVenmoClient.

The activity parameter 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 createJar task packages only compiled Java classes, excluding BuildConfig and R classes. 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

📥 Commits

Reviewing files that changed from the base of the PR and between d1f4976 and b1f6fc9.

📒 Files selected for processing (4)
  • android/build.gradle
  • android/src/main/java/com/pw/droplet/braintree/Braintree.java
  • ios/RCTBraintree/RCTBraintree.m
  • package.json

Comment on lines 56 to 65
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'
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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.

Comment on lines +252 to +256
// 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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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 @objc bridging)

    - (void)collectDeviceData:(void (^ _Nonnull)(NSString * _Nullable deviceData,
                                                NSError  * _Nullable error))completion;

    (Same semantics: either deviceData or error is 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.

Comment on lines 257 to +259
} else if ([dataSelector isEqualToString:@"paypal"] || [dataSelector isEqualToString:@"venmo"]) {
deviceData = [PPDataCollector collectPayPalDeviceData];
NSString *deviceData = [PPDataCollector collectPayPalDeviceData];
callback(@[[NSNull null], deviceData]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f \( -name "Podfile" -o -name "*.podspec" \) | head -20

Repository: 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=50

Repository: skillz/react-native-braintree-custom-ui

Length of output: 1213


🏁 Script executed:

rg "import.*Braintree|#import.*Braintree" -g "*.m" -g "*.h" -A 2

Repository: skillz/react-native-braintree-custom-ui

Length of output: 617


🏁 Script executed:

find . -name "Podfile" -o -name "*.podspec" | xargs cat 2>/dev/null

Repository: skillz/react-native-braintree-custom-ui

Length of output: 65


🏁 Script executed:

rg "pod.*Braintree" -A 2 -B 2

Repository: skillz/react-native-braintree-custom-ui

Length of output: 65


🏁 Script executed:

ls -la

Repository: skillz/react-native-braintree-custom-ui

Length of output: 906


🏁 Script executed:

git log --oneline --all -- ios/RCTBraintree/ | head -20

Repository: 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`)
}

(developer.paypal.com)

Notes / version differences

  • v6 migration: PayPalDataCollector was removed “in favor of a single” collector module (BraintreeDataCollector / BTDataCollector). (developer.paypal.com)
  • v5 guidance: BTDataCollector.collectDeviceData() replaced the older collectCardFraudData() 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:


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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant