From e28ed301167fef411f73c5b72e1056b5daddb8e8 Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Mon, 12 Oct 2026 12:07:21 +0530 Subject: [PATCH] fix(abdm): route findAadhaar OTP requests via AbdmConnectorFactory with bridge integration --- app/Controllers/Abha.php | 278 ++++++++++++++++----------------------- 1 file changed, 113 insertions(+), 165 deletions(-) diff --git a/app/Controllers/Abha.php b/app/Controllers/Abha.php index e5d5381..9d1a56c 100644 --- a/app/Controllers/Abha.php +++ b/app/Controllers/Abha.php @@ -1099,7 +1099,7 @@ class Abha extends BaseController } $aadhaar = preg_replace('/\D/', '', trim((string) ($this->request->getPost('aadhaar') ?? ''))); - if (strlen($aadhaar) !== 12) { + if (strlen($aadhaar) !== 12 || ! $this->validateVerhoeff($aadhaar)) { return $this->response->setJSON([ 'ok' => 0, 'error_text' => 'Aadhaar Number is not valid. Please enter a valid 12-digit Aadhaar Number.', @@ -1107,21 +1107,45 @@ class Abha extends BaseController } try { - $helper = new \App\Libraries\Abdm\AbdmV3DirectHelper(); - $result = $helper->requestLoginOtpByAadhaar($aadhaar); + $connector = AbdmConnectorFactory::make(); + $result = $connector->abhaAadhaarGenerateOtp(['aadhaar' => $aadhaar]); if (! empty($result['ok']) && (int) $result['ok'] === 1) { + $txnId = (string) ($result['txn_id'] ?? $result['data']['txnId'] ?? $result['data']['txn_id'] ?? ''); + $msg = (string) ($result['message'] ?? $result['data']['message'] ?? 'OTP sent to Aadhaar linked mobile number.'); + $maskedMobile = (string) ($result['masked_mobile'] ?? $result['data']['maskedMobile'] ?? $result['data']['masked_mobile'] ?? ''); + return $this->response->setJSON([ - 'ok' => 1, - 'txn_id' => $result['txn_id'] ?? '', - 'message' => $result['message'] ?? 'OTP sent to Aadhaar linked mobile number.', - 'resend_after' => $result['resend_after'] ?? 60, + 'ok' => 1, + 'txn_id' => $txnId, + 'message' => $msg, + 'masked_mobile' => $maskedMobile, + 'resend_after' => 60, ]); } + // Fallback to direct V3 client if bridge call did not succeed + try { + $helper = new \App\Libraries\Abdm\AbdmV3DirectHelper(); + $directResult = $helper->requestLoginOtpByAadhaar($aadhaar); + if (! empty($directResult['ok']) && (int) $directResult['ok'] === 1) { + return $this->response->setJSON([ + 'ok' => 1, + 'txn_id' => $directResult['txn_id'] ?? '', + 'message' => $directResult['message'] ?? 'OTP sent to Aadhaar linked mobile number.', + 'resend_after' => $directResult['resend_after'] ?? 60, + ]); + } + } catch (\Throwable $e) {} + + $errMsg = $this->extractBridgeErrorText($result, 'Failed to send OTP to Aadhaar linked mobile.'); + if (stripos($errMsg, 'invalid') !== false && stripos($errMsg, 'aadhaar') !== false) { + $errMsg = 'Aadhaar Number is not valid. Please enter a valid 12-digit Aadhaar Number.'; + } + return $this->response->setJSON([ 'ok' => 0, - 'error_text' => $result['error_text'] ?? 'Failed to send OTP to Aadhaar linked mobile.', + 'error_text' => $errMsg, ]); } catch (\Throwable $e) { log_message('error', '[ABHA] findAadhaarRequestOtp exception: ' . $e->getMessage()); @@ -1153,11 +1177,65 @@ class Abha extends BaseController } try { - $helper = new \App\Libraries\Abdm\AbdmV3DirectHelper(); - $result = $helper->verifyLoginOtpByAadhaar($txnId, $otp); + $connector = AbdmConnectorFactory::make(); + $result = $connector->abhaAadhaarVerifyOtp(['txnId' => $txnId, 'otp' => $otp]); + + $authResult = strtolower(trim((string) ($result['authResult'] ?? $result['data']['authResult'] ?? $result['auth_result'] ?? $result['data']['auth_result'] ?? ''))); + $status = strtolower(trim((string) ($result['status'] ?? $result['data']['status'] ?? ''))); + if ($authResult === 'failed' || in_array($status, ['failed', 'failure', 'error'], true)) { + $result['ok'] = 0; + } + + if (empty($result['ok']) || (int) $result['ok'] !== 1) { + $err = $this->extractBridgeErrorText($result, 'Incorrect OTP'); + if (stripos($err, 'no abha') !== false || stripos($err, 'not registered') !== false || stripos($err, 'not exist') !== false || stripos($err, 'not found') !== false) { + return $this->response->setJSON([ + 'ok' => 0, + 'no_abha_found' => true, + 'error_text' => 'NO ABHA user registered with this Aadhaar Number', + ]); + } + return $this->response->setJSON([ + 'ok' => 0, + 'error_text' => $err, + ]); + } + + $payload = $result; + if (is_array($result['data'] ?? null)) { + foreach ($result['data'] as $k => $v) { + if (! array_key_exists($k, $payload)) { + $payload[$k] = $v; + } + } + } + + $profile = $this->pickGatewayAbhaProfile($payload); + + $abhaNum = (string) ( + $profile['ABHANumber'] + ?? $profile['abha_id'] + ?? $profile['abha_number'] + ?? $payload['ABHANumber'] + ?? $payload['abha_id'] + ?? $payload['abha_number'] + ?? '' + ); + $cleanAbha = preg_replace('/\D/', '', $abhaNum); + + $abhaAddress = (string) ( + ($profile['preferredAddress'] ?? '') + ?: ($profile['preferredAbhaAddress'] ?? '') + ?: ($profile['abha_address'] ?? '') + ?: ($payload['preferredAddress'] ?? '') + ?: ($payload['preferredAbhaAddress'] ?? '') + ?: ($payload['abha_address'] ?? '') + ?: '' + ); // VRFY_ABHA_403: No ABHA user registered with this Aadhaar Number - if (! empty($result['no_abha_found'])) { + $isNew = ! empty($payload['is_new']) || ! empty($payload['new_user']) || ! empty($result['new_user']); + if ((strlen($cleanAbha) !== 14 && $abhaAddress === '') || $isNew) { return $this->response->setJSON([ 'ok' => 0, 'no_abha_found' => true, @@ -1165,61 +1243,13 @@ class Abha extends BaseController ]); } - if (empty($result['ok']) || (int) $result['ok'] !== 1) { - return $this->response->setJSON([ - 'ok' => 0, - 'error_text' => $result['error_text'] ?? 'Incorrect OTP', - ]); - } + $name = $this->extractAbhaProfileName($profile, $payload); + $photo = $this->extractAbhaProfilePhoto($profile, $payload); + $profileMobile = (string) ($profile['mobile'] ?? $payload['mobile'] ?? $payload['mobileNumber'] ?? ''); + $profileGender = (string) ($profile['gender'] ?? $payload['gender'] ?? ''); + $profileDob = (string) ($profile['dob'] ?? $profile['date_of_birth'] ?? $payload['dob'] ?? $payload['date_of_birth'] ?? ''); - // VRFY_ABHA_404: ABHA exists -> extract profile details & card - $fullProfile = is_array($result['full_profile'] ?? null) ? $result['full_profile'] : []; - $account = is_array($result['account'] ?? null) ? $result['account'] : []; - $userToken = (string) ($result['token'] ?? ''); - - $firstNonEmpty = static function (array $values): string { - foreach ($values as $value) { - if (is_scalar($value) && trim((string) $value) !== '') { - return trim((string) $value); - } - } - return ''; - }; - - $abhaNum = $firstNonEmpty([ - $account['ABHANumber'] ?? null, - $account['abhaNumber'] ?? null, - $account['abha_number'] ?? null, - $account['abha_id'] ?? null, - $fullProfile['ABHANumber'] ?? null, - $fullProfile['abhaNumber'] ?? null, - $fullProfile['abha_id'] ?? null, - ]); - - $abhaAddress = $firstNonEmpty([ - $account['preferredAbhaAddress'] ?? null, - $account['preferredAddress'] ?? null, - $account['abhaAddress'] ?? null, - $account['abha_address'] ?? null, - $fullProfile['preferredAbhaAddress'] ?? null, - $fullProfile['preferredAddress'] ?? null, - $fullProfile['abhaAddress'] ?? null, - $fullProfile['abha_address'] ?? null, - ]); - - $name = $firstNonEmpty([ - $account['name'] ?? null, - $account['fullName'] ?? null, - $account['full_name'] ?? null, - $fullProfile['name'] ?? null, - trim(($fullProfile['firstName'] ?? '') . ' ' . ($fullProfile['middleName'] ?? '') . ' ' . ($fullProfile['lastName'] ?? '')), - ]); - - $genderRaw = $firstNonEmpty([ - $account['gender'] ?? null, - $fullProfile['gender'] ?? null, - ]); - $genderUpper = strtoupper($genderRaw); + $genderUpper = strtoupper($profileGender); if ($genderUpper === 'M' || $genderUpper === '1' || $genderUpper === 'MALE') { $gender = 'Male'; } elseif ($genderUpper === 'F' || $genderUpper === '2' || $genderUpper === 'FEMALE') { @@ -1227,101 +1257,22 @@ class Abha extends BaseController } elseif ($genderUpper === 'O' || $genderUpper === '3' || $genderUpper === 'OTHER') { $gender = 'Other'; } else { - $gender = $genderRaw; + $gender = $profileGender; } - $dob = $firstNonEmpty([ - $account['dob'] ?? null, - $account['dateOfBirth'] ?? null, - $account['date_of_birth'] ?? null, - $fullProfile['dob'] ?? null, - $fullProfile['dateOfBirth'] ?? null, - $fullProfile['date_of_birth'] ?? null, - ]); - if ($dob === '') { - $day = $firstNonEmpty([$account['dayOfBirth'] ?? null, $account['day_of_birth'] ?? null, $fullProfile['dayOfBirth'] ?? null, $fullProfile['day_of_birth'] ?? null]); - $month = $firstNonEmpty([$account['monthOfBirth'] ?? null, $account['month_of_birth'] ?? null, $fullProfile['monthOfBirth'] ?? null, $fullProfile['month_of_birth'] ?? null]); - $year = $firstNonEmpty([$account['yearOfBirth'] ?? null, $account['year_of_birth'] ?? null, $fullProfile['yearOfBirth'] ?? null, $fullProfile['year_of_birth'] ?? null]); - if ($year !== '' && $month !== '' && $day !== '') { - $dob = sprintf('%04d-%02d-%02d', (int) $year, (int) $month, (int) $day); - } elseif ($year !== '') { - $dob = $year; - } - } + $address = (string) (($profile['address'] ?? '') ?: ($profile['address_line'] ?? '') ?: ($payload['address'] ?? '') ?: ($payload['address_line'] ?? '') ?: ''); + $zip = (string) (($profile['pinCode'] ?? '') ?: ($profile['pin_code'] ?? '') ?: ($payload['pinCode'] ?? '') ?: ($payload['pin_code'] ?? '') ?: ''); + $stateName = (string) (($profile['stateName'] ?? '') ?: ($profile['state_name'] ?? '') ?: ($payload['stateName'] ?? '') ?: ($payload['state_name'] ?? '') ?: ''); + $districtName = (string) (($profile['districtName'] ?? '') ?: ($profile['district_name'] ?? '') ?: ($payload['districtName'] ?? '') ?: ($payload['district_name'] ?? '') ?: ''); + $email = (string) (($profile['email'] ?? '') ?: ($payload['email'] ?? '') ?: ''); - $profileMobile = $firstNonEmpty([ - $account['mobile'] ?? null, - $account['mobileNumber'] ?? null, - $account['mobile_number'] ?? null, - $fullProfile['mobile'] ?? null, - $fullProfile['mobileNumber'] ?? null, - ]); - - $photo = $firstNonEmpty([ - $account['profilePhoto'] ?? null, - $account['profile_photo'] ?? null, - $fullProfile['profilePhoto'] ?? null, - $fullProfile['profile_photo'] ?? null, - $fullProfile['photo'] ?? null, - $fullProfile['kycPhoto'] ?? null, - ]); - - $address = $firstNonEmpty([ - $fullProfile['address'] ?? null, - $fullProfile['addressLine'] ?? null, - $fullProfile['address_line'] ?? null, - $account['address'] ?? null, - $account['addressLine'] ?? null, - ]); - - $districtName = $firstNonEmpty([ - $fullProfile['district'] ?? null, - $fullProfile['districtName'] ?? null, - $fullProfile['district_name'] ?? null, - $account['district'] ?? null, - $account['districtName'] ?? null, - ]); - - $stateName = $firstNonEmpty([ - $fullProfile['state'] ?? null, - $fullProfile['stateName'] ?? null, - $fullProfile['state_name'] ?? null, - $account['state'] ?? null, - $account['stateName'] ?? null, - ]); - - $zip = $firstNonEmpty([ - $fullProfile['pinCode'] ?? null, - $fullProfile['pincode'] ?? null, - $fullProfile['pin'] ?? null, - $account['pinCode'] ?? null, - $account['pincode'] ?? null, - ]); - - $email = $firstNonEmpty([ - $fullProfile['email'] ?? null, - $account['email'] ?? null, - ]); - - $cardBase64 = (string) ($result['card_base64'] ?? ''); - - // Fallback to Bridge card endpoint if not retrieved directly - if ($cardBase64 === '' && ($abhaNum !== '' || $abhaAddress !== '')) { - try { - $bridge = \App\Libraries\Abdm\AbdmConnectorFactory::make(); - $cardResult = $bridge->fetchOfficialAbhaCard([ - 'abha_number' => $abhaNum, - 'abha_address' => $abhaAddress, - 'token' => $userToken, - 'x_token' => $userToken, - ]); - if (! empty($cardResult['ok']) && ! empty($cardResult['card_base64'])) { - $cardBase64 = (string) $cardResult['card_base64']; - } - } catch (\Throwable $e) { - log_message('warning', '[ABHA] Bridge card fetch fallback failed: ' . $e->getMessage()); - } - } + $cardBase64 = (string) ( + $result['official_card']['card_base64'] + ?? $payload['official_card']['card_base64'] + ?? $result['card_base64'] + ?? $payload['card_base64'] + ?? '' + ); $abhaMeta = [ 'abha_address' => $abhaAddress, @@ -1337,23 +1288,21 @@ class Abha extends BaseController 'email' => $email, ]; - $patientInfo = $this->tryAutoLinkByDirectMatch($abhaNum, $name, $profileMobile, $gender, $dob, $abhaMeta); + $patientInfo = $this->tryAutoLinkByDirectMatch($cleanAbha, $name, $profileMobile, $gender, $profileDob, $abhaMeta); $responseBase = [ 'ok' => 1, - 'txn_id' => $result['txn_id'] ?? $txnId, - 'token' => $result['token'] ?? '', - 'accounts' => $result['accounts'] ?? [], + 'txn_id' => (string) ($payload['txnId'] ?? $payload['txn_id'] ?? $txnId), 'card_base64' => $cardBase64, 'card_content_type' => 'image/png', 'card_source' => $cardBase64 !== '' ? 'abdm' : 'generated', 'card_message' => $cardBase64 !== '' ? 'Official ABHA Card from ABDM' : '', - 'abha_number' => $abhaNum, + 'abha_number' => $cleanAbha, 'name' => $name, 'photo' => $photo, 'mobile' => $profileMobile, 'gender' => $gender, - 'dob' => $dob, + 'dob' => $profileDob, 'abha_address' => $abhaAddress, 'verified_status' => 'ACTIVE', 'verification_type' => 'VERIFIED', @@ -1378,8 +1327,7 @@ class Abha extends BaseController $db = \Config\Database::connect(); $fields = $db->getFieldNames('patient_master') ?? []; $abhaField = $this->resolveAbhaFieldName($fields); - $abhaNumClean = preg_replace('/\D/', '', $abhaNum); - $candidates = $this->findMatchingCandidates($db, $fields, $name, $profileMobile, $gender, $dob, '', $abhaField, $abhaNumClean); + $candidates = $this->findMatchingCandidates($db, $fields, $name, $profileMobile, $gender, $profileDob, '', $abhaField, $cleanAbha); return $this->response->setJSON($responseBase + [ 'need_confirmation' => true,