devsofttech_hms_ci4/DUAL_BRIDGE_ARCHITECTURE.md
DevSoftTech Server 0628f8f813 feat: ABDM OPD Queue + ABHA Register flow + patient ABHA search fix
ABDM OPD Queue (today):
- New EAtriaBridgeConnector: opdQueueFetch/opdTokenCreate/opdTokenUpdateStatus + patch() helper
- New AbdmOpdQueue controller: index/list/fetchQueue/createToken/updateTokenStatus/processScannedToken
  - fetchQueue syncs gateway tokens into abdm_opd_tokens (local DB) and enriches with HMS patient data
  - processScannedToken auto-finds/creates patient_master record from ABHA scan data, marks token CALLED
- New migration: abdm_opd_tokens table (gateway_token_id, source, status, patient_id, opd_id, processed_at)
- New view: abdm/opd_queue.php - reception screen with auto-refresh 30s, HMS Patient column (green row when OPD registered)
- Sidebar: ABDM OPD Queue nav link added
- Routes: AbdmOpdQueue with abdmPermFilter

ABHA Register tab (earlier this session):
- New Abha controller: initiate/verifyOtp/communication/verifyCommOtp/validateAbha/card + autoCreateOrFindPatient
- New views: abha/card.php, partials/abha_create_panel.php, partials/abha_otp_modal.php
- Patient_V.php: ABHA Register tab with 4-method wizard (ABHA number, QR scan, facility QR, mobile OTP)

Patient ABHA search fix:
- Patient::search() now detects ABHA column (abha_id/abha_no/abha/abha_address) and searches by it

ABDM Bridge infrastructure:
- EAtriaBridgeConnector: new connector for e-atria.in ABDM bridge gateway
- AbdmConnectorFactory/Interface updated
- AbdmGatewaySettings controller + view
- SNOMED/CSNOTK terminology service skeleton
- Finance payout OPD consult views
- Migration: AddAbdmPushFieldsToOpdAndPatient, CreateSnomedCoreTables
- Docs: abdm/ API specs, architecture, gateway guides
2026-05-16 04:21:31 +05:30

9.6 KiB

Dual-Bridge Architecture Configuration Guide

Architecture Overview

HMS CI4 Queues Events
    ↓ Routes by event_type prefix
    ├─→ snomed.* events
    │    ↓ POST to BRIDGE_SYNC_URL
    │    https://csnotk.e-atria.in/api/bridge
    │    (CSNOtk Bridge: Codes diagnosis → FHIR)
    │
    └─→ abdm.* events
         ↓ POST to ABDM_BRIDGE_URL  
         https://abdm-bridge.e-atria.in/api/v1/bridge
         (ABDM Bridge: Routes to ABDM M1 server)

Event Type Routing Table

Event Type Destination Purpose
snomed.diagnosis.validate CSNOtk /api/bridge Validate & code diagnosis with SNOMED CT
snomed.medication.validate CSNOtk /api/bridge Validate & code medication with SNOMED CT
abdm.abha.validate ABDM Bridge /api/v1/bridge Validate ABHA number with ABDM M1
abdm.scan_share.lookup ABDM Bridge /api/v1/bridge QR code lookup for health records
abdm.fhir.share.requested ABDM Bridge /api/v1/bridge Push FHIR bundle to ABDM M1
abdm.opd.prescription.share.requested ABDM Bridge /api/v1/bridge Push OPD prescription bundle
abdm.ipd.discharge.share.requested ABDM Bridge /api/v1/bridge Push IPD discharge bundle
abdm.diagnosis.report.share.requested ABDM Bridge /api/v1/bridge Push diagnosis report bundle
abdm.consent.requested ABDM Bridge /api/v1/bridge Request patient consent for data access

HMS .env Configuration Required

Step 1: Dual Bridge URLs

Replace the current BRIDGE configuration with this:

# ==================== CSNOtk Bridge (SNOMED CT Terminology) ====================
# Purpose: Receive diagnosis/medication events, validate with SNOMED CT, return FHIR-coded data
# Endpoint: POST /api/bridge
# Used by: BridgeSyncService when routing snomed.* events
BRIDGE_SYNC_URL = https://csnotk.e-atria.in/api/bridge
BRIDGE_SYNC_TOKEN = 
BRIDGE_SYNC_TIMEOUT = 20

# ==================== ABDM Bridge (ABDM M1 Communication) ====================
# Purpose: Receive ABDM events, route to ABDM Live/Sandbox M1 server
# Endpoint: POST /api/v1/bridge
# Used by: BridgeSyncService when routing abdm.* events
# Architecture: Multiple HMS instances → Single ABDM Bridge → ABDM M1 Server
ABDM_BRIDGE_URL = https://abdm-bridge.e-atria.in/api/v1/bridge
ABDM_BRIDGE_TOKEN = 

# ==================== Bridge Configuration ====================
BRIDGE_SOURCE_CODE = SBXID_033661
BRIDGE_SYNC_PROVIDER = bridge

# ==================== SNOMED CT Terminology Service (Direct REST API) ====================
# Purpose: Direct access to CSNOtk REST API for autocomplete (OPD diagnosis search)
# Endpoints: /rest/search/suggest, /rest/search/lookup, /rest/search/validate/id
# Used by: CsnotkTerminologyService in OPD_Prescription controller
snomed.csnotk.baseUrl = https://csnotk.e-atria.in/csnoserv
snomed.csnotk.enabled = true
snomed.csnotk.timeoutSec = 10

Step 2: Generate Bearer Tokens

Generate TWO random tokens (one for each bridge):

# Token 1 for CSNOtk Bridge
openssl rand -hex 32
# Example: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2

# Token 2 for ABDM Bridge  
openssl rand -hex 32
# Example: f2e1d0c9b8a7z6y5x4w3v2u1t0s9r8q7p6o5n4m3l2k1j0i9h8g7f6e5d4c3b2a1

Step 3: Update .env Tokens

BRIDGE_SYNC_TOKEN = a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2
ABDM_BRIDGE_TOKEN = f2e1d0c9b8a7z6y5x4w3v2u1t0s9r8q7p6o5n4m3l2k1j0i9h8g7f6e5d4c3b2a1

BridgeSyncService Routing Logic (To Be Implemented)

Update app/Libraries/BridgeSyncService.php buildBridgeDispatchContext() to route based on event prefix:

Pseudocode Logic

private function buildBridgeDispatchContext(array $row, array $payload): array
{
    $eventType = (string) ($row['event_type'] ?? '');
    
    // Route snomed.* events to CSNOtk
    if (str_starts_with($eventType, 'snomed.')) {
        return [
            'ok' => true,
            'channel' => 'csnotk',
            'endpoint' => $this->readSetting('BRIDGE_SYNC_URL'),  // CSNOtk bridge
            'method' => 'POST',
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $this->readSetting('BRIDGE_SYNC_TOKEN'),
            ],
            'body' => [
                'queue_id' => (int) ($row['id'] ?? 0),
                'source' => $source,
                'event_type' => $eventType,
                'payload' => $payload,
                'occurred_at' => (string) ($row['created_at'] ?? ''),
            ],
        ];
    }
    
    // Route abdm.* events to ABDM Bridge
    if (str_starts_with($eventType, 'abdm.')) {
        return [
            'ok' => true,
            'channel' => 'abdm',
            'endpoint' => $this->readSetting('ABDM_BRIDGE_URL'),  // ABDM bridge
            'method' => 'POST',
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $this->readSetting('ABDM_BRIDGE_TOKEN'),
            ],
            'body' => [
                'queue_id' => (int) ($row['id'] ?? 0),
                'source' => $source,
                'event_type' => $eventType,
                'payload' => $payload,
                'occurred_at' => (string) ($row['created_at'] ?? ''),
            ],
        ];
    }
    
    // Unknown event type
    return ['ok' => false, 'error' => 'Unsupported event_type prefix'];
}

CSNOtk Bridge /api/bridge Contract

Request Format

{
    "queue_id": 12345,
    "source": "SBXID_033661",
    "event_type": "snomed.diagnosis.validate",
    "payload": {
        "diagnosis_text": "fever",
        "return_limit": 10
    },
    "occurred_at": "2024-05-13T10:30:00+05:30"
}

Response Format (Expected)

{
    "ok": 1,
    "event_type": "snomed.diagnosis.validate",
    "data": [
        {
            "concept_id": "25064002",
            "term": "Headache",
            "hierarchy": "disorder",
            "is_preferred": 1,
            "concept_fsn": "Headache (disorder)"
        }
    ],
    "request_id": "REQ-abc123"
}

ABDM Bridge /api/v1/bridge Contract

(See gateway-php-ci4/README.md for full API contract)

Request Format

{
    "queue_id": 12345,
    "source": "SBXID_033661",
    "event_type": "abdm.abha.validate",
    "payload": {
        "abha_id": "00-0000-0000-0000"
    },
    "occurred_at": "2024-05-13T10:30:00+05:30"
}

Response Format (Expected)

{
    "ok": 1,
    "event_type": "abdm.abha.validate",
    "request_id": "REQ-abc123",
    "data": {
        "abha": "00-0000-0000-0000",
        "status": "VALID"
    }
}

Implementation Checklist

Phase 1: Configuration

  • Update HMS .env with both BRIDGE_SYNC_URL (CSNOtk) and ABDM_BRIDGE_URL (ABDM)
  • Generate two separate bearer tokens
  • Set BRIDGE_SYNC_TOKEN and ABDM_BRIDGE_TOKEN in .env
  • Document tokens in secure location (password manager)

Phase 2: Code Changes

  • Update BridgeSyncService::buildBridgeDispatchContext() with routing logic
  • Update BridgeSyncService::buildDispatchContext() to handle snomed.* routing
  • Update BridgeSyncService::readSetting() to support ABDM_BRIDGE_URL and ABDM_BRIDGE_TOKEN
  • Add logging to distinguish between CSNOtk and ABDM bridge calls in abdm_api_logs table

Phase 3: Testing

  • Queue a snomed.diagnosis.validate event and verify it goes to CSNOtk
  • Queue an abdm.abha.validate event and verify it goes to ABDM Bridge
  • Check abdm_api_logs for successful routing
  • Verify CSNOtk returns SNOMED-coded data
  • Verify ABDM Bridge routes to ABDM M1 correctly

Phase 4: Deployment

  • Deploy PHP gateway to https://abdm-bridge.e-atria.in (if not already live)
  • Configure gateway .env with GATEWAY_BEARER_TOKEN matching HMS ABDM_BRIDGE_TOKEN
  • Run php spark bridge:sync --limit 10 to test both bridge types
  • Monitor logs for any routing or authentication errors

Database Logging

The abdm_api_logs table will now record:

  • channel = 'csnotk' or 'abdm' (for routing differentiation)
  • event_type = snomed.* or abdm.*
  • endpoint = CSNOtk or ABDM bridge URL used
  • status = 'success' or 'error'
  • error_message = 403 Unauthorized, 404 Not Found, etc.

Troubleshooting

Problem: 403 Unauthorized on CSNOtk

  • Check: BRIDGE_SYNC_TOKEN matches CSNOtk Bridge expectation
  • Verify: Request to https://csnotk.e-atria.in/api/bridge includes bearer token

Problem: 403 Unauthorized on ABDM Bridge

  • Check: ABDM_BRIDGE_TOKEN matches PHP gateway GATEWAY_BEARER_TOKEN
  • Verify: PHP gateway .env has the same token as HMS ABDM_BRIDGE_TOKEN

Problem: Events stuck in pending status

  • Check: Routing logic correctly identifies event prefix (snomed.* vs abdm.*)
  • Query: SELECT * FROM bridge_sync_queue WHERE status='pending' ORDER BY created_at DESC
  • Debug: Add logging to see which endpoint was called

Problem: CSNOtk returns 404 on /api/bridge

  • Cause: /api/bridge endpoint may not be exposed on the CSNOtk public server
  • Fallback: Check if CSNOtk only exposes /rest/search/* endpoints (direct API calls only)
  • Action: If needed, create intermediate queue processor for CSNOtk terminology service

References

  • CSNOtk REST API Reference: docs/CSNOTK_API_REFERENCE.md
  • CSNOtk Database: SnomedCT_InternationalRF2_PRODUCTION_20260501T120000Z/
  • ABDM PHP Gateway: gateway-php-ci4/ (deployment guide: ABDM_GATEWAY_PHP_DEPLOYMENT.md)
  • Current CSNOtk Direct API Integration: app/Libraries/CsnotkTerminologyService.php
  • Bridge Service: app/Libraries/BridgeSyncService.php