fix(abdm): resolve JSON parse error on fetch and auto-reconcile revoked consents

This commit is contained in:
DevSoftTech Server 2026-10-08 11:41:30 +05:30
parent cb0a1967a4
commit 9cb4ccd701
5 changed files with 88 additions and 15 deletions

View File

@ -83,6 +83,12 @@ class Filters extends BaseFilters
'except' => [
'Opd/*',
'Opd_prescription/*',
'billing/patient/abdm_*',
'billing/patient/abdm_*/*',
'AbdmHiu',
'AbdmHiu/*',
'AbdmGateway/*',
'api/*',
],
],
// 'honeypot',

View File

@ -2853,7 +2853,10 @@ class Patient extends BaseController
'message' => 'Records fetched successfully using existing granted consent.',
'documents_persisted' => $reqDocumentsPersisted,
'documents_updated' => $reqDocumentsUpdated,
'data' => ['data_fetch' => $fetchResult, 'data_fetch_by_request_id' => $requestIdFetchResult],
'data' => [
'data_fetch' => $this->sanitizeFetchResultForResponse($fetchResult),
'data_fetch_by_request_id' => $this->sanitizeFetchResultForResponse($requestIdFetchResult),
],
]);
}
}
@ -2887,7 +2890,10 @@ class Patient extends BaseController
'message' => 'Records fetched successfully from historical ABDM records for this ABHA address.',
'documents_persisted' => $abhaDocumentsPersisted,
'documents_updated' => $abhaDocumentsUpdated,
'data' => ['data_fetch' => $fetchResult, 'data_fetch_by_abha_address' => $abhaFetchResult],
'data' => [
'data_fetch' => $this->sanitizeFetchResultForResponse($fetchResult),
'data_fetch_by_abha_address' => $this->sanitizeFetchResultForResponse($abhaFetchResult),
],
]);
}
}
@ -2900,7 +2906,7 @@ class Patient extends BaseController
'message' => 'Consent is granted, but the health information provider has not sent any records yet. Please try again in a few minutes.',
'documents_persisted' => 0,
'documents_updated' => 0,
'data' => ['data_fetch' => $fetchResult],
'data' => ['data_fetch' => $this->sanitizeFetchResultForResponse($fetchResult)],
]);
}
@ -2910,10 +2916,23 @@ class Patient extends BaseController
'message' => 'Records fetched successfully using existing granted consent.',
'documents_persisted' => $documentsPersisted,
'documents_updated' => $documentsUpdated,
'data' => ['data_fetch' => $fetchResult],
'data' => ['data_fetch' => $this->sanitizeFetchResultForResponse($fetchResult)],
]);
}
private function sanitizeFetchResultForResponse(array $result): array
{
$clean = $result;
if (isset($clean['sessions']) && is_array($clean['sessions'])) {
$clean['sessions_count'] = count($clean['sessions']);
unset($clean['sessions']);
}
if (isset($clean['data']) && is_array($clean['data']) && isset($clean['data']['sessions'])) {
unset($clean['data']['sessions']);
}
return $clean;
}
/**
* Consent detail breakdown for the "View Consent" modal — shows the requested vs
* granted Health Information Types for the current consent artifact, plus the
@ -3037,9 +3056,11 @@ class Patient extends BaseController
continue;
}
$sessionRequestId = trim((string) ($consent['request_id'] ?? ''));
$sessionGatewayRequestId = trim((string) ($consent['gateway_request_id'] ?? ''));
$sessionConsentRequestId = trim((string) ($consent['consent_request_id'] ?? ''));
$sessionConsentId = trim((string) ($consent['consent_id'] ?? ''));
if ($sessionRequestId === '' && $sessionConsentRequestId === '' && $sessionConsentId === '') {
$rawConsentId = trim((string) ($consent['consent_id'] ?? ''));
$sessionConsentId = $rawConsentId !== '' ? trim((string) explode(':', $rawConsentId)[0]) : '';
if ($sessionRequestId === '' && $sessionGatewayRequestId === '' && $sessionConsentRequestId === '' && $sessionConsentId === '') {
continue;
}
$reconcileJobs[] = [
@ -3047,6 +3068,7 @@ class Patient extends BaseController
'payload' => [
'abha_address' => $abhaAddress,
'request_id' => $sessionRequestId,
'gateway_request_id' => $sessionGatewayRequestId,
'abdm_consent_request_id' => $sessionConsentRequestId,
'abdm_consent_artifact_id' => $sessionConsentId,
'consent_id' => $sessionConsentId,
@ -3058,7 +3080,8 @@ class Patient extends BaseController
$snapshot = $this->getLatestAbdmSyncSnapshot($abhaAddress);
$requestId = trim((string) ($snapshot['request_id'] ?? ''));
$consentRequestId = trim((string) ($snapshot['consent_request_id'] ?? ''));
$consentId = trim((string) ($snapshot['consent_id'] ?? ''));
$rawConsentId = trim((string) ($snapshot['consent_id'] ?? ''));
$consentId = $rawConsentId !== '' ? trim((string) explode(':', $rawConsentId)[0]) : '';
if ($requestId === '' && $consentRequestId === '' && $consentId === '') {
return $this->response->setStatusCode(422)->setJSON([
'ok' => 0,
@ -5211,6 +5234,7 @@ class Patient extends BaseController
$rowRequestId = trim((string) ($row['request_id'] ?? ''));
$rowAbdmConsentRequestId = trim((string) ($row['abdm_consent_request_id'] ?? ''));
$rowConsentId = trim((string) ($row['consent_id'] ?? ''));
$rowGatewayRequestId = trim((string) ($row['gateway_request_id'] ?? ''));
if ($operation === 'CONSENT_REQUEST') {
$anchorKey = 'anchor_' . count($order);
@ -5219,6 +5243,9 @@ class Patient extends BaseController
if ($rowRequestId !== '') {
$requestIdToAnchor[$rowRequestId] = $anchorKey;
}
if ($rowGatewayRequestId !== '') {
$requestIdToAnchor[$rowGatewayRequestId] = $anchorKey;
}
// Reconcile/status rows usually carry only the ABDM ids, so the
// anchor must publish its own ids too or those rows cannot match.
if ($rowAbdmConsentRequestId !== '') {
@ -5229,6 +5256,9 @@ class Patient extends BaseController
if ($rowConsentId !== '') {
$abdmIdToAnchor[$rowConsentId] = $anchorKey;
$requestIdToAnchor[$rowConsentId] = $anchorKey;
$cleanConsentId = trim((string) explode(':', $rowConsentId)[0]);
$abdmIdToAnchor[$cleanConsentId] = $anchorKey;
$requestIdToAnchor[$cleanConsentId] = $anchorKey;
}
$openAnchor = $anchorKey;
continue;
@ -5239,8 +5269,12 @@ class Patient extends BaseController
$anchorKey = $abdmIdToAnchor[$rowAbdmConsentRequestId];
} elseif ($rowRequestId !== '' && isset($requestIdToAnchor[$rowRequestId])) {
$anchorKey = $requestIdToAnchor[$rowRequestId];
} elseif ($rowGatewayRequestId !== '' && isset($requestIdToAnchor[$rowGatewayRequestId])) {
$anchorKey = $requestIdToAnchor[$rowGatewayRequestId];
} elseif ($rowConsentId !== '' && isset($requestIdToAnchor[$rowConsentId])) {
$anchorKey = $requestIdToAnchor[$rowConsentId];
} elseif ($rowConsentId !== '' && isset($requestIdToAnchor[trim((string) explode(':', $rowConsentId)[0])])) {
$anchorKey = $requestIdToAnchor[trim((string) explode(':', $rowConsentId)[0])];
} elseif ($openAnchor !== null && ($rowAbdmConsentRequestId === '' || empty($anchorHasAbdmId[$openAnchor]))) {
// Covers the first reconcile that discovers the ABDM id. A row
// naming a consent request that belongs elsewhere must not be
@ -5268,8 +5302,14 @@ class Patient extends BaseController
if ($rowRequestId !== '') {
$requestIdToAnchor[$rowRequestId] = $anchorKey;
}
if ($rowGatewayRequestId !== '') {
$requestIdToAnchor[$rowGatewayRequestId] = $anchorKey;
}
if ($rowConsentId !== '') {
$requestIdToAnchor[$rowConsentId] = $anchorKey;
$cleanConsentId = trim((string) explode(':', $rowConsentId)[0]);
$abdmIdToAnchor[$cleanConsentId] = $anchorKey;
$requestIdToAnchor[$cleanConsentId] = $anchorKey;
}
}
@ -5704,7 +5744,9 @@ class Patient extends BaseController
'consent' => [
'id' => $displayId,
'request_id' => trim((string) (is_array($consentRequestRow) ? ($consentRequestRow['request_id'] ?? '') : ($best['request_id'] ?? ''))),
'consent_id' => $consentId,
'gateway_request_id' => trim((string) (is_array($consentRequestRow) ? ($consentRequestRow['gateway_request_id'] ?? '') : ($best['gateway_request_id'] ?? ''))),
'consent_id' => $consentId !== '' ? trim((string) explode(':', $consentId)[0]) : '',
'consent_id_raw' => $consentId,
'consent_request_id' => $consentRequestId,
'abha_address' => $abhaAddress,
'status' => $phase,

View File

@ -70,6 +70,12 @@ class M3HiuGatewayClient
public function reconcileConsentStatus(array $payload): array
{
if (isset($payload['consent_id']) && is_string($payload['consent_id']) && str_contains($payload['consent_id'], ':')) {
$payload['consent_id'] = trim((string) explode(':', $payload['consent_id'])[0]);
}
if (isset($payload['consentId']) && is_string($payload['consentId']) && str_contains($payload['consentId'], ':')) {
$payload['consentId'] = trim((string) explode(':', $payload['consentId'])[0]);
}
return $this->callGet('/v1/hiu/consent/status', $payload, 'consent.status.reconcile');
}

View File

@ -202,7 +202,7 @@ class M3HiuWorkflowService
$fields = $this->db->getFieldNames('abdm_hiu_workflows') ?? [];
$selectCols = ['id', 'request_id', 'transaction_id', 'consent_id', 'abha_address', 'hfr_id', 'workflow_state', 'status', 'created_at', 'updated_at'];
foreach (['abdm_consent_request_id', 'abdm_consent_artifact_id', 'completed_at', 'expired_at', 'revoked_at'] as $col) {
foreach (['gateway_request_id', 'abdm_consent_request_id', 'abdm_consent_artifact_id', 'completed_at', 'expired_at', 'revoked_at'] as $col) {
if (in_array($col, $fields, true)) {
$selectCols[] = $col;
}
@ -212,11 +212,8 @@ class M3HiuWorkflowService
->select(implode(', ', $selectCols))
->where('operation', 'consent_request')
->where('status', 'success')
->whereNotIn('workflow_state', ['COMPLETED', 'EXPIRED', 'REVOKED', 'DENIED']);
->whereNotIn('workflow_state', ['EXPIRED', 'REVOKED', 'DENIED']);
if (in_array('completed_at', $fields, true)) {
$builder->where('completed_at IS NULL');
}
if (in_array('expired_at', $fields, true)) {
$builder->where('expired_at IS NULL');
}
@ -263,6 +260,7 @@ class M3HiuWorkflowService
$payload = [
'request_id' => $requestId,
'gateway_request_id' => trim((string) ($row['gateway_request_id'] ?? '')),
'transaction_id' => trim((string) ($row['transaction_id'] ?? '')),
'consent_id' => trim((string) ($row['consent_id'] ?? '')),
'abdm_consent_request_id' => trim((string) ($row['abdm_consent_request_id'] ?? '')),
@ -1083,8 +1081,20 @@ class M3HiuWorkflowService
if ($requestIdGenerated) {
$requestIdRef = '';
}
$gatewayRequestId = trim((string) ($clean['gateway_request_id'] ?? ''));
if ($gatewayRequestId !== '' && preg_match('/^REQ-/i', $gatewayRequestId) === 1 && stripos($gatewayRequestId, 'REQ-HIU-') !== 0) {
$requestIdRef = $gatewayRequestId;
} elseif (stripos($requestIdRef, 'REQ-HIU-') === 0 && $gatewayRequestId === '') {
$requestIdRef = '';
}
$consentRequestId = trim((string) ($clean['abdm_consent_request_id'] ?? $clean['consentRequestId'] ?? ''));
$consentId = trim((string) ($clean['abdm_consent_artifact_id'] ?? $clean['consentId'] ?? $clean['consent_id'] ?? ''));
$rawConsentId = trim((string) ($clean['abdm_consent_artifact_id'] ?? $clean['consentId'] ?? $clean['consent_id'] ?? ''));
$consentId = $rawConsentId !== '' ? trim((string) explode(':', $rawConsentId)[0]) : '';
if ($requestIdRef === '' && $consentRequestId === '' && $consentId === '') {
$requestIdRef = trim((string) ($clean['request_id'] ?? $clean['requestId'] ?? ''));
}
if ($requestIdRef === '' && $consentRequestId === '' && $consentId === '') {
return [

View File

@ -1769,7 +1769,16 @@ $(function() {
} else if (data.granted) {
setAbdmStatus('Live status: GRANTED. Fetched data for ' + (data.artifacts_fetched || 0) + ' facility artifact(s)' + (data.data_fetch_failed ? (', ' + data.data_fetch_failed + ' failed') : '') + '.');
} else {
setAbdmStatus('Live status refreshed from ABDM bridge.');
var latestStatus = ((currentAbdmRequests[0] && currentAbdmRequests[0].status) || '').toString().toUpperCase();
if (latestStatus === 'REVOKED') {
setAbdmStatus('Live status: REVOKED (consent was revoked in patient ABHA app).');
} else if (latestStatus === 'DENIED') {
setAbdmStatus('Live status: DENIED (consent was denied by patient).');
} else if (latestStatus === 'EXPIRED') {
setAbdmStatus('Live status: EXPIRED.');
} else {
setAbdmStatus('Live status refreshed from ABDM bridge.');
}
}
})
.catch(function(err) {