Fetch official ABHA card and surface bridge card provenance
This commit is contained in:
parent
b4714270a9
commit
b25ab98fd7
|
|
@ -775,6 +775,7 @@ $routes->post('abha/create/confirm_patient', 'Abha::confirmPatient', ['filter' =
|
|||
$routes->post('abha/create/address', 'Abha::address', ['filter' => 'permission:abdm.abha.create']);
|
||||
$routes->post('abha/create/address_suggestions', 'Abha::addressSuggestions', ['filter' => 'permission:abdm.abha.create']);
|
||||
$routes->get('abha/card/(:segment)', 'Abha::card/$1', ['filter' => 'permission:abdm.abha.create']);
|
||||
$routes->post('abha/register/card', 'Abha::fetchOfficialCard', ['filter' => 'permission:abdm.abha.create']);
|
||||
$routes->post('abha/register/validate', 'Abha::validateAbha', ['filter' => 'permission:abdm.abha.create']);
|
||||
$routes->post('abha/register/qr_scan', 'Abha::qrScan', ['filter' => 'permission:abdm.abha.create']);
|
||||
$routes->post('abha/register/login/request-otp', 'Abha::loginRequestOtp', ['filter' => 'permission:abdm.abha.create']);
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ class Abha extends BaseController
|
|||
'card_base64' => $this->extractAbhaCardData($result),
|
||||
'card_content_type' => $this->resolveAbhaCardContentType($result),
|
||||
'card_source' => $this->resolveAbhaCardSource($result),
|
||||
'card_message' => $this->resolveAbhaCardMessage($result),
|
||||
'abha_number' => $abhaNum,
|
||||
'name' => $name,
|
||||
'photo' => $photo,
|
||||
|
|
@ -325,6 +326,7 @@ class Abha extends BaseController
|
|||
'card_base64' => $this->extractAbhaCardData($result),
|
||||
'card_content_type' => $this->resolveAbhaCardContentType($result),
|
||||
'card_source' => $this->resolveAbhaCardSource($result),
|
||||
'card_message' => $this->resolveAbhaCardMessage($result),
|
||||
'abha_number' => $abhaNum,
|
||||
'name' => $name,
|
||||
'photo' => $photo,
|
||||
|
|
@ -2105,6 +2107,7 @@ class Abha extends BaseController
|
|||
'card_base64' => $cardData,
|
||||
'card_content_type' => $this->resolveAbhaCardContentType($payload),
|
||||
'card_source' => $this->resolveAbhaCardSource($payload),
|
||||
'card_message' => $this->resolveAbhaCardMessage($payload),
|
||||
'candidates' => $candidates,
|
||||
]);
|
||||
}
|
||||
|
|
@ -2224,7 +2227,9 @@ class Abha extends BaseController
|
|||
];
|
||||
foreach ($sources as $source) {
|
||||
if (! is_array($source)) { continue; }
|
||||
foreach (['official_card', 'card_data_uri', 'card_base64', 'card_data', 'abhaCard', 'abha_card', 'cardData', 'card'] as $key) {
|
||||
// card_base64 first: the connector puts the resolved official card there,
|
||||
// while official_card can still hold a Bridge-generated image.
|
||||
foreach (['card_base64', 'official_card', 'card_data_uri', 'card_data', 'abhaCard', 'abha_card', 'cardData', 'card'] as $key) {
|
||||
$value = $source[$key] ?? '';
|
||||
if (is_string($value) && trim($value) !== '') { return trim($value); }
|
||||
if (is_array($value)) {
|
||||
|
|
@ -2239,6 +2244,23 @@ class Abha extends BaseController
|
|||
return '';
|
||||
}
|
||||
|
||||
private function resolveAbhaCardMessage(array $payload): string
|
||||
{
|
||||
$data = is_array($payload['data'] ?? null) ? $payload['data'] : [];
|
||||
foreach ([
|
||||
$payload['card_message'] ?? null,
|
||||
$payload['message'] ?? null,
|
||||
$data['message'] ?? null,
|
||||
] as $candidate) {
|
||||
$value = trim((string) $candidate);
|
||||
if ($value !== '') {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function resolveAbhaCardSource(array $payload): string
|
||||
{
|
||||
$data = is_array($payload['data'] ?? null) ? $payload['data'] : [];
|
||||
|
|
@ -2249,9 +2271,15 @@ class Abha extends BaseController
|
|||
$data['source'] ?? null,
|
||||
] as $candidate) {
|
||||
$value = strtolower(trim((string) $candidate));
|
||||
if ($value !== '') {
|
||||
return str_contains($value, 'abdm') ? 'abdm' : 'generated';
|
||||
if ($value === '') {
|
||||
continue;
|
||||
}
|
||||
if (str_contains($value, 'abdm')) {
|
||||
return 'abdm';
|
||||
}
|
||||
|
||||
// "none" means ABDM issued no card for this session, not a local render.
|
||||
return $value === 'none' ? 'none' : 'generated';
|
||||
}
|
||||
|
||||
return '';
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ interface AbdmConnectorInterface
|
|||
*/
|
||||
public function abhaLoginVerifyOtp(array $payload): array;
|
||||
|
||||
/**
|
||||
* Fetch the official ABHA card from the Bridge card endpoint.
|
||||
*
|
||||
* @param array<string, mixed> $payload Must contain abha_number or abha_address.
|
||||
*/
|
||||
public function fetchOfficialAbhaCard(array $payload): array;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Consent
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@ class DirectAbdmConnector implements AbdmConnectorInterface
|
|||
throw new \RuntimeException('DirectAbdmConnector::abhaLoginVerifyOtp() not implemented.');
|
||||
}
|
||||
|
||||
public function fetchOfficialAbhaCard(array $payload): array
|
||||
{
|
||||
return ['ok' => 0, 'error_text' => 'Official ABHA card download requires the e-Atria Bridge connector.'];
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Consent
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -51,6 +51,11 @@ class DreamsoftConnector implements AbdmConnectorInterface
|
|||
throw new \RuntimeException('ABHA login OTP verification requires the synchronous e-Atria Bridge connector.');
|
||||
}
|
||||
|
||||
public function fetchOfficialAbhaCard(array $payload): array
|
||||
{
|
||||
return ['ok' => 0, 'error_text' => 'Official ABHA card download requires the e-Atria Bridge connector.'];
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Consent
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -733,7 +733,7 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
|||
if ($card === '' || $source !== 'abdm') {
|
||||
return [
|
||||
'ok' => 0,
|
||||
'error_text' => 'The Bridge did not return an official ABHA card.',
|
||||
'error_text' => trim((string) ($result['message'] ?? $data['message'] ?? '')) ?: 'The Bridge did not return an official ABHA card.',
|
||||
'card_source' => $source,
|
||||
'request_id' => (string) ($result['request_id'] ?? ''),
|
||||
];
|
||||
|
|
@ -773,11 +773,16 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
|||
?: $this->extractOfficialCard($profile)
|
||||
?: $this->extractOfficialCard(is_array($result['ABHAProfile'] ?? null) ? $result['ABHAProfile'] : []);
|
||||
|
||||
$inlineSource = $this->extractCardSource($result) ?: $this->extractCardSource($data);
|
||||
if ($card !== '') {
|
||||
$result['card_source'] = $this->extractCardSource($result) ?: $this->extractCardSource($data);
|
||||
$result['card_base64'] = $card;
|
||||
$result['card_content_type'] = $this->resolveCardContentType($result) ?: $this->resolveCardContentType($data);
|
||||
$result['card_source'] = $inlineSource;
|
||||
}
|
||||
|
||||
if ($card === '' && ($abhaNumber !== '' || $abhaAddress !== '')) {
|
||||
// Verify responses often embed a Bridge-generated card under official_card,
|
||||
// so only the dedicated card endpoint can supply the real ABDM card.
|
||||
if (($card === '' || $inlineSource === 'generated') && ($abhaNumber !== '' || $abhaAddress !== '')) {
|
||||
// Without the patient X-Token the Bridge can only return a generated
|
||||
// card; with it, ABDM's genuine card is fetched.
|
||||
$patientToken = $this->extractPatientXToken($result);
|
||||
|
|
@ -791,11 +796,16 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
|||
);
|
||||
if (! empty($cardResult['ok']) && (int) $cardResult['ok'] === 1) {
|
||||
$cardData = is_array($cardResult['data'] ?? null) ? $cardResult['data'] : $cardResult;
|
||||
$card = $this->extractOfficialCard($cardResult) ?: $this->extractOfficialCard($cardData);
|
||||
if ($card !== '') {
|
||||
$result['card_base64'] = $card;
|
||||
$fetchedCard = $this->extractOfficialCard($cardResult) ?: $this->extractOfficialCard($cardData);
|
||||
$fetchedSource = $this->extractCardSource($cardResult) ?: $this->extractCardSource($cardData);
|
||||
// Never trade a card we already hold for another generated one.
|
||||
if ($fetchedCard !== '' && ($fetchedSource === 'abdm' || $card === '')) {
|
||||
$result['card_base64'] = $fetchedCard;
|
||||
$result['card_content_type'] = $this->resolveCardContentType($cardData);
|
||||
$result['card_source'] = $this->extractCardSource($cardResult) ?: $this->extractCardSource($cardData);
|
||||
$result['card_source'] = $fetchedSource;
|
||||
} elseif ($fetchedCard === '') {
|
||||
// Bridge reports the upstream ABDM reason here (e.g. token expired).
|
||||
$result['card_message'] = trim((string) ($cardResult['message'] ?? $cardData['message'] ?? ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -810,9 +820,15 @@ class EAtriaBridgeConnector implements AbdmConnectorInterface
|
|||
{
|
||||
foreach ([$source['card_source'] ?? null, $source['source'] ?? null] as $candidate) {
|
||||
$value = strtolower(trim((string) $candidate));
|
||||
if ($value !== '') {
|
||||
return str_contains($value, 'abdm') ? 'abdm' : 'generated';
|
||||
if ($value === '') {
|
||||
continue;
|
||||
}
|
||||
if (str_contains($value, 'abdm')) {
|
||||
return 'abdm';
|
||||
}
|
||||
|
||||
// "none" means ABDM issued no card for this session, not a local render.
|
||||
return $value === 'none' ? 'none' : 'generated';
|
||||
}
|
||||
|
||||
return '';
|
||||
|
|
|
|||
|
|
@ -420,14 +420,15 @@ window.AbhaCreateModal = (function () {
|
|||
var card = profile.card_base64 || '';
|
||||
if (card) {
|
||||
var cardSrc = String(card).indexOf('data:') === 0 ? card : 'data:' + (profile.card_content_type || 'image/png') + ';base64,' + card;
|
||||
var isOfficial = profile.card_source === 'abdm';
|
||||
var isOfficial = profile.card_source !== 'generated';
|
||||
var note = isOfficial
|
||||
? ''
|
||||
: '<div class="small text-warning mt-1"><i class="bi bi-exclamation-triangle me-1"></i>Provisional card generated by the Bridge, not the official ABDM card.</div>';
|
||||
$('#abhaCreateCardWrap').html('<img src="' + cardSrc + '" alt="ABHA card">' + note);
|
||||
$('#abhaCreateDownloadCard').attr('href', cardSrc).removeClass('d-none');
|
||||
} else {
|
||||
$('#abhaCreateCardWrap').html('<div class="text-center text-muted"><i class="bi bi-card-image fs-1 d-block mb-2"></i><span>Official ABHA card was not returned by the Bridge.</span></div>');
|
||||
var reason = profile.card_message ? escapeHtml(profile.card_message) : 'ABDM did not issue an official ABHA card for this session.';
|
||||
$('#abhaCreateCardWrap').html('<div class="text-center text-muted"><i class="bi bi-card-image fs-1 d-block mb-2"></i><span>' + reason + '</span></div>');
|
||||
$('#abhaCreateDownloadCard').addClass('d-none');
|
||||
}
|
||||
showStep(4);
|
||||
|
|
|
|||
|
|
@ -229,12 +229,13 @@ window.AbhaMobileModal = (function () {
|
|||
var card = profile.card_base64 || '';
|
||||
if (card) {
|
||||
var cardSrc = String(card).indexOf('data:') === 0 ? card : 'data:' + (profile.card_content_type || 'image/png') + ';base64,' + card;
|
||||
var note = profile.card_source === 'abdm'
|
||||
var note = profile.card_source !== 'generated'
|
||||
? ''
|
||||
: '<div class="small text-warning mt-1"><i class="bi bi-exclamation-triangle me-1"></i>Provisional card generated by the Bridge, not the official ABDM card.</div>';
|
||||
$('#abhaMobileCardWrap').html('<img src="' + cardSrc + '" alt="ABHA card">' + note);
|
||||
} else {
|
||||
$('#abhaMobileCardWrap').html('<div class="text-center text-muted"><i class="bi bi-card-image fs-1 d-block mb-2"></i>Official ABHA card was not returned by the Bridge.</div>');
|
||||
var reason = profile.card_message ? escapeHtml(profile.card_message) : 'ABDM did not issue an official ABHA card for this session.';
|
||||
$('#abhaMobileCardWrap').html('<div class="text-center text-muted"><i class="bi bi-card-image fs-1 d-block mb-2"></i>' + reason + '</div>');
|
||||
}
|
||||
showStep(3);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,14 +227,15 @@ window.AbhaVerifyModal = (function () {
|
|||
var card = profile.card_base64 || '';
|
||||
if (card) {
|
||||
var cardSrc = String(card).indexOf('data:') === 0 ? card : 'data:' + (profile.card_content_type || 'image/png') + ';base64,' + card;
|
||||
var isOfficial = profile.card_source === 'abdm';
|
||||
var isOfficial = profile.card_source !== 'generated';
|
||||
var note = isOfficial
|
||||
? ''
|
||||
: '<div class="small text-warning mt-1"><i class="bi bi-exclamation-triangle me-1"></i>Provisional card generated by the Bridge, not the official ABDM card.</div>';
|
||||
$('#abhaVerifyCardWrap').html('<img src="' + cardSrc + '" alt="ABHA card">' + note);
|
||||
$('#abhaVerifyDownloadCard').attr('href', cardSrc).removeClass('d-none');
|
||||
} else {
|
||||
$('#abhaVerifyCardWrap').html('<div class="text-center text-muted"><i class="bi bi-card-image fs-1 d-block mb-2"></i>Official ABHA card was not returned by the Bridge.</div>');
|
||||
var reason = profile.card_message ? escapeHtml(profile.card_message) : 'ABDM did not issue an official ABHA card for this session.';
|
||||
$('#abhaVerifyCardWrap').html('<div class="text-center text-muted"><i class="bi bi-card-image fs-1 d-block mb-2"></i>' + reason + '</div>');
|
||||
$('#abhaVerifyDownloadCard').addClass('d-none');
|
||||
}
|
||||
showStep(4);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user