Fix ABDM M3 HIU ID mapping, polling guards, and migration compatibility
This commit is contained in:
parent
3d40642116
commit
0f8bd43e72
|
|
@ -163,6 +163,7 @@ class AbdmHiu extends BaseController
|
|||
if ($abhaAddress === '') {
|
||||
$abhaAddress = $this->extractAbhaAddressFromLog((string) ($row['patient_log'] ?? ''));
|
||||
}
|
||||
$abhaAddress = $this->canonicalizeAbhaAddress($abhaAddress, $abhaNumber);
|
||||
$latest = $this->findLatestConsentByAbhaAddress($abhaAddress);
|
||||
|
||||
$items[] = [
|
||||
|
|
@ -173,7 +174,7 @@ class AbdmHiu extends BaseController
|
|||
'abha_number' => $abhaNumber,
|
||||
'abha_address' => $abhaAddress,
|
||||
'has_abha_number' => preg_match('/^\d{14}$/', $abhaNumber) === 1 ? 1 : 0,
|
||||
'has_abha_address' => $abhaAddress !== '' ? 1 : 0,
|
||||
'has_abha_address' => $this->isValidAbhaAddress($abhaAddress) ? 1 : 0,
|
||||
'latest_consent' => $latest,
|
||||
];
|
||||
}
|
||||
|
|
@ -228,8 +229,27 @@ class AbdmHiu extends BaseController
|
|||
return null;
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('abdm_hiu_workflows') ?? [];
|
||||
$select = [
|
||||
'id',
|
||||
'operation',
|
||||
'workflow_state',
|
||||
'status',
|
||||
'consent_id',
|
||||
'request_id',
|
||||
'transaction_id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
if (in_array('abdm_consent_request_id', $fields, true)) {
|
||||
$select[] = 'abdm_consent_request_id';
|
||||
}
|
||||
if (in_array('abdm_consent_artifact_id', $fields, true)) {
|
||||
$select[] = 'abdm_consent_artifact_id';
|
||||
}
|
||||
|
||||
$row = $this->db->table('abdm_hiu_workflows')
|
||||
->select('id, operation, workflow_state, status, consent_id, request_id, transaction_id, created_at, updated_at')
|
||||
->select(implode(', ', $select))
|
||||
->where('abha_address', $abhaAddress)
|
||||
->whereIn('operation', ['consent_request', 'consent_status', 'consent_fetch'])
|
||||
->orderBy('id', 'DESC')
|
||||
|
|
@ -262,4 +282,29 @@ class AbdmHiu extends BaseController
|
|||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function canonicalizeAbhaAddress(string $abhaAddress, string $abhaNumber): string
|
||||
{
|
||||
$candidate = trim($abhaAddress);
|
||||
if ($candidate !== '' && $this->isValidAbhaAddress($candidate)) {
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
$addressDigits = preg_replace('/\D+/', '', $candidate) ?? '';
|
||||
if (preg_match('/^\d{14}$/', $addressDigits) !== 1) {
|
||||
$addressDigits = preg_replace('/\D+/', '', trim($abhaNumber)) ?? '';
|
||||
}
|
||||
|
||||
if (preg_match('/^\d{14}$/', $addressDigits) === 1) {
|
||||
$domain = trim((string) (getenv('ABDM_ABHA_ADDRESS_DEFAULT_DOMAIN') ?: 'sbx'));
|
||||
return $addressDigits . '@' . $domain;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function isValidAbhaAddress(string $value): bool
|
||||
{
|
||||
return preg_match('/^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$/', trim($value)) === 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ class CreateAbdmPushSyncTables extends Migration
|
|||
],
|
||||
'care_context_reference' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 255,
|
||||
'constraint' => 191,
|
||||
'null' => false,
|
||||
],
|
||||
'care_context_display' => [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddConsentMappingColumnsToAbdmHiuWorkflows extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('abdm_hiu_workflows') ?? [];
|
||||
|
||||
$add = [];
|
||||
if (! in_array('hms_request_id', $fields, true)) {
|
||||
$add['hms_request_id'] = [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 160,
|
||||
'null' => true,
|
||||
'after' => 'request_id',
|
||||
];
|
||||
}
|
||||
if (! in_array('gateway_request_id', $fields, true)) {
|
||||
$add['gateway_request_id'] = [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 160,
|
||||
'null' => true,
|
||||
'after' => 'hms_request_id',
|
||||
];
|
||||
}
|
||||
if (! in_array('abdm_consent_request_id', $fields, true)) {
|
||||
$add['abdm_consent_request_id'] = [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 160,
|
||||
'null' => true,
|
||||
'after' => 'consent_id',
|
||||
];
|
||||
}
|
||||
if (! in_array('abdm_consent_artifact_id', $fields, true)) {
|
||||
$add['abdm_consent_artifact_id'] = [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 160,
|
||||
'null' => true,
|
||||
'after' => 'abdm_consent_request_id',
|
||||
];
|
||||
}
|
||||
|
||||
if (! empty($add)) {
|
||||
$this->forge->addColumn('abdm_hiu_workflows', $add);
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('abdm_hiu_workflows') ?? [];
|
||||
if (in_array('abdm_consent_request_id', $fields, true)) {
|
||||
if (! $this->indexExists('abdm_hiu_workflows', 'idx_abdm_hiu_abdm_consent_req')) {
|
||||
$this->db->query('CREATE INDEX idx_abdm_hiu_abdm_consent_req ON abdm_hiu_workflows (abdm_consent_request_id)');
|
||||
}
|
||||
}
|
||||
if (in_array('hms_request_id', $fields, true)) {
|
||||
if (! $this->indexExists('abdm_hiu_workflows', 'idx_abdm_hiu_hms_req')) {
|
||||
$this->db->query('CREATE INDEX idx_abdm_hiu_hms_req ON abdm_hiu_workflows (hms_request_id)');
|
||||
}
|
||||
}
|
||||
if (in_array('gateway_request_id', $fields, true)) {
|
||||
if (! $this->indexExists('abdm_hiu_workflows', 'idx_abdm_hiu_gateway_req')) {
|
||||
$this->db->query('CREATE INDEX idx_abdm_hiu_gateway_req ON abdm_hiu_workflows (gateway_request_id)');
|
||||
}
|
||||
}
|
||||
if (in_array('abdm_consent_artifact_id', $fields, true)) {
|
||||
if (! $this->indexExists('abdm_hiu_workflows', 'idx_abdm_hiu_abdm_consent_artifact')) {
|
||||
$this->db->query('CREATE INDEX idx_abdm_hiu_abdm_consent_artifact ON abdm_hiu_workflows (abdm_consent_artifact_id)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('abdm_hiu_workflows') ?? [];
|
||||
|
||||
if (in_array('hms_request_id', $fields, true)) {
|
||||
$this->forge->dropColumn('abdm_hiu_workflows', 'hms_request_id');
|
||||
}
|
||||
if (in_array('gateway_request_id', $fields, true)) {
|
||||
$this->forge->dropColumn('abdm_hiu_workflows', 'gateway_request_id');
|
||||
}
|
||||
if (in_array('abdm_consent_request_id', $fields, true)) {
|
||||
$this->forge->dropColumn('abdm_hiu_workflows', 'abdm_consent_request_id');
|
||||
}
|
||||
if (in_array('abdm_consent_artifact_id', $fields, true)) {
|
||||
$this->forge->dropColumn('abdm_hiu_workflows', 'abdm_consent_artifact_id');
|
||||
}
|
||||
}
|
||||
|
||||
private function indexExists(string $table, string $indexName): bool
|
||||
{
|
||||
$rows = $this->db->query('SHOW INDEX FROM ' . $this->db->escapeIdentifiers($table))->getResultArray();
|
||||
foreach ($rows as $row) {
|
||||
if (strcasecmp((string) ($row['Key_name'] ?? ''), $indexName) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -105,15 +105,44 @@ class M3HiuGatewayClient
|
|||
$response['http_code'] = $httpCode;
|
||||
$response['error_text'] = $errorText;
|
||||
$response['retryable'] = $retryable;
|
||||
|
||||
$gatewayRequestId = trim((string) ($response['request_id'] ?? $response['requestId'] ?? ''));
|
||||
if ($gatewayRequestId === '') {
|
||||
$gatewayRequestId = trim((string) ($payload['requestId'] ?? $payload['request_id'] ?? ''));
|
||||
}
|
||||
$response['gateway_request_id'] = $gatewayRequestId;
|
||||
|
||||
$abdmConsentRequestId = $this->findFirstValueByKeys($response, [
|
||||
'consentRequestId',
|
||||
'consent_request_id',
|
||||
'abdm_consent_request_id',
|
||||
]);
|
||||
if ($abdmConsentRequestId === '') {
|
||||
$abdmConsentRequestId = trim((string) ($payload['consentRequestId'] ?? ''));
|
||||
}
|
||||
if ($abdmConsentRequestId !== '') {
|
||||
$response['abdm_consent_request_id'] = $abdmConsentRequestId;
|
||||
$response['consent_request_id'] = $abdmConsentRequestId;
|
||||
}
|
||||
|
||||
$abdmConsentId = $this->findFirstValueByKeys($response, [
|
||||
'consentId',
|
||||
'consent_id',
|
||||
]);
|
||||
if ($abdmConsentId === '') {
|
||||
$abdmConsentId = trim((string) ($payload['consentId'] ?? $payload['consent_id'] ?? ''));
|
||||
}
|
||||
if ($abdmConsentId !== '') {
|
||||
$response['consent_id'] = $abdmConsentId;
|
||||
$response['abdm_consent_artifact_id'] = $abdmConsentId;
|
||||
}
|
||||
|
||||
if (! isset($response['request_id'])) {
|
||||
$response['request_id'] = (string) ($response['requestId'] ?? $payload['requestId'] ?? $payload['request_id'] ?? '');
|
||||
$response['request_id'] = $gatewayRequestId;
|
||||
}
|
||||
if (! isset($response['transaction_id'])) {
|
||||
$response['transaction_id'] = (string) ($response['transactionId'] ?? $payload['transactionId'] ?? $payload['transaction_id'] ?? '');
|
||||
}
|
||||
if (! isset($response['consent_id'])) {
|
||||
$response['consent_id'] = (string) ($response['consentId'] ?? $response['consentRequestId'] ?? $payload['consentId'] ?? $payload['consentRequestId'] ?? $payload['consent_id'] ?? '');
|
||||
}
|
||||
|
||||
$this->writeApiLog($eventKey, $path, $payload, $response, $httpCode, $ok, $errorText, $hospitalId);
|
||||
|
||||
|
|
@ -211,4 +240,32 @@ class M3HiuGatewayClient
|
|||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function findFirstValueByKeys(array $root, array $keys): string
|
||||
{
|
||||
$queue = [$root];
|
||||
while (! empty($queue)) {
|
||||
$node = array_shift($queue);
|
||||
if (! is_array($node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (isset($node[$key])) {
|
||||
$value = trim((string) $node[$key]);
|
||||
if ($value !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($node as $child) {
|
||||
if (is_array($child)) {
|
||||
$queue[] = $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,35 @@ class M3HiuWorkflowService
|
|||
$hasWorkflowTable = $this->db->tableExists('abdm_hiu_workflows');
|
||||
|
||||
$normalized = $this->normalizePayload($payload);
|
||||
$prepared = $this->preparePayloadForOperation($operation, $normalized);
|
||||
if (($prepared['ok'] ?? 0) !== 1) {
|
||||
$failedResult = [
|
||||
'ok' => 0,
|
||||
'http_code' => (int) ($prepared['http_code'] ?? 422),
|
||||
'error_text' => (string) ($prepared['error_text'] ?? 'Validation failed'),
|
||||
'retryable' => 0,
|
||||
'request_id' => (string) ($normalized['request_id'] ?? ''),
|
||||
'transaction_id' => (string) ($normalized['transaction_id'] ?? ''),
|
||||
'consent_id' => (string) ($normalized['consent_id'] ?? ''),
|
||||
'abha_address' => (string) ($normalized['abha_address'] ?? ''),
|
||||
];
|
||||
if ($hasWorkflowTable) {
|
||||
$this->upsertWorkflow(
|
||||
$operation,
|
||||
$normalized,
|
||||
$failedResult,
|
||||
'FAILED',
|
||||
'failed',
|
||||
(int) $failedResult['http_code'],
|
||||
false
|
||||
);
|
||||
$failedResult['workflow_persisted'] = 1;
|
||||
}
|
||||
|
||||
return $failedResult;
|
||||
}
|
||||
$dispatchPayload = (array) ($prepared['payload'] ?? []);
|
||||
|
||||
if ($hasWorkflowTable && $operation !== 'consent_status') {
|
||||
$duplicate = $this->findDuplicate($operation, $normalized);
|
||||
if ($duplicate !== null) {
|
||||
|
|
@ -33,7 +62,7 @@ class M3HiuWorkflowService
|
|||
}
|
||||
}
|
||||
|
||||
$result = $this->dispatch($operation, $normalized);
|
||||
$result = $this->dispatch($operation, $dispatchPayload);
|
||||
$httpCode = (int) ($result['http_code'] ?? 0);
|
||||
$ok = (int) ($result['ok'] ?? 0) === 1;
|
||||
|
||||
|
|
@ -42,7 +71,7 @@ class M3HiuWorkflowService
|
|||
$retryable = (int) ($result['retryable'] ?? 0) === 1;
|
||||
|
||||
if ($hasWorkflowTable) {
|
||||
$this->upsertWorkflow($operation, $normalized, $result, $state, $status, $httpCode, $retryable);
|
||||
$this->upsertWorkflow($operation, $dispatchPayload, $result, $state, $status, $httpCode, $retryable);
|
||||
$result['workflow_persisted'] = 1;
|
||||
} else {
|
||||
$result['workflow_persisted'] = 0;
|
||||
|
|
@ -164,6 +193,12 @@ class M3HiuWorkflowService
|
|||
if (! isset($normalized['consent_id']) && isset($normalized['consentRequestId'])) {
|
||||
$normalized['consent_id'] = (string) $normalized['consentRequestId'];
|
||||
}
|
||||
if (! isset($normalized['abdm_consent_request_id']) && isset($normalized['consentRequestId'])) {
|
||||
$normalized['abdm_consent_request_id'] = (string) $normalized['consentRequestId'];
|
||||
}
|
||||
if (! isset($normalized['abdm_consent_artifact_id']) && isset($normalized['consentId'])) {
|
||||
$normalized['abdm_consent_artifact_id'] = (string) $normalized['consentId'];
|
||||
}
|
||||
|
||||
if (isset($normalized['abha_id']) && ! isset($normalized['abha_address'])) {
|
||||
$normalized['abha_address'] = (string) $normalized['abha_id'];
|
||||
|
|
@ -176,6 +211,11 @@ class M3HiuWorkflowService
|
|||
$normalized[$k] = trim((string) $normalized[$k]);
|
||||
}
|
||||
|
||||
$normalized['abha_address'] = $this->canonicalizeAbhaAddress(
|
||||
(string) ($normalized['abha_address'] ?? ''),
|
||||
(string) ($normalized['abha_id'] ?? '')
|
||||
);
|
||||
|
||||
if ($normalized['request_id'] === '') {
|
||||
$normalized['request_id'] = 'REQ-HIU-' . date('YmdHis') . '-' . strtoupper(bin2hex(random_bytes(3)));
|
||||
}
|
||||
|
|
@ -196,10 +236,371 @@ class M3HiuWorkflowService
|
|||
return $normalized;
|
||||
}
|
||||
|
||||
private function preparePayloadForOperation(string $operation, array $normalized): array
|
||||
{
|
||||
$clean = $this->stripInternalNoise($normalized);
|
||||
$requestId = trim((string) ($clean['requestId'] ?? $clean['request_id'] ?? ''));
|
||||
if ($requestId === '') {
|
||||
$requestId = 'REQ-HIU-' . date('YmdHis') . '-' . strtoupper(bin2hex(random_bytes(3)));
|
||||
}
|
||||
$timestamp = trim((string) ($clean['timestamp'] ?? ''));
|
||||
if ($timestamp === '') {
|
||||
$timestamp = gmdate('Y-m-d\TH:i:s.000\Z');
|
||||
}
|
||||
|
||||
$base = [
|
||||
'requestId' => $requestId,
|
||||
'timestamp' => $timestamp,
|
||||
];
|
||||
$hfr = trim((string) ($clean['hfr_id'] ?? ''));
|
||||
if ($hfr !== '') {
|
||||
$base['hfr_id'] = $hfr;
|
||||
}
|
||||
|
||||
if ($operation === 'consent_request') {
|
||||
$consent = $clean['consent'] ?? null;
|
||||
if (! is_array($consent) || empty($consent)) {
|
||||
$consent = $this->buildDefaultConsent($clean);
|
||||
}
|
||||
if (! is_array($consent) || empty($consent)) {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 422,
|
||||
'error_text' => 'Consent object is required for HIU consent request.',
|
||||
];
|
||||
}
|
||||
|
||||
$patientId = trim((string) ($consent['patient']['id'] ?? ''));
|
||||
if ($patientId === '') {
|
||||
$patientId = trim((string) ($clean['abha_address'] ?? ''));
|
||||
}
|
||||
if (! $this->isValidAbhaAddress($patientId)) {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 422,
|
||||
'error_text' => 'Patient ABHA address must be in address format like 91510165305101@sbx.',
|
||||
];
|
||||
}
|
||||
if (! isset($consent['patient']) || ! is_array($consent['patient'])) {
|
||||
$consent['patient'] = [];
|
||||
}
|
||||
$consent['patient']['id'] = $patientId;
|
||||
|
||||
$requesterName = trim((string) ($consent['requester']['name'] ?? ''));
|
||||
if ($requesterName === '' || strcasecmp($requesterName, 'Hospital HMS') === 0) {
|
||||
$resolvedRequesterName = $this->resolveRequesterName();
|
||||
if ($resolvedRequesterName !== '') {
|
||||
if (! isset($consent['requester']) || ! is_array($consent['requester'])) {
|
||||
$consent['requester'] = [];
|
||||
}
|
||||
$consent['requester']['name'] = $resolvedRequesterName;
|
||||
}
|
||||
}
|
||||
|
||||
$hiuId = trim((string) ($consent['hiu']['id'] ?? ''));
|
||||
if ($hiuId === '') {
|
||||
$hiuId = $this->resolveHiuServiceId();
|
||||
if ($hiuId === '') {
|
||||
$hiuId = trim((string) ($clean['hfr_id'] ?? ''));
|
||||
}
|
||||
if ($hiuId !== '') {
|
||||
if (! isset($consent['hiu']) || ! is_array($consent['hiu'])) {
|
||||
$consent['hiu'] = [];
|
||||
}
|
||||
$consent['hiu']['id'] = $hiuId;
|
||||
}
|
||||
}
|
||||
|
||||
$requesterIdentifierValue = trim((string) ($consent['requester']['identifier']['value'] ?? ''));
|
||||
if ($requesterIdentifierValue === '') {
|
||||
$requesterIdentifierValue = $hiuId !== '' ? $hiuId : trim((string) ($clean['hfr_id'] ?? ''));
|
||||
}
|
||||
if ($requesterIdentifierValue !== '') {
|
||||
if (! isset($consent['requester']) || ! is_array($consent['requester'])) {
|
||||
$consent['requester'] = [];
|
||||
}
|
||||
if (! isset($consent['requester']['identifier']) || ! is_array($consent['requester']['identifier'])) {
|
||||
$consent['requester']['identifier'] = [];
|
||||
}
|
||||
$consent['requester']['identifier']['system'] = 'https://abdm.gov.in/facility';
|
||||
$consent['requester']['identifier']['type'] = 'HIU';
|
||||
$consent['requester']['identifier']['value'] = $requesterIdentifierValue;
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => 1,
|
||||
'payload' => $base + ['consent' => $consent],
|
||||
];
|
||||
}
|
||||
|
||||
if ($operation === 'consent_status') {
|
||||
$consentRequestId = trim((string) ($clean['abdm_consent_request_id'] ?? $clean['consentRequestId'] ?? $clean['consent_id'] ?? ''));
|
||||
if ($consentRequestId === '') {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 422,
|
||||
'error_text' => 'consentRequestId is required for consent status.',
|
||||
];
|
||||
}
|
||||
if ($this->isGatewayRequestIdPattern($consentRequestId)) {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 422,
|
||||
'error_text' => 'mapping_error: consentRequestId appears to be gateway request_id; use stored ABDM consentRequestId.',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => 1,
|
||||
'payload' => $base + ['consentRequestId' => $consentRequestId],
|
||||
];
|
||||
}
|
||||
|
||||
if ($operation === 'consent_fetch') {
|
||||
$consentId = trim((string) ($clean['abdm_consent_artifact_id'] ?? $clean['consentId'] ?? $clean['consent_id'] ?? ''));
|
||||
if ($consentId === '') {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 422,
|
||||
'error_text' => 'consentId is required for consent fetch.',
|
||||
];
|
||||
}
|
||||
if ($this->isGatewayRequestIdPattern($consentId)) {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 422,
|
||||
'error_text' => 'mapping_error: consentId appears to be gateway request_id; use ABDM consent artifact id only.',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => 1,
|
||||
'payload' => $base + ['consentId' => $consentId],
|
||||
];
|
||||
}
|
||||
|
||||
if ($operation === 'hi_request') {
|
||||
$hiRequest = $clean['hiRequest'] ?? null;
|
||||
if (! is_array($hiRequest)) {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 422,
|
||||
'error_text' => 'hiRequest object is required for health information request.',
|
||||
];
|
||||
}
|
||||
|
||||
$consentId = trim((string) ($hiRequest['consent']['id'] ?? $clean['consentId'] ?? $clean['consent_id'] ?? ''));
|
||||
if ($consentId === '') {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 422,
|
||||
'error_text' => 'hiRequest.consent.id is required for health information request.',
|
||||
];
|
||||
}
|
||||
|
||||
if (! isset($hiRequest['consent']) || ! is_array($hiRequest['consent'])) {
|
||||
$hiRequest['consent'] = [];
|
||||
}
|
||||
$hiRequest['consent']['id'] = $consentId;
|
||||
|
||||
return [
|
||||
'ok' => 1,
|
||||
'payload' => $base + ['hiRequest' => $hiRequest],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => 0,
|
||||
'http_code' => 400,
|
||||
'error_text' => 'Unsupported operation: ' . $operation,
|
||||
];
|
||||
}
|
||||
|
||||
private function stripInternalNoise(array $payload): array
|
||||
{
|
||||
unset(
|
||||
$payload['csrf_test_name'],
|
||||
$payload['patient_id'],
|
||||
$payload['abha_id'],
|
||||
$payload['abhaId']
|
||||
);
|
||||
|
||||
if (isset($payload['requestId'])) {
|
||||
unset($payload['request_id']);
|
||||
}
|
||||
if (isset($payload['transactionId'])) {
|
||||
unset($payload['transaction_id']);
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
private function buildDefaultConsent(array $payload): array
|
||||
{
|
||||
$patientId = trim((string) ($payload['abha_address'] ?? ''));
|
||||
if (! $this->isValidAbhaAddress($patientId)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$requesterName = $this->resolveRequesterName();
|
||||
if ($requesterName === '') {
|
||||
$requesterName = 'Hospital HMS';
|
||||
}
|
||||
|
||||
$hiuId = $this->resolveHiuServiceId();
|
||||
if ($hiuId === '') {
|
||||
$hiuId = trim((string) ($payload['hfr_id'] ?? ''));
|
||||
}
|
||||
|
||||
$consent = [
|
||||
'purpose' => [
|
||||
'code' => 'CAREMGT',
|
||||
'text' => 'Care Management',
|
||||
'refUri' => 'https://abdm.gov.in',
|
||||
],
|
||||
'patient' => [
|
||||
'id' => $patientId,
|
||||
],
|
||||
'requester' => [
|
||||
'name' => $requesterName,
|
||||
],
|
||||
'hiTypes' => ['OPConsultation', 'DiagnosticReport'],
|
||||
'permission' => [
|
||||
'accessMode' => 'VIEW',
|
||||
'dateRange' => [
|
||||
'from' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('-30 days')),
|
||||
'to' => gmdate('Y-m-d\T00:00:00.000\Z'),
|
||||
],
|
||||
'dataEraseAt' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('+365 days')),
|
||||
'frequency' => [
|
||||
'unit' => 'HOUR',
|
||||
'value' => 1,
|
||||
'repeats' => 0,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if ($hiuId !== '') {
|
||||
$consent['hiu'] = ['id' => $hiuId];
|
||||
$consent['requester']['identifier'] = [
|
||||
'system' => 'https://abdm.gov.in/facility',
|
||||
'type' => 'HIU',
|
||||
'value' => $hiuId,
|
||||
];
|
||||
} else {
|
||||
$fallbackId = trim((string) ($payload['hfr_id'] ?? ''));
|
||||
if ($fallbackId !== '') {
|
||||
$consent['requester']['identifier'] = [
|
||||
'system' => 'https://abdm.gov.in/facility',
|
||||
'type' => 'HIU',
|
||||
'value' => $fallbackId,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $consent;
|
||||
}
|
||||
|
||||
private function readSettingValue(string $key): string
|
||||
{
|
||||
if (! $this->db->tableExists('hospital_setting')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$row = $this->db->table('hospital_setting')
|
||||
->select('s_value')
|
||||
->where('s_name', $key)
|
||||
->orderBy('id', 'DESC')
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
|
||||
return trim((string) ($row['s_value'] ?? ''));
|
||||
}
|
||||
|
||||
private function resolveRequesterName(): string
|
||||
{
|
||||
foreach (['ABDM_HMS_NAME', 'H_Name', 'HOSPITAL_NAME'] as $key) {
|
||||
$value = $this->readSettingValue($key);
|
||||
if ($value !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
$envName = trim((string) (getenv('HOSPITAL_NAME') ?: getenv('app.name') ?: ''));
|
||||
return $envName;
|
||||
}
|
||||
|
||||
private function resolveHiuServiceId(): string
|
||||
{
|
||||
if (! $this->db->tableExists('hospital_setting')) {
|
||||
$envHiu = trim((string) (getenv('ABDM_HIU_ID') ?: ''));
|
||||
if ($envHiu !== '') {
|
||||
return $envHiu;
|
||||
}
|
||||
return trim((string) (getenv('ABDM_HFR_ID') ?: ''));
|
||||
}
|
||||
|
||||
$row = $this->db->table('hospital_setting')
|
||||
->select('s_value')
|
||||
->whereIn('s_name', ['ABDM_HIU_ID', 'EATRIA_HIU_ID'])
|
||||
->orderBy('id', 'DESC')
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
|
||||
$value = trim((string) ($row['s_value'] ?? ''));
|
||||
if ($value !== '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$hfr = $this->readSettingValue('ABDM_HFR_ID');
|
||||
if ($hfr !== '') {
|
||||
return $hfr;
|
||||
}
|
||||
|
||||
$envHiu = trim((string) (getenv('ABDM_HIU_ID') ?: ''));
|
||||
if ($envHiu !== '') {
|
||||
return $envHiu;
|
||||
}
|
||||
|
||||
return trim((string) (getenv('ABDM_HFR_ID') ?: ''));
|
||||
}
|
||||
|
||||
private function canonicalizeAbhaAddress(string $abhaAddress, string $abhaId): string
|
||||
{
|
||||
$candidate = trim($abhaAddress);
|
||||
if ($candidate !== '' && $this->isValidAbhaAddress($candidate)) {
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
if ($candidate !== '') {
|
||||
$digits = preg_replace('/\D+/', '', $candidate) ?? '';
|
||||
if (preg_match('/^\d{14}$/', $digits) === 1) {
|
||||
$domain = trim((string) (getenv('ABDM_ABHA_ADDRESS_DEFAULT_DOMAIN') ?: 'sbx'));
|
||||
return $digits . '@' . $domain;
|
||||
}
|
||||
}
|
||||
|
||||
$abhaDigits = preg_replace('/\D+/', '', trim($abhaId)) ?? '';
|
||||
if (preg_match('/^\d{14}$/', $abhaDigits) === 1) {
|
||||
$domain = trim((string) (getenv('ABDM_ABHA_ADDRESS_DEFAULT_DOMAIN') ?: 'sbx'));
|
||||
return $abhaDigits . '@' . $domain;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function isValidAbhaAddress(string $value): bool
|
||||
{
|
||||
return preg_match('/^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$/', trim($value)) === 1;
|
||||
}
|
||||
|
||||
private function resolveState(string $operation, array $result): string
|
||||
{
|
||||
$ok = (int) ($result['ok'] ?? 0) === 1;
|
||||
if (! $ok) {
|
||||
if (str_contains(strtolower((string) ($result['error_text'] ?? '')), 'mapping_error')) {
|
||||
return 'MAPPING_ERROR';
|
||||
}
|
||||
return 'FAILED';
|
||||
}
|
||||
|
||||
|
|
@ -260,9 +661,17 @@ class M3HiuWorkflowService
|
|||
int $forceId = 0
|
||||
): void {
|
||||
$now = Time::now('Asia/Kolkata')->toDateTimeString();
|
||||
$requestId = trim((string) ($payload['request_id'] ?? $result['request_id'] ?? ''));
|
||||
$transactionId = trim((string) ($payload['transaction_id'] ?? $result['transaction_id'] ?? ''));
|
||||
$consentId = trim((string) ($payload['consent_id'] ?? $result['consent_id'] ?? ''));
|
||||
$hmsRequestId = trim((string) ($payload['request_id'] ?? $payload['requestId'] ?? ''));
|
||||
if ($hmsRequestId === '') {
|
||||
$hmsRequestId = trim((string) ($result['hms_request_id'] ?? ''));
|
||||
}
|
||||
$gatewayRequestId = trim((string) ($result['gateway_request_id'] ?? $result['request_id'] ?? $result['requestId'] ?? ''));
|
||||
$abdmConsentRequestId = trim((string) ($result['abdm_consent_request_id'] ?? $result['consent_request_id'] ?? $payload['consentRequestId'] ?? ''));
|
||||
$transactionId = trim((string) ($payload['transaction_id'] ?? $payload['transactionId'] ?? $result['transaction_id'] ?? ''));
|
||||
$consentId = trim((string) ($payload['abdm_consent_artifact_id'] ?? $payload['consent_id'] ?? $payload['consentId'] ?? $result['abdm_consent_artifact_id'] ?? $result['consent_id'] ?? ''));
|
||||
if ($consentId === '' && $operation !== 'consent_fetch' && $operation !== 'hi_request') {
|
||||
$consentId = $abdmConsentRequestId;
|
||||
}
|
||||
$abhaAddress = trim((string) ($payload['abha_address'] ?? $payload['abha_id'] ?? $result['abha_address'] ?? ''));
|
||||
$hfrId = trim((string) ($payload['hfr_id'] ?? $result['hfr_id'] ?? ''));
|
||||
$hospitalId = trim((string) ($payload['hospital_id'] ?? $result['hospital_id'] ?? ''));
|
||||
|
|
@ -271,7 +680,7 @@ class M3HiuWorkflowService
|
|||
$retryCount = 0;
|
||||
$id = $forceId;
|
||||
if ($id <= 0) {
|
||||
$existing = $this->findDuplicate($operation, ['request_id' => $requestId, 'transaction_id' => $transactionId]);
|
||||
$existing = $this->findDuplicate($operation, ['request_id' => $hmsRequestId, 'transaction_id' => $transactionId]);
|
||||
if (! empty($existing)) {
|
||||
$id = (int) ($existing['id'] ?? 0);
|
||||
$retryCount = (int) ($existing['retry_count'] ?? 0);
|
||||
|
|
@ -296,7 +705,7 @@ class M3HiuWorkflowService
|
|||
'hfr_id' => $hfrId !== '' ? $hfrId : 'UNKNOWN',
|
||||
'abha_address' => $abhaAddress !== '' ? $abhaAddress : null,
|
||||
'consent_id' => $consentId !== '' ? $consentId : null,
|
||||
'request_id' => $requestId !== '' ? $requestId : null,
|
||||
'request_id' => $hmsRequestId !== '' ? $hmsRequestId : null,
|
||||
'transaction_id' => $transactionId !== '' ? $transactionId : null,
|
||||
'operation' => $operation,
|
||||
'workflow_state' => $state,
|
||||
|
|
@ -311,6 +720,19 @@ class M3HiuWorkflowService
|
|||
'updated_at' => $now,
|
||||
];
|
||||
|
||||
if (in_array('hms_request_id', $this->db->getFieldNames('abdm_hiu_workflows') ?? [], true)) {
|
||||
$row['hms_request_id'] = $hmsRequestId !== '' ? $hmsRequestId : null;
|
||||
}
|
||||
if (in_array('gateway_request_id', $this->db->getFieldNames('abdm_hiu_workflows') ?? [], true)) {
|
||||
$row['gateway_request_id'] = $gatewayRequestId !== '' ? $gatewayRequestId : null;
|
||||
}
|
||||
if (in_array('abdm_consent_request_id', $this->db->getFieldNames('abdm_hiu_workflows') ?? [], true)) {
|
||||
$row['abdm_consent_request_id'] = $abdmConsentRequestId !== '' ? $abdmConsentRequestId : null;
|
||||
}
|
||||
if (in_array('abdm_consent_artifact_id', $this->db->getFieldNames('abdm_hiu_workflows') ?? [], true)) {
|
||||
$row['abdm_consent_artifact_id'] = $consentId !== '' ? $consentId : null;
|
||||
}
|
||||
|
||||
if (in_array($state, ['COMPLETED', 'CONSENT_FETCHED'], true)) {
|
||||
$row['completed_at'] = $now;
|
||||
}
|
||||
|
|
@ -329,4 +751,9 @@ class M3HiuWorkflowService
|
|||
$row['created_at'] = $now;
|
||||
$this->db->table('abdm_hiu_workflows')->insert($row);
|
||||
}
|
||||
|
||||
private function isGatewayRequestIdPattern(string $value): bool
|
||||
{
|
||||
return preg_match('/^REQ-\d{14}-[A-Za-z0-9]{6,}$/', trim($value)) === 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,11 +44,12 @@
|
|||
</div>
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-md-3"><input id="selected_patient" class="form-control" placeholder="Selected Patient" readonly></div>
|
||||
<div class="col-md-2"><input id="selected_patient" class="form-control" placeholder="Selected Patient" readonly></div>
|
||||
<div class="col-md-2"><input id="request_id" class="form-control" placeholder="requestId"></div>
|
||||
<div class="col-md-2"><input id="transaction_id" class="form-control" placeholder="transactionId"></div>
|
||||
<div class="col-md-2"><input id="consent_id" class="form-control" placeholder="consentId / consentRequestId"></div>
|
||||
<div class="col-md-3"><input id="abha_address" class="form-control" placeholder="abha_address" readonly></div>
|
||||
<div class="col-md-2"><input id="consent_request_id" class="form-control" placeholder="ABDM consentRequestId"></div>
|
||||
<div class="col-md-2"><input id="consent_artifact_id" class="form-control" placeholder="ABDM consentId (artifact)"></div>
|
||||
<div class="col-md-2"><input id="abha_address" class="form-control" placeholder="abha_address" readonly></div>
|
||||
</div>
|
||||
|
||||
<div class="small mb-3" id="readiness_box"></div>
|
||||
|
|
@ -115,6 +116,8 @@
|
|||
var csrfHash = '<?= csrf_hash() ?>';
|
||||
var selectedPatient = null;
|
||||
var consentPollTimer = null;
|
||||
var consentPollAttempts = 0;
|
||||
var consentPollMaxAttempts = 12;
|
||||
|
||||
var opRoute = {
|
||||
consent_request: '<?= base_url('AbdmHiu/consent_request') ?>',
|
||||
|
|
@ -134,6 +137,28 @@
|
|||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function fieldEl(ids) {
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var el = document.getElementById(ids[i]);
|
||||
if (el) {
|
||||
return el;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getFieldValue(ids) {
|
||||
var el = fieldEl(ids);
|
||||
return el ? (el.value || '').toString().trim() : '';
|
||||
}
|
||||
|
||||
function setFieldValue(ids, value) {
|
||||
var el = fieldEl(ids);
|
||||
if (el) {
|
||||
el.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
function genReq(prefix) {
|
||||
var p = prefix || 'REQ-HIU';
|
||||
return p + '-' + Date.now();
|
||||
|
|
@ -161,32 +186,53 @@
|
|||
return ['approved', 'granted', 'active', 'linked', 'completed'].indexOf(s) !== -1;
|
||||
}
|
||||
|
||||
function isTerminalConsentState(statusText) {
|
||||
var s = (statusText || '').toString().trim().toLowerCase();
|
||||
return ['approved', 'granted', 'active', 'completed', 'revoked', 'denied', 'expired', 'rejected', 'failed', 'error'].indexOf(s) !== -1;
|
||||
}
|
||||
|
||||
function isGatewayRequestId(v) {
|
||||
return /^REQ-\d{14}-[A-Za-z0-9]{6,}$/.test((v || '').toString().trim());
|
||||
}
|
||||
|
||||
function isValidConsentArtifactId(v) {
|
||||
var val = (v || '').toString().trim();
|
||||
return val !== '' && !isGatewayRequestId(val);
|
||||
}
|
||||
|
||||
function syncButtons(consentStateText) {
|
||||
var hasPatient = !!selectedPatient;
|
||||
var hasNum = hasPatient && selectedPatient.has_abha_number === 1;
|
||||
var hasAddr = hasPatient && selectedPatient.has_abha_address === 1;
|
||||
var hasConsentRef = (document.getElementById('consent_id').value.trim() !== '' || document.getElementById('request_id').value.trim() !== '');
|
||||
var hasConsentRequestRef = (getFieldValue(['consent_request_id', 'consent_id']) !== '');
|
||||
var hasConsentArtifactRef = (getFieldValue(['consent_artifact_id', 'consent_id']) !== '');
|
||||
|
||||
document.getElementById('btnConsentRequest').disabled = !(hasNum && hasAddr);
|
||||
document.getElementById('btnConsentStatus').disabled = !hasConsentRef;
|
||||
document.getElementById('btnConsentFetch').disabled = !hasConsentRef;
|
||||
document.getElementById('btnHiRequest').disabled = !(hasConsentRef && isConsentApproved(consentStateText));
|
||||
document.getElementById('btnConsentStatus').disabled = !hasConsentRequestRef;
|
||||
document.getElementById('btnConsentFetch').disabled = !hasConsentArtifactRef;
|
||||
document.getElementById('btnHiRequest').disabled = !(hasConsentArtifactRef && isConsentApproved(consentStateText));
|
||||
updateReadinessBox(consentStateText);
|
||||
}
|
||||
|
||||
function setSelectedPatient(p) {
|
||||
selectedPatient = p;
|
||||
document.getElementById('selected_patient').value = (p.patient_name || '') + (p.patient_code ? ' (' + p.patient_code + ')' : '');
|
||||
document.getElementById('abha_address').value = p.abha_address || '';
|
||||
document.getElementById('f_abha_address').value = p.abha_address || '';
|
||||
document.getElementById('request_id').value = genReq('REQ-CONSENT');
|
||||
document.getElementById('transaction_id').value = genReq('TXN-HIU');
|
||||
setFieldValue(['selected_patient'], (p.patient_name || '') + (p.patient_code ? ' (' + p.patient_code + ')' : ''));
|
||||
setFieldValue(['abha_address'], p.abha_address || '');
|
||||
setFieldValue(['f_abha_address'], p.abha_address || '');
|
||||
setFieldValue(['request_id'], genReq('REQ-CONSENT'));
|
||||
setFieldValue(['transaction_id'], genReq('TXN-HIU'));
|
||||
|
||||
var latest = p.latest_consent || {};
|
||||
var consentRef = (latest.consent_id || latest.request_id || '').toString();
|
||||
if (consentRef) {
|
||||
document.getElementById('consent_id').value = consentRef;
|
||||
document.getElementById('f_consent_id').value = consentRef;
|
||||
var consentRequestRef = (latest.abdm_consent_request_id || '').toString();
|
||||
var consentArtifactRef = (latest.abdm_consent_artifact_id || latest.consent_id || '').toString();
|
||||
if (consentRequestRef && !isGatewayRequestId(consentRequestRef)) {
|
||||
setFieldValue(['consent_request_id', 'consent_id'], consentRequestRef);
|
||||
setFieldValue(['f_consent_id'], consentRequestRef);
|
||||
}
|
||||
if (isValidConsentArtifactId(consentArtifactRef)) {
|
||||
setFieldValue(['consent_artifact_id', 'consent_id'], consentArtifactRef);
|
||||
} else {
|
||||
setFieldValue(['consent_artifact_id', 'consent_id'], '');
|
||||
}
|
||||
|
||||
var consentState = (latest.workflow_state || latest.status || 'Unknown').toString();
|
||||
|
|
@ -248,26 +294,141 @@
|
|||
}
|
||||
|
||||
var payload = Object.assign({}, custom, {
|
||||
requestId: document.getElementById('request_id').value.trim(),
|
||||
transactionId: document.getElementById('transaction_id').value.trim(),
|
||||
abha_address: document.getElementById('abha_address').value.trim(),
|
||||
requestId: getFieldValue(['request_id']),
|
||||
transactionId: getFieldValue(['transaction_id']),
|
||||
abha_address: getFieldValue(['abha_address']),
|
||||
patient_id: selectedPatient ? selectedPatient.patient_id : null
|
||||
});
|
||||
|
||||
var consentVal = document.getElementById('consent_id').value.trim();
|
||||
if (consentVal !== '') {
|
||||
payload.consentId = consentVal;
|
||||
payload.consentRequestId = consentVal;
|
||||
var consentRequestVal = getFieldValue(['consent_request_id', 'consent_id']);
|
||||
if (consentRequestVal !== '') {
|
||||
payload.consentRequestId = consentRequestVal;
|
||||
payload.abdm_consent_request_id = consentRequestVal;
|
||||
}
|
||||
|
||||
var consentArtifactVal = getFieldValue(['consent_artifact_id', 'consent_id']);
|
||||
if (consentArtifactVal !== '') {
|
||||
payload.consentId = consentArtifactVal;
|
||||
payload.abdm_consent_artifact_id = consentArtifactVal;
|
||||
}
|
||||
|
||||
if (!payload.timestamp) {
|
||||
payload.timestamp = new Date().toISOString();
|
||||
}
|
||||
|
||||
delete payload.abha_id;
|
||||
delete payload.abhaId;
|
||||
delete payload.consent_id;
|
||||
|
||||
payload[csrfName] = csrfHash;
|
||||
return payload;
|
||||
}
|
||||
|
||||
function isValidAbhaAddress(v) {
|
||||
return /^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$/.test((v || '').toString().trim());
|
||||
}
|
||||
|
||||
function buildGatewayPayload(op, raw) {
|
||||
var requestId = (raw.requestId || raw.request_id || '').toString().trim();
|
||||
var timestamp = (raw.timestamp || '').toString().trim();
|
||||
var abhaAddress = (raw.abha_address || '').toString().trim();
|
||||
var base = {
|
||||
requestId: requestId,
|
||||
timestamp: timestamp || new Date().toISOString()
|
||||
};
|
||||
|
||||
if (op === 'consent_request') {
|
||||
var consent = raw.consent;
|
||||
if (!consent || typeof consent !== 'object' || Array.isArray(consent) || Object.keys(consent).length === 0) {
|
||||
if (!isValidAbhaAddress(abhaAddress)) {
|
||||
throw new Error('Patient ABHA address must be in format like 91510165305101@sbx.');
|
||||
}
|
||||
consent = {
|
||||
purpose: {
|
||||
code: 'CAREMGT',
|
||||
text: 'Care Management',
|
||||
refUri: 'https://abdm.gov.in'
|
||||
},
|
||||
patient: { id: abhaAddress },
|
||||
requester: { name: 'Hospital HMS' },
|
||||
hiTypes: ['OPConsultation', 'DiagnosticReport'],
|
||||
permission: {
|
||||
accessMode: 'VIEW',
|
||||
dateRange: {
|
||||
from: new Date(Date.now() - (30 * 24 * 60 * 60 * 1000)).toISOString(),
|
||||
to: new Date().toISOString()
|
||||
},
|
||||
dataEraseAt: new Date(Date.now() + (365 * 24 * 60 * 60 * 1000)).toISOString(),
|
||||
frequency: {
|
||||
unit: 'HOUR',
|
||||
value: 1,
|
||||
repeats: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!consent.patient || typeof consent.patient !== 'object') {
|
||||
consent.patient = {};
|
||||
}
|
||||
if (!consent.patient.id && isValidAbhaAddress(abhaAddress)) {
|
||||
consent.patient.id = abhaAddress;
|
||||
}
|
||||
if (!isValidAbhaAddress(consent.patient.id || '')) {
|
||||
throw new Error('Patient ABHA address must be in format like 91510165305101@sbx.');
|
||||
}
|
||||
|
||||
base.consent = consent;
|
||||
return base;
|
||||
}
|
||||
|
||||
if (op === 'consent_status') {
|
||||
var consentRequestId = (raw.abdm_consent_request_id || raw.consentRequestId || '').toString().trim();
|
||||
if (!consentRequestId) {
|
||||
throw new Error('consentRequestId is required for consent status.');
|
||||
}
|
||||
if (isGatewayRequestId(consentRequestId)) {
|
||||
throw new Error('mapping_error: consentRequestId looks like gateway request_id (REQ-...). Use actual ABDM consentRequestId from init response.');
|
||||
}
|
||||
base.consentRequestId = consentRequestId;
|
||||
return base;
|
||||
}
|
||||
|
||||
if (op === 'consent_fetch') {
|
||||
var consentId = (raw.abdm_consent_artifact_id || raw.consentId || '').toString().trim();
|
||||
if (!consentId) {
|
||||
throw new Error('consentId is required for consent fetch.');
|
||||
}
|
||||
if (isGatewayRequestId(consentId)) {
|
||||
throw new Error('mapping_error: consentId looks like gateway request_id (REQ-...). Use ABDM consent artifact id only.');
|
||||
}
|
||||
base.consentId = consentId;
|
||||
return base;
|
||||
}
|
||||
|
||||
if (op === 'hi_request') {
|
||||
var hiRequest = raw.hiRequest;
|
||||
if (!hiRequest || typeof hiRequest !== 'object' || Array.isArray(hiRequest)) {
|
||||
throw new Error('hiRequest object is required for health information request.');
|
||||
}
|
||||
|
||||
if (!hiRequest.consent || typeof hiRequest.consent !== 'object') {
|
||||
hiRequest.consent = {};
|
||||
}
|
||||
if (!hiRequest.consent.id) {
|
||||
hiRequest.consent.id = (raw.consentId || raw.consent_id || '').toString().trim();
|
||||
}
|
||||
if (!(hiRequest.consent.id || '').toString().trim()) {
|
||||
throw new Error('hiRequest.consent.id is required for health information request.');
|
||||
}
|
||||
|
||||
base.hiRequest = hiRequest;
|
||||
return base;
|
||||
}
|
||||
|
||||
throw new Error('Unsupported operation');
|
||||
}
|
||||
|
||||
function setResult(obj) {
|
||||
document.getElementById('result_box').textContent = JSON.stringify(obj, null, 2);
|
||||
}
|
||||
|
|
@ -292,7 +453,13 @@
|
|||
setResult({ ok: 0, message: 'Unsupported operation' });
|
||||
return;
|
||||
}
|
||||
var payload = readPayload(op);
|
||||
var payload;
|
||||
try {
|
||||
payload = buildGatewayPayload(op, readPayload(op));
|
||||
} catch (e) {
|
||||
setResult({ ok: 0, message: e.message || 'Validation failed' });
|
||||
return;
|
||||
}
|
||||
postJson(url, payload).then(function (res) {
|
||||
if (!options.silent) {
|
||||
setResult(res);
|
||||
|
|
@ -300,10 +467,15 @@
|
|||
|
||||
if ((res.ok || 0) === 1) {
|
||||
var data = res.data || {};
|
||||
var consentRef = (data.consentId || data.consentRequestId || data.consent_id || data.requestId || data.request_id || '').toString();
|
||||
if (consentRef) {
|
||||
document.getElementById('consent_id').value = consentRef;
|
||||
document.getElementById('f_consent_id').value = consentRef;
|
||||
var consentReqRef = (data.abdm_consent_request_id || data.consentRequestId || data.consent_request_id || '').toString();
|
||||
if (consentReqRef && !isGatewayRequestId(consentReqRef)) {
|
||||
setFieldValue(['consent_request_id', 'consent_id'], consentReqRef);
|
||||
setFieldValue(['f_consent_id'], consentReqRef);
|
||||
}
|
||||
|
||||
var consentArtifactRef = (data.abdm_consent_artifact_id || data.consentId || data.consent_id || '').toString();
|
||||
if (isValidConsentArtifactId(consentArtifactRef)) {
|
||||
setFieldValue(['consent_artifact_id', 'consent_id'], consentArtifactRef);
|
||||
}
|
||||
|
||||
var state = (data.workflow_state || data.consent_status || data.status || res.workflow_state || '').toString();
|
||||
|
|
@ -312,9 +484,20 @@
|
|||
if (op === 'consent_request') {
|
||||
startConsentPolling();
|
||||
}
|
||||
if (op === 'consent_status' && isConsentApproved(state)) {
|
||||
if (op === 'consent_status' && isTerminalConsentState(state)) {
|
||||
stopConsentPolling();
|
||||
}
|
||||
} else if (op === 'consent_status') {
|
||||
var errState = ((res.data || {}).workflow_state || res.workflow_state || '').toString();
|
||||
var errText = (res.error || ((res.data || {}).error_text) || '').toString().toLowerCase();
|
||||
if (isTerminalConsentState(errState) || errText.indexOf('mapping_error') !== -1) {
|
||||
stopConsentPolling();
|
||||
}
|
||||
} else if (op === 'consent_fetch') {
|
||||
var fetchErr = (res.error || ((res.data || {}).error_text) || '').toString().toLowerCase();
|
||||
if (fetchErr.indexOf('mapping_error') !== -1) {
|
||||
setFieldValue(['consent_artifact_id', 'consent_id'], '');
|
||||
}
|
||||
}
|
||||
|
||||
fetchTimeline();
|
||||
|
|
@ -325,7 +508,14 @@
|
|||
|
||||
function startConsentPolling() {
|
||||
stopConsentPolling();
|
||||
consentPollAttempts = 0;
|
||||
consentPollTimer = setInterval(function () {
|
||||
consentPollAttempts++;
|
||||
if (consentPollAttempts > consentPollMaxAttempts) {
|
||||
stopConsentPolling();
|
||||
setResult({ ok: 0, message: 'Consent status polling stopped after max retries. Use Refresh/Status manually or wait for callback.' });
|
||||
return;
|
||||
}
|
||||
run('consent_status', { silent: true });
|
||||
}, 15000);
|
||||
}
|
||||
|
|
@ -335,16 +525,17 @@
|
|||
clearInterval(consentPollTimer);
|
||||
consentPollTimer = null;
|
||||
}
|
||||
consentPollAttempts = 0;
|
||||
}
|
||||
|
||||
function fetchTimeline() {
|
||||
var qs = new URLSearchParams({
|
||||
hfr_id: document.getElementById('f_hfr_id').value.trim(),
|
||||
consent_id: document.getElementById('f_consent_id').value.trim(),
|
||||
transaction_id: document.getElementById('f_transaction_id').value.trim(),
|
||||
abha_address: document.getElementById('f_abha_address').value.trim(),
|
||||
date_from: document.getElementById('f_date_from').value,
|
||||
date_to: document.getElementById('f_date_to').value
|
||||
hfr_id: getFieldValue(['f_hfr_id']),
|
||||
consent_id: getFieldValue(['f_consent_id']),
|
||||
transaction_id: getFieldValue(['f_transaction_id']),
|
||||
abha_address: getFieldValue(['f_abha_address']),
|
||||
date_from: getFieldValue(['f_date_from']),
|
||||
date_to: getFieldValue(['f_date_to'])
|
||||
});
|
||||
|
||||
fetch('<?= base_url('AbdmHiu/timeline') ?>?' + qs.toString(), {
|
||||
|
|
@ -373,13 +564,19 @@
|
|||
});
|
||||
}
|
||||
|
||||
document.getElementById('btnPatientSearch').addEventListener('click', searchPatients);
|
||||
document.getElementById('patient_search').addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
searchPatients();
|
||||
}
|
||||
});
|
||||
var btnPatientSearch = document.getElementById('btnPatientSearch');
|
||||
if (btnPatientSearch) {
|
||||
btnPatientSearch.addEventListener('click', searchPatients);
|
||||
}
|
||||
var patientSearch = document.getElementById('patient_search');
|
||||
if (patientSearch) {
|
||||
patientSearch.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
searchPatients();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('button[data-op]').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
|
|
@ -387,12 +584,28 @@
|
|||
});
|
||||
});
|
||||
|
||||
document.getElementById('consent_id').addEventListener('input', function () {
|
||||
syncButtons();
|
||||
});
|
||||
var consentReqInput = fieldEl(['consent_request_id', 'consent_id']);
|
||||
if (consentReqInput) {
|
||||
consentReqInput.addEventListener('input', function () {
|
||||
syncButtons();
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('btnApplyFilter').addEventListener('click', fetchTimeline);
|
||||
document.getElementById('btnRefreshTimeline').addEventListener('click', fetchTimeline);
|
||||
var consentArtifactInput = fieldEl(['consent_artifact_id']);
|
||||
if (consentArtifactInput) {
|
||||
consentArtifactInput.addEventListener('input', function () {
|
||||
syncButtons();
|
||||
});
|
||||
}
|
||||
|
||||
var btnApplyFilter = document.getElementById('btnApplyFilter');
|
||||
if (btnApplyFilter) {
|
||||
btnApplyFilter.addEventListener('click', fetchTimeline);
|
||||
}
|
||||
var btnRefreshTimeline = document.getElementById('btnRefreshTimeline');
|
||||
if (btnRefreshTimeline) {
|
||||
btnRefreshTimeline.addEventListener('click', fetchTimeline);
|
||||
}
|
||||
|
||||
syncButtons();
|
||||
fetchTimeline();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user