Skip to content

feat: add proxy and custom HTTP agent support for Braintree#38

Open
lcmohsen wants to merge 10 commits intomainfrom
chore/proxy-support
Open

feat: add proxy and custom HTTP agent support for Braintree#38
lcmohsen wants to merge 10 commits intomainfrom
chore/proxy-support

Conversation

@lcmohsen
Copy link
Copy Markdown
Collaborator

@lcmohsen lcmohsen commented Apr 21, 2026

Summary

  • Adds outbound network transport customization for Braintree gateway calls.
  • Supports three configuration paths: direct customHttpAgent, proxyUrl-based proxy agent, and configurable standard httpAgent.
  • Wires the resolved agent into Braintree gateway initialization so all provider API calls use the configured transport.

Motivation

Some deployments require egress through corporate proxies or custom TLS/socket behavior. This change makes the provider usable in restricted network environments without forking provider code.

What’s Included

  • New options on provider config:
    • customHttpAgent: pass a prebuilt Node HTTP(S) agent.
    • proxyUrl: build an HTTPS proxy agent from URL (e.g. authenticated proxy).
    • httpAgent: define standard HTTPS agent settings (keepAlive, socket limits, timeout, TLS validation, etc.).
  • Agent resolution logic added in provider core:
    • Priority order: customHttpAgent -> proxyUrl -> httpAgent.
    • Graceful fallback when proxy agent package is unavailable (warn + continue with non-proxy path).
  • Gateway initialization updated to attach resolved agent via runtime-supported customHttpAgent.
  • Types expanded to include HttpAgentConfig and the new options.
  • README updated with configuration and precedence details.

Behavioral Notes

  • No behavior change when none of the new options are set.
  • If proxyUrl is configured, install https-proxy-agent in the host project.
  • customHttpAgent is intended for advanced/custom transport scenarios and overrides all other agent options.

Summary by CodeRabbit

  • New Features

    • Added HTTP/HTTPS agent configuration for Braintree requests via customHttpAgent, proxyUrl, and httpAgent. Agent resolution order: customHttpAgent → proxyUrl → httpAgent.
  • Documentation

    • README updated with transport option details, precedence note, and installation note: install https-proxy-agent when using proxyUrl.
  • Chores

    • Package version bumped to 0.1.1.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds configurable HTTP(S) transport for the Braintree plugin: new customHttpAgent, proxyUrl, and httpAgent options with defined precedence, a HttpAgentConfig type, runtime agent-resolution logic (including dynamic https-proxy-agent require and fallbacks), and README + version bump.

Changes

Cohort / File(s) Summary
Type Definitions
plugins/braintree-payment/src/providers/payment-braintree/src/types/index.ts
Added exported HttpAgentConfig interface and extended BraintreeOptions with httpAgent?: HttpAgentConfig, proxyUrl?: string, and `customHttpAgent?: Agent
Core Implementation
plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts
Added createHttpAgent() to resolve an HTTP(S) agent by precedence (customHttpAgentproxyUrlhttpAgent), including dynamic https-proxy-agent require with warning/fallback behavior; refactored init() to build gatewayConfig, attach the resolved agent as customHttpAgent, and instantiate BraintreeGateway. Extended options validation to include proxyUrl and httpAgent fields.
Documentation
plugins/braintree-payment/README.md
Documented new HTTP transport options, the precedence order (customHttpAgentproxyUrlhttpAgent), and noted that https-proxy-agent must be installed when proxyUrl is used.
Package Manifest
plugins/braintree-payment/package.json
Bumped package version from 0.1.0 to 0.1.1.

Sequence Diagram(s)

sequenceDiagram
    participant Init as Plugin Init
    participant Resolver as createHttpAgent()
    participant Custom as customHttpAgent
    participant ProxyMod as https-proxy-agent
    participant NodeAgent as Node https.Agent
    participant Braintree as BraintreeGateway

    Init->>Resolver: resolve agent (options)
    alt customHttpAgent present
        Resolver->>Custom: return provided agent
        Resolver-->>Init: agent
    else proxyUrl present
        Resolver->>ProxyMod: require('https-proxy-agent')
        alt require succeeds
            ProxyMod-->>Resolver: HttpsProxyAgent(proxyUrl)
            Resolver-->>Init: agent
        else require fails (module missing)
            Resolver--xInit: log warning, fall back
            Resolver->>NodeAgent: build from httpAgent or defaults
            NodeAgent-->>Resolver: agent
            Resolver-->>Init: agent
        end
    else httpAgent present
        Resolver->>NodeAgent: build https.Agent from httpAgent opts
        NodeAgent-->>Resolver: agent
        Resolver-->>Init: agent
    end
    Init->>Braintree: new BraintreeGateway({..., customHttpAgent: agent})
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add proxy and custom HTTP agent support for Braintree' directly and clearly summarizes the main changes: new proxy and custom HTTP agent configuration options for the Braintree payment plugin.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/proxy-support

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

@lcmohsen lcmohsen self-assigned this Apr 21, 2026
Copy link
Copy Markdown
Contributor

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts`:
- Around line 193-207: The catch block around requiring/constructing
HttpsProxyAgent in the proxy handling for this.options_.proxyUrl should not
treat every error as a missing module; update the try/catch in braintree-base.ts
so it checks the caught error (when requiring 'https-proxy-agent' or calling new
HttpsProxyAgent(this.options_.proxyUrl)) and only fall back to a regular agent
when the error is a module-not-found error (e.g., error.code ===
'MODULE_NOT_FOUND' or equivalent); for all other errors (invalid proxy URL,
construction failures) rethrow or log an error and fail loudly via
this.logger.error/throw so the misconfiguration is not silently ignored—keep
references to require('https-proxy-agent'), new
HttpsProxyAgent(this.options_.proxyUrl), the catch block, and
this.logger.warn/this.logger.error when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: da696554-67d3-4aea-9c97-59b09d3db4d0

📥 Commits

Reviewing files that changed from the base of the PR and between dbee10c and 3ea8bf5.

📒 Files selected for processing (3)
  • plugins/braintree-payment/README.md
  • plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts
  • plugins/braintree-payment/src/providers/payment-braintree/src/types/index.ts

Copy link
Copy Markdown
Contributor

@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: 2

🧹 Nitpick comments (1)
plugins/braintree-payment/README.md (1)

97-98: Document the direct-connect fallback explicitly.

The note says to install https-proxy-agent, but it doesn't mention that the current implementation in plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts:267-297 only warns and then continues without the proxy. That behavior is important for locked-down environments because traffic will go out over a different network path than the operator intended.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/braintree-payment/README.md` around lines 97 - 98, Update the README
to explicitly document the direct-connect fallback: state that when proxyUrl is
set but the https-proxy-agent package is not installed, the braintree
proxy-agent creation block (the code that inspects
proxyUrl/customHttpAgent/httpAgent in braintree-base.ts) logs a warning and
continues without a proxy, causing traffic to use a direct connection; mention
the security/network implications for locked-down environments and recommend
installing https-proxy-agent (or alternately validate and fail fast in the
proxy-agent creation code if you want stricter behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts`:
- Around line 261-318: The createHttpAgent method assumes this.options_.proxyUrl
is a valid URL string and this.options_.httpAgent fields are correctly typed;
update validateOptions (the options validation function) to perform explicit
checks for these new transport options: ensure options_.proxyUrl, if present, is
a non-empty string and a parseable URL; ensure options_.httpAgent, if present,
is an object and its fields (keepAlive boolean, keepAliveMsecs number >=0,
maxSockets/maxFreeSockets numbers >=0, timeout number >=0, rejectUnauthorized
boolean) have the correct types/ranges; on invalid values throw or return a
clear validation error so createHttpAgent can assume well-formed inputs (refer
to createHttpAgent, options_.proxyUrl, and options_.httpAgent to locate the
relevant code).
- Around line 278-289: The current fallback treats any MODULE_NOT_FOUND as
missing https-proxy-agent; narrow the condition in the error handling inside the
BraintreeBase class (around the isModuleNotFound / isRequireMissingProxyAgent
checks) so the fallback only triggers when the error specifically indicates
"Cannot find module 'https-proxy-agent'" (e.g., use the regex check against
error.message or check error.code plus error.message contains
"https-proxy-agent") and remove or tighten the generic MODULE_NOT_FOUND branch;
keep the logger.warn message but only call it when the
https-proxy-agent-specific check matches so other MODULE_NOT_FOUND errors are
allowed to bubble or be rethrown.

---

Nitpick comments:
In `@plugins/braintree-payment/README.md`:
- Around line 97-98: Update the README to explicitly document the direct-connect
fallback: state that when proxyUrl is set but the https-proxy-agent package is
not installed, the braintree proxy-agent creation block (the code that inspects
proxyUrl/customHttpAgent/httpAgent in braintree-base.ts) logs a warning and
continues without a proxy, causing traffic to use a direct connection; mention
the security/network implications for locked-down environments and recommend
installing https-proxy-agent (or alternately validate and fail fast in the
proxy-agent creation code if you want stricter behavior).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 803d36e1-81ff-4fab-9688-45283db7a42b

📥 Commits

Reviewing files that changed from the base of the PR and between 3ea8bf5 and c726eda.

📒 Files selected for processing (3)
  • plugins/braintree-payment/README.md
  • plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts
  • plugins/braintree-payment/src/providers/payment-braintree/src/types/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugins/braintree-payment/src/providers/payment-braintree/src/types/index.ts

Copy link
Copy Markdown
Contributor

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts`:
- Around line 396-402: The validation block that calls new URL(options.proxyUrl)
must not echo the raw options.proxyUrl (which may contain credentials) in the
thrown MedusaError; replace the error message to avoid leaking secrets by either
using a generic message like `Option "proxyUrl" must be a valid URL` or include
only a safe component (e.g., the URL's hostname) after parsing with new
URL(...)—update the try/catch around new URL(options.proxyUrl) in
braintree-base.ts to throw a MedusaError without interpolating options.proxyUrl.
- Around line 427-452: The numeric validation for httpAgent options
(keepAliveMsecs, maxSockets, maxFreeSockets, timeout) must also reject NaN and
non-finite numbers; update each guard in braintree-base.ts (the checks around
keepAliveMsecs, maxSockets, maxFreeSockets, timeout) to use Number.isFinite(...)
(or !Number.isNaN/Number.isFinite) instead of typeof === 'number', e.g. if
(isDefined(keepAliveMsecs) && (!Number.isFinite(keepAliveMsecs) ||
keepAliveMsecs < 0)) throw the existing MedusaError, and apply the same change
for maxSockets, maxFreeSockets and timeout so malformed NaN values are rejected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7c620045-e07a-4d35-8c0f-8e09c6080839

📥 Commits

Reviewing files that changed from the base of the PR and between 9fcaa75 and e2bcff7.

📒 Files selected for processing (1)
  • plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts

Copy link
Copy Markdown
Contributor

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

♻️ Duplicate comments (2)
plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts (2)

429-455: ⚠️ Potential issue | 🟡 Minor

Reject NaN in numeric httpAgent fields.

The current guards check typeof value === 'number' and value >= 0, but NaN passes both checks because:

  • typeof NaN === 'number' is true
  • NaN < 0 is false (NaN comparisons always return false)

This allows malformed configs to slip through validation.

Proposed fix

Add a helper function and update the guards:

+      const isNonNegativeNumber = (value: unknown): value is number =>
+        typeof value === 'number' && !Number.isNaN(value) && value >= 0;
+
-      if (isDefined(keepAliveMsecs) && (typeof keepAliveMsecs !== 'number' || keepAliveMsecs < 0)) {
+      if (isDefined(keepAliveMsecs) && !isNonNegativeNumber(keepAliveMsecs)) {
         throw new MedusaError(
           MedusaError.Types.INVALID_ARGUMENT,
           'Option "httpAgent.keepAliveMsecs" must be a number greater than or equal to 0 in Braintree plugin',
         );
       }
 
-      if (isDefined(maxSockets) && (typeof maxSockets !== 'number' || maxSockets < 0)) {
+      if (isDefined(maxSockets) && !isNonNegativeNumber(maxSockets)) {
         throw new MedusaError(
           MedusaError.Types.INVALID_ARGUMENT,
           'Option "httpAgent.maxSockets" must be a number greater than or equal to 0 in Braintree plugin',
         );
       }
 
-      if (isDefined(maxFreeSockets) && (typeof maxFreeSockets !== 'number' || maxFreeSockets < 0)) {
+      if (isDefined(maxFreeSockets) && !isNonNegativeNumber(maxFreeSockets)) {
         throw new MedusaError(
           MedusaError.Types.INVALID_ARGUMENT,
           'Option "httpAgent.maxFreeSockets" must be a number greater than or equal to 0 in Braintree plugin',
         );
       }
 
-      if (isDefined(timeout) && (typeof timeout !== 'number' || timeout < 0)) {
+      if (isDefined(timeout) && !isNonNegativeNumber(timeout)) {
         throw new MedusaError(
           MedusaError.Types.INVALID_ARGUMENT,
           'Option "httpAgent.timeout" must be a number greater than or equal to 0 in Braintree plugin',
         );
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts`
around lines 429 - 455, The numeric validation for httpAgent fields
(keepAliveMsecs, maxSockets, maxFreeSockets, timeout) currently allows NaN
because it only checks typeof === 'number' and >= 0; update those guards to
reject NaN/Infinity by using Number.isFinite(value) (or a small helper like
isValidNonNegativeNumber) and value >= 0, throwing the same MedusaError for each
variable (keepAliveMsecs, maxSockets, maxFreeSockets, timeout) in
braintree-base.ts so malformed configs are rejected.

398-405: ⚠️ Potential issue | 🟠 Major

Do not echo the raw proxyUrl in the validation error.

The proxy URL can legally contain credentials in the format http://username:password@proxy.example.com:8080. Interpolating options.proxyUrl in the error message will leak these credentials into exception logs and telemetry when the config is invalid.

Proposed fix
       try {
         new URL(options.proxyUrl);
       } catch {
         throw new MedusaError(
           MedusaError.Types.INVALID_ARGUMENT,
-          `Option "proxyUrl" must be a valid URL in Braintree plugin: "${options.proxyUrl}"`,
+          'Option "proxyUrl" must be a valid URL in Braintree plugin',
         );
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts`
around lines 398 - 405, The validation error exposes sensitive credentials by
interpolating options.proxyUrl into the thrown MedusaError; change the error
message in the proxy URL validation (the try/catch around new
URL(options.proxyUrl)) to avoid echoing the raw value—either omit the URL
entirely or log a sanitized form (e.g., only the hostname/port obtained from new
URL(...) or a generic message like "invalid proxy URL") when throwing
MedusaError.Types.INVALID_ARGUMENT for proxyUrl validation so credentials are
not leaked.
🧹 Nitpick comments (1)
plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts (1)

48-49: Unused http import.

The http module is imported but never used. Only https.Agent is instantiated in createHttpAgent(). Consider removing the unused import.

-import http from 'http';
 import https from 'https';

The return type http.Agent | https.Agent | undefined is still valid since https.Agent extends http.Agent, but the explicit import is unnecessary.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts`
around lines 48 - 49, Remove the unused "http" import and update the agent
return type to use only https.Agent (or the broader Agent type from https) in
createHttpAgent; specifically, delete the line importing http, change the
function signature/return annotation in createHttpAgent() from "http.Agent |
https.Agent | undefined" to "https.Agent | undefined" (or simply "https.Agent"
union with undefined), and ensure any other references to http.Agent in this
file are updated to https.Agent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts`:
- Around line 429-455: The numeric validation for httpAgent fields
(keepAliveMsecs, maxSockets, maxFreeSockets, timeout) currently allows NaN
because it only checks typeof === 'number' and >= 0; update those guards to
reject NaN/Infinity by using Number.isFinite(value) (or a small helper like
isValidNonNegativeNumber) and value >= 0, throwing the same MedusaError for each
variable (keepAliveMsecs, maxSockets, maxFreeSockets, timeout) in
braintree-base.ts so malformed configs are rejected.
- Around line 398-405: The validation error exposes sensitive credentials by
interpolating options.proxyUrl into the thrown MedusaError; change the error
message in the proxy URL validation (the try/catch around new
URL(options.proxyUrl)) to avoid echoing the raw value—either omit the URL
entirely or log a sanitized form (e.g., only the hostname/port obtained from new
URL(...) or a generic message like "invalid proxy URL") when throwing
MedusaError.Types.INVALID_ARGUMENT for proxyUrl validation so credentials are
not leaked.

---

Nitpick comments:
In
`@plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts`:
- Around line 48-49: Remove the unused "http" import and update the agent return
type to use only https.Agent (or the broader Agent type from https) in
createHttpAgent; specifically, delete the line importing http, change the
function signature/return annotation in createHttpAgent() from "http.Agent |
https.Agent | undefined" to "https.Agent | undefined" (or simply "https.Agent"
union with undefined), and ensure any other references to http.Agent in this
file are updated to https.Agent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d4efcb6-58de-43b2-b7eb-ba6d597ad8e9

📥 Commits

Reviewing files that changed from the base of the PR and between e2bcff7 and 05e03c9.

📒 Files selected for processing (1)
  • plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts

@lcmohsen lcmohsen requested a review from dwene April 21, 2026 16:25
lcmohsen and others added 4 commits April 21, 2026 19:25
…core/braintree-base.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
}

private createHttpAgent(): http.Agent | https.Agent | undefined {
// Backward compatibility: if customHttpAgent is directly provided, use it
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

backwards compatibility? We didn't have this before right? Who would be providing options.customHttpAgent?

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.

2 participants