ABDM HIU callback hardening, OPD ABHA optional confirm, and AJAX memory cleanup
This commit is contained in:
parent
658e67ad3b
commit
acabc6abdd
|
|
@ -624,6 +624,10 @@ $routes->post('AbdmGateway/scan_share_link_patient/(:num)', 'AbdmGateway::scanSh
|
||||||
$routes->post('AbdmGateway/scan_share_lookup_result_callback', 'AbdmGateway::scanShareLookupResultCallback');
|
$routes->post('AbdmGateway/scan_share_lookup_result_callback', 'AbdmGateway::scanShareLookupResultCallback');
|
||||||
$routes->post('AbdmGateway/consent_request', 'AbdmGateway::consentRequest', ['filter' => $abdmPermFilter]);
|
$routes->post('AbdmGateway/consent_request', 'AbdmGateway::consentRequest', ['filter' => $abdmPermFilter]);
|
||||||
$routes->post('AbdmGateway/consent_callback', 'AbdmGateway::consentCallback');
|
$routes->post('AbdmGateway/consent_callback', 'AbdmGateway::consentCallback');
|
||||||
|
$routes->post('AbdmGateway/hip_consent_notify_callback', 'AbdmGateway::hipConsentNotifyCallback');
|
||||||
|
$routes->post('api/v3/hiu/consent/on-notify', 'AbdmGateway::hipConsentNotifyCallback');
|
||||||
|
$routes->post('api/v3/hiu/consent/on_notify', 'AbdmGateway::hipConsentNotifyCallback');
|
||||||
|
$routes->post('api/v3/hiu/consent/notify', 'AbdmGateway::hipConsentNotifyCallback');
|
||||||
$routes->post('AbdmGateway/consent_revoked_callback', 'AbdmGateway::consentRevokedCallback');
|
$routes->post('AbdmGateway/consent_revoked_callback', 'AbdmGateway::consentRevokedCallback');
|
||||||
$routes->post('AbdmGateway/record_linked_callback', 'AbdmGateway::recordLinkedCallback');
|
$routes->post('AbdmGateway/record_linked_callback', 'AbdmGateway::recordLinkedCallback');
|
||||||
// ABDM M3 HIU (HMS -> Gateway)
|
// ABDM M3 HIU (HMS -> Gateway)
|
||||||
|
|
@ -637,9 +641,17 @@ $routes->post('AbdmHiu/consent_request_fetch', 'AbdmHiu::consentRequestFetch', [
|
||||||
$routes->post('AbdmHiu/health_information_request', 'AbdmHiu::healthInformationRequest', ['filter' => $abdmPermFilter]);
|
$routes->post('AbdmHiu/health_information_request', 'AbdmHiu::healthInformationRequest', ['filter' => $abdmPermFilter]);
|
||||||
$routes->post('AbdmHiu/consent_reconcile', 'AbdmHiu::consentReconcile', ['filter' => $abdmPermFilter]);
|
$routes->post('AbdmHiu/consent_reconcile', 'AbdmHiu::consentReconcile', ['filter' => $abdmPermFilter]);
|
||||||
$routes->post('AbdmHiu/data_fetch', 'AbdmHiu::dataFetch', ['filter' => $abdmPermFilter]);
|
$routes->post('AbdmHiu/data_fetch', 'AbdmHiu::dataFetch', ['filter' => $abdmPermFilter]);
|
||||||
|
$routes->post('AbdmHiu/consent_update_callback', 'AbdmHiu::consentUpdateWebhook');
|
||||||
|
$routes->post('AbdmHiu/health_information_on_request_callback', 'AbdmHiu::healthInformationOnRequestWebhook');
|
||||||
|
$routes->post('AbdmHiu/health_information_data_push_callback', 'AbdmHiu::healthInformationDataPushWebhook');
|
||||||
$routes->post('api/v3/hiu/consent/request', 'AbdmHiu::consentRequest', ['filter' => $abdmPermFilter]);
|
$routes->post('api/v3/hiu/consent/request', 'AbdmHiu::consentRequest', ['filter' => $abdmPermFilter]);
|
||||||
$routes->post('api/v3/hiu/consent/request/status', 'AbdmHiu::consentRequestStatus', ['filter' => $abdmPermFilter]);
|
$routes->post('api/v3/hiu/consent/request/status', 'AbdmHiu::consentRequestStatus', ['filter' => $abdmPermFilter]);
|
||||||
$routes->post('api/v3/hiu/consent/request/fetch', 'AbdmHiu::consentRequestFetch', ['filter' => $abdmPermFilter]);
|
$routes->post('api/v3/hiu/consent/request/fetch', 'AbdmHiu::consentRequestFetch', ['filter' => $abdmPermFilter]);
|
||||||
|
$routes->post('api/v3/hiu/consent/on-update', 'AbdmHiu::consentUpdateWebhook');
|
||||||
|
$routes->post('api/v3/hiu/consent/on_update', 'AbdmHiu::consentUpdateWebhook');
|
||||||
|
$routes->post('api/v3/hiu/health-information/on-request', 'AbdmHiu::healthInformationOnRequestWebhook');
|
||||||
|
$routes->post('api/v3/hiu/health-information/on_request', 'AbdmHiu::healthInformationOnRequestWebhook');
|
||||||
|
$routes->post('api/v3/hiu/data/push', 'AbdmHiu::healthInformationDataPushWebhook');
|
||||||
$routes->post('api/v3/hiu/health-information/request', 'AbdmHiu::healthInformationRequest', ['filter' => $abdmPermFilter]);
|
$routes->post('api/v3/hiu/health-information/request', 'AbdmHiu::healthInformationRequest', ['filter' => $abdmPermFilter]);
|
||||||
$routes->post('api/v1/hiu/consent/status', 'AbdmHiu::consentReconcile', ['filter' => $abdmPermFilter]);
|
$routes->post('api/v1/hiu/consent/status', 'AbdmHiu::consentReconcile', ['filter' => $abdmPermFilter]);
|
||||||
$routes->post('api/v1/hiu/data/fetch', 'AbdmHiu::dataFetch', ['filter' => $abdmPermFilter]);
|
$routes->post('api/v1/hiu/data/fetch', 'AbdmHiu::dataFetch', ['filter' => $abdmPermFilter]);
|
||||||
|
|
|
||||||
|
|
@ -670,6 +670,121 @@ class AbdmGateway extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// HIP Consent Notify Callback — gateway forwards consent notify updates.
|
||||||
|
// POST /AbdmGateway/hip_consent_notify_callback (no auth filter — public webhook)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
public function hipConsentNotifyCallback()
|
||||||
|
{
|
||||||
|
$signatureFailure = $this->validateWebhookSignature();
|
||||||
|
if ($signatureFailure !== null) {
|
||||||
|
return $signatureFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = $this->request->getJSON(true);
|
||||||
|
if (! is_array($payload) || empty($payload)) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'error_text' => 'Invalid JSON payload',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$consentObj = [];
|
||||||
|
if (is_array($payload['consent'] ?? null)) {
|
||||||
|
$consentObj = (array) $payload['consent'];
|
||||||
|
} elseif (is_array($payload['consentDetail'] ?? null)) {
|
||||||
|
$consentObj = (array) $payload['consentDetail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestId = trim((string) (
|
||||||
|
$payload['request_id']
|
||||||
|
?? $payload['requestId']
|
||||||
|
?? ($payload['response']['requestId'] ?? '')
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
$consentId = trim((string) (
|
||||||
|
$consentObj['id']
|
||||||
|
?? $payload['consent_id']
|
||||||
|
?? $payload['consentId']
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
$abhaAddress = trim((string) (
|
||||||
|
$consentObj['patient']['id']
|
||||||
|
?? $payload['abha_address']
|
||||||
|
?? $payload['abha_id']
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
$statusRaw = trim((string) (
|
||||||
|
$consentObj['status']
|
||||||
|
?? $payload['consent_status']
|
||||||
|
?? $payload['status']
|
||||||
|
?? 'status_checked'
|
||||||
|
));
|
||||||
|
$status = strtolower($statusRaw);
|
||||||
|
|
||||||
|
// Keep existing M3 workflow in sync for dateRange and consent status lookups.
|
||||||
|
try {
|
||||||
|
$m3Service = new \App\Libraries\Abdm\M3HiuWorkflowService();
|
||||||
|
$m3Service->ingestConsentUpdateWebhook($payload);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Fail-open: callback must still ACK to avoid upstream retry storms.
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->tableExists('consent_logs')) {
|
||||||
|
$logStatus = match ($status) {
|
||||||
|
'granted', 'approved', 'active' => 'GRANTED',
|
||||||
|
'revoked', 'denied' => 'REVOKED',
|
||||||
|
'expired' => 'EXPIRED',
|
||||||
|
default => 'GRANTED',
|
||||||
|
};
|
||||||
|
|
||||||
|
$dateRange = $consentObj['permission']['dateRange'] ?? [];
|
||||||
|
$expiresAt = trim((string) ($dateRange['to'] ?? $payload['expires_at'] ?? ''));
|
||||||
|
|
||||||
|
$this->db->table('consent_logs')->insert([
|
||||||
|
'patient_id' => (int) ($payload['patient_id'] ?? 0),
|
||||||
|
'abha_id' => $abhaAddress !== '' ? $abhaAddress : null,
|
||||||
|
'consent_id' => $consentId !== '' ? $consentId : ($requestId !== '' ? $requestId : null),
|
||||||
|
'purpose' => trim((string) ($consentObj['purpose']['text'] ?? '')) ?: null,
|
||||||
|
'status' => $logStatus,
|
||||||
|
'created_at' => Time::now('Asia/Kolkata')->toDateTimeString(),
|
||||||
|
'expires_at' => $expiresAt !== '' ? $expiresAt : null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->db->tableExists('abdm_api_logs')) {
|
||||||
|
$this->db->table('abdm_api_logs')->insert([
|
||||||
|
'channel' => 'bridge',
|
||||||
|
'event_type' => 'abdm.hip.consent.notify.callback',
|
||||||
|
'endpoint' => '/AbdmGateway/hip_consent_notify_callback',
|
||||||
|
'http_method' => 'POST',
|
||||||
|
'entity_type' => 'consent',
|
||||||
|
'entity_id' => $consentId !== '' ? $consentId : $requestId,
|
||||||
|
'request_json' => (string) json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||||
|
'response_code' => 202,
|
||||||
|
'response_json' => (string) json_encode([
|
||||||
|
'ok' => 1,
|
||||||
|
'status' => 'accepted',
|
||||||
|
'request_id' => $requestId,
|
||||||
|
'consent_id' => $consentId,
|
||||||
|
'consent_status' => $statusRaw,
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||||
|
'status' => 'success',
|
||||||
|
'error_message' => null,
|
||||||
|
'created_at' => Time::now('Asia/Kolkata')->toDateTimeString(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setStatusCode(202)->setJSON([
|
||||||
|
'ok' => 1,
|
||||||
|
'status' => 'accepted',
|
||||||
|
'request_id' => $requestId,
|
||||||
|
'consent_id' => $consentId !== '' ? $consentId : null,
|
||||||
|
'consent_status' => $statusRaw,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function scanShareLookup()
|
public function scanShareLookup()
|
||||||
{
|
{
|
||||||
if (! $this->request->isAJAX()) {
|
if (! $this->request->isAJAX()) {
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,75 @@ class AbdmHiu extends BaseController
|
||||||
return $this->handleOperation('data_fetch', 'DATA_PENDING');
|
return $this->handleOperation('data_fetch', 'DATA_PENDING');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function consentUpdateWebhook()
|
||||||
|
{
|
||||||
|
$signatureFailure = $this->validateGatewayWebhookSignature();
|
||||||
|
if ($signatureFailure !== null) {
|
||||||
|
return $signatureFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = $this->request->getJSON(true);
|
||||||
|
if (! is_array($payload) || empty($payload)) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'error' => 'Invalid consent callback payload',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->service->ingestConsentUpdateWebhook($payload);
|
||||||
|
$httpCode = (int) ($result['ok'] ?? 0) === 1 ? 202 : 400;
|
||||||
|
|
||||||
|
return $this->response->setStatusCode($httpCode)->setJSON($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function healthInformationOnRequestWebhook()
|
||||||
|
{
|
||||||
|
$signatureFailure = $this->validateGatewayWebhookSignature();
|
||||||
|
if ($signatureFailure !== null) {
|
||||||
|
return $signatureFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = $this->request->getJSON(true);
|
||||||
|
if (! is_array($payload) || empty($payload)) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'error' => 'Invalid health-information callback payload',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->service->ingestHealthInformationCallback($payload, 'hi_on_request_callback');
|
||||||
|
|
||||||
|
return $this->response->setStatusCode(202)->setJSON([
|
||||||
|
'ok' => 1,
|
||||||
|
'status' => 'accepted',
|
||||||
|
'result' => $result,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function healthInformationDataPushWebhook()
|
||||||
|
{
|
||||||
|
$signatureFailure = $this->validateGatewayWebhookSignature();
|
||||||
|
if ($signatureFailure !== null) {
|
||||||
|
return $signatureFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = $this->request->getJSON(true);
|
||||||
|
if (! is_array($payload) || empty($payload)) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'error' => 'Invalid data push payload',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->service->ingestHealthInformationCallback($payload, 'hi_data_push_callback');
|
||||||
|
|
||||||
|
return $this->response->setStatusCode(202)->setJSON([
|
||||||
|
'ok' => 1,
|
||||||
|
'status' => 'accepted',
|
||||||
|
'result' => $result,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function timeline()
|
public function timeline()
|
||||||
{
|
{
|
||||||
$filters = [
|
$filters = [
|
||||||
|
|
@ -328,4 +397,59 @@ class AbdmHiu extends BaseController
|
||||||
{
|
{
|
||||||
return preg_match('/^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$/', trim($value)) === 1;
|
return preg_match('/^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$/', trim($value)) === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function validateGatewayWebhookSignature(): ?\CodeIgniter\HTTP\ResponseInterface
|
||||||
|
{
|
||||||
|
$secret = trim((string) (
|
||||||
|
$this->readRuntimeSetting('GATEWAY_TO_HMS_HMAC_SECRET')
|
||||||
|
?: $this->readRuntimeSetting('EKA_WEBHOOK_SECRET')
|
||||||
|
?: $this->readRuntimeSetting('HMS_WEBHOOK_SECRET')
|
||||||
|
));
|
||||||
|
|
||||||
|
// If no secret is configured, accept payload to keep callback flow functional.
|
||||||
|
if ($secret === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rawBody = (string) $this->request->getBody();
|
||||||
|
$provided = trim((string) (
|
||||||
|
$this->request->getHeaderLine('X-Signature')
|
||||||
|
?: $this->request->getHeaderLine('X-Hub-Signature-256')
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($provided === '') {
|
||||||
|
return $this->response->setStatusCode(401)->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'error' => 'Missing webhook signature',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$expected = hash_hmac('sha256', $rawBody, $secret);
|
||||||
|
$normalizedProvided = preg_replace('/^sha256=/i', '', $provided) ?? '';
|
||||||
|
|
||||||
|
if (! hash_equals($expected, $normalizedProvided)) {
|
||||||
|
return $this->response->setStatusCode(401)->setJSON([
|
||||||
|
'ok' => 0,
|
||||||
|
'error' => 'Invalid webhook signature',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function readRuntimeSetting(string $name): string
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('hospital_setting')) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = $this->db->table('hospital_setting')
|
||||||
|
->select('s_value')
|
||||||
|
->where('s_name', $name)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
|
||||||
|
return trim((string) ($row['s_value'] ?? ''));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1660,15 +1660,6 @@ class Opd extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($abhaAddress !== '' && ! $this->isValidAbhaAddress($abhaAddress)) {
|
|
||||||
return $this->response->setJSON([
|
|
||||||
'insertid' => 0,
|
|
||||||
'error_text' => 'Invalid ABHA Address format.',
|
|
||||||
'csrfName' => csrf_token(),
|
|
||||||
'csrfHash' => csrf_hash(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql = "select * from patient_master where id=" . $pid;
|
$sql = "select * from patient_master where id=" . $pid;
|
||||||
$query = $this->db->query($sql);
|
$query = $this->db->query($sql);
|
||||||
$personInfo = $query->getResult();
|
$personInfo = $query->getResult();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Database\Migrations;
|
||||||
|
|
||||||
|
use CodeIgniter\Database\Migration;
|
||||||
|
|
||||||
|
class AdjustAbdmHiuWorkflowRequestOperationIndex extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$indexes = $this->db->query('SHOW INDEX FROM abdm_hiu_workflows')->getResultArray();
|
||||||
|
$hasReqOpUnique = false;
|
||||||
|
$hasReqOpIdx = false;
|
||||||
|
|
||||||
|
foreach ($indexes as $idx) {
|
||||||
|
$name = (string) ($idx['Key_name'] ?? '');
|
||||||
|
if ($name === 'uniq_abdm_hiu_req_op') {
|
||||||
|
$hasReqOpUnique = true;
|
||||||
|
}
|
||||||
|
if ($name === 'idx_abdm_hiu_req_op') {
|
||||||
|
$hasReqOpIdx = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasReqOpUnique) {
|
||||||
|
$this->db->query('ALTER TABLE abdm_hiu_workflows DROP INDEX uniq_abdm_hiu_req_op');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $hasReqOpIdx) {
|
||||||
|
$this->db->query('ALTER TABLE abdm_hiu_workflows ADD INDEX idx_abdm_hiu_req_op (request_id, operation)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$indexes = $this->db->query('SHOW INDEX FROM abdm_hiu_workflows')->getResultArray();
|
||||||
|
$hasReqOpUnique = false;
|
||||||
|
$hasReqOpIdx = false;
|
||||||
|
|
||||||
|
foreach ($indexes as $idx) {
|
||||||
|
$name = (string) ($idx['Key_name'] ?? '');
|
||||||
|
if ($name === 'uniq_abdm_hiu_req_op') {
|
||||||
|
$hasReqOpUnique = true;
|
||||||
|
}
|
||||||
|
if ($name === 'idx_abdm_hiu_req_op') {
|
||||||
|
$hasReqOpIdx = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasReqOpIdx) {
|
||||||
|
$this->db->query('ALTER TABLE abdm_hiu_workflows DROP INDEX idx_abdm_hiu_req_op');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $hasReqOpUnique) {
|
||||||
|
$this->db->query('ALTER TABLE abdm_hiu_workflows ADD UNIQUE INDEX uniq_abdm_hiu_req_op (request_id, operation)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,23 @@ class M3HiuGatewayClient
|
||||||
$this->baseUrl = rtrim((string) (getenv('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api'), '/');
|
$this->baseUrl = rtrim((string) (getenv('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api'), '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sanitizeBearerToken(string $token): string
|
||||||
|
{
|
||||||
|
$token = trim($token);
|
||||||
|
if ($token === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stripos($token, 'Bearer ') === 0) {
|
||||||
|
$token = trim(substr($token, 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = trim($token, " \t\n\r\0\x0B\"'");
|
||||||
|
$token = (string) preg_replace('/\s+/u', '', $token);
|
||||||
|
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
public function consentRequest(array $payload): array
|
public function consentRequest(array $payload): array
|
||||||
{
|
{
|
||||||
return $this->call('/v3/hiu/consent/request', $payload, 'consent.request');
|
return $this->call('/v3/hiu/consent/request', $payload, 'consent.request');
|
||||||
|
|
@ -54,6 +71,10 @@ class M3HiuGatewayClient
|
||||||
$token = (string) ($settings['token'] ?? '');
|
$token = (string) ($settings['token'] ?? '');
|
||||||
$hfrId = (string) ($settings['hfr_id'] ?? '');
|
$hfrId = (string) ($settings['hfr_id'] ?? '');
|
||||||
$hospitalId = (string) ($settings['hospital_id'] ?? '');
|
$hospitalId = (string) ($settings['hospital_id'] ?? '');
|
||||||
|
$runtimeBaseUrl = trim((string) ($settings['base_url'] ?? ''));
|
||||||
|
if ($runtimeBaseUrl !== '') {
|
||||||
|
$this->baseUrl = rtrim($runtimeBaseUrl, '/');
|
||||||
|
}
|
||||||
|
|
||||||
$payloadHfrId = trim((string) ($payload['hfr_id'] ?? ''));
|
$payloadHfrId = trim((string) ($payload['hfr_id'] ?? ''));
|
||||||
if ($payloadHfrId !== '' && strcasecmp($payloadHfrId, $hfrId) !== 0) {
|
if ($payloadHfrId !== '' && strcasecmp($payloadHfrId, $hfrId) !== 0) {
|
||||||
|
|
@ -105,7 +126,7 @@ class M3HiuGatewayClient
|
||||||
: ('HTTP ' . $httpCode . ' non-JSON response');
|
: ('HTTP ' . $httpCode . ' non-JSON response');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isCloudFrontBlockedResponse($httpCode, $raw, $response ?? [])) {
|
if ($this->isCloudFrontBlockedResponse($httpCode, $raw, $decoded ?? [])) {
|
||||||
$errorText = 'Gateway edge block (CloudFront 403 Request blocked). Retry later or ask gateway team to unblock the endpoint.';
|
$errorText = 'Gateway edge block (CloudFront 403 Request blocked). Retry later or ask gateway team to unblock the endpoint.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,6 +199,10 @@ class M3HiuGatewayClient
|
||||||
$token = (string) ($settings['token'] ?? '');
|
$token = (string) ($settings['token'] ?? '');
|
||||||
$hfrId = (string) ($settings['hfr_id'] ?? '');
|
$hfrId = (string) ($settings['hfr_id'] ?? '');
|
||||||
$hospitalId = (string) ($settings['hospital_id'] ?? '');
|
$hospitalId = (string) ($settings['hospital_id'] ?? '');
|
||||||
|
$runtimeBaseUrl = trim((string) ($settings['base_url'] ?? ''));
|
||||||
|
if ($runtimeBaseUrl !== '') {
|
||||||
|
$this->baseUrl = rtrim($runtimeBaseUrl, '/');
|
||||||
|
}
|
||||||
|
|
||||||
$payloadHfrId = trim((string) ($query['hfr_id'] ?? ''));
|
$payloadHfrId = trim((string) ($query['hfr_id'] ?? ''));
|
||||||
if ($payloadHfrId !== '' && strcasecmp($payloadHfrId, $hfrId) !== 0) {
|
if ($payloadHfrId !== '' && strcasecmp($payloadHfrId, $hfrId) !== 0) {
|
||||||
|
|
@ -305,24 +330,46 @@ class M3HiuGatewayClient
|
||||||
return ['ok' => 0, 'http_code' => 500, 'error_text' => 'hospital_setting table not found', 'retryable' => 0];
|
return ['ok' => 0, 'http_code' => 500, 'error_text' => 'hospital_setting table not found', 'retryable' => 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows = $this->db->table('hospital_setting')
|
$settingFields = $this->db->getFieldNames('hospital_setting') ?? [];
|
||||||
|
$orderCol = null;
|
||||||
|
foreach (['id', 's_id'] as $candidateOrderCol) {
|
||||||
|
if (in_array($candidateOrderCol, $settingFields, true)) {
|
||||||
|
$orderCol = $candidateOrderCol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rowsBuilder = $this->db->table('hospital_setting')
|
||||||
->select('s_name, s_value')
|
->select('s_name, s_value')
|
||||||
->whereIn('s_name', ['EATRIA_BRIDGE_TOKEN', 'ABDM_HFR_ID', 'ABDM_HOSPITAL_ID'])
|
->whereIn('s_name', [
|
||||||
->get()
|
'EATRIA_BRIDGE_TOKEN',
|
||||||
->getResultArray();
|
'ABDM_HFR_ID',
|
||||||
|
'H_HFR_ID',
|
||||||
|
'ABDM_HOSPITAL_HFR_ID',
|
||||||
|
'ABDM_HOSPITAL_ID',
|
||||||
|
'ABDM_BRIDGE_HOSPITAL_ID',
|
||||||
|
'EATRIA_BRIDGE_HOSPITAL_ID',
|
||||||
|
'EATRIA_BRIDGE_URL',
|
||||||
|
]);
|
||||||
|
if ($orderCol !== null) {
|
||||||
|
$rowsBuilder->orderBy($orderCol, 'DESC');
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $rowsBuilder->get()->getResultArray();
|
||||||
|
|
||||||
$kv = [];
|
$kv = [];
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
$key = trim((string) ($row['s_name'] ?? ''));
|
$key = trim((string) ($row['s_name'] ?? ''));
|
||||||
if ($key === '') {
|
if ($key === '' || array_key_exists($key, $kv)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$kv[$key] = trim((string) ($row['s_value'] ?? ''));
|
$kv[$key] = trim((string) ($row['s_value'] ?? ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = (string) ($kv['EATRIA_BRIDGE_TOKEN'] ?? '');
|
$token = $this->sanitizeBearerToken((string) ($kv['EATRIA_BRIDGE_TOKEN'] ?? ''));
|
||||||
$hfrId = (string) ($kv['ABDM_HFR_ID'] ?? '');
|
$hfrId = (string) ($kv['ABDM_HFR_ID'] ?? $kv['H_HFR_ID'] ?? $kv['ABDM_HOSPITAL_HFR_ID'] ?? '');
|
||||||
$hospitalId = (string) ($kv['ABDM_HOSPITAL_ID'] ?? '');
|
$hospitalId = (string) ($kv['ABDM_HOSPITAL_ID'] ?? $kv['ABDM_BRIDGE_HOSPITAL_ID'] ?? $kv['EATRIA_BRIDGE_HOSPITAL_ID'] ?? '');
|
||||||
|
$baseUrl = trim((string) ($kv['EATRIA_BRIDGE_URL'] ?? ''));
|
||||||
|
|
||||||
if ($token === '') {
|
if ($token === '') {
|
||||||
return [
|
return [
|
||||||
|
|
@ -346,6 +393,7 @@ class M3HiuGatewayClient
|
||||||
'token' => $token,
|
'token' => $token,
|
||||||
'hfr_id' => $hfrId,
|
'hfr_id' => $hfrId,
|
||||||
'hospital_id' => $hospitalId,
|
'hospital_id' => $hospitalId,
|
||||||
|
'base_url' => $baseUrl,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -246,6 +246,7 @@ class M3HiuWorkflowService
|
||||||
}
|
}
|
||||||
|
|
||||||
$builder = $this->db->table('abdm_hiu_workflows')->select('*')->orderBy('id', 'DESC');
|
$builder = $this->db->table('abdm_hiu_workflows')->select('*')->orderBy('id', 'DESC');
|
||||||
|
$fields = $this->db->getFieldNames('abdm_hiu_workflows') ?? [];
|
||||||
|
|
||||||
$hfrId = trim((string) ($filters['hfr_id'] ?? ''));
|
$hfrId = trim((string) ($filters['hfr_id'] ?? ''));
|
||||||
if ($hfrId !== '') {
|
if ($hfrId !== '') {
|
||||||
|
|
@ -253,7 +254,16 @@ class M3HiuWorkflowService
|
||||||
}
|
}
|
||||||
$consentId = trim((string) ($filters['consent_id'] ?? ''));
|
$consentId = trim((string) ($filters['consent_id'] ?? ''));
|
||||||
if ($consentId !== '') {
|
if ($consentId !== '') {
|
||||||
|
$builder->groupStart();
|
||||||
$builder->where('consent_id', $consentId);
|
$builder->where('consent_id', $consentId);
|
||||||
|
if (in_array('abdm_consent_request_id', $fields, true)) {
|
||||||
|
$builder->orWhere('abdm_consent_request_id', $consentId);
|
||||||
|
}
|
||||||
|
if (in_array('abdm_consent_artifact_id', $fields, true)) {
|
||||||
|
$builder->orWhere('abdm_consent_artifact_id', $consentId);
|
||||||
|
}
|
||||||
|
$builder->orWhere('request_id', $consentId);
|
||||||
|
$builder->groupEnd();
|
||||||
}
|
}
|
||||||
$transactionId = trim((string) ($filters['transaction_id'] ?? ''));
|
$transactionId = trim((string) ($filters['transaction_id'] ?? ''));
|
||||||
if ($transactionId !== '') {
|
if ($transactionId !== '') {
|
||||||
|
|
@ -261,7 +271,12 @@ class M3HiuWorkflowService
|
||||||
}
|
}
|
||||||
$abhaAddress = trim((string) ($filters['abha_address'] ?? ''));
|
$abhaAddress = trim((string) ($filters['abha_address'] ?? ''));
|
||||||
if ($abhaAddress !== '') {
|
if ($abhaAddress !== '') {
|
||||||
|
$builder->groupStart();
|
||||||
$builder->where('abha_address', $abhaAddress);
|
$builder->where('abha_address', $abhaAddress);
|
||||||
|
// Older rows may store ABHA address only inside request_json consent.patient.id.
|
||||||
|
$builder->orLike('request_json', '"abha_address":"' . str_replace('"', '\\"', $abhaAddress) . '"');
|
||||||
|
$builder->orLike('request_json', '"id":"' . str_replace('"', '\\"', $abhaAddress) . '"');
|
||||||
|
$builder->groupEnd();
|
||||||
}
|
}
|
||||||
$from = trim((string) ($filters['date_from'] ?? ''));
|
$from = trim((string) ($filters['date_from'] ?? ''));
|
||||||
if ($from !== '') {
|
if ($from !== '') {
|
||||||
|
|
@ -356,6 +371,188 @@ class M3HiuWorkflowService
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function ingestConsentUpdateWebhook(array $payload): array
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||||
|
return [
|
||||||
|
'ok' => 1,
|
||||||
|
'persisted' => 0,
|
||||||
|
'message' => 'abdm_hiu_workflows table not found; callback accepted without persistence',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$consent = is_array($payload['consent'] ?? null) ? (array) $payload['consent'] : [];
|
||||||
|
$consentDetail = is_array($payload['consentDetail'] ?? null) ? (array) $payload['consentDetail'] : [];
|
||||||
|
$consentObj = ! empty($consent) ? $consent : $consentDetail;
|
||||||
|
|
||||||
|
$status = strtolower(trim((string) (
|
||||||
|
$consentObj['status']
|
||||||
|
?? $payload['consent_status']
|
||||||
|
?? $payload['status']
|
||||||
|
?? ''
|
||||||
|
)));
|
||||||
|
if ($status === '') {
|
||||||
|
$status = 'status_checked';
|
||||||
|
}
|
||||||
|
|
||||||
|
$consentId = trim((string) (
|
||||||
|
$consentObj['id']
|
||||||
|
?? $payload['consent_id']
|
||||||
|
?? $payload['consentId']
|
||||||
|
?? $payload['abdm_consent_artifact_id']
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
|
||||||
|
$requestId = trim((string) (
|
||||||
|
$payload['request_id']
|
||||||
|
?? $payload['requestId']
|
||||||
|
?? ($payload['response']['requestId'] ?? '')
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
$transactionId = trim((string) (
|
||||||
|
$payload['transaction_id']
|
||||||
|
?? $payload['transactionId']
|
||||||
|
?? ($consentObj['transactionId'] ?? '')
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
|
||||||
|
$abhaAddress = trim((string) (
|
||||||
|
($consentObj['patient']['id'] ?? '')
|
||||||
|
?: ($payload['abha_address'] ?? '')
|
||||||
|
?: ($payload['abha_id'] ?? '')
|
||||||
|
));
|
||||||
|
$abhaAddress = $this->canonicalizeAbhaAddress($abhaAddress, (string) ($payload['abha_id'] ?? ''));
|
||||||
|
|
||||||
|
$persistPayload = [
|
||||||
|
'request_id' => $requestId,
|
||||||
|
'requestId' => $requestId,
|
||||||
|
'transaction_id' => $transactionId,
|
||||||
|
'transactionId' => $transactionId,
|
||||||
|
'consent_id' => $consentId,
|
||||||
|
'consentId' => $consentId,
|
||||||
|
'abdm_consent_artifact_id' => $consentId,
|
||||||
|
'abha_address' => $abhaAddress,
|
||||||
|
'hfr_id' => trim((string) ($payload['hfr_id'] ?? '')),
|
||||||
|
'consent' => $consentObj,
|
||||||
|
'callback' => $payload,
|
||||||
|
];
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'ok' => 1,
|
||||||
|
'http_code' => 202,
|
||||||
|
'status' => $status,
|
||||||
|
'consent_id' => $consentId,
|
||||||
|
'consentId' => $consentId,
|
||||||
|
'abdm_consent_artifact_id' => $consentId,
|
||||||
|
'request_id' => $requestId,
|
||||||
|
'transaction_id' => $transactionId,
|
||||||
|
'abha_address' => $abhaAddress,
|
||||||
|
'consent' => $consentObj,
|
||||||
|
'data' => $payload,
|
||||||
|
'retryable' => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
$state = match ($status) {
|
||||||
|
'granted', 'approved', 'active' => 'GRANTED',
|
||||||
|
'revoked', 'denied' => 'REVOKED',
|
||||||
|
'expired' => 'EXPIRED',
|
||||||
|
default => 'STATUS_CHECKED',
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->upsertWorkflow('consent_callback', $persistPayload, $result, $state, 'success', 202, false);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ok' => 1,
|
||||||
|
'persisted' => 1,
|
||||||
|
'workflow_state' => $state,
|
||||||
|
'consent_id' => $consentId,
|
||||||
|
'request_id' => $requestId,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ingestHealthInformationCallback(array $payload, string $operation = 'hi_on_request_callback'): array
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||||
|
return [
|
||||||
|
'ok' => 1,
|
||||||
|
'persisted' => 0,
|
||||||
|
'message' => 'abdm_hiu_workflows table not found; callback accepted without persistence',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$requestId = trim((string) (
|
||||||
|
$payload['request_id']
|
||||||
|
?? $payload['requestId']
|
||||||
|
?? ($payload['response']['requestId'] ?? '')
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
$transactionId = trim((string) (
|
||||||
|
$payload['transaction_id']
|
||||||
|
?? $payload['transactionId']
|
||||||
|
?? ($payload['response']['transactionId'] ?? '')
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
$consentId = trim((string) (
|
||||||
|
($payload['hiRequest']['consent']['id'] ?? '')
|
||||||
|
?: ($payload['consent']['id'] ?? '')
|
||||||
|
?: ($payload['consent_id'] ?? '')
|
||||||
|
?: ($payload['consentId'] ?? '')
|
||||||
|
));
|
||||||
|
$abhaAddress = $this->canonicalizeAbhaAddress(trim((string) (
|
||||||
|
($payload['patient']['id'] ?? '')
|
||||||
|
?: ($payload['abha_address'] ?? '')
|
||||||
|
?: ($payload['abha_id'] ?? '')
|
||||||
|
)), (string) ($payload['abha_id'] ?? ''));
|
||||||
|
|
||||||
|
$errorCode = trim((string) ($payload['error']['code'] ?? ''));
|
||||||
|
$errorMessage = trim((string) ($payload['error']['message'] ?? ''));
|
||||||
|
$errorText = trim(($errorCode !== '' ? $errorCode . ' ' : '') . $errorMessage);
|
||||||
|
|
||||||
|
$hasData = isset($payload['fhir_bundle'])
|
||||||
|
|| isset($payload['fhirBundle'])
|
||||||
|
|| isset($payload['bundles'])
|
||||||
|
|| isset($payload['entries'])
|
||||||
|
|| isset($payload['data']);
|
||||||
|
|
||||||
|
$ok = $errorText === '' ? 1 : 0;
|
||||||
|
$state = $hasData ? 'DATA_RECEIVED' : ($ok === 1 ? 'DATA_PENDING' : 'FAILED');
|
||||||
|
|
||||||
|
$persistPayload = [
|
||||||
|
'request_id' => $requestId,
|
||||||
|
'requestId' => $requestId,
|
||||||
|
'transaction_id' => $transactionId,
|
||||||
|
'transactionId' => $transactionId,
|
||||||
|
'consent_id' => $consentId,
|
||||||
|
'consentId' => $consentId,
|
||||||
|
'abdm_consent_artifact_id' => $consentId,
|
||||||
|
'abha_address' => $abhaAddress,
|
||||||
|
'callback' => $payload,
|
||||||
|
];
|
||||||
|
$result = [
|
||||||
|
'ok' => $ok,
|
||||||
|
'http_code' => 202,
|
||||||
|
'status' => strtolower($state),
|
||||||
|
'error_text' => $errorText,
|
||||||
|
'request_id' => $requestId,
|
||||||
|
'transaction_id' => $transactionId,
|
||||||
|
'consent_id' => $consentId,
|
||||||
|
'abha_address' => $abhaAddress,
|
||||||
|
'retryable' => 0,
|
||||||
|
'data' => $payload,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->upsertWorkflow($operation, $persistPayload, $result, $state, $ok === 1 ? 'success' : 'failed', 202, false);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ok' => 1,
|
||||||
|
'persisted' => 1,
|
||||||
|
'workflow_state' => $state,
|
||||||
|
'request_id' => $requestId,
|
||||||
|
'transaction_id' => $transactionId,
|
||||||
|
'error_text' => $errorText,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
private function dispatch(string $operation, array $payload): array
|
private function dispatch(string $operation, array $payload): array
|
||||||
{
|
{
|
||||||
return match ($operation) {
|
return match ($operation) {
|
||||||
|
|
@ -554,11 +751,14 @@ class M3HiuWorkflowService
|
||||||
|
|
||||||
if ($operation === 'consent_fetch') {
|
if ($operation === 'consent_fetch') {
|
||||||
$consentId = trim((string) ($clean['abdm_consent_artifact_id'] ?? $clean['consentId'] ?? $clean['consent_id'] ?? ''));
|
$consentId = trim((string) ($clean['abdm_consent_artifact_id'] ?? $clean['consentId'] ?? $clean['consent_id'] ?? ''));
|
||||||
|
if ($consentId === '') {
|
||||||
|
$consentId = $this->resolveAbdmConsentArtifactIdFromWorkflow($clean);
|
||||||
|
}
|
||||||
if ($consentId === '') {
|
if ($consentId === '') {
|
||||||
return [
|
return [
|
||||||
'ok' => 0,
|
'ok' => 0,
|
||||||
'http_code' => 422,
|
'http_code' => 422,
|
||||||
'error_text' => 'consentId is required for consent fetch.',
|
'error_text' => 'consentId is required for consent fetch. Consent artifact is not available yet; run Check Consent Status until consent is GRANTED, then fetch using artifact id.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if ($this->isGatewayRequestIdPattern($consentId)) {
|
if ($this->isGatewayRequestIdPattern($consentId)) {
|
||||||
|
|
@ -611,10 +811,11 @@ class M3HiuWorkflowService
|
||||||
|
|
||||||
if (! isset($hiRequest['dateRange']) || ! is_array($hiRequest['dateRange'])) {
|
if (! isset($hiRequest['dateRange']) || ! is_array($hiRequest['dateRange'])) {
|
||||||
$hiRequest['dateRange'] = [
|
$hiRequest['dateRange'] = [
|
||||||
'from' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('-30 days')),
|
'from' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('-365 days')),
|
||||||
'to' => gmdate('Y-m-d\T00:00:00.000\Z'),
|
'to' => gmdate('Y-m-d\T00:00:00.000\Z'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
$hiRequest['dateRange'] = $this->resolveSafeHiDateRange($requestIdRef, $consentId, $hiRequest['dateRange']);
|
||||||
if (trim((string) ($hiRequest['dataPushUrl'] ?? '')) === '') {
|
if (trim((string) ($hiRequest['dataPushUrl'] ?? '')) === '') {
|
||||||
$hiRequest['dataPushUrl'] = rtrim((string) (getenv('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api'), '/') . '/v3/hiu/data/push';
|
$hiRequest['dataPushUrl'] = rtrim((string) (getenv('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api'), '/') . '/v3/hiu/data/push';
|
||||||
}
|
}
|
||||||
|
|
@ -743,7 +944,7 @@ class M3HiuWorkflowService
|
||||||
'permission' => [
|
'permission' => [
|
||||||
'accessMode' => 'VIEW',
|
'accessMode' => 'VIEW',
|
||||||
'dateRange' => [
|
'dateRange' => [
|
||||||
'from' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('-30 days')),
|
'from' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('-365 days')),
|
||||||
'to' => gmdate('Y-m-d\T00:00:00.000\Z'),
|
'to' => gmdate('Y-m-d\T00:00:00.000\Z'),
|
||||||
],
|
],
|
||||||
'dataEraseAt' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('+365 days')),
|
'dataEraseAt' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('+365 days')),
|
||||||
|
|
@ -793,15 +994,257 @@ class M3HiuWorkflowService
|
||||||
'consent' => [
|
'consent' => [
|
||||||
'id' => $consentId,
|
'id' => $consentId,
|
||||||
],
|
],
|
||||||
'dateRange' => [
|
'dateRange' => $this->resolveSafeHiDateRange($requestIdRef, $consentId, [
|
||||||
'from' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('-30 days')),
|
'from' => gmdate('Y-m-d\T00:00:00.000\Z', strtotime('-365 days')),
|
||||||
'to' => gmdate('Y-m-d\T00:00:00.000\Z'),
|
'to' => gmdate('Y-m-d\T00:00:00.000\Z'),
|
||||||
],
|
]),
|
||||||
'dataPushUrl' => rtrim((string) (getenv('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api'), '/') . '/v3/hiu/data/push',
|
'dataPushUrl' => rtrim((string) (getenv('EATRIA_BRIDGE_URL') ?: 'https://abdm-bridge.e-atria.in/api'), '/') . '/v3/hiu/data/push',
|
||||||
'transactionId' => $transactionId,
|
'transactionId' => $transactionId,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a safe HI date range by enforcing consent-granted permission window
|
||||||
|
* (from consent fetch/reconcile details), then clamping with from <= to <= now.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $fallbackRange
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
private function resolveSafeHiDateRange(string $requestIdRef, string $consentId, array $fallbackRange): array
|
||||||
|
{
|
||||||
|
$grantedRange = $this->resolveGrantedConsentDateRange($requestIdRef, $consentId);
|
||||||
|
$consentRange = ! empty($grantedRange)
|
||||||
|
? $grantedRange
|
||||||
|
: $this->resolveConsentRequestDateRangeByRequestId($requestIdRef);
|
||||||
|
|
||||||
|
$fromRaw = trim((string) ($consentRange['from'] ?? $fallbackRange['from'] ?? ''));
|
||||||
|
$toRaw = trim((string) ($consentRange['to'] ?? $fallbackRange['to'] ?? ''));
|
||||||
|
|
||||||
|
$nowMs = (int) floor(microtime(true) * 1000);
|
||||||
|
$fromMs = $this->parseIsoUtcToEpochMillis($fromRaw);
|
||||||
|
$toMs = $this->parseIsoUtcToEpochMillis($toRaw);
|
||||||
|
$grantedFromMs = $this->parseIsoUtcToEpochMillis((string) ($grantedRange['from'] ?? ''));
|
||||||
|
$grantedToMs = $this->parseIsoUtcToEpochMillis((string) ($grantedRange['to'] ?? ''));
|
||||||
|
|
||||||
|
if ($fromMs === null) {
|
||||||
|
$fromMs = $nowMs - (365 * 24 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
if ($toMs === null) {
|
||||||
|
$toMs = $nowMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HMS-side requirement: keep HI range equal to or strictly within granted consent permission range.
|
||||||
|
if ($grantedFromMs !== null && $fromMs < $grantedFromMs) {
|
||||||
|
$fromMs = $grantedFromMs;
|
||||||
|
}
|
||||||
|
if ($grantedToMs !== null && $toMs > $grantedToMs) {
|
||||||
|
$toMs = $grantedToMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($toMs > $nowMs) {
|
||||||
|
$toMs = $nowMs;
|
||||||
|
}
|
||||||
|
if ($fromMs > $toMs) {
|
||||||
|
if ($grantedFromMs !== null && $grantedFromMs <= $toMs) {
|
||||||
|
$fromMs = $grantedFromMs;
|
||||||
|
} else {
|
||||||
|
$fromMs = $toMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'from' => $this->formatEpochMillisToIsoUtc($fromMs),
|
||||||
|
'to' => $this->formatEpochMillisToIsoUtc($toMs),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseIsoUtcToEpochMillis(string $value): ?int
|
||||||
|
{
|
||||||
|
$raw = trim($value);
|
||||||
|
if ($raw === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dt = new \DateTimeImmutable($raw);
|
||||||
|
} catch (\Throwable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sec = (int) $dt->format('U');
|
||||||
|
$micro = (int) $dt->format('u');
|
||||||
|
|
||||||
|
return ($sec * 1000) + intdiv($micro, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatEpochMillisToIsoUtc(int $epochMs): string
|
||||||
|
{
|
||||||
|
$sec = intdiv($epochMs, 1000);
|
||||||
|
$ms = $epochMs - ($sec * 1000);
|
||||||
|
if ($ms < 0) {
|
||||||
|
$ms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dt = (new \DateTimeImmutable('@' . $sec))->setTimezone(new \DateTimeZone('UTC'));
|
||||||
|
|
||||||
|
return $dt->format('Y-m-d\TH:i:s') . '.' . str_pad((string) $ms, 3, '0', STR_PAD_LEFT) . 'Z';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{from?: string, to?: string}
|
||||||
|
*/
|
||||||
|
private function resolveGrantedConsentDateRange(string $requestId, string $consentId): array
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('abdm_hiu_workflows') ?? [];
|
||||||
|
$builder = $this->db->table('abdm_hiu_workflows')
|
||||||
|
->select('response_json, request_json')
|
||||||
|
->whereIn('operation', ['consent_fetch', 'consent_reconcile', 'consent_status', 'consent_callback']);
|
||||||
|
|
||||||
|
$hasFilter = false;
|
||||||
|
$builder->groupStart();
|
||||||
|
if ($requestId !== '') {
|
||||||
|
$builder->orWhere('request_id', $requestId);
|
||||||
|
$hasFilter = true;
|
||||||
|
}
|
||||||
|
if ($consentId !== '') {
|
||||||
|
$builder->orWhere('consent_id', $consentId);
|
||||||
|
$hasFilter = true;
|
||||||
|
if (in_array('abdm_consent_artifact_id', $fields, true)) {
|
||||||
|
$builder->orWhere('abdm_consent_artifact_id', $consentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$builder->groupEnd();
|
||||||
|
|
||||||
|
if (! $hasFilter) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $builder->orderBy('id', 'DESC')->get(30)->getResultArray();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
foreach (['response_json', 'request_json'] as $jsonCol) {
|
||||||
|
$decoded = json_decode((string) ($row[$jsonCol] ?? ''), true);
|
||||||
|
if (! is_array($decoded)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$range = $this->extractPermissionDateRange($decoded);
|
||||||
|
if (! empty($range)) {
|
||||||
|
return $range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
* @return array{from?: string, to?: string}
|
||||||
|
*/
|
||||||
|
private function extractPermissionDateRange(array $data): array
|
||||||
|
{
|
||||||
|
$pathCandidates = [
|
||||||
|
$data['consent']['permission']['dateRange'] ?? null,
|
||||||
|
$data['consentDetail']['permission']['dateRange'] ?? null,
|
||||||
|
$data['data']['consent']['permission']['dateRange'] ?? null,
|
||||||
|
$data['data']['consentDetail']['permission']['dateRange'] ?? null,
|
||||||
|
$data['response']['consent']['permission']['dateRange'] ?? null,
|
||||||
|
$data['response']['consentDetail']['permission']['dateRange'] ?? null,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($pathCandidates as $candidate) {
|
||||||
|
if (is_array($candidate)) {
|
||||||
|
$from = trim((string) ($candidate['from'] ?? ''));
|
||||||
|
$to = trim((string) ($candidate['to'] ?? ''));
|
||||||
|
if ($from !== '' || $to !== '') {
|
||||||
|
$out = [];
|
||||||
|
if ($from !== '') {
|
||||||
|
$out['from'] = $from;
|
||||||
|
}
|
||||||
|
if ($to !== '') {
|
||||||
|
$out['to'] = $to;
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['permission']) && is_array($data['permission'])) {
|
||||||
|
$range = $data['permission']['dateRange'] ?? null;
|
||||||
|
if (is_array($range)) {
|
||||||
|
$from = trim((string) ($range['from'] ?? ''));
|
||||||
|
$to = trim((string) ($range['to'] ?? ''));
|
||||||
|
if ($from !== '' || $to !== '') {
|
||||||
|
$out = [];
|
||||||
|
if ($from !== '') {
|
||||||
|
$out['from'] = $from;
|
||||||
|
}
|
||||||
|
if ($to !== '') {
|
||||||
|
$out['to'] = $to;
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($data as $value) {
|
||||||
|
if (! is_array($value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nested = $this->extractPermissionDateRange($value);
|
||||||
|
if (! empty($nested)) {
|
||||||
|
return $nested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{from?: string, to?: string}
|
||||||
|
*/
|
||||||
|
private function resolveConsentRequestDateRangeByRequestId(string $requestId): array
|
||||||
|
{
|
||||||
|
if ($requestId === '' || ! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = $this->db->table('abdm_hiu_workflows')
|
||||||
|
->select('request_json')
|
||||||
|
->where('operation', 'consent_request')
|
||||||
|
->where('request_id', $requestId)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
|
||||||
|
if (empty($row['request_json'])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = json_decode((string) $row['request_json'], true);
|
||||||
|
if (! is_array($decoded)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$from = trim((string) ($decoded['consent']['permission']['dateRange']['from'] ?? ''));
|
||||||
|
$to = trim((string) ($decoded['consent']['permission']['dateRange']['to'] ?? ''));
|
||||||
|
|
||||||
|
$out = [];
|
||||||
|
if ($from !== '') {
|
||||||
|
$out['from'] = $from;
|
||||||
|
}
|
||||||
|
if ($to !== '') {
|
||||||
|
$out['to'] = $to;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
private function readSettingValue(string $key): string
|
private function readSettingValue(string $key): string
|
||||||
{
|
{
|
||||||
if (! $this->db->tableExists('hospital_setting')) {
|
if (! $this->db->tableExists('hospital_setting')) {
|
||||||
|
|
@ -937,7 +1380,10 @@ class M3HiuWorkflowService
|
||||||
'consent_request' => 'REQUESTED',
|
'consent_request' => 'REQUESTED',
|
||||||
'consent_status' => 'STATUS_CHECKED',
|
'consent_status' => 'STATUS_CHECKED',
|
||||||
'consent_fetch' => 'CONSENT_FETCHED',
|
'consent_fetch' => 'CONSENT_FETCHED',
|
||||||
|
'consent_callback' => ($statusStr === 'granted' || $statusStr === 'approved' ? 'GRANTED' : ($statusStr === 'revoked' ? 'REVOKED' : ($statusStr === 'expired' ? 'EXPIRED' : 'STATUS_CHECKED'))),
|
||||||
'hi_request' => ($statusStr === 'completed' ? 'COMPLETED' : 'DATA_REQUESTED'),
|
'hi_request' => ($statusStr === 'completed' ? 'COMPLETED' : 'DATA_REQUESTED'),
|
||||||
|
'hi_on_request_callback' => (($statusStr === 'data_received' || isset($result['fhir_bundle']) || isset($result['bundles']) || isset($result['entries'])) ? 'DATA_RECEIVED' : ($ok ? 'DATA_PENDING' : 'FAILED')),
|
||||||
|
'hi_data_push_callback' => (($statusStr === 'data_received' || isset($result['fhir_bundle']) || isset($result['bundles']) || isset($result['entries'])) ? 'DATA_RECEIVED' : ($ok ? 'DATA_PENDING' : 'FAILED')),
|
||||||
'consent_reconcile' => ($statusStr === 'granted' ? 'GRANTED' : ($statusStr === 'revoked' ? 'REVOKED' : ($statusStr === 'expired' ? 'EXPIRED' : 'STATUS_CHECKED'))),
|
'consent_reconcile' => ($statusStr === 'granted' ? 'GRANTED' : ($statusStr === 'revoked' ? 'REVOKED' : ($statusStr === 'expired' ? 'EXPIRED' : 'STATUS_CHECKED'))),
|
||||||
'data_fetch' => (($statusStr === 'completed' || isset($result['fhir_bundle']) || isset($result['bundles'])) ? 'DATA_RECEIVED' : 'DATA_PENDING'),
|
'data_fetch' => (($statusStr === 'completed' || isset($result['fhir_bundle']) || isset($result['bundles'])) ? 'DATA_RECEIVED' : 'DATA_PENDING'),
|
||||||
default => 'COMPLETED',
|
default => 'COMPLETED',
|
||||||
|
|
@ -949,6 +1395,47 @@ class M3HiuWorkflowService
|
||||||
$requestId = trim((string) ($payload['request_id'] ?? ''));
|
$requestId = trim((string) ($payload['request_id'] ?? ''));
|
||||||
$transactionId = trim((string) ($payload['transaction_id'] ?? ''));
|
$transactionId = trim((string) ($payload['transaction_id'] ?? ''));
|
||||||
|
|
||||||
|
// HI operations should dedupe by transaction id so retries/new requests
|
||||||
|
// keep separate timeline rows instead of overwriting prior transactions.
|
||||||
|
if (in_array($operation, ['hi_request', 'data_fetch'], true)) {
|
||||||
|
if ($transactionId === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->db->table('abdm_hiu_workflows')
|
||||||
|
->select('*')
|
||||||
|
->where('operation', $operation)
|
||||||
|
->where('transaction_id', $transactionId)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->get(1)
|
||||||
|
->getRowArray() ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($operation, ['hi_on_request_callback', 'hi_data_push_callback'], true)) {
|
||||||
|
$builder = $this->db->table('abdm_hiu_workflows')->select('*')->where('operation', $operation);
|
||||||
|
if ($transactionId !== '') {
|
||||||
|
$row = $builder->where('transaction_id', $transactionId)->orderBy('id', 'DESC')->get(1)->getRowArray();
|
||||||
|
if (! empty($row)) {
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($requestId !== '') {
|
||||||
|
$row = $this->db->table('abdm_hiu_workflows')
|
||||||
|
->select('*')
|
||||||
|
->where('operation', $operation)
|
||||||
|
->where('request_id', $requestId)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->get(1)
|
||||||
|
->getRowArray();
|
||||||
|
if (! empty($row)) {
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$builder = $this->db->table('abdm_hiu_workflows')->select('*')->where('operation', $operation);
|
$builder = $this->db->table('abdm_hiu_workflows')->select('*')->where('operation', $operation);
|
||||||
if ($requestId !== '') {
|
if ($requestId !== '') {
|
||||||
$row = $builder->where('request_id', $requestId)->orderBy('id', 'DESC')->get(1)->getRowArray();
|
$row = $builder->where('request_id', $requestId)->orderBy('id', 'DESC')->get(1)->getRowArray();
|
||||||
|
|
@ -1089,21 +1576,67 @@ class M3HiuWorkflowService
|
||||||
int $forceId = 0
|
int $forceId = 0
|
||||||
): void {
|
): void {
|
||||||
$now = Time::now('Asia/Kolkata')->toDateTimeString();
|
$now = Time::now('Asia/Kolkata')->toDateTimeString();
|
||||||
$hmsRequestId = trim((string) ($payload['request_id'] ?? $payload['requestId'] ?? ''));
|
$hmsRequestId = $this->firstNonEmptyString([
|
||||||
|
$payload['request_id'] ?? null,
|
||||||
|
$payload['requestId'] ?? null,
|
||||||
|
]);
|
||||||
if ($hmsRequestId === '') {
|
if ($hmsRequestId === '') {
|
||||||
$hmsRequestId = trim((string) ($result['hms_request_id'] ?? ''));
|
$hmsRequestId = $this->firstNonEmptyString([
|
||||||
|
$result['hms_request_id'] ?? null,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
$gatewayRequestId = trim((string) ($result['gateway_request_id'] ?? $result['request_id'] ?? $result['requestId'] ?? ''));
|
$gatewayRequestId = $this->firstNonEmptyString([
|
||||||
$abdmConsentRequestId = trim((string) ($result['abdm_consent_request_id'] ?? $result['consent_request_id'] ?? $payload['consentRequestId'] ?? $payload['consent_request_id'] ?? ''));
|
$result['gateway_request_id'] ?? null,
|
||||||
$transactionId = trim((string) ($payload['transaction_id'] ?? $payload['transactionId'] ?? $result['transaction_id'] ?? ''));
|
$result['request_id'] ?? null,
|
||||||
$consentId = trim((string) ($payload['abdm_consent_artifact_id'] ?? $payload['consent_id'] ?? $payload['consentId'] ?? $result['abdm_consent_artifact_id'] ?? $result['consent_id'] ?? $result['consentId'] ?? ''));
|
$result['requestId'] ?? null,
|
||||||
|
]);
|
||||||
|
$abdmConsentRequestId = $this->firstNonEmptyString([
|
||||||
|
$result['abdm_consent_request_id'] ?? null,
|
||||||
|
$result['consent_request_id'] ?? null,
|
||||||
|
$payload['consentRequestId'] ?? null,
|
||||||
|
$payload['consent_request_id'] ?? null,
|
||||||
|
]);
|
||||||
|
$transactionId = $this->firstNonEmptyString([
|
||||||
|
$payload['transaction_id'] ?? null,
|
||||||
|
$payload['transactionId'] ?? null,
|
||||||
|
$result['transaction_id'] ?? null,
|
||||||
|
$result['transactionId'] ?? null,
|
||||||
|
]);
|
||||||
|
$consentId = $this->firstNonEmptyString([
|
||||||
|
$payload['abdm_consent_artifact_id'] ?? null,
|
||||||
|
$payload['consent_id'] ?? null,
|
||||||
|
$payload['consentId'] ?? null,
|
||||||
|
$payload['hiRequest']['consent']['id'] ?? null,
|
||||||
|
$result['abdm_consent_artifact_id'] ?? null,
|
||||||
|
$result['consent_id'] ?? null,
|
||||||
|
$result['consentId'] ?? null,
|
||||||
|
$result['hiRequest']['consent']['id'] ?? null,
|
||||||
|
]);
|
||||||
if ($consentId === '' && $operation !== 'consent_fetch' && $operation !== 'hi_request') {
|
if ($consentId === '' && $operation !== 'consent_fetch' && $operation !== 'hi_request') {
|
||||||
$consentId = $abdmConsentRequestId;
|
$consentId = $abdmConsentRequestId;
|
||||||
}
|
}
|
||||||
$abhaAddress = trim((string) ($payload['abha_address'] ?? $payload['abha_id'] ?? $result['abha_address'] ?? ''));
|
$abhaAddress = $this->firstNonEmptyString([
|
||||||
$hfrId = $this->normalizeStoredHfrId((string) ($payload['hfr_id'] ?? $result['hfr_id'] ?? ''));
|
$payload['abha_address'] ?? null,
|
||||||
$hospitalId = trim((string) ($payload['hospital_id'] ?? $result['hospital_id'] ?? ''));
|
$payload['abha_id'] ?? null,
|
||||||
$errorText = trim((string) ($result['error_text'] ?? $result['message'] ?? ''));
|
$result['abha_address'] ?? null,
|
||||||
|
]);
|
||||||
|
if ($abhaAddress === '') {
|
||||||
|
$abhaAddress = $this->firstNonEmptyString([
|
||||||
|
$payload['consent']['patient']['id'] ?? null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$hfrId = $this->normalizeStoredHfrId($this->firstNonEmptyString([
|
||||||
|
$payload['hfr_id'] ?? null,
|
||||||
|
$result['hfr_id'] ?? null,
|
||||||
|
]));
|
||||||
|
$hospitalId = $this->firstNonEmptyString([
|
||||||
|
$payload['hospital_id'] ?? null,
|
||||||
|
$result['hospital_id'] ?? null,
|
||||||
|
]);
|
||||||
|
$errorText = $this->firstNonEmptyString([
|
||||||
|
$result['error_text'] ?? null,
|
||||||
|
$result['message'] ?? null,
|
||||||
|
]);
|
||||||
|
|
||||||
$retryCount = 0;
|
$retryCount = 0;
|
||||||
$id = $forceId;
|
$id = $forceId;
|
||||||
|
|
@ -1185,6 +1718,21 @@ class M3HiuWorkflowService
|
||||||
return preg_match('/^REQ-/i', trim($value)) === 1;
|
return preg_match('/^REQ-/i', trim($value)) === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, mixed> $values
|
||||||
|
*/
|
||||||
|
private function firstNonEmptyString(array $values): string
|
||||||
|
{
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$text = trim((string) ($value ?? ''));
|
||||||
|
if ($text !== '') {
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
private function resolveAbdmConsentRequestIdFromWorkflow(array $payload): string
|
private function resolveAbdmConsentRequestIdFromWorkflow(array $payload): string
|
||||||
{
|
{
|
||||||
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||||
|
|
@ -1262,4 +1810,82 @@ class M3HiuWorkflowService
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolveAbdmConsentArtifactIdFromWorkflow(array $payload): string
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('abdm_hiu_workflows')) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('abdm_hiu_workflows') ?? [];
|
||||||
|
$hasAbdmConsentArtifactId = in_array('abdm_consent_artifact_id', $fields, true);
|
||||||
|
|
||||||
|
$requestId = trim((string) ($payload['requestId'] ?? $payload['request_id'] ?? ''));
|
||||||
|
$transactionId = trim((string) ($payload['transactionId'] ?? $payload['transaction_id'] ?? ''));
|
||||||
|
$abhaAddress = trim((string) ($payload['abha_address'] ?? $payload['abha_id'] ?? ''));
|
||||||
|
|
||||||
|
$tryBuilder = function (?string $whereCol, string $whereVal) use ($hasAbdmConsentArtifactId): string {
|
||||||
|
if ($whereCol === null || $whereVal === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$select = ['consent_id', 'response_json'];
|
||||||
|
if ($hasAbdmConsentArtifactId) {
|
||||||
|
$select[] = 'abdm_consent_artifact_id';
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $this->db->table('abdm_hiu_workflows')
|
||||||
|
->select(implode(', ', $select))
|
||||||
|
->where($whereCol, $whereVal)
|
||||||
|
->whereIn('operation', ['consent_fetch', 'consent_reconcile', 'consent_status', 'hi_request'])
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->get(25)
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$candidate = '';
|
||||||
|
if ($hasAbdmConsentArtifactId) {
|
||||||
|
$candidate = trim((string) ($row['abdm_consent_artifact_id'] ?? ''));
|
||||||
|
}
|
||||||
|
if ($candidate === '') {
|
||||||
|
$candidate = trim((string) ($row['consent_id'] ?? ''));
|
||||||
|
}
|
||||||
|
if ($candidate !== '' && ! $this->isGatewayRequestIdPattern($candidate)) {
|
||||||
|
return $candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = json_decode((string) ($row['response_json'] ?? ''), true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$candidate = trim((string) (
|
||||||
|
$decoded['abdm_consent_artifact_id']
|
||||||
|
?? $decoded['consent_id']
|
||||||
|
?? $decoded['consentId']
|
||||||
|
?? ''
|
||||||
|
));
|
||||||
|
if ($candidate !== '' && ! $this->isGatewayRequestIdPattern($candidate)) {
|
||||||
|
return $candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
$resolved = $tryBuilder('request_id', $requestId);
|
||||||
|
if ($resolved !== '') {
|
||||||
|
return $resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolved = $tryBuilder('transaction_id', $transactionId);
|
||||||
|
if ($resolved !== '') {
|
||||||
|
return $resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolved = $tryBuilder('abha_address', $abhaAddress);
|
||||||
|
if ($resolved !== '') {
|
||||||
|
return $resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -30,12 +30,6 @@
|
||||||
<input class="form-control" id="appointment_time" name="appointment_time"
|
<input class="form-control" id="appointment_time" name="appointment_time"
|
||||||
type="time" value="<?= date('H:i') ?>">
|
type="time" value="<?= date('H:i') ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
|
||||||
<label class="form-label">ABHA Address</label>
|
|
||||||
<input class="form-control" id="abha_address" name="abha_address" type="text"
|
|
||||||
maxlength="18" value="<?= esc($person_info[0]->abha_address ?? $person_info[0]->abha ?? '') ?>"
|
|
||||||
placeholder="e.g. ravi1234 or ravi.1234">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-3 mt-2" id="ShowDoctor">
|
<div class="row g-3 mt-2" id="ShowDoctor">
|
||||||
|
|
@ -95,7 +89,7 @@
|
||||||
|
|
||||||
$('#showfee').hide();
|
$('#showfee').hide();
|
||||||
|
|
||||||
$('#btnnextfee').click(function() {
|
$('#btnnextfee').off('click.opdAppt').on('click.opdAppt', function() {
|
||||||
var checkValue = $('#rdoc_id:checked').val();
|
var checkValue = $('#rdoc_id:checked').val();
|
||||||
if (!checkValue) {
|
if (!checkValue) {
|
||||||
alert('Please Select Doctor Name');
|
alert('Please Select Doctor Name');
|
||||||
|
|
@ -117,12 +111,12 @@
|
||||||
$('#ShowDoctor').hide();
|
$('#ShowDoctor').hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#backtodoc').click(function() {
|
$('#backtodoc').off('click.opdAppt').on('click.opdAppt', function() {
|
||||||
$('#ShowDoctor').show();
|
$('#ShowDoctor').show();
|
||||||
$('#showfee').hide();
|
$('#showfee').hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btnnextconfirm').click(function() {
|
$('#btnnextconfirm').off('click.opdAppt').on('click.opdAppt', function() {
|
||||||
var checkValue = $('#fee_id:checked').val();
|
var checkValue = $('#fee_id:checked').val();
|
||||||
if (!checkValue) {
|
if (!checkValue) {
|
||||||
alert('Please Select Fee Name');
|
alert('Please Select Fee Name');
|
||||||
|
|
@ -138,7 +132,6 @@
|
||||||
"pid": $('#pid').val(),
|
"pid": $('#pid').val(),
|
||||||
"datepicker_appointment": $('#datepicker_appointment').val(),
|
"datepicker_appointment": $('#datepicker_appointment').val(),
|
||||||
"appointment_time": $('#appointment_time').val(),
|
"appointment_time": $('#appointment_time').val(),
|
||||||
"abha_address": $('#abha_address').val(),
|
|
||||||
[csrf.name]: csrf.value
|
[csrf.name]: csrf.value
|
||||||
}, function(data) {
|
}, function(data) {
|
||||||
updateCsrf(data);
|
updateCsrf(data);
|
||||||
|
|
|
||||||
|
|
@ -255,12 +255,8 @@
|
||||||
delete_varible();
|
delete_varible();
|
||||||
}
|
}
|
||||||
$("#wait").css("display", "none");
|
$("#wait").css("display", "none");
|
||||||
var mainEl = document.getElementById('main');
|
// Use jQuery html() so old node handlers/data are cleaned up.
|
||||||
if (mainEl) {
|
|
||||||
mainEl.innerHTML = html;
|
|
||||||
} else {
|
|
||||||
$("#main").html(html);
|
$("#main").html(html);
|
||||||
}
|
|
||||||
executeInjectedScripts('main');
|
executeInjectedScripts('main');
|
||||||
if (typeof initfunc === 'function') {
|
if (typeof initfunc === 'function') {
|
||||||
initfunc();
|
initfunc();
|
||||||
|
|
@ -306,12 +302,8 @@
|
||||||
timeout: REQUEST_TIMEOUT_MS
|
timeout: REQUEST_TIMEOUT_MS
|
||||||
})
|
})
|
||||||
.done(function(html) {
|
.done(function(html) {
|
||||||
var targetEl = document.getElementById(xdiv);
|
// Use jQuery html() so old node handlers/data are cleaned up.
|
||||||
if (targetEl) {
|
|
||||||
targetEl.innerHTML = html;
|
|
||||||
} else {
|
|
||||||
$("#" + xdiv).html(html);
|
$("#" + xdiv).html(html);
|
||||||
}
|
|
||||||
executeInjectedScripts(xdiv);
|
executeInjectedScripts(xdiv);
|
||||||
if (typeof initfunc === 'function') {
|
if (typeof initfunc === 'function') {
|
||||||
initfunc();
|
initfunc();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user