devsofttech_hms_ci4/app/Controllers/Setting/AbdmGatewaySettings.php
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

271 lines
9.1 KiB
PHP

<?php
namespace App\Controllers\Setting;
use App\Controllers\BaseController;
class AbdmGatewaySettings extends BaseController
{
public function index()
{
if (! $this->canManageSettings()) {
return $this->response->setStatusCode(403)->setBody('Access denied');
}
$token = $this->readSettingValue('EATRIA_BRIDGE_TOKEN');
$hfrId = $this->readSettingValue('ABDM_HFR_ID');
$hmsName = $this->readSettingValue('ABDM_HMS_NAME');
$gwUrl = $this->readSettingValue('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api';
return view('Setting/Admin/abdm_gateway_settings', [
'gateway_url' => $gwUrl,
'hfr_id' => $hfrId,
'hms_name' => $hmsName,
'token_masked' => $this->maskKey($token),
'token_exists' => $token !== '',
'connector' => $this->readSettingValue('ABDM_CONNECTOR') ?: 'eatria_bridge',
'abdm_sync_provider' => $this->readSettingValue('ABDM_SYNC_PROVIDER') ?: 'eatria',
]);
}
public function save()
{
if (! $this->request->isAJAX()) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
if (! $this->canManageSettings()) {
return $this->response->setStatusCode(403)->setJSON(['update' => 0, 'error_text' => 'Access denied']);
}
if (! $this->db->tableExists('hospital_setting')) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'hospital_setting table not found']);
}
$gwUrl = trim((string) $this->request->getPost('gateway_url'));
$token = trim((string) $this->request->getPost('api_token'));
$hfrId = trim((string) $this->request->getPost('hfr_id'));
$hmsName = trim((string) $this->request->getPost('hms_name'));
// Validate gateway URL format
if ($gwUrl !== '' && ! filter_var($gwUrl, FILTER_VALIDATE_URL)) {
return $this->response->setJSON([
'update' => 0,
'error_text' => 'Gateway URL is not a valid URL.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$saved = 0;
if ($gwUrl !== '') {
$this->upsertSettingValue('EATRIA_BRIDGE_URL', rtrim($gwUrl, '/'));
$saved++;
}
if ($token !== '') {
$this->upsertSettingValue('EATRIA_BRIDGE_TOKEN', $token);
$saved++;
}
if ($hfrId !== '') {
$this->upsertSettingValue('ABDM_HFR_ID', $hfrId);
$saved++;
}
if ($hmsName !== '') {
$this->upsertSettingValue('ABDM_HMS_NAME', $hmsName);
$saved++;
}
// Always set connector to eatria_bridge when saving from this page
$this->upsertSettingValue('ABDM_CONNECTOR', 'eatria_bridge');
$this->upsertSettingValue('ABDM_SYNC_PROVIDER', 'eatria');
if ($saved === 0) {
return $this->response->setJSON([
'update' => 0,
'error_text' => 'Provide at least one value to save.',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$storedToken = $this->readSettingValue('EATRIA_BRIDGE_TOKEN');
return $this->response->setJSON([
'update' => 1,
'error_text' => 'ABDM Gateway settings saved.',
'token_exists' => $storedToken !== '',
'token_masked' => $this->maskKey($storedToken),
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
public function testConnection()
{
if (! $this->request->isAJAX()) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
if (! $this->canManageSettings()) {
return $this->response->setStatusCode(403)->setJSON(['update' => 0, 'error_text' => 'Access denied']);
}
// Use posted values first, then stored settings
$gwUrl = trim((string) $this->request->getPost('gateway_url'));
$token = trim((string) $this->request->getPost('api_token'));
if ($gwUrl === '') {
$gwUrl = $this->readSettingValue('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api';
}
if ($token === '') {
$token = $this->readSettingValue('EATRIA_BRIDGE_TOKEN');
}
$gwUrl = rtrim($gwUrl, '/');
$healthUrl = $gwUrl . '/v3/health';
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $healthUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_HTTPHEADER => ['Accept: application/json'],
]);
$raw = curl_exec($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
curl_close($ch);
if ($curlErr !== '') {
return $this->response->setJSON([
'update' => 0,
'error_text' => 'Connection failed: ' . $curlErr,
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
if ($code !== 200) {
return $this->response->setJSON([
'update' => 0,
'error_text' => 'Gateway returned HTTP ' . $code,
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
$body = json_decode((string) $raw, true);
$mode = (string) ($body['mode'] ?? 'unknown');
$ver = (string) ($body['version'] ?? '');
// Test Bearer auth with /api/v3/gateway/status
$authOk = false;
$authMsg = '';
if ($token !== '') {
$ch2 = curl_init();
curl_setopt_array($ch2, [
CURLOPT_URL => $gwUrl . '/v3/gateway/status',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Authorization: Bearer ' . $token,
],
]);
$raw2 = curl_exec($ch2);
$code2 = (int) curl_getinfo($ch2, CURLINFO_HTTP_CODE);
curl_close($ch2);
$body2 = json_decode((string) $raw2, true);
$authOk = $code2 === 200 && isset($body2['ok']) && $body2['ok'] == 1;
$authMsg = $authOk
? 'API key authenticated ✓'
: ('Auth check: HTTP ' . $code2 . ($code2 === 403 ? ' — invalid API key' : ''));
}
return $this->response->setJSON([
'update' => 1,
'error_text' => 'Gateway reachable — mode: ' . $mode . ($ver !== '' ? ', v' . $ver : ''),
'mode' => $mode,
'version' => $ver,
'auth_ok' => $authOk,
'auth_msg' => $authMsg,
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
// -------------------------------------------------------------------------
// Helpers (same pattern as HealthplixSettings)
// -------------------------------------------------------------------------
private function canManageSettings(): bool
{
if (! function_exists('auth') || ! auth()->loggedIn()) {
return false;
}
return true;
}
private function readSettingValue(string $name): string
{
if (defined($name)) {
$v = trim((string) constant($name));
if ($v !== '') {
return $v;
}
}
if (! $this->db->tableExists('hospital_setting')) {
return '';
}
$row = $this->db->table('hospital_setting')
->select('s_value')
->where('s_name', $name)
->get(1)
->getRowArray();
return trim((string) ($row['s_value'] ?? ''));
}
private function upsertSettingValue(string $name, string $value): bool
{
$existing = $this->db->table('hospital_setting')
->select('id, s_value')
->where('s_name', $name)
->get(1)
->getRowArray();
if ($existing) {
return (bool) $this->db->table('hospital_setting')
->where('id', (int) $existing['id'])
->update(['s_value' => $value]);
}
return (bool) $this->db->table('hospital_setting')->insert([
's_name' => $name,
's_value' => $value,
]);
}
private function maskKey(string $key): string
{
$key = trim($key);
if ($key === '') {
return '';
}
if (mb_strlen($key) <= 8) {
return str_repeat('*', mb_strlen($key));
}
return mb_substr($key, 0, 4) . str_repeat('*', mb_strlen($key) - 8) . mb_substr($key, -4);
}
}