fix(abdm-m1): user friendly OTP errors, remove bridge request id from UI, and add beneficiary name consent collection

This commit is contained in:
DevSoftTech Server 2026-09-29 23:14:48 +05:30
parent 763af46de2
commit 6bd24303e3
7 changed files with 256 additions and 27 deletions

View File

@ -25,6 +25,7 @@ class Abha extends BaseController
$aadhaar = preg_replace('/\D/', '', trim((string) ($this->request->getPost('aadhaar') ?? '')));
$authType = trim((string) ($this->request->getPost('auth_type') ?? 'aadhaar_otp'));
$consent = $this->request->getPost('consent');
$beneficiaryName = trim((string) ($this->request->getPost('beneficiary_name') ?? ''));
if (strlen($aadhaar) !== 12 || ! $this->validateVerhoeff($aadhaar)) {
return $this->response->setJSON(['ok' => 0, 'error_text' => 'Aadhaar Number is not valid. Valid 12-digit Aadhaar number is required.']);
@ -47,6 +48,9 @@ class Abha extends BaseController
// later for local-patient name/age/gender/aadhaar matching (never sent to browser).
if ($txnId) {
session()->set('abha_aadhaar_txn_' . $txnId, $aadhaar);
if ($beneficiaryName !== '') {
session()->set('abha_beneficiary_' . $txnId, $beneficiaryName);
}
}
return $this->response->setJSON([
'ok' => 1,
@ -102,10 +106,7 @@ class Abha extends BaseController
if (empty($result['ok']) || $result['ok'] != 1) {
$requestId = trim((string) ($result['request_id'] ?? ''));
$errorText = $this->extractBridgeErrorText($result, 'Please enter a valid OTP. Entered OTP is either expired or incorrect.');
if ($requestId !== '') {
$errorText .= ' (Bridge Request ID: ' . $requestId . ')';
}
$errorText = $this->extractBridgeErrorText($result, 'Incorrect OTP');
return $this->response->setJSON([
'ok' => 0,
'error_text' => $errorText,
@ -1669,7 +1670,22 @@ class Abha extends BaseController
if (stripos($trimmed, 'loginId') !== false && stripos($trimmed, 'invalid') !== false) {
return 'Aadhaar Number is not valid. Valid 12-digit Aadhaar number is required.';
}
return $trimmed;
// Normalize ABDM/UIDAI OTP errors: replace technical error codes with clear user-friendly messages (ABDM M1)
if (preg_match('/(?:UIDAI Error code\s*:\s*403|attempts for OTP match is exceeded|max(?:imum)? number of attempts.*exceeded)/i', $trimmed)) {
return 'Incorrect OTP. Maximum number of attempts exceeded. Please generate a fresh OTP and try again.';
}
if (preg_match('/(?:UIDAI Error code\s*:\s*400|Invalid (?:Aadhaar )?OTP value|Invalid OTP|Incorrect OTP|OTP mismatch|OTP.*not valid|OTP.*either expired or incorrect)/i', $trimmed)) {
return 'Incorrect OTP';
}
// Strip raw technical error code prefixes (e.g. ABDM-1204 : or UIDAI Error code : 400 :)
$cleaned = preg_replace('/^(?:ABDM-\d+\s*:\s*)?(?:UIDAI Error code\s*:\s*\d+\s*:\s*)?/i', '', $trimmed);
if (preg_match('/(?:invalid.*otp|incorrect.*otp|otp.*invalid|otp.*incorrect|otp.*match)/i', $cleaned)) {
if (preg_match('/(?:attempts|exceeded|403)/i', $cleaned)) {
return 'Incorrect OTP. Maximum number of attempts exceeded. Please generate a fresh OTP and try again.';
}
return 'Incorrect OTP';
}
return $cleaned;
}
}
@ -1691,6 +1707,10 @@ class Abha extends BaseController
$fieldMsg = trim($message);
if (strcasecmp($field, 'loginId') === 0 && stripos($fieldMsg, 'invalid') !== false) {
$fieldErrors[] = 'Aadhaar Number is not valid. Valid 12-digit Aadhaar number is required.';
} elseif (preg_match('/(?:attempts.*exceeded|403)/i', $fieldMsg)) {
$fieldErrors[] = 'Incorrect OTP. Maximum number of attempts exceeded. Please generate a fresh OTP and try again.';
} elseif (preg_match('/(?:invalid.*otp|incorrect.*otp|otp.*match)/i', $fieldMsg)) {
$fieldErrors[] = 'Incorrect OTP';
} else {
$fieldErrors[] = $field . ': ' . $fieldMsg;
}
@ -1700,6 +1720,10 @@ class Abha extends BaseController
return implode('; ', $fieldErrors);
}
if (preg_match('/(?:invalid.*otp|incorrect.*otp|otp.*expired)/i', $fallback)) {
return 'Incorrect OTP';
}
return $fallback;
}

View File

@ -844,10 +844,22 @@
for (var i = 0; i < candidates.length; i++) {
if (typeof candidates[i] === 'string' && candidates[i].trim() !== '') {
return candidates[i].trim();
var val = candidates[i].trim();
if (/UIDAI Error code/i.test(val) || /ABDM-1204/i.test(val) || /attempts.*exceeded/i.test(val) || /invalid.*otp/i.test(val) || /incorrect.*otp/i.test(val) || /otp.*match/i.test(val) || /otp.*expired/i.test(val)) {
if (/attempts.*exceeded/i.test(val) || /403/.test(val)) {
return 'Incorrect OTP. Maximum number of attempts exceeded. Please generate a fresh OTP and try again.';
}
return 'Incorrect OTP';
}
val = val.replace(/\s*\(Bridge Request ID:[^)]*\)/gi, '').replace(/^ABDM-\d+\s*:\s*/i, '');
return val;
}
}
if (/invalid.*otp|incorrect.*otp/i.test(fallbackText)) {
return 'Incorrect OTP';
}
return fallbackText;
}
@ -1439,7 +1451,7 @@
window.AbhaPatientMatchModal.open(profile, profile.candidates || [], function(finalResponse) {
if (typeof showRegResult === 'function') showRegResult(finalResponse);
});
}, $('#input_mphone1').val());
}, $('#input_mphone1').val(), $('#input_name').val());
}).on('click.abhaLauncher', '#abha_open_verify_modal_btn', function() {
openAbhaAccountVerification('', null);
});

View File

@ -563,7 +563,7 @@ $(document).ready(function() {
});
}
window.AbhaPatientMatchModal.open(profile, candidates, refreshProfile, currentPatientId);
}, <?= json_encode((string) ($data[0]->mphone1 ?? '')) ?>);
}, <?= json_encode((string) ($data[0]->mphone1 ?? '')) ?>, <?= json_encode(trim((string) ($data[0]->p_fname ?? ''))) ?>);
});
function getPatientIdOrWarn() {

View File

@ -10,8 +10,33 @@
* Step 5: ABHA Profile, Official Card & HIMS linking (CRT_ABHA_113, 114, 115 / TAGGING_UNIQUEPATIENTID_UNIQUEABHANUMBER)
*
* Include once per page: <?= view('partials/abha_create_modal') ?>
* Then call: window.AbhaCreateModal.open(function (profile) { ... }, prefillMobile);
* Then call: window.AbhaCreateModal.open(function (profile) { ... }, prefillMobile, prefillName);
*/
$healthcareWorkerName = 'Healthcare Worker';
if (function_exists('auth') && auth()->loggedIn()) {
$u = auth()->user();
$healthcareWorkerName = $u->username ?? 'Healthcare Worker';
if ($u && !empty($u->id)) {
try {
$db = \Config\Database::connect();
$tables = config('Auth')->tables ?? [];
$identitiesTable = $tables['identities'] ?? 'auth_identities';
if ($db && $db->tableExists($identitiesTable)) {
$row = $db->table($identitiesTable)
->where('user_id', $u->id)
->where('type', 'email_password')
->get()
->getRowArray();
if (!empty($row['extra'])) {
$extra = json_decode($row['extra'], true);
if (!empty($extra['full_name'])) {
$healthcareWorkerName = trim($extra['full_name']);
}
}
}
} catch (\Throwable $e) {}
}
}
?>
<style>
.abha-create-modal .modal-content { border:0; border-radius:12px; overflow:hidden; }
@ -28,6 +53,8 @@
.abha-create-consent.border-danger { border-color:#dc3545 !important; border-style:solid !important; }
#abhaCreateConsentAgreeWrap { transition: all .15s ease-in-out; }
#abhaCreateConsentAgreeWrap.border-danger { border-color:#dc3545 !important; background-color:#fff5f5 !important; }
.abha-declaration-card { background:#fbfcfe; border:1px solid #dce3ec; border-radius:8px; padding:10px 14px; margin-top:10px; transition:border-color .15s ease-in-out, background-color .15s ease-in-out; }
.abha-declaration-card.border-danger { border-color:#dc3545 !important; background-color:#fff5f5 !important; }
.abha-create-address { display:block; border:1px solid #dce3ec; border-radius:8px; padding:11px 14px; cursor:pointer; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; }
.abha-create-address:has(input:checked) { border-color:#356cf4; background:#edf2ff; box-shadow:0 0 0 1px #356cf4; }
.abha-profile-list { margin:0; }
@ -128,8 +155,25 @@
I Agree &mdash; I have read and agree to all consent declarations for Aadhaar authentication and ABHA creation <span class="text-danger">*</span>
</label>
</div>
<!-- ABDM M1 Declarations: Healthcare Worker & Beneficiary Name (Image 3) -->
<div class="abha-declaration-card" id="abhaCreateDeclarationWrap">
<div class="form-check d-flex align-items-start" id="abhaCreateHcwConsentWrap">
<input class="form-check-input me-2 mt-1 flex-shrink-0" type="checkbox" id="abhaCreateHcwConsent" style="cursor:pointer;">
<label class="form-check-label small text-dark mb-0" for="abhaCreateHcwConsent" style="cursor:pointer;">
I, the healthcare worker, <strong id="abhaCreateHcwName"><?= esc($healthcareWorkerName) ?></strong>, confirm that I have duly informed and explained the beneficiary the contents of consent for the aforementioned purposes.
</label>
</div>
<div class="form-check mt-2 d-flex align-items-center flex-wrap" id="abhaCreateBeneficiaryConsentWrap">
<input class="form-check-input me-2 flex-shrink-0" type="checkbox" id="abhaCreateBeneficiaryConsent" style="cursor:pointer;">
<label class="form-check-label small text-dark mb-0" for="abhaCreateBeneficiaryConsent" style="cursor:pointer;">
I, <input type="text" class="form-control form-control-sm d-inline-block mx-1 fw-bold" id="abhaCreateBeneficiaryName" placeholder="Enter beneficiary name" autocomplete="name" style="width:170px; display:inline-block; vertical-align:middle; height:30px;" required> have been explained about the consent as stated above and hereby provide my consent for the aforementioned purposes. <span class="text-danger">*</span>
</label>
</div>
</div>
<div class="text-danger small mt-1 d-none" id="abhaCreateConsentError">
<i class="bi bi-exclamation-circle-fill me-1"></i>Consent is mandatory. Please check "I Agree" to proceed to the next step.
<i class="bi bi-exclamation-circle-fill me-1"></i><span id="abhaCreateConsentErrorText">Consent is mandatory. Please enter beneficiary name and agree to all consent declarations.</span>
</div>
</div>
@ -145,7 +189,6 @@
<i class="bi bi-chat-dots-fill text-success" style="font-size:2.2rem"></i>
<h6 class="mt-2 mb-1">Enter Aadhaar OTP</h6>
<div class="text-muted small" id="abhaCreateOtpHint">We just sent an OTP on the Mobile Number linked with Aadhaar. Enter the OTP below to proceed with ABHA creation.</div>
<div class="text-muted small mt-1 d-none" id="abhaCreateOtpRequestId"></div>
</div>
<label class="form-label fw-semibold" for="abhaCreateOtp">6-digit Aadhaar OTP</label>
<div class="input-group input-group-lg">
@ -298,10 +341,17 @@ window.AbhaCreateModal = (function () {
if (/loginId/i.test(raw) && /invalid/i.test(raw)) {
raw = 'Aadhaar Number is not valid. Valid 12-digit Aadhaar number is required.';
}
var message = escapeHtml(raw);
return response && response.request_id
? message + ' <small class="d-block mt-1">Bridge Request ID: ' + escapeHtml(response.request_id) + '</small>'
: message;
// ABDM M1: Map incorrect OTP errors to clear message without raw error codes
if (/UIDAI Error code/i.test(raw) || /ABDM-1204/i.test(raw) || /attempts.*exceeded/i.test(raw) || /invalid.*otp/i.test(raw) || /incorrect.*otp/i.test(raw) || /otp.*match/i.test(raw) || /otp.*expired/i.test(raw)) {
if (/attempts.*exceeded/i.test(raw) || /403/.test(raw)) {
raw = 'Incorrect OTP. Maximum number of attempts exceeded. Please generate a fresh OTP and try again.';
} else {
raw = 'Incorrect OTP';
}
}
// Strip any residual (Bridge Request ID: ...) or error code traces
raw = raw.replace(/\s*\(Bridge Request ID:[^)]*\)/gi, '').replace(/^ABDM-\d+\s*:\s*/i, '');
return escapeHtml(raw);
}
function validateVerhoeff(num) {
num = digits(num);
@ -429,15 +479,22 @@ window.AbhaCreateModal = (function () {
function sendAadhaarOtp(isResend) {
var aadhaar = digits($('#abhaCreateAadhaar').val());
var mobile = digits($('#abhaCreateMobile').val());
var beneficiaryName = $.trim($('#abhaCreateBeneficiaryName').val());
var totalConsent = $('.abha-consent-chk').length;
var checkedConsent = $('.abha-consent-chk:checked').length;
var isAllConsentChecked = (totalConsent > 0 && checkedConsent === totalConsent) || $('#abhaCreateConsentAgree').is(':checked');
var isHcwConsentChecked = $('#abhaCreateHcwConsent').is(':checked');
var isBeneficiaryConsentChecked = $('#abhaCreateBeneficiaryConsent').is(':checked');
// Reset visual validation states
$('#abhaCreateAadhaar').removeClass('is-invalid');
$('#abhaCreateMobile').removeClass('is-invalid');
$('#abhaCreateBeneficiaryName').removeClass('is-invalid');
$('#abhaCreateConsentWrap').removeClass('border-danger');
$('#abhaCreateConsentAgreeWrap').removeClass('border-danger');
$('#abhaCreateDeclarationWrap').removeClass('border-danger');
$('#abhaCreateHcwConsentWrap').removeClass('border-danger');
$('#abhaCreateBeneficiaryConsentWrap').removeClass('border-danger');
$('#abhaCreateConsentError').addClass('d-none');
if (aadhaar.length !== 12 || !validateVerhoeff(aadhaar)) {
@ -450,6 +507,27 @@ window.AbhaCreateModal = (function () {
alertBox('warning', 'Please enter a valid 10-digit mobile number for ABHA communication.');
return;
}
if (beneficiaryName === '') {
$('#abhaCreateBeneficiaryName').addClass('is-invalid').trigger('focus');
$('#abhaCreateBeneficiaryConsentWrap').addClass('border-danger');
$('#abhaCreateDeclarationWrap').addClass('border-danger');
alertBox('warning', 'While creating an ABHA, the user is required to enter their name during the consent collection process.');
return;
}
if (!isHcwConsentChecked) {
$('#abhaCreateHcwConsentWrap').addClass('border-danger');
$('#abhaCreateDeclarationWrap').addClass('border-danger');
alertBox('warning', 'Please accept the healthcare worker consent declaration.');
$('#abhaCreateHcwConsent').trigger('focus');
return;
}
if (!isBeneficiaryConsentChecked) {
$('#abhaCreateBeneficiaryConsentWrap').addClass('border-danger');
$('#abhaCreateDeclarationWrap').addClass('border-danger');
alertBox('warning', 'Please accept the beneficiary consent declaration.');
$('#abhaCreateBeneficiaryConsent').trigger('focus');
return;
}
if (!isAllConsentChecked) {
$('#abhaCreateConsentWrap').addClass('border-danger');
$('#abhaCreateConsentAgreeWrap').addClass('border-danger');
@ -475,6 +553,7 @@ window.AbhaCreateModal = (function () {
aadhaar: aadhaar,
auth_type: 'aadhaar_otp',
consent: 1,
beneficiary_name: beneficiaryName,
'<?= csrf_token() ?>': csrf()
}, function (response) {
button.prop('disabled', false).html('<i class="bi bi-send me-1"></i>Send OTP');
@ -487,9 +566,6 @@ window.AbhaCreateModal = (function () {
var destination = response.masked_mobile || ((response.message || '').match(/\*{2,}\d{4}/) || [''])[0] || '******XXXX';
var promptMessage = 'We just sent an OTP on the Mobile Number ' + destination + ' linked with Aadhaar. Enter the OTP below to proceed with ABHA creation.';
$('#abhaCreateOtpHint').text(promptMessage);
$('#abhaCreateOtpRequestId')
.toggleClass('d-none', !response.request_id)
.text(response.request_id ? 'Bridge Request ID: ' + response.request_id : '');
showStep(2);
startResendTimer($('#abhaCreateResendBtn'), 60);
$('#abhaCreateOtp').trigger('focus');
@ -728,8 +804,12 @@ window.AbhaCreateModal = (function () {
function clearConsentValidation() {
$('#abhaCreateConsentWrap').removeClass('border-danger');
$('#abhaCreateConsentAgreeWrap').removeClass('border-danger');
$('#abhaCreateDeclarationWrap').removeClass('border-danger');
$('#abhaCreateHcwConsentWrap').removeClass('border-danger');
$('#abhaCreateBeneficiaryConsentWrap').removeClass('border-danger');
$('#abhaCreateBeneficiaryName').removeClass('is-invalid');
$('#abhaCreateConsentError').addClass('d-none');
if ($('#abhaCreateAlert').text().indexOf('Consent is mandatory') !== -1) {
if ($('#abhaCreateAlert').text().indexOf('Consent') !== -1 || $('#abhaCreateAlert').text().indexOf('beneficiary name') !== -1) {
alertBox('', '');
}
}
@ -737,6 +817,12 @@ window.AbhaCreateModal = (function () {
$('#abhaCreateConsentAgree').on('change', function () {
var checked = $(this).is(':checked');
$('.abha-consent-chk').prop('checked', checked);
$('#abhaCreateHcwConsent').prop('checked', checked);
if (checked && $.trim($('#abhaCreateBeneficiaryName').val()) !== '') {
$('#abhaCreateBeneficiaryConsent').prop('checked', true);
} else if (!checked) {
$('#abhaCreateBeneficiaryConsent').prop('checked', false);
}
$('#abhaCreateSelectAllConsent').text(checked ? 'Deselect All' : 'Select All');
if (checked) {
clearConsentValidation();
@ -755,17 +841,34 @@ window.AbhaCreateModal = (function () {
});
$('#abhaCreateSelectAllConsent').on('click', function () {
var total = $('.abha-consent-chk').length;
var checked = $('.abha-consent-chk:checked').length;
var total = $('.abha-consent-chk').length + 2;
var checked = $('.abha-consent-chk:checked').length + ($('#abhaCreateHcwConsent').is(':checked') ? 1 : 0) + ($('#abhaCreateBeneficiaryConsent').is(':checked') ? 1 : 0);
var shouldCheck = (checked < total);
$('.abha-consent-chk').prop('checked', shouldCheck);
$('#abhaCreateConsentAgree').prop('checked', shouldCheck);
$('#abhaCreateHcwConsent').prop('checked', shouldCheck);
$('#abhaCreateBeneficiaryConsent').prop('checked', shouldCheck);
$(this).text(shouldCheck ? 'Deselect All' : 'Select All');
if (shouldCheck) {
clearConsentValidation();
}
});
$('#abhaCreateBeneficiaryName').on('click', function (e) {
e.stopPropagation();
}).on('input', function () {
$(this).removeClass('is-invalid');
if ($.trim($(this).val()) !== '') {
$('#abhaCreateBeneficiaryConsent').prop('checked', true);
$('#abhaCreateBeneficiaryConsentWrap').removeClass('border-danger');
}
clearConsentValidation();
});
$('#abhaCreateHcwConsent, #abhaCreateBeneficiaryConsent').on('change', function () {
clearConsentValidation();
});
$('#abhaCreateAadhaar').on('input', function () {
$(this).removeClass('is-invalid');
if ($('#abhaCreateAlert').text().indexOf('Aadhaar Number is not valid') !== -1) {
@ -825,7 +928,7 @@ window.AbhaCreateModal = (function () {
});
return {
open: function (callback, prefillMobile) {
open: function (callback, prefillMobile, prefillName, prefillHcwName) {
onCompleted = callback;
createTxnId = '';
mobileTxnId = '';
@ -836,9 +939,12 @@ window.AbhaCreateModal = (function () {
stopTimers();
$('#abhaCreateAadhaar,#abhaCreateMobile,#abhaCreateOtp,#abhaCreateMobileOtp,#abhaCreateCustomAddress').val('').removeClass('is-invalid');
$('#abhaCreateMobile').val(digits(prefillMobile));
$('#abhaCreateOtpRequestId').addClass('d-none').text('');
$('#abhaCreateConsent1,#abhaCreateConsent2,#abhaCreateConsent3,#abhaCreateConsent4,#abhaCreateConsentAgree').prop('checked', false);
$('#abhaCreateConsentWrap,#abhaCreateConsentAgreeWrap').removeClass('border-danger');
$('#abhaCreateBeneficiaryName').val(prefillName ? String(prefillName).trim() : '').removeClass('is-invalid');
if (prefillHcwName) {
$('#abhaCreateHcwName').text(prefillHcwName);
}
$('#abhaCreateConsent1,#abhaCreateConsent2,#abhaCreateConsent3,#abhaCreateConsent4,#abhaCreateConsentAgree,#abhaCreateHcwConsent,#abhaCreateBeneficiaryConsent').prop('checked', false);
$('#abhaCreateConsentWrap,#abhaCreateConsentAgreeWrap,#abhaCreateDeclarationWrap,#abhaCreateHcwConsentWrap,#abhaCreateBeneficiaryConsentWrap').removeClass('border-danger');
$('#abhaCreateConsentError').addClass('d-none');
$('#abhaCreateSelectAllConsent').text('Select All');
$('#abhaCreatePhoto,#abhaCreateDownloadCard').addClass('d-none');

View File

@ -255,7 +255,18 @@ window.AbhaMobileModal = (function () {
}
function digits(value) { return String(value == null ? '' : value).replace(/\D/g, ''); }
function apiMessage(response, fallback) {
return response && (response.error_text || response.message) ? escapeHtml(response.error_text || response.message) : fallback;
var raw = response && (response.error_text || response.message)
? String(response.error_text || response.message)
: fallback;
if (/UIDAI Error code/i.test(raw) || /ABDM-1204/i.test(raw) || /attempts.*exceeded/i.test(raw) || /invalid.*otp/i.test(raw) || /incorrect.*otp/i.test(raw) || /otp.*match/i.test(raw) || /otp.*expired/i.test(raw)) {
if (/attempts.*exceeded/i.test(raw) || /403/.test(raw)) {
raw = 'Incorrect OTP. Maximum number of attempts exceeded. Please generate a fresh OTP and try again.';
} else {
raw = 'Incorrect OTP';
}
}
raw = raw.replace(/\s*\(Bridge Request ID:[^)]*\)/gi, '').replace(/^ABDM-\d+\s*:\s*/i, '');
return escapeHtml(raw);
}
function alertBox(type, message) {
var alertEl = document.getElementById('abhaMobileAlert');

View File

@ -117,7 +117,18 @@ window.AbhaVerifyModal = (function () {
}
function escapeHtml(value) { return $('<div>').text(value == null ? '' : String(value)).html(); }
function apiMessage(response, fallback) {
return response && (response.error_text || response.message) ? escapeHtml(response.error_text || response.message) : fallback;
var raw = response && (response.error_text || response.message)
? String(response.error_text || response.message)
: fallback;
if (/UIDAI Error code/i.test(raw) || /ABDM-1204/i.test(raw) || /attempts.*exceeded/i.test(raw) || /invalid.*otp/i.test(raw) || /incorrect.*otp/i.test(raw) || /otp.*match/i.test(raw) || /otp.*expired/i.test(raw)) {
if (/attempts.*exceeded/i.test(raw) || /403/.test(raw)) {
raw = 'Incorrect OTP. Maximum number of attempts exceeded. Please generate a fresh OTP and try again.';
} else {
raw = 'Incorrect OTP';
}
}
raw = raw.replace(/\s*\(Bridge Request ID:[^)]*\)/gi, '').replace(/^ABDM-\d+\s*:\s*/i, '');
return escapeHtml(raw);
}
function alertBox(type, message) { $('#abhaVerifyAlert').html(message ? '<div class="alert alert-' + type + ' py-2">' + message + '</div>' : ''); }
function showStep(step) {

View File

@ -0,0 +1,65 @@
<?php
namespace Tests\Unit\Abdm;
use App\Controllers\Abha;
use CodeIgniter\Test\CIUnitTestCase;
use ReflectionClass;
final class AbhaOtpErrorHandlingTest extends CIUnitTestCase
{
private Abha $controller;
private \ReflectionMethod $extractMethod;
protected function setUp(): void
{
parent::setUp();
$this->controller = new Abha();
$ref = new ReflectionClass($this->controller);
$this->extractMethod = $ref->getMethod('extractBridgeErrorText');
$this->extractMethod->setAccessible(true);
}
public function testExtractBridgeErrorTextMapsUidai400ToIncorrectOtp(): void
{
$payload = [
'error' => [
'code' => 'ABDM-1204',
'message' => 'UIDAI Error code : 400 : Invalid Aadhaar OTP value.',
],
];
$result = $this->extractMethod->invoke($this->controller, $payload, 'Please enter a valid OTP.');
$this->assertSame('Incorrect OTP', $result);
}
public function testExtractBridgeErrorTextMapsUidai403ToAttemptsExceeded(): void
{
$payload = [
'error_text' => 'UIDAI Error code : 403 : Maximum number of attempts for OTP match is exceeded or OTP is not generated. Please generate a fresh OTP and try to authenticate again',
];
$result = $this->extractMethod->invoke($this->controller, $payload, 'Please enter a valid OTP.');
$this->assertStringContainsString('Incorrect OTP', $result);
$this->assertStringContainsString('Maximum number of attempts exceeded', $result);
$this->assertStringNotContainsString('403', $result);
$this->assertStringNotContainsString('UIDAI Error code', $result);
}
public function testExtractBridgeErrorTextStripsRawErrorCodesFromGenericOtpError(): void
{
$payload = [
'message' => 'ABDM-1204 : Invalid OTP entered by user',
];
$result = $this->extractMethod->invoke($this->controller, $payload, 'Please enter a valid OTP.');
$this->assertSame('Incorrect OTP', $result);
}
public function testExtractBridgeErrorTextFallbackNormalizesOtpText(): void
{
$payload = [];
$result = $this->extractMethod->invoke($this->controller, $payload, 'Please enter a valid OTP. Entered OTP is either expired or incorrect.');
$this->assertSame('Incorrect OTP', $result);
}
}