request->isAJAX()) { return $this->response->setStatusCode(400)->setJSON(['ok' => 0, 'error_text' => 'Invalid request']); } $aadhaar = preg_replace('/\D/', '', trim((string) ($this->request->getPost('aadhaar') ?? ''))); $authType = trim((string) ($this->request->getPost('auth_type') ?? 'aadhaar_otp')); if (strlen($aadhaar) !== 12) { return $this->response->setJSON(['ok' => 0, 'error_text' => 'Valid 12-digit Aadhaar number is required']); } try { $result = AbdmConnectorFactory::make()->abhaAadhaarGenerateOtp(['aadhaar' => $aadhaar]); } catch (\Throwable $e) { return $this->response->setStatusCode(500)->setJSON(['ok' => 0, 'error_text' => $e->getMessage()]); } // Normalise for the wizard: expose txn_id at top level if (! empty($result['ok']) && $result['ok'] == 1) { $txnId = $result['txn_id'] ?? $result['data']['txnId'] ?? $result['data']['txn_id'] ?? null; return $this->response->setJSON(['ok' => 1, 'txn_id' => $txnId]); } return $this->response->setJSON([ 'ok' => 0, 'error_text' => $result['error_text'] ?? $result['message'] ?? $result['data']['message'] ?? 'Failed to send OTP', ]); } // ------------------------------------------------------------------------- // Step 2 — Verify Aadhaar OTP // POST abha/create/verify_otp // Response includes skip_mobile=true and abha_number if ABHA already linked. // ------------------------------------------------------------------------- public function verifyOtp() { if (! $this->request->isAJAX()) { return $this->response->setStatusCode(400)->setJSON(['ok' => 0, 'error_text' => 'Invalid request']); } $txnId = trim((string) ($this->request->getPost('txn_id') ?? $this->request->getPost('txnId') ?? '')); $otp = trim((string) ($this->request->getPost('otp') ?? '')); $mobile = trim((string) ($this->request->getPost('mobile') ?? '')); if ($txnId === '' || $otp === '') { return $this->response->setJSON(['ok' => 0, 'error_text' => 'txn_id and otp are required']); } try { $result = AbdmConnectorFactory::make()->abhaAadhaarVerifyOtp(['txnId' => $txnId, 'otp' => $otp, 'mobile' => $mobile]); } catch (\Throwable $e) { return $this->response->setStatusCode(500)->setJSON(['ok' => 0, 'error_text' => $e->getMessage()]); } if (empty($result['ok']) || $result['ok'] != 1) { return $this->response->setJSON([ 'ok' => 0, 'error_text' => $result['error_text'] ?? $result['message'] ?? $result['data']['message'] ?? 'OTP verification failed', ]); } $payload = $result['data'] ?? $result; $newTxnId = $payload['txnId'] ?? $payload['txn_id'] ?? $txnId; // aadhaar/verify-otp returns ABHAProfile when ABHA is created/found $profile = $payload['ABHAProfile'] ?? []; if (empty($profile) && ! empty($payload['accounts'][0])) { $profile = $payload['accounts'][0]; // legacy fallback } $abhaNum = (string) ($profile['ABHANumber'] ?? $payload['ABHANumber'] ?? ''); $name = (string) ($profile['name'] ?? $profile['fullName'] ?? ''); $photo = (string) ($profile['profilePhoto'] ?? ''); $mobile = (string) ($profile['mobile'] ?? $payload['mobile'] ?? $payload['mobileNumber'] ?? ''); $profileGender = (string) ($profile['gender'] ?? ''); $profileDob = (string) ($profile['dob'] ?? ''); $patientInfo = $this->autoCreateOrFindPatient($abhaNum, $name, $mobile, $profileGender, $profileDob); return $this->response->setJSON([ 'ok' => 1, 'txn_id' => $newTxnId, 'skip_mobile' => true, 'abha_number' => $abhaNum, 'name' => $name, 'photo' => $photo, 'mobile' => $mobile, 'patient_id' => $patientInfo['patient_id'], 'p_code' => $patientInfo['p_code'], 'is_new_patient' => $patientInfo['is_new'], ]); } // ------------------------------------------------------------------------- // Step 3a — Send mobile OTP // POST abha/create/communication // ------------------------------------------------------------------------- public function communication() { if (! $this->request->isAJAX()) { return $this->response->setStatusCode(400)->setJSON(['ok' => 0, 'error_text' => 'Invalid request']); } $mobile = preg_replace('/\D/', '', trim((string) ($this->request->getPost('mobile') ?? ''))); $txnId = trim((string) ($this->request->getPost('txn_id') ?? $this->request->getPost('txnId') ?? '')); if (strlen($mobile) !== 10) { return $this->response->setJSON(['ok' => 0, 'error_text' => 'Valid 10-digit mobile number is required']); } try { $result = AbdmConnectorFactory::make()->abhaMobileGenerateOtp(['mobile' => $mobile, 'txnId' => $txnId]); } catch (\Throwable $e) { return $this->response->setStatusCode(500)->setJSON(['ok' => 0, 'error_text' => $e->getMessage()]); } if (! empty($result['ok']) && $result['ok'] == 1) { $newTxnId = $result['txn_id'] ?? $result['data']['txnId'] ?? $result['data']['txn_id'] ?? $txnId; return $this->response->setJSON(['ok' => 1, 'txn_id' => $newTxnId]); } return $this->response->setJSON([ 'ok' => 0, 'error_text' => $result['error_text'] ?? $result['message'] ?? $result['data']['message'] ?? 'Failed to send mobile OTP', ]); } // ------------------------------------------------------------------------- // Step 3b — Verify mobile OTP // POST abha/create/verify_comm_otp // ------------------------------------------------------------------------- public function verifyCommOtp() { if (! $this->request->isAJAX()) { return $this->response->setStatusCode(400)->setJSON(['ok' => 0, 'error_text' => 'Invalid request']); } $txnId = trim((string) ($this->request->getPost('txn_id') ?? $this->request->getPost('txnId') ?? '')); $otp = trim((string) ($this->request->getPost('otp') ?? '')); if ($txnId === '' || $otp === '') { return $this->response->setJSON(['ok' => 0, 'error_text' => 'txn_id and otp are required']); } try { $result = AbdmConnectorFactory::make()->abhaMobileVerifyOtp(['txnId' => $txnId, 'otp' => $otp]); } catch (\Throwable $e) { return $this->response->setStatusCode(500)->setJSON(['ok' => 0, 'error_text' => $e->getMessage()]); } if (empty($result['ok']) || $result['ok'] != 1) { return $this->response->setJSON([ 'ok' => 0, 'error_text' => $result['error_text'] ?? $result['message'] ?? $result['data']['message'] ?? 'Mobile OTP verification failed', ]); } $payload = $result['data'] ?? $result; // mobile/verify-otp returns data.profile (new format) or data.ABHAProfile / accounts[0] (legacy) $profile = $payload['profile'] ?? $payload['ABHAProfile'] ?? []; if (empty($profile) && ! empty($payload['accounts'][0])) { $profile = $payload['accounts'][0]; } $abhaNum = (string) ($profile['ABHANumber'] ?? $payload['ABHANumber'] ?? ''); $name = (string) ($profile['name'] ?? $profile['fullName'] ?? $payload['name'] ?? ''); $photo = (string) ($profile['profilePhoto'] ?? $payload['profilePhoto'] ?? ''); $gender = (string) ($profile['gender'] ?? $payload['gender'] ?? ''); $dob = (string) ($profile['dob'] ?? $payload['dob'] ?? ''); $mobile = (string) ($profile['mobile'] ?? $payload['mobile'] ?? ''); $patientInfo = $this->autoCreateOrFindPatient($abhaNum, $name, $mobile, $gender, $dob); return $this->response->setJSON([ 'ok' => 1, 'abha_number' => $abhaNum, 'name' => $name, 'photo' => $photo, 'gender' => $gender, 'dob' => $dob, 'patient_id' => $patientInfo['patient_id'], 'p_code' => $patientInfo['p_code'], 'is_new_patient' => $patientInfo['is_new'], ]); } // ------------------------------------------------------------------------- // Step 4 — Save/finalise ABHA address // POST abha/create/address // Gateway has no address-assignment endpoint; return ok=1 as confirmation. // ------------------------------------------------------------------------- public function address() { if (! $this->request->isAJAX()) { return $this->response->setStatusCode(400)->setJSON(['ok' => 0, 'error_text' => 'Invalid request']); } // Address assignment is noted for the staff. No gateway call available. return $this->response->setJSON(['ok' => 1, 'message' => 'ABHA created successfully.']); } // ------------------------------------------------------------------------- // ABHA Card view — GET abha/card/{abha_number} // Renders a printable card page for a patient whose ABHA is stored in HMS. // ------------------------------------------------------------------------- public function card(string $abhaNumber = '') { $abhaNumClean = preg_replace('/\D/', '', $abhaNumber); if (strlen($abhaNumClean) !== 14) { return $this->response->setStatusCode(400) ->setBody('