PCI Compliance & Vault Configuration Support
## Overview
Implement comprehensive PCI (Payment Card Industry) compliance support in UCS (Universal Connector Service) by enabling merchants to route sensitive payment data through secure vault providers. This eliminates the need for UCS to handle raw card data directly, reducing PCI scope and compliance burden.
---
## PCI Compliance Modes
UCS supports three distinct PCI compliance modes, allowing merchants to choose the approach that best fits their infrastructure and compliance requirements:
### Mode 1: No Vault (Current/Default)
**Description:** UCS connects directly to Payment Service Providers (PSPs) like Stripe, Checkout, etc. Merchants handle tokenization themselves or send raw card data (not recommended).
**PCI Scope:** SAQ D (Full) - if handling raw card data
```mermaid
sequenceDiagram
autonumber
participant FE as Merchant Frontend
participant BE as Merchant Backend
participant UCS as Connector Service
participant PSP as Payment Provider
FE->>BE: Payment request (merchant handles tokenization)
BE->>UCS: POST /payments<br/>{token or card data, amount, connector}
UCS->>PSP: POST api.stripe.com/v1/payment_intents
PSP-->>UCS: Authorization response
UCS-->>BE: Unified response
BE-->>FE: Payment result
```
**Configuration:**
```toml
[connectors.stripe]
base_url = "https://api.stripe.com"
api_key = "${STRIPE_API_KEY}"
# enable_vault_proxy defaults to false
```
---
### Mode 2: Network Proxy (VGS, Evervault)
**Description:** Traffic is routed through a network-level proxy that transparently detokenizes in transit. UCS simply routes to the proxy URL instead of the PSP directly.
**Key Characteristics:**
- **Integration Level:** Network/Transport layer
- **UCS Transformation:** ❌ None - just routes to proxy URL
- **Token Handling:** Proxy detokenizes transparently
**PCI Scope:** SAQ A or A-EP (Reduced) - card data never touches merchant servers
```mermaid
sequenceDiagram
autonumber
participant FE as Merchant Frontend
participant VSDK as Vault SDK (VGS Collect/Evervault)
participant Vault as PCI Vault (VGS/Evervault)
participant BE as Merchant Backend
participant UCS as Connector Service
participant PSP as Payment Provider
Note over FE,Vault: Step 1: Card Tokenization
FE->>VSDK: Render secure card fields
VSDK->>Vault: Tokenize/encrypt card data
Vault-->>VSDK: Return token
VSDK-->>FE: Return token to frontend
Note over BE,PSP: Step 2: Payment via Network Proxy
FE->>BE: Send token + payment request
BE->>UCS: authorize(token, amount, connector)
UCS->>Vault: POST vault-proxy-url/v1/payment_intents
Note over UCS: Same payload structure,<br/>just different URL
Vault->>Vault: Auto-detokenize in transit
Vault->>PSP: POST api.stripe.com/v1/payment_intents<br/>(with real card data)
PSP-->>Vault: Authorization response
Vault-->>UCS: Return response
UCS-->>BE: Unified response
BE-->>FE: Payment result
```
**Supported Providers:**
| Provider | Mechanism | Token Format |
|----------|-----------|--------------|
| **VGS** | Forward proxy with route mapping | `tok_sandbox_xxxx` (Luhn-valid) |
| **Evervault** | HTTP CONNECT Relay | `ev:encrypted:<base64>` |
**Configuration:**
```toml
[vault]
provider = "vgs"
tenant_id = "tntSANDBOX123"
environment = "sandbox"
[connectors.stripe]
base_url = "https://api.stripe.com"
# URL will be transformed to: tntSANDBOX123.sandbox.verygoodproxy.com
enable_vault_proxy = true
```
---
### Mode 3: Application Proxy (Hyperswitch Vault, TokenEx, Basis Theory)
**Description:** UCS formats requests using vault-specific protocols - adding headers, wrapping in expressions, or constructing special request bodies. The vault provider acts as an application-level intermediary.
**Key Characteristics:**
- **Integration Level:** Application layer
- **UCS Transformation:** ✅ Yes - formats tokens for vault protocol
- **Token Handling:** UCS wraps tokens in vault-specific syntax
**PCI Scope:** SAQ A or A-EP (Reduced) - card data never touches merchant servers
```mermaid
sequenceDiagram
autonumber
participant FE as Merchant Frontend
participant VaultSDK as Vault SDK
participant Vault as PCI Vault
participant BE as Merchant Backend
participant UCS as Connector Service
participant PSP as Payment Provider
Note over FE,Vault: Step 1: Card Tokenization
FE->>VaultSDK: Tokenize card data
VaultSDK->>Vault: Store card data
Vault-->>VaultSDK: Return vault_token
VaultSDK-->>FE: Return token
Note over BE,PSP: Step 2: Payment via Application Proxy
FE->>BE: Send token + payment request
BE->>UCS: authorize(token, amount, connector)
Note over UCS: UCS constructs PSP request<br/>with token in vault format
UCS->>Vault: Proxy request (vault-specific format)
Note over Vault: Vault substitutes token<br/>with raw card data
Vault->>PSP: Forward PSP request (with real card data)
PSP-->>Vault: Authorization response
Vault-->>UCS: Return response
UCS-->>BE: Unified response
BE-->>FE: Payment result
```
**Supported Providers:**
| Provider | Routing Mechanism | Token Syntax |
|----------|-------------------|--------------|
| **Hyperswitch Vault** | Wrapped request body | `{{$card_number}}`, `{{$cvv}}` |
| **TokenEx** | Headers (`TX-URL`, `TX-Method`) | `{token}` |
| **Basis Theory** | Header (`BT-PROXY-URL`) | `{{ token.property }}` |
**Configuration:**
```toml
[vault]
provider = "hyperswitch_vault"
api_key = "${HYPERSWITCH_API_KEY}"
profile_id = "${HYPERSWITCH_PROFILE_ID}"
proxy_url = "https://sandbox.hyperswitch.io/proxy"
[connectors.stripe]
base_url = "https://api.stripe.com"
enable_vault_proxy = true
```
---
## Comparison: Network Proxy vs Application Proxy
| Aspect | Network Proxy | Application Proxy |
|--------|---------------|-------------------|
| **What merchant sends** | Token | Token |
| **UCS transformation** | ❌ No—routes to proxy URL | ✅ Yes—formats for vault protocol |
| **Complexity** | Lower | Higher |
| **Provider flexibility** | Coupled to specific vault | Can switch vaults without code changes |
| **Best for** | Quick integration, existing vault users | Multi-PSP setups, vendor flexibility |
---
## Implementation Strategy
### Multi-PR Approach
This feature is being implemented across multiple focused PRs:
#### PR #1: Configuration Types (Current: #617)
**Status:** In Review
**Scope:** Core type definitions and config integration
**Files Changed:**
- `backend/domain_types/src/types.rs` - VaultConfig enum, provider configs, ConnectorParams updates
- `backend/ucs_env/src/configs.rs` - Config integration
- `backend/external-services/src/vault.rs` - HTTP client helpers
- `backend/external-services/src/lib.rs` - Module export
**What's Included:**
- `VaultConfig` enum with 5 provider variants
- Provider-specific config structs with `Secret<String>` for credentials
- `ConnectorParams` updates: `enable_vault_proxy`, `vault_proxy_override`
- `resolve_vault()` method for vault resolution logic
- `transform_connector_url()`, `get_vault_headers()` helpers
- Unit tests for VGS URL transformation
**What's NOT Included (Intentional):**
- HTTP request/response transformation for application proxies
- Actual HTTP client integration with vault routing
- Connector-level integration (each connector must opt-in)
#### PR #2: HTTP Client Integration (Planned)
**Scope:** Wire vault functions into actual HTTP request pipeline
**Open Questions:**
- How to handle TLS verification with VGS custom CA certificates
- Request/response body transformation for application proxies
- Error handling when vault tokenization fails
#### PR #3: Connector Integrations (Planned)
**Scope:** Update individual connectors to use vault-aware HTTP client
**Approach:**
- Start with Stripe as reference implementation
- Each connector PR can proceed independently once PR #2 is merged
---
## Technical Design
### Configuration Schema
```toml
# Global vault configuration (optional)
[vault]
provider = "vgs" # or "evervault", "hyperswitch_vault", "tokenex", "basis_theory"
tenant_id = "tntSANDBOX123"
environment = "sandbox"
# Per-connector configuration
[connectors.stripe]
base_url = "https://api.stripe.com"
enable_vault_proxy = true # Use global vault
[connectors.checkout]
base_url = "https://api.checkout.com"
enable_vault_proxy = true
vault_proxy_override = { provider = "hyperswitch_vault", api_key = "...", profile_id = "...", proxy_url = "..." }
```
### Security Considerations
1. **No Default VaultConfig** - Vault configuration must be explicit; no insecure defaults
2. **Patch Ignore for Vault** - Vault settings require service restart (marked with `#[patch(ignore)]`)
3. **Secret Masking** - All credentials use `Secret<String>` from hyperswitch_masking crate
4. **PCI Compliance Note** - Changes to vault configuration require restart for PCI compliance
### Provider Summary
| Provider | Pattern | Auth Method | Notes |
|----------|---------|-------------|-------|
| **VGS** | Network | URL-based (`{tenant}.{env}.verygoodproxy.com`) | CA cert reserved for future TLS work |
| **Evervault** | Network | HTTP CONNECT + `Proxy-Authorization` header | Relay URL constructed from team_id |
| **Hyperswitch Vault** | Application | `x-api-key`, `x-profile-id` headers | Requests wrapped with `{{$variable}}` |
| **TokenEx** | Application | `TX-ApiKey`, `TX-TokenExID` headers | TGAPI protocol with token markers |
| **Basis Theory** | Application | `BT-API-KEY`, `BT-PROXY-URL` headers | Proxy URL via header, `{{}}` token syntax |
---
## Migration Path
### For Existing Merchants
No action required. `enable_vault_proxy` defaults to `false`, so existing configs continue working unchanged.
### For Merchants Enabling Vault
1. Choose vault provider and obtain credentials
2. Add `[vault]` section to TOML config
3. Set `enable_vault_proxy = true` for desired connectors
4. Restart UCS service (hot-reload not supported for vault config)
5. Test in sandbox environment first
---
## Open Questions / Future Work
1. **Request/Response Transformation**: Application proxies require formatting request bodies with vault-specific token syntax (e.g., `{{card_number}}`). This needs connector-specific integration.
2. **TLS Certificate Verification**: VGS optionally uses custom CA certificates for TLS verification. The `ca_certificate` field is reserved but not yet wired up.
3. **Multi-Vault Scenarios**: Current design supports one global vault + per-connector overrides. Do we need multi-vault fallback?
4. **Observability**: Should vault operations be explicitly logged/metrics for debugging?
---
0 条评论