hero/interfaces/websocket/server/docs/webhooks.md
2025-07-29 01:15:23 +02:00

9.9 KiB

Webhook Integration Architecture

Overview

This document outlines the architecture for adding webhook handling capabilities to the Circle WebSocket Server. The integration adds HTTP webhook endpoints alongside the existing WebSocket functionality without disrupting the current system.

Architecture Diagram

graph TB
    subgraph "External Services"
        A[Stripe Webhooks]
        B[iDenfy Webhooks]
    end
    
    subgraph "Circle Server"
        C[HTTP Router]
        D[WebSocket Handler]
        E[Webhook Handler]
        F[Stripe Verifier]
        G[iDenfy Verifier]
        H[Script Dispatcher]
        I[RhaiDispatcherBuilder]
    end
    
    subgraph "Configuration"
        J[.env File]
        K[Environment Variables]
    end
    
    subgraph "Backend"
        L[Redis]
        M[Rhai Worker]
    end
    
    A --> |POST /webhooks/stripe/{circle_pk}| E
    B --> |POST /webhooks/idenfy/{circle_pk}| E
    
    C --> D
    C --> E
    
    E --> F
    E --> G
    F --> H
    G --> H
    H --> I
    I --> L
    L --> M
    
    J --> K
    K --> F
    K --> G
    
    D --> I

URL Structure

Webhook Endpoints

  • Stripe: POST /webhooks/stripe/{circle_pk}
  • iDenfy: POST /webhooks/idenfy/{circle_pk}

Existing WebSocket Endpoints (Unchanged)

  • WebSocket: GET /{circle_pk} (upgrades to WebSocket)

Configuration

Environment Variables (.env file)

# Webhook secrets for signature verification
STRIPE_WEBHOOK_SECRET=whsec_...
IDENFY_WEBHOOK_SECRET=your_idenfy_secret

# Existing configuration
REDIS_URL=redis://127.0.0.1/

Server Configuration Updates

pub struct ServerConfig {
    // ... existing fields
    pub stripe_webhook_secret: Option<String>,
    pub idenfy_webhook_secret: Option<String>,
}

Webhook Processing Flow

1. Request Reception

sequenceDiagram
    participant WS as Webhook Service
    participant CS as Circle Server
    participant WV as Webhook Verifier
    participant SD as Script Dispatcher
    participant RC as RhaiDispatcher
    participant RW as Rhai Worker

    WS->>CS: POST /webhooks/stripe/{circle_pk}
    CS->>CS: Extract circle_pk from URL
    CS->>CS: Read request body and headers
    CS->>WV: Verify webhook signature
    
    alt Stripe Webhook
        WV->>WV: Verify Stripe signature using STRIPE_WEBHOOK_SECRET
        WV->>WV: Deserialize to Stripe webhook payload
    else iDenfy Webhook
        WV->>WV: Verify iDenfy signature using IDENFY_WEBHOOK_SECRET
        WV->>WV: Deserialize to iDenfy webhook payload
    end
    
    WV->>CS: Return verification result + parsed payload
    
    alt Verification Success
        CS->>SD: Dispatch appropriate script
        SD->>RC: Create RhaiDispatcherBuilder
        RC->>RC: Set caller_id="stripe" or "idenfy"
        RC->>RC: Set recipient_id=circle_pk
        RC->>RC: Set script="stripe_webhook_received" or "idenfy_webhook_received"
        RC->>RW: Execute via Redis
        RW->>RC: Return result
        RC->>CS: Script execution result
        CS->>WS: HTTP 200 OK
    else Verification Failed
        CS->>WS: HTTP 401 Unauthorized
    end

2. Signature Verification

Stripe Verification

  • Uses Stripe-Signature header
  • HMAC-SHA256 verification with STRIPE_WEBHOOK_SECRET
  • Follows Stripe's webhook signature verification protocol

iDenfy Verification

  • Uses appropriate iDenfy signature header
  • HMAC verification with IDENFY_WEBHOOK_SECRET
  • Follows iDenfy's webhook signature verification protocol

3. Payload Deserialization

Type Definitions in Heromodels Library

Webhook payload types are now defined in the heromodels library for better code organization and reusability:

  • Stripe Types: Located in heromodels::models::payment::stripe
  • iDenfy Types: Located in heromodels::models::identity::kyc

Stripe Payload Structure

// From heromodels::models::payment::StripeWebhookEvent
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct StripeWebhookEvent {
    pub id: String,
    pub object: String,
    pub api_version: Option<String>,
    pub created: i64,
    pub data: StripeEventData,
    pub livemode: bool,
    pub pending_webhooks: i32,
    pub request: Option<StripeEventRequest>,
    #[serde(rename = "type")]
    pub event_type: String,
}

iDenfy Payload Structure

// From heromodels::models::identity::IdenfyWebhookEvent
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct IdenfyWebhookEvent {
    #[serde(rename = "clientId")]
    pub client_id: String,
    #[serde(rename = "scanRef")]
    pub scan_ref: String,
    pub status: String,
    pub platform: String,
    #[serde(rename = "startedAt")]
    pub started_at: String,
    #[serde(rename = "finishedAt")]
    pub finished_at: Option<String>,
    pub data: Option<IdenfyVerificationData>,
    // ... additional fields
}

4. Script Execution

Script Names

  • Stripe: stripe_webhook_received
  • iDenfy: idenfy_webhook_received

Script Context

The Rhai scripts will receive structured data:

// For Stripe webhooks
let webhook_data = {
    "caller_id": "stripe",
    "circle_id": "circle_public_key",
    "event_type": "payment_intent.succeeded",
    "event_id": "evt_...",
    "created": 1234567890,
    "livemode": false,
    "data": { /* Stripe event data */ }
};

// For iDenfy webhooks  
let webhook_data = {
    "caller_id": "idenfy",
    "circle_id": "circle_public_key", 
    "final_decision": "APPROVED",
    "platform": "PC",
    "status": { /* iDenfy status data */ },
    "data": { /* iDenfy verification data */ }
};

Implementation Structure

Current File Structure

src/server/src/
├── webhook/
│   ├── mod.rs              # Main webhook module with route configuration
│   ├── handlers/
│   │   ├── mod.rs         # Handler module exports
│   │   ├── common.rs      # Common utilities and app state
│   │   ├── stripe.rs      # Stripe webhook handler
│   │   └── idenfy.rs      # iDenfy webhook handler
│   ├── verifiers.rs       # Signature verification for all providers
│   └── types.rs           # Local webhook types (config, errors, etc.)
└── .env                   # Environment configuration

Heromodels Library Structure

heromodels/src/models/
├── payment/
│   ├── mod.rs             # Payment module exports
│   └── stripe.rs          # Stripe webhook event types
└── identity/
    ├── mod.rs             # Identity module exports
    └── kyc.rs             # iDenfy KYC webhook event types

Key Architectural Changes

  • Type Organization: Webhook payload types moved to heromodels library for reusability
  • Modular Handlers: Separate handler files for each webhook provider
  • Simplified Architecture: Removed unnecessary dispatcher complexity
  • Direct Script Execution: Handlers directly use RhaiDispatcher for script execution

Modified Files

  • src/lib.rs - Add webhook routes and module imports
  • Cargo.toml - Add heromodels dependency and webhook-related dependencies
  • cmd/main.rs - Load .env file and configure webhook secrets

Dependencies

[dependencies]
# Existing dependencies...

# Heromodels library for shared types
heromodels = { path = "../../../db/heromodels" }

# For webhook signature verification
hmac = "0.12"
sha2 = "0.10"
hex = { workspace = true }

# For environment variable loading
dotenv = "0.15"

# For HTTP request handling
bytes = "1.0"
thiserror = { workspace = true }

Security Considerations

Signature Verification

  • Mandatory: All webhook requests must have valid signatures
  • Timing Attack Protection: Use constant-time comparison for signatures
  • Secret Management: Webhook secrets loaded from environment variables only

Error Handling

  • No Information Leakage: Generic error responses for invalid webhooks
  • Logging: Detailed logging for debugging (same as existing WebSocket errors)
  • Graceful Degradation: Webhook failures don't affect WebSocket functionality

Request Validation

  • Content-Type: Verify appropriate content types
  • Payload Size: No explicit limits initially (as requested)
  • Rate Limiting: Consider future implementation

Backward Compatibility

WebSocket Functionality

  • Zero Impact: Existing WebSocket routes and functionality unchanged
  • Authentication: WebSocket authentication system remains independent
  • Performance: No performance impact on WebSocket connections

Configuration

  • Optional: Webhook functionality only enabled when secrets are configured
  • Graceful Fallback: Server starts normally even without webhook configuration

Testing Strategy

Unit Tests

  • Webhook signature verification for both providers
  • Payload deserialization
  • Error handling scenarios

Integration Tests

  • End-to-end webhook processing
  • Script dispatch verification
  • Configuration loading

Mock Testing

  • Simulated Stripe webhook calls
  • Simulated iDenfy webhook calls
  • Invalid signature scenarios

Deployment Considerations

Environment Setup

# .env file in src/server/
STRIPE_WEBHOOK_SECRET=whsec_1234567890abcdef...
IDENFY_WEBHOOK_SECRET=your_idenfy_webhook_secret
REDIS_URL=redis://127.0.0.1/

Server Startup

  • Load .env file before server initialization
  • Validate webhook secrets if webhook endpoints are to be enabled
  • Log webhook endpoint availability

Monitoring

  • Log webhook reception and processing
  • Track script execution success/failure rates
  • Monitor webhook signature verification failures

Future Enhancements

Potential Additions

  • Additional webhook providers
  • Webhook retry mechanisms
  • Webhook event filtering
  • Rate limiting implementation
  • Webhook event queuing for high-volume scenarios

Scalability Considerations

  • Webhook processing can be made asynchronous if needed
  • Multiple server instances can handle webhooks independently
  • Redis-based script execution provides natural load distribution