From 9cb4ccd701b34bd825474505c8c17b7373b2b4aa Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Thu, 8 Oct 2026 11:41:30 +0530 Subject: [PATCH] fix(abdm): resolve JSON parse error on fetch and auto-reconcile revoked consents --- app/Config/Filters.php | 6 +++ app/Controllers/Patient.php | 58 ++++++++++++++++++--- app/Libraries/Abdm/M3HiuGatewayClient.php | 6 +++ app/Libraries/Abdm/M3HiuWorkflowService.php | 22 +++++--- app/Views/billing/Patient_Profile_Opd_V.php | 11 +++- 5 files changed, 88 insertions(+), 15 deletions(-) diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 9f0ab6c..ccb9eb0 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -83,6 +83,12 @@ class Filters extends BaseFilters 'except' => [ 'Opd/*', 'Opd_prescription/*', + 'billing/patient/abdm_*', + 'billing/patient/abdm_*/*', + 'AbdmHiu', + 'AbdmHiu/*', + 'AbdmGateway/*', + 'api/*', ], ], // 'honeypot', diff --git a/app/Controllers/Patient.php b/app/Controllers/Patient.php index 7fb1c13..0a53a3a 100644 --- a/app/Controllers/Patient.php +++ b/app/Controllers/Patient.php @@ -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, diff --git a/app/Libraries/Abdm/M3HiuGatewayClient.php b/app/Libraries/Abdm/M3HiuGatewayClient.php index 2c546ae..2c0ee5b 100644 --- a/app/Libraries/Abdm/M3HiuGatewayClient.php +++ b/app/Libraries/Abdm/M3HiuGatewayClient.php @@ -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'); } diff --git a/app/Libraries/Abdm/M3HiuWorkflowService.php b/app/Libraries/Abdm/M3HiuWorkflowService.php index d979bb5..4255fd5 100644 --- a/app/Libraries/Abdm/M3HiuWorkflowService.php +++ b/app/Libraries/Abdm/M3HiuWorkflowService.php @@ -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 [ diff --git a/app/Views/billing/Patient_Profile_Opd_V.php b/app/Views/billing/Patient_Profile_Opd_V.php index ad8ba4c..f7a4815 100644 --- a/app/Views/billing/Patient_Profile_Opd_V.php +++ b/app/Views/billing/Patient_Profile_Opd_V.php @@ -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) {