Skip to content

Conversation

@Debarshi-Gupta
Copy link
Contributor

@Debarshi-Gupta Debarshi-Gupta commented Jan 19, 2026

Description

Connector Environment Architecture Explanation

Let me explain the complete architecture around connector.environment in this codebase.

1. Configuration Layer (TOML Files)

In config/development.toml, config/sandbox.toml, and config/production.toml, there's a structure:

[connectors]
environment = "sandbox"  # or "production"

[connectors.sandbox]
stripe.base_url = "..."
adyen.base_url = "..."
# ... other connectors

[connectors.production]
stripe.base_url = "..."
adyen.base_url = "..."
# ... other connectors
  • [connectors] environment: This is the master switch that determines which environment (sandbox or production) is currently active
  • [connectors.sandbox]: Contains all connector configurations for sandbox/testing environments
  • [connectors.production]: Contains all connector configurations for production/live environments

When the application starts, the appropriate TOML file is loaded based on the runtime environment, and the environment field tells the system which config set to use.

2. Type Definitions (types.rs)

Enum Definition

#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, config_patch_derive::Patch)]
#[serde(rename_all = "snake_case")]
pub enum ConnectorEnvironment {
    Sandbox,
    Production,
}

This enum maps directly to the string values in the TOML ("sandbox"Sandbox, "production"Production) via serde deserialization.

Connector Struct

#[derive(Clone, serde::Deserialize, serde::Serialize, Debug, PartialEq, config_patch_derive::Patch)]
pub struct Connectors {
    pub environment: ConnectorEnvironment,
    pub sandbox: ConnectorConfigSet,
    pub production: ConnectorConfigSet,
}

The Connectors struct:

  • Stores the current environment selector (environment)
  • Holds BOTH configuration sets (sandbox and production)
  • Allows runtime switching between environments

Config Set

#[derive(Clone, serde::Deserialize, serde::Serialize, Debug, Default, PartialEq, config_patch_derive::Patch)]
pub struct ConnectorConfigSet {
    pub adyen: ConnectorParams,
    pub stripe: ConnectorParams,
    // ... 70+ connector params
}

#[derive(Clone, Deserialize, Serialize, Debug, Default, PartialEq, config_patch_derive::Patch)]
pub struct ConnectorParams {
    pub base_url: String,
    pub dispute_base_url: Option<String>,
    pub secondary_base_url: Option<String>,
    pub third_base_url: Option<String>,
}

Each connector has its params (URLs, etc.) stored in the appropriate config set.

3. The get_config() Implementation

impl Connectors {
    pub fn get_config(&self) -> &ConnectorConfigSet {
        match self.environment {
            ConnectorEnvironment::Sandbox => &self.sandbox,
            ConnectorEnvironment::Production => &self.production,
        }
    }
}

This is a simple but elegant pattern:

  • Takes &self (a reference to the current Connectors instance)
  • Returns a reference to the appropriate ConnectorConfigSet
  • Zero-copy - just returns a reference based on the environment selector
  • Immutable - cannot accidentally change which config is returned

4. Usage Pattern in Connector Files

In individual connector files (like stripe.rs, adyen.rs, etc.), you'll typically see:

// Inside a connector implementation
pub async fn execute_payment(router_data: &RouterData) -> Result<...> {
    // Get the connectors from the request context
    let connectors = &router_data.connectors;
    
    // Get the ACTIVE config set (based on environment)
    let config_set = connectors.get_config();
    
    // Access the specific connector's config
    let stripe_config = &config_set.stripe;
    
    // Now use the config
    let base_url = &stripe_config.base_url;  // Will be sandbox or production URL
    
    // Make API call to the correct endpoint
    let response = reqwest::Client::post(base_url)
        .send()
        .await?;
        
    // ...
}

5. Complete Data Flow

┌─────────────────────────────────────────────────────────────┐
│  1. Application Startup                                     │
│     - Loads config/{environment}.toml                      │
│     - Parses [connectors] environment = "sandbox"           │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Config Parsing                                          │
│     - Connectors struct instantiated                         │
│     - environment field: ConnectorEnvironment::Sandbox       │
│     - sandbox field: populated with sandbox URLs            │
│     - production field: populated with production URLs     │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Incoming Payment Request                               │
│     - PaymentFlowData includes connectors field             │
│     - connectors: Connectors instance created at startup   │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Connector Execution                                     │
│     let config_set = connectors.get_config();               │
│              ↓                                               │
│     Returns &ConnectorConfigSet::Sandbox                   │
│              ↓                                               │
│     let stripe_config = &config_set.stripe;                │
│              ↓                                               │
│     stripe_config.base_url = "https://api.stripe.com/"    │
└─────────────────────────────────────────────────────────────┘

6. Key Benefits of This Architecture

  1. Zero-Copy: No cloning or allocation when switching configs
  2. Type Safety: Compile-time guarantees you're using the right struct
  3. Easy Testing: Switch environment by just changing one string
  4. Single Source of Truth: All connector configs in one struct
  5. Runtime Flexibility: Can theoretically switch environments without restart (though rare)
  6. Explicit Control: Must call get_config() - prevents accidental wrong environment use

Summary

The connector.environment is essentially a routing mechanism that directs the application to the correct set of connector endpoints. The get_config() method is the "dispatcher" that, based on the environment flag, hands you the right configuration set without any runtime overhead beyond a simple enum match.

Would you like me to show you a specific connector file to see how it's used in practice?

Motivation and Context

Additional Changes

  • This PR modifies the API contract
  • This PR modifies application configuration/environment variables

How did you test it?

@Debarshi-Gupta Debarshi-Gupta requested review from a team as code owners January 19, 2026 08:00
@Debarshi-Gupta Debarshi-Gupta self-assigned this Jan 21, 2026
@Debarshi-Gupta Debarshi-Gupta changed the title Test mode support feat(framework): Added Connector Environment support as environment config Jan 21, 2026
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