diff --git a/pom.xml b/pom.xml index b8614a9..080b8fe 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ io.hcxprotocol hcx-integrator-sdk - 1.0.5 + 1.0.7 com.nimbusds @@ -73,6 +73,11 @@ postgresql 42.3.1 + + com.amazonaws + aws-java-sdk + 1.12.353 + diff --git a/src/main/java/org/swasth/hcx/HCXApplication.java b/src/main/java/org/swasth/hcx/HCXApplication.java index 74b5a0c..0a37bab 100644 --- a/src/main/java/org/swasth/hcx/HCXApplication.java +++ b/src/main/java/org/swasth/hcx/HCXApplication.java @@ -8,6 +8,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication +@EnableAsync public class HCXApplication { public static void main(String[] args) { diff --git a/src/main/java/org/swasth/hcx/controllers/BaseController.java b/src/main/java/org/swasth/hcx/controllers/BaseController.java index ce3f2f7..b19392c 100644 --- a/src/main/java/org/swasth/hcx/controllers/BaseController.java +++ b/src/main/java/org/swasth/hcx/controllers/BaseController.java @@ -2,10 +2,11 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.IParser; +import com.amazonaws.services.dynamodbv2.xspec.S; import io.hcxprotocol.init.HCXIntegrator; import io.hcxprotocol.utils.Operations; -import lombok.SneakyThrows; import org.apache.commons.collections.MapUtils; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.*; import org.slf4j.Logger; @@ -24,14 +25,15 @@ import org.swasth.hcx.exception.ServiceUnavailbleException; import org.swasth.hcx.fhirexamples.OnActionFhirExamples; import org.swasth.hcx.helpers.EventGenerator; -import org.swasth.hcx.service.HcxIntegratorService; -import org.swasth.hcx.service.HeaderAuditService; -import org.swasth.hcx.service.NotificationService; -import org.swasth.hcx.service.PayerService; +import org.swasth.hcx.service.*; import org.swasth.hcx.utils.JSONUtils; import org.swasth.hcx.utils.OnActionCall; -import javax.annotation.PostConstruct; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.*; import static org.swasth.hcx.utils.Constants.*; @@ -44,6 +46,11 @@ public class BaseController { @Autowired private EventGenerator eventGenerator; + @Autowired + private BeneficiaryService beneficiaryService; + + @Autowired + private PostgresService postgresService; @Autowired protected Environment env; @@ -54,26 +61,28 @@ public class BaseController { protected NotificationService notificationService; @Autowired - protected HcxIntegratorService hcxIntegratorService; + protected HcxIntegratorService hcxIntegratorService; private String baseURL; @Value("${autoresponse}") private Boolean autoResponse; + @Value("${postgres.table.payerData}") + private String table; @Autowired private PayerService payerService; private static final Logger logger = LoggerFactory.getLogger(BaseController.class); - protected Response errorResponse(Response response, ErrorCodes code, java.lang.Exception e){ - ResponseError error= new ResponseError(code, e.getMessage(), e.getCause()); + protected Response errorResponse(Response response, ErrorCodes code, java.lang.Exception e) { + ResponseError error = new ResponseError(code, e.getMessage(), e.getCause()); response.setError(error); return response; } - protected void replaceResourceInBundleEntry(Bundle bundle, String bundleURL, Class matchClass, Bundle.BundleEntryComponent bundleEntry){ + protected void replaceResourceInBundleEntry(Bundle bundle, String bundleURL, Class matchClass, Bundle.BundleEntryComponent bundleEntry) { //updating the meta Meta meta = new Meta(); @@ -81,12 +90,12 @@ protected void replaceResourceInBundleEntry(Bundle bundle, String bundleURL, Cla meta.setLastUpdated(new Date()); bundle.setMeta(meta); - for(int i=0; i < bundle.getEntry().size(); i++){ + for (int i = 0; i < bundle.getEntry().size(); i++) { System.out.println("in the loop " + i); Bundle.BundleEntryComponent par = bundle.getEntry().get(i); DomainResource dm = (DomainResource) par.getResource(); - if(dm.getClass() == matchClass){ - bundle.getEntry().set(i,bundleEntry); + if (dm.getClass() == matchClass) { + bundle.getEntry().set(i, bundleEntry); } } } @@ -96,61 +105,123 @@ protected void processAndValidate(String onApiAction, String metadataTopic, Requ String mid = UUID.randomUUID().toString(); String serviceMode = env.getProperty(SERVICE_MODE); System.out.println("\n" + "Mode: " + serviceMode + " :: mid: " + mid + " :: Event: " + onApiAction); - if(StringUtils.equalsIgnoreCase(serviceMode, GATEWAY)) { - Map pay = new HashMap<>(); - System.out.println("payload received " + requestBody); - pay.put("payload", String.valueOf(requestBody.get("payload"))); - Map output = new HashMap<>(); - Map outputOfOnAction = new HashMap<>(); - System.out.println("create the oncheck payload"); - Bundle bundle = new Bundle(); - Request req = new Request(requestBody, apiAction); - HCXIntegrator hcxIntegrator = hcxIntegratorService.getHCXIntegrator(req.getRecipientCode()); - if (COVERAGE_ELIGIBILITY_ONCHECK.equalsIgnoreCase(onApiAction)) { - boolean result = hcxIntegrator.processIncoming(JSONUtils.serialize(pay), Operations.COVERAGE_ELIGIBILITY_CHECK,output); - if(!result){ - System.out.println("Error while processing incoming request: " + output); - } - System.out.println("outmap after decryption " + output.get("fhirPayload")); - System.out.println("decryption successful"); - //processing the decrypted incoming bundle - bundle = p.parseResource(Bundle.class, (String) output.get("fhirPayload")); - CoverageEligibilityResponse covRes = OnActionFhirExamples.coverageEligibilityResponseExample(); - replaceResourceInBundleEntry(bundle, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-CoverageEligibilityResponseBundle.html", CoverageEligibilityRequest.class, new Bundle.BundleEntryComponent().setFullUrl(covRes.getResourceType() + "/" + covRes.getId().toString().replace("#","")).setResource(covRes)); - System.out.println("bundle reply " + p.encodeResourceToString(bundle)); - //sending the onaction call - sendResponse(apiAction,p.encodeResourceToString(bundle),(String) output.get("fhirPayload"), Operations.COVERAGE_ELIGIBILITY_ON_CHECK, String.valueOf(requestBody.get("payload")),"response.complete" ,outputOfOnAction); - } else if (CLAIM_ONSUBMIT.equalsIgnoreCase(onApiAction)) { - boolean result = hcxIntegrator.processIncoming(JSONUtils.serialize(pay), Operations.CLAIM_SUBMIT,output); - if(!result){ - System.out.println("Error while processing incoming request: " + output); - } - System.out.println("outmap after decryption " + output); - System.out.println("decryption successful"); - //processing the decrypted incoming bundle - bundle = p.parseResource(Bundle.class, (String) output.get("fhirPayload")); - ClaimResponse claimRes = OnActionFhirExamples.claimResponseExample(); - replaceResourceInBundleEntry(bundle, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-ClaimResponseBundle.html", Claim.class, new Bundle.BundleEntryComponent().setFullUrl(claimRes.getResourceType() + "/" + claimRes.getId().toString().replace("#","")).setResource(claimRes)); - sendResponse(apiAction,p.encodeResourceToString(bundle), (String) output.get("fhirPayload"), Operations.CLAIM_ON_SUBMIT, String.valueOf(requestBody.get("payload")),"response.complete" ,outputOfOnAction); - } else if (PRE_AUTH_ONSUBMIT.equalsIgnoreCase(onApiAction)) { - boolean result = hcxIntegrator.processIncoming(JSONUtils.serialize(pay), Operations.PRE_AUTH_SUBMIT,output); - if(!result){ - System.out.println("Error while processing incoming request: " + output); - } - System.out.println("outmap after decryption " + output); - System.out.println("decryption successful"); - //processing the decrypted incoming bundle - bundle = p.parseResource(Bundle.class, (String) output.get("fhirPayload")); - ClaimResponse preAuthRes = OnActionFhirExamples.claimResponseExample(); - preAuthRes.setUse(ClaimResponse.Use.PREAUTHORIZATION); - replaceResourceInBundleEntry(bundle, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-ClaimResponseBundle.html", Claim.class, new Bundle.BundleEntryComponent().setFullUrl(preAuthRes.getResourceType() + "/" + preAuthRes.getId().toString().replace("#","")).setResource(preAuthRes)); - sendResponse(apiAction,p.encodeResourceToString(bundle), (String) output.get("fhirPayload"), Operations.PRE_AUTH_ON_SUBMIT, String.valueOf(requestBody.get("payload")),"response.complete" ,outputOfOnAction); + if (StringUtils.equalsIgnoreCase(serviceMode, GATEWAY)) { + Map pay = new HashMap<>(); + System.out.println("payload received " + requestBody); + pay.put("payload", String.valueOf(requestBody.get("payload"))); + Map output = new HashMap<>(); + Map outputOfOnAction = new HashMap<>(); + System.out.println("create the oncheck payload"); + Bundle bundle = new Bundle(); + Request req = new Request(requestBody, apiAction); + HCXIntegrator hcxIntegrator = hcxIntegratorService.getHCXIntegrator(req.getRecipientCode()); + if (COVERAGE_ELIGIBILITY_CHECK.equalsIgnoreCase(onApiAction)) { + boolean result = hcxIntegrator.processIncoming(JSONUtils.serialize(pay), Operations.COVERAGE_ELIGIBILITY_CHECK, output); + if (!result) { + System.out.println("Error while processing incoming request: " + output); + } + System.out.println("output map after decryption coverageEligibility" + output.get("fhirPayload")); + System.out.println("decryption successful"); + //processing the decrypted incoming bundle + bundle = p.parseResource(Bundle.class, (String) output.get("fhirPayload")); + CoverageEligibilityResponse covRes = OnActionFhirExamples.coverageEligibilityResponseExample(); + covRes.setPatient(new Reference("Patient/RVH1003")); + replaceResourceInBundleEntry(bundle, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-CoverageEligibilityResponseBundle.html", CoverageEligibilityRequest.class, new Bundle.BundleEntryComponent().setFullUrl(covRes.getResourceType() + "/" + covRes.getId().toString().replace("#", "")).setResource(covRes)); + System.out.println("bundle reply " + p.encodeResourceToString(bundle)); + //sending the onaction call + sendResponse(apiAction, p.encodeResourceToString(bundle), (String) output.get("fhirPayload"), Operations.COVERAGE_ELIGIBILITY_ON_CHECK, String.valueOf(requestBody.get("payload")), "response.complete", outputOfOnAction); + updateMobileNumber(request.getApiCallId()); + } else if (CLAIM_ONSUBMIT.equalsIgnoreCase(onApiAction)) { + boolean result = hcxIntegrator.processIncoming(JSONUtils.serialize(pay), Operations.CLAIM_SUBMIT, output); + if (!result) { + System.out.println("Error while processing incoming request: " + output); + } + System.out.println("output map after decryption claim " + output); + System.out.println("decryption successful"); + //processing the decrypted incoming bundle + bundle = p.parseResource(Bundle.class, (String) output.get("fhirPayload")); + ClaimResponse claimRes = OnActionFhirExamples.claimResponseExample(); + claimRes.setPatient(new Reference("Patient/RVH1003")); + replaceResourceInBundleEntry(bundle, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-ClaimResponseBundle.html", Claim.class, new Bundle.BundleEntryComponent().setFullUrl(claimRes.getResourceType() + "/" + claimRes.getId().toString().replace("#", "")).setResource(claimRes)); + System.out.println("bundle reply " + p.encodeResourceToString(bundle)); + sendResponse(apiAction, p.encodeResourceToString(bundle), (String) output.get("fhirPayload"), Operations.CLAIM_ON_SUBMIT, String.valueOf(requestBody.get("payload")), "response.complete", outputOfOnAction); + updateMobileNumber(request.getApiCallId()); + } else if (PRE_AUTH_ONSUBMIT.equalsIgnoreCase(onApiAction)) { + boolean result = hcxIntegrator.processIncoming(JSONUtils.serialize(pay), Operations.PRE_AUTH_SUBMIT, output); + if (!result) { + System.out.println("Error while processing incoming request: " + output); } + System.out.println("output map after decryption preauth " + output); + System.out.println("decryption successful"); + //processing the decrypted incoming bundle + bundle = p.parseResource(Bundle.class, (String) output.get("fhirPayload")); + ClaimResponse preAuthRes = OnActionFhirExamples.claimResponseExample(); + preAuthRes.setPatient(new Reference("Patient/RVH1003")); + preAuthRes.setUse(ClaimResponse.Use.PREAUTHORIZATION); + replaceResourceInBundleEntry(bundle, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-ClaimResponseBundle.html", Claim.class, new Bundle.BundleEntryComponent().setFullUrl(preAuthRes.getResourceType() + "/" + preAuthRes.getId().toString().replace("#", "")).setResource(preAuthRes)); + sendResponse(apiAction, p.encodeResourceToString(bundle), (String) output.get("fhirPayload"), Operations.PRE_AUTH_ON_SUBMIT, String.valueOf(requestBody.get("payload")), "response.complete", outputOfOnAction); + updateMobileNumber(request.getApiCallId()); + } else if (COMMUNICATION_ONREQUEST.equalsIgnoreCase(onApiAction)) { + HCXIntegrator hcxIntegrator1 = HCXIntegrator.getInstance(initializingConfigMap()); + boolean result = hcxIntegrator1.processIncoming(JSONUtils.serialize(pay), Operations.COMMUNICATION_REQUEST, output); + if (!result) { + System.out.println("Error while processing incoming request: " + output); + } + System.out.println("output map after decryption communication" + output); + System.out.println("decryption successful"); + String selectQuery = String.format("SELECT otp_verification from %s WHERE correlation_id = '%s'", table, request.getCorrelationId()); + ResultSet resultSet = postgresService.executeQuery(selectQuery); + String otpVerification = ""; + while (resultSet.next()) { + otpVerification = resultSet.getString("otp_verification"); + } + if (StringUtils.equalsIgnoreCase(otpVerification, "successful")) { + String query1 = String.format("UPDATE %s SET bank_details = '%s' WHERE correlation_id = '%s'", table, "initiated", request.getCorrelationId()); + postgresService.execute(query1); + } else if (StringUtils.equalsIgnoreCase(otpVerification, "Pending")) { + String query = String.format("UPDATE %s SET otp_verification = '%s' WHERE correlation_id ='%s'", table, "initiated", request.getCorrelationId()); + postgresService.execute(query); + } + } } + } + public void processAndValidateRequest(String onApiAction, String metadataTopic, Request request, Map requestBody, String apiAction) throws Exception { + IParser p = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + String mid = UUID.randomUUID().toString(); + String serviceMode = env.getProperty(SERVICE_MODE); + System.out.println("\n" + "Mode: " + serviceMode + " :: mid: " + mid + " :: Event: " + onApiAction); + if (StringUtils.equalsIgnoreCase(serviceMode, GATEWAY)) { + Map pay = new HashMap<>(); + System.out.println("payload received " + requestBody); + pay.put("payload", String.valueOf(requestBody.get("payload"))); + Map output = new HashMap<>(); + Map outputOfOnAction = new HashMap<>(); + System.out.println("create the oncheck payload"); + Bundle bundle = new Bundle(); + Request req = new Request(requestBody, apiAction); + HCXIntegrator hcxIntegrator = hcxIntegratorService.getHCXIntegrator(req.getRecipientCode()); + if (COVERAGE_ELIGIBILITY_ONCHECK.equalsIgnoreCase(onApiAction)) { + boolean result = hcxIntegrator.processIncoming(JSONUtils.serialize(pay), Operations.COVERAGE_ELIGIBILITY_ON_CHECK, output); + if (!result) { + System.out.println("Error while processing incoming request: " + output); + } + System.out.println("output map after decryption coverageEligibility" + output.get("fhirPayload")); + System.out.println("decryption successful"); + //processing the decrypted incoming bundle + bundle = p.parseResource(Bundle.class, (String) output.get("fhirPayload")); + CoverageEligibilityResponse covRes = OnActionFhirExamples.coverageEligibilityResponseExample(); + covRes.setPatient(new Reference("Patient/RVH1003")); + replaceResourceInBundleEntry(bundle, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-CoverageEligibilityResponseBundle.html", CoverageEligibilityRequest.class, new Bundle.BundleEntryComponent().setFullUrl(covRes.getResourceType() + "/" + covRes.getId().toString().replace("#", "")).setResource(covRes)); + System.out.println("bundle reply " + p.encodeResourceToString(bundle)); + //sending the onaction call + onActionCall.sendOnAction(request.getRecipientCode(),(String) output.get("fhirPayload") , Operations.COVERAGE_ELIGIBILITY_ON_CHECK, String.valueOf(requestBody.get("payload")), "response.complete", outputOfOnAction); + updateMobileNumber(request.getApiCallId()); + } + } } - private void sendResponse(String apiAction, String respfhir, String reqFhir, Operations operation, String actionJwe, String onActionStatus, Map output) throws Exception { + private void sendResponse(String apiAction, String respfhir, String reqFhir, Operations operation, String actionJwe, String onActionStatus, Map output) throws Exception { Request request = new Request(Collections.singletonMap("payload", actionJwe), apiAction); if (autoResponse || StringUtils.equalsIgnoreCase(request.getRecipientCode(), env.getProperty("mock_payer.participant_code"))) { onActionCall.sendOnAction(request.getRecipientCode(), respfhir, operation, actionJwe, onActionStatus, output); @@ -165,7 +236,21 @@ public ResponseEntity processRequest(Map requestBody, St Request request = new Request(requestBody, apiAction); //notificationService.notify(request,onApiAction.split("/")[2],"Request received"); setResponseParams(request, response); - processAndValidate(onApiAction, kafkaTopic, request, requestBody,apiAction); + processAndValidate(onApiAction, kafkaTopic, request, requestBody, apiAction); + System.out.println("http respond sent"); + return new ResponseEntity<>(response, HttpStatus.ACCEPTED); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("error " + e); + return exceptionHandler(response, e); + } + } + public ResponseEntity processRequestIncoming(Map requestBody, String apiAction, String onApiAction, String kafkaTopic) { + Response response = new Response(); + try { + Request request = new Request(requestBody, apiAction); + setResponseParams(request, response); + processAndValidateRequest(onApiAction, kafkaTopic, request, requestBody, apiAction); System.out.println("http respond sent"); return new ResponseEntity<>(response, HttpStatus.ACCEPTED); } catch (Exception e) { @@ -175,12 +260,12 @@ public ResponseEntity processRequest(Map requestBody, St } } - protected void setResponseParams(Request request, Response response){ + protected void setResponseParams(Request request, Response response) { response.setCorrelationId(request.getCorrelationId()); response.setApiCallId(request.getApiCallId()); } - protected ResponseEntity exceptionHandler(Response response, Exception e){ + protected ResponseEntity exceptionHandler(Response response, Exception e) { e.printStackTrace(); if (e instanceof ClientException) { return new ResponseEntity<>(errorResponse(response, ((ClientException) e).getErrCode(), e), HttpStatus.BAD_REQUEST); @@ -194,13 +279,36 @@ protected ResponseEntity exceptionHandler(Response response, Exception e } protected void validateStr(String field, String value) throws ClientException { - if(StringUtils.isEmpty(value)) + if (StringUtils.isEmpty(value)) throw new ClientException("Missing required field " + field); } - protected void validateMap(String field, Map value) throws ClientException { - if(MapUtils.isEmpty(value)) + protected void validateMap(String field, Map value) throws ClientException { + if (MapUtils.isEmpty(value)) throw new ClientException("Missing required field " + field); } + public void updateMobileNumber(String requestID) throws SQLException, ClientException { + IParser parser = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + Map payloadMap = beneficiaryService.getPayloadMap(requestID); + Bundle parsed = parser.parseResource(Bundle.class, (String) payloadMap.get("request_fhir")); + Patient patient = parser.parseResource(Patient.class, parser.encodeResourceToString(parsed.getEntry().get(3).getResource())); + String mobile = patient.getTelecom().get(0).getValue(); + String query = String.format("UPDATE %s SET mobile = '%s' WHERE request_id ='%s'", table, mobile, requestID); + postgresService.execute(query); + } + + public Map initializingConfigMap() throws IOException { + Map configMap = new HashMap<>(); + configMap.put("protocolBasePath", "https://dev-hcx.swasth.app/api/v0.8"); + configMap.put("participantCode", "testprovider1.apollo@swasth-hcx-dev"); + configMap.put("username", "testprovider1@apollo.com"); + configMap.put("password", "Opensaber@123"); + String keyUrl = "https://raw.githubusercontent.com/Swasth-Digital-Health-Foundation/hcx-platform/main/hcx-apis/src/test/resources/examples/test-keys/private-key.pem"; + String certificate = IOUtils.toString(new URL(keyUrl), StandardCharsets.UTF_8); + configMap.put("encryptionPrivateKey", certificate); + configMap.put("signingPrivateKey", certificate); + return configMap; + } + } diff --git a/src/main/java/org/swasth/hcx/controllers/v1/BeneficiaryController.java b/src/main/java/org/swasth/hcx/controllers/v1/BeneficiaryController.java new file mode 100644 index 0000000..7d524c3 --- /dev/null +++ b/src/main/java/org/swasth/hcx/controllers/v1/BeneficiaryController.java @@ -0,0 +1,118 @@ +package org.swasth.hcx.controllers.v1; + +import io.hcxprotocol.utils.Operations; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.swasth.hcx.controllers.BaseController; +import org.swasth.hcx.exception.ClientException; +import org.swasth.hcx.service.BeneficiaryService; +import org.swasth.hcx.service.CloudStorageClient; +import org.swasth.hcx.service.GenerateOutgoingRequest; +import org.swasth.hcx.utils.Constants; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import static org.swasth.hcx.utils.Constants.*; + +@RestController +@RequestMapping(Constants.VERSION_PREFIX) +public class BeneficiaryController extends BaseController { + + private static final Logger logger = LoggerFactory.getLogger(BeneficiaryController.class); + @Autowired + private GenerateOutgoingRequest outgoingRequest; + + @Autowired + private CloudStorageClient cloudStorageClient; + @Autowired + private BeneficiaryService beneficiaryService; + @Value("${phone.beneficiary-register}") + private String beneficiaryRegisterContent; + + @PostMapping(CREATE_COVERAGEELIGIBILITY_REQUEST) + public ResponseEntity createCoverageEligibility(@RequestHeader HttpHeaders headers, @RequestBody Map requestBody) throws Exception { + return outgoingRequest.createCoverageEligibilityRequest(requestBody,Operations.COVERAGE_ELIGIBILITY_CHECK); + } + + @PostMapping(CREATE_CLAIM_SUBMIT) + public ResponseEntity createClaimSubmit(@RequestBody Map requestBody) { + return outgoingRequest.createClaimRequest(requestBody, Operations.CLAIM_SUBMIT); + } + + @PostMapping(CREATE_PRE_AUTH_SUBMIT) + public ResponseEntity createPreAuthSubmit(@RequestBody Map requestBody) { + return outgoingRequest.createClaimRequest(requestBody, Operations.PRE_AUTH_SUBMIT); + } + + @PostMapping(CREATE_COMMUNICATION_REQUEST) + public ResponseEntity createCommunication(@RequestBody Map requestBody) { + return outgoingRequest.createCommunicationRequest(requestBody, Operations.COMMUNICATION_REQUEST); + } + + @PostMapping(CREATE_COMMUNICATION_ON_REQUEST) + public ResponseEntity createOnCommunication(@RequestBody Map requestBody) throws ClientException { + return outgoingRequest.createCommunicationOnRequest(requestBody); + } + + @PostMapping(BSP_REQUEST_LIST) + public ResponseEntity requestList(@RequestBody Map requestBody) throws Exception { + if(requestBody.containsKey("mobile")){ + return beneficiaryService.getRequestListFromDatabase(requestBody); + } else { + return beneficiaryService.getDataFromWorkflowId(requestBody); + } + } + + @PostMapping(SEND_OTP) + public ResponseEntity sendOTP(@RequestBody Map requestBody) { + try { + String mobile = (String) requestBody.get(MOBILE); + beneficiaryService.sendOTP(mobile, beneficiaryRegisterContent); + return ResponseEntity.ok(Map.of("message", "OTP sent successfully", "mobile", mobile)); + } catch (Exception e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + + @PostMapping(VERIFY_OTP) + public ResponseEntity verifyOTP(@RequestBody Map requestBody) { + try { + return beneficiaryService.verifyOTP(requestBody); + } catch (Exception e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + + @PostMapping(UPLOAD_DOCUMENTS) + public ResponseEntity uploadDocuments(@RequestParam("file") List files, @RequestParam("mobile") String mobile) { + try { + List> responses = beneficiaryService.getDocumentUrls(files, mobile); + return ResponseEntity.ok(responses); + } catch (Exception e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + + @PostMapping("/check/communication/request") + public ResponseEntity checkCommunicationRequest(@RequestBody Map requestBody) throws ClientException, SQLException { + System.out.println("-------------- request body ------------------" + requestBody); + if (requestBody.isEmpty()) { + throw new ClientException("Request body cannot be empty"); + } + boolean isCommunicationInitiated = beneficiaryService.checkCommunicationRequest(requestBody); + if (isCommunicationInitiated) { + return ResponseEntity.ok("Communication is initiated"); + } else { + return ResponseEntity.badRequest().body("Communication is not initiated"); + } + } + +} diff --git a/src/main/java/org/swasth/hcx/controllers/v1/CommunicationController.java b/src/main/java/org/swasth/hcx/controllers/v1/CommunicationController.java index e72ef4e..ee3c156 100644 --- a/src/main/java/org/swasth/hcx/controllers/v1/CommunicationController.java +++ b/src/main/java/org/swasth/hcx/controllers/v1/CommunicationController.java @@ -2,23 +2,20 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.swasth.hcx.utils.Constants; import org.swasth.hcx.controllers.BaseController; import java.util.Map; @RestController() -@RequestMapping(value = "/"+ "${hcx_application.api_version}"+ "/communication") +@RequestMapping(Constants.VERSION_PREFIX) public class CommunicationController extends BaseController { @Value("${kafka.topic.communication}") private String kafkaTopic; - @RequestMapping(value = "/request", method = RequestMethod.POST) + @PostMapping(Constants.COMMUNICATION_REQUEST) public ResponseEntity communicationRequest(@RequestBody Map requestBody) throws Exception { return processRequest(requestBody, Constants.COMMUNICATION_REQUEST, Constants.COMMUNICATION_ONREQUEST, kafkaTopic); } diff --git a/src/main/java/org/swasth/hcx/controllers/v1/CoverageEligibilityController.java b/src/main/java/org/swasth/hcx/controllers/v1/CoverageEligibilityController.java index 9e599db..2ed22b9 100644 --- a/src/main/java/org/swasth/hcx/controllers/v1/CoverageEligibilityController.java +++ b/src/main/java/org/swasth/hcx/controllers/v1/CoverageEligibilityController.java @@ -21,13 +21,11 @@ public class CoverageEligibilityController extends BaseController { @RequestMapping(value = "/check", method = RequestMethod.POST) public ResponseEntity checkCoverageEligibility(@RequestBody Map requestBody) throws Exception { - System.out.println("---------request body--------" + requestBody); - System.out.println("-----------request size--------" + JSONUtils.serialize(requestBody).getBytes().length); - return processRequest(requestBody, Constants.COVERAGE_ELIGIBILITY_CHECK, Constants.COVERAGE_ELIGIBILITY_ONCHECK, kafkaTopic); + return processRequest(requestBody, Constants.COVERAGE_ELIGIBILITY_CHECK, Constants.COVERAGE_ELIGIBILITY_CHECK, kafkaTopic); } @RequestMapping(value = "/on_check", method = RequestMethod.POST) public ResponseEntity onCheckCoverageEligibility(@RequestBody Map requestBody) throws Exception { - return processRequest(requestBody, Constants.COVERAGE_ELIGIBILITY_ONCHECK, Constants.COVERAGE_ELIGIBILITY_ONCHECK, kafkaTopic); + return processRequestIncoming(requestBody, Constants.COVERAGE_ELIGIBILITY_ONCHECK, Constants.COVERAGE_ELIGIBILITY_ONCHECK, kafkaTopic); } } diff --git a/src/main/java/org/swasth/hcx/controllers/v1/PayerController.java b/src/main/java/org/swasth/hcx/controllers/v1/PayerController.java index c27b205..564412e 100644 --- a/src/main/java/org/swasth/hcx/controllers/v1/PayerController.java +++ b/src/main/java/org/swasth/hcx/controllers/v1/PayerController.java @@ -69,6 +69,9 @@ public ResponseEntity requestList(@RequestBody Map reque map.put("status", resultSet.getString("status")); map.put("additional_info", JSONUtils.deserialize(resultSet.getString("additional_info"), Map.class)); map.put("payload", JSONUtils.deserialize(resultSet.getString("request_fhir"), Map.class)); + map.put("otp_verification", resultSet.getString("otp_verification")); + map.put("account_number", resultSet.getString("account_number")); + map.put("ifsc_code", resultSet.getString("ifsc_code")); result.add(map); } resp.put(type, result); @@ -162,6 +165,8 @@ public ResponseEntity review(Map requestBody, String ent if(!requestBody.containsKey("approved_amount") || !(requestBody.get("approved_amount") instanceof Integer)) throw new ClientException("Approved amount is mandatory field and should be a number"); info.put("approved_amount", requestBody.getOrDefault("approved_amount", 0)); + info.put("account_number", requestBody.getOrDefault("account_number", 0)); + info.put("ifsc_code", requestBody.getOrDefault("ifsc_code", "")); } String query = String.format("UPDATE %s SET additional_info = jsonb_set(additional_info::jsonb, '{%s}', '%s'),updated_on = %d WHERE request_id = '%s' RETURNING %s,%s,%s,%s,%s", table, type, JSONUtils.serialize(info), System.currentTimeMillis(), id, "additional_info", "status", "raw_payload", "response_fhir", "action"); diff --git a/src/main/java/org/swasth/hcx/exception/ClientException.java b/src/main/java/org/swasth/hcx/exception/ClientException.java index a782b00..e96d754 100644 --- a/src/main/java/org/swasth/hcx/exception/ClientException.java +++ b/src/main/java/org/swasth/hcx/exception/ClientException.java @@ -1,10 +1,8 @@ package org.swasth.hcx.exception; -import org.swasth.hcx.exception.ErrorCodes; - public class ClientException extends Exception { - private org.swasth.hcx.exception.ErrorCodes errCode; + private ErrorCodes errCode; public ClientException(String message) { super(message); diff --git a/src/main/java/org/swasth/hcx/exception/ErrorCodes.java b/src/main/java/org/swasth/hcx/exception/ErrorCodes.java index caa4262..0846693 100644 --- a/src/main/java/org/swasth/hcx/exception/ErrorCodes.java +++ b/src/main/java/org/swasth/hcx/exception/ErrorCodes.java @@ -13,6 +13,7 @@ public enum ErrorCodes { ERR_WRONG_DOMAIN_PAYLOAD, ERR_INVALID_DOMAIN_PAYLOAD, ERR_SENDER_NOT_SUPPORTED, - ERR_DOMAIN_PROCESSING + ERR_DOMAIN_PROCESSING, + ERR_SENDING_OTP; } diff --git a/src/main/java/org/swasth/hcx/fhirexamples/OnActionFhirExamples.java b/src/main/java/org/swasth/hcx/fhirexamples/OnActionFhirExamples.java index b2bb305..3f5a3a6 100644 --- a/src/main/java/org/swasth/hcx/fhirexamples/OnActionFhirExamples.java +++ b/src/main/java/org/swasth/hcx/fhirexamples/OnActionFhirExamples.java @@ -2,6 +2,8 @@ import org.hl7.fhir.r4.model.*; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.UUID; @@ -52,4 +54,199 @@ public static ClaimResponse claimResponseExample(){ return claimRes; } + public static CoverageEligibilityRequest coverageEligibilityRequestExample() { + + //Creating coverage eligibility request + CoverageEligibilityRequest ce = new CoverageEligibilityRequest(); + ce.setId("dc82673b-8c71-48c2-8a17-16dcb3b035f6"); + Meta meta = new Meta(); + meta.getProfile().add(new CanonicalType("https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-CoverageEligibilityRequest.html")); + ce.setMeta(meta); + ce.getIdentifier().add(new Identifier().setValue("req_70e02576-f5f5-424f-b115-b5f1029704d4")); + ce.setStatus(CoverageEligibilityRequest.EligibilityRequestStatus.ACTIVE); + ce.setPriority(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/processpriority").setCode("normal"))); + String date_string = "2022-06-21"; + SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); + Date date; + try { + date = formatter.parse(date_string); + } catch (ParseException e) { + throw new RuntimeException(e); + } + ce.setServiced(new DateType().setValue(date)); + EnumFactory fact = new CoverageEligibilityRequest.EligibilityRequestPurposeEnumFactory(); + ce.setPurpose(List.of((Enumeration) new Enumeration<>(fact).setValue(CoverageEligibilityRequest.EligibilityRequestPurpose.BENEFITS))); + CoverageEligibilityRequest.DetailsComponent details = new CoverageEligibilityRequest.DetailsComponent(); + details.getDiagnosis().add(new CoverageEligibilityRequest.DiagnosisComponent().setDiagnosis(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("E906184").setDisplay("SINGLE INCISION LAPAROSCOPIC APPENDECTOMY")).setText("SINGLE INCISION LAPAROSCOPIC APPENDECTOMY"))); + details.setProductOrService(new CodeableConcept(new Coding().setCode("E101021").setSystem("https://irdai.gov.in/package-code").setDisplay("Twin Sharing Ac")).setText(" twin sharing basis room package")); + ce.getItem().add(details); + ce.setPatient(new Reference("Patient/RVH1003")); + //ce.getServicedPeriod().setStart(new Date(System.currentTimeMillis())).setEnd(new Date(System.currentTimeMillis())); + ce.setCreated(new Date(System.currentTimeMillis())); + ce.setEnterer(new Reference("Practitioner/PractitionerViswasKar")); + ce.setProvider(new Reference("Organization/GICOFINDIA")); + ce.setInsurer(new Reference( "Organization/WeMeanWell01")); + ce.setFacility(ce.getFacility().setReference("http://sgh.com.sa/Location/4461281")); + ce.getInsurance().add(new CoverageEligibilityRequest.InsuranceComponent(new Reference("Coverage/COVERAGE1"))); + return ce; + } + + public static Organization providerOrganizationExample(){ + //making the hospital org resource + Organization hos = new Organization(); + hos.setId("WeMeanWell01"); + Meta metaorg1 = new Meta(); + metaorg1.getProfile().add(new CanonicalType("https://nrces.in/ndhm/fhir/r4/StructureDefinition/Organization")); + hos.setMeta(metaorg1); +// hos.setName("WeMeanWell Hospital"); + hos.getAddress().add(new Address().setText(" Bannerghatta Road, Bengaluru ").setCity("Bengaluru").setCountry("India")); + hos.getIdentifier().add(new Identifier().setSystem("http://abdm.gov.in/facilities").setValue("HFR-ID-FOR-TMH").setType(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0203").setCode("AC").setDisplay("Narayana")))); + return hos; + } + + public static Patient patientExample() { + + //making a Patient resource + Patient pat = new Patient(); + pat.setId("RVH1003"); + Meta metapat = new Meta(); + metapat.getProfile().add(new CanonicalType("https://nrces.in/ndhm/fhir/r4/StructureDefinition/Patient")); + pat.setMeta(metapat); + pat.getIdentifier().add(new Identifier().setType(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0203").setCode("SN").setDisplay("Subscriber Number"))).setSystem("http://gicofIndia.com/beneficiaries").setValue("BEN-101")); + //pat.getIdentifier().add(new Identifier().setType(new CodeableConcept(new Coding().setSystem("https://nrces.in/ndhm/fhir/r4/CodeSystem/ndhm-identifier-type-code").setCode("PMJAY").setDisplay("Pradhan Mantri Jan Aarogya Yojana (PMJAY) ID"))).setValue("PMJAY-0101")); + pat.setGender(Enumerations.AdministrativeGender.MALE); +// pat.getName().add(new HumanName().setText("Prasidh Dixit")); +// pat.getTelecom().add(new ContactPoint().setValue("9008496789").setSystem(ContactPoint.ContactPointSystem.PHONE)); + String date_string = "26-09-1960"; + SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); + Date date; + try { + date = formatter.parse(date_string); + } catch (ParseException e) { + throw new RuntimeException(e); + } + pat.setBirthDate(date); + pat.addAddress((new Address().setText("#39 Kalena Agrahara, Kamanahalli, Bengaluru - 560056").setCity("Bengaluru").setPostalCode("560056").setState("Karnataka").setCountry("India"))); + return pat; + } + + public static Claim claimExample(){ + //Creating the Claims request + Claim claim = new Claim(); + claim.setId(UUID.randomUUID().toString()); + Meta metaClaim = new Meta(); + metaClaim.getProfile().add(new CanonicalType("https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-Claim.html")); + metaClaim.setLastUpdated(new Date()); + claim.setMeta(metaClaim); + claim.setStatus(org.hl7.fhir.r4.model.Claim.ClaimStatus.ACTIVE); + claim.setType(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/claim-type").setCode("institutional"))); + claim.setUse(org.hl7.fhir.r4.model.Claim.Use.CLAIM); + claim.setPatient(new Reference("Patient/RVH1003")); + claim.addIdentifier(new Identifier().setSystem("http://identifiersystem.com").setValue("IdentifierValue")); + claim.setCreated(new Date()); + claim.setInsurer(new Reference("Organization/GICOFINDIA")); + claim.setProvider(new Reference("Organization/WeMeanWell01")); + claim.setPriority(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/processpriority").setCode("normal"))); + claim.setPayee(new org.hl7.fhir.r4.model.Claim.PayeeComponent().setParty(new Reference("Organization/WeMeanWell01")).setType(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/payeetype").setCode("provider")))); + claim.getCareTeam().add(new org.hl7.fhir.r4.model.Claim.CareTeamComponent().setSequence(4).setProvider(new Reference("Organization/WeMeanWell01"))); + claim.addInsurance(new org.hl7.fhir.r4.model.Claim.InsuranceComponent().setFocal(true).setCoverage(new Reference("Coverage/COVERAGE1")).setSequence(1)); + claim.getDiagnosis().add(new Claim.DiagnosisComponent().addType(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/ex-diagnosistype").setCode("admitting").setDisplay("Admitting Diagnosis"))).setSequence(1).setDiagnosis(new CodeableConcept(new Coding().setSystem("http://irdai.com").setCode("E906184").setDisplay("SINGLE INCISION LAPAROSCOPIC APPENDECTOMY")).setText("SINGLE INCISION LAPAROSCOPIC APPENDECTOMY"))); + claim.getDiagnosis().add(new Claim.DiagnosisComponent().addType(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/ex-diagnosistype").setCode("admitting").setDisplay("Admitting Diagnosis"))).setSequence(2).setDiagnosis(new Reference("Condition/1234"))); + //adding procedure items to the claims component + // To check type is OPD + claim.setSubType(new CodeableConcept(new Coding().setSystem("https://staging-hcx.swasth.app/hapi-fhir/fhir/CodeSystem/hcx-claim-sub-types").setCode("OPD"))); + //admission fees + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setCode("E101021").setSystem("https://irdai.gov.in/package-code").setDisplay("Twin Sharing Ac"))).setUnitPrice(new Money().setValue(100000).setCurrency("INR"))); + //tests + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("E924260").setDisplay("CLINICAL TOXICOLOGY SCREEN, BLOOD"))).setUnitPrice(new Money().setValue(2000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("E924261").setDisplay("CLINICAL TOXICOLOGY SCREEN,URINE"))).setUnitPrice(new Money().setValue(1000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("E507029").setDisplay("ECG"))).setUnitPrice(new Money().setValue(5000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("E6080377").setDisplay("UltraSound Abdomen"))).setUnitPrice(new Money().setValue(5000).setCurrency("INR"))); + + //Consultation Medication and Hospital Charges + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("502001").setDisplay("Surgeons Charges"))).setUnitPrice(new Money().setValue(1000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("5020021").setDisplay("Anesthesiologist charges"))).setUnitPrice(new Money().setValue(1000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("E6080373").setDisplay("Physician charges"))).setUnitPrice(new Money().setValue(1000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("201008").setDisplay("Recovery Room"))).setUnitPrice(new Money().setValue(10000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("406003").setDisplay("intra -venous (iv) set"))).setUnitPrice(new Money().setValue(5000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("E507353").setDisplay("Oral Medication"))).setUnitPrice(new Money().setValue(5000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("E925171").setDisplay("Hospital charges"))).setUnitPrice(new Money().setValue(5000).setCurrency("INR"))); + claim.getItem().add(new org.hl7.fhir.r4.model.Claim.ItemComponent().setSequence(1).setProductOrService(new CodeableConcept(new Coding().setSystem("https://irdai.gov.in/package-code").setCode("501001").setDisplay("Consultation Charges"))).setUnitPrice(new Money().setValue(5000).setCurrency("INR"))); + claim.getProcedure().add(new Claim.ProcedureComponent().setSequence(1).setProcedure(new CodeableConcept(new Coding().setCode("PROC1").setCode("SampleCOde1").setSystem("https://irdai.gov.in/package-code")))); + claim.getProcedure().add(new Claim.ProcedureComponent().setSequence(2).setProcedure(new CodeableConcept(new Coding().setCode("PROC2").setCode("SampleCOde2").setSystem("https://irdai.gov.in/package-code")))); + + //total cost +// claim.setTotal(new Money().setCurrency("INR").setValue(146000.0)); + return claim; + } + + public static Coverage coverageExample(){ + //making the coverage resource + Coverage cov = new Coverage(); + cov.setId("COVERAGE1"); + Meta metacov = new Meta(); + metacov.getProfile().add(new CanonicalType("https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-Coverage.html")); + cov.setMeta(metacov); + cov.setStatus(Coverage.CoverageStatus.ACTIVE); + cov.getIdentifier().add(new Identifier().setValue("policy-RVH1003").setSystem("https://www.gicofIndia.in/policies")); + cov.getSubscriber().setReference("Patient/RVH1003"); +// cov.setSubscriberId("2XX8971"); + cov.getBeneficiary().setReference( "Patient/RVH1003"); + cov.setRelationship(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/subscriber-relationship").setCode("self"))); + cov.getPayor().add(new Reference("Organization/GICOFINDIA")); + return cov; + } + + public static CommunicationRequest communicationRequestExample(){ + CommunicationRequest comReq = new CommunicationRequest(); + comReq.setId(UUID.randomUUID().toString()); + Meta meta = new Meta(); + meta.getProfile().add(new CanonicalType("https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-CommunicationRequest.html")); + meta.setLastUpdated(new Date()); + comReq.setMeta(meta); + comReq.getIdentifier().add(new Identifier().setSystem("http://irdai.gov.in/insurer/123456").setValue("ABCD123")); + comReq.setStatus(CommunicationRequest.CommunicationRequestStatus.ACTIVE); + comReq.getBasedOn().add(new Reference("Patient/RVH1003")); +// comReq.getPayload().add(new CommunicationRequest.CommunicationRequestPayloadComponent().setContent(new StringType("Please verify the OTP sent to your mobile number to proceed."))); + return comReq; + } + + + public static Communication communicationExample(){ + Communication comm = new Communication(); + comm.setId(UUID.randomUUID().toString()); + Meta meta = new Meta(); + meta.getProfile().add(new CanonicalType("https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-Communication.html")); + meta.setLastUpdated(new Date()); + comm.setMeta(meta); + comm.getIdentifier().add(new Identifier().setSystem("http://www.providerco.com/communication").setValue("12345")); + comm.setStatus(Communication.CommunicationStatus.COMPLETED); + comm.getPayload().add(new Communication.CommunicationPayloadComponent().setContent(new Attachment().setContentType("application/pdf").setData("abcd".getBytes()).setTitle("accident_notes.pdf").setCreation(new Date()))); + return comm; + } + + public static Practitioner practitionerExample() { + //making the hospital org resource + Practitioner hos = new Practitioner(); + hos.setId("PractitionerViswasKar"); + Meta metaorg1 = new Meta(); + metaorg1.getProfile().add(new CanonicalType("https://nrces.in/ndhm/fhir/r4/StructureDefinition/Practitioner")); + hos.setMeta(metaorg1); + hos.getName().add(new HumanName().setText("Dr Viswas kar")); + hos.getIdentifier().add(new Identifier().setSystem("http://abdm.gov.in/facilities").setValue("DOC-123/456").setType(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0203").setCode("MD").setDisplay("Medical License number")))); + return hos; + } + + public static Organization insurerOrganizationExample(){ + //making an organization resource + Organization org = new Organization(); + Meta metaorg = new Meta(); + metaorg.getProfile().add(new CanonicalType("https://nrces.in/ndhm/fhir/r4/StructureDefinition/Organization")); + org.setMeta(metaorg); + org.setId("GICOFINDIA"); +// org.setName("GICOFINDIA"); + org.getIdentifier().add(new Identifier().setSystem("http://irdai.gov.in/insurers").setValue("GICOFINDIA").setType(new CodeableConcept(new Coding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0203").setCode("AC").setDisplay("GOVOFINDIA")))); + return org; + } } + diff --git a/src/main/java/org/swasth/hcx/service/BeneficiaryService.java b/src/main/java/org/swasth/hcx/service/BeneficiaryService.java new file mode 100644 index 0000000..1bd54c5 --- /dev/null +++ b/src/main/java/org/swasth/hcx/service/BeneficiaryService.java @@ -0,0 +1,292 @@ +package org.swasth.hcx.service; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Claim; +import org.hl7.fhir.r4.model.Coverage; +import org.hl7.fhir.r4.model.Money; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import org.swasth.hcx.exception.ClientException; +import org.swasth.hcx.exception.ErrorCodes; +import org.swasth.hcx.utils.Constants; + +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +@Service +public class BeneficiaryService { + + @Value("${postgres.table.payerData}") + private String payorDataTable; + @Autowired + private PostgresService postgresService; + + @Value("${certificates.bucketName}") + private String bucketName; + @Autowired + private CloudStorageClient cloudStorageClient; + @Autowired + private SMSService smsService; + @Value("${postgres.table.beneficiary}") + private String beneficiaryTable; + @Value("${otp.expiry}") + private int otpExpiry; + @Value("${otp.send-per-minute}") + private int otpSendPerMinute; + long lastOTPSendTime = 0; + int otpSentThisMinute = 0; + + public void sendOTP(String mobile, String phoneContent) throws ClientException, SQLException { + if (!isRateLimited()) { + try { + int otpCode = 100_000 + new Random().nextInt(900_000); + String query = String.format("SELECT * FROM %s WHERE mobile = '%s'", beneficiaryTable, mobile); + ResultSet resultSet = postgresService.executeQuery(query); + if (!resultSet.next()) { + String beneficiaryReferenceId = String.valueOf(UUID.randomUUID()); + String insertQuery = String.format("INSERT INTO %s (mobile, otp_code, mobile_verified, createdon, otp_expiry, bsp_reference_id) VALUES ('%s', %d, false, %d, %d, '%s')", beneficiaryTable, mobile, otpCode, System.currentTimeMillis(), System.currentTimeMillis() + otpExpiry, beneficiaryReferenceId); + postgresService.execute(insertQuery); + smsService.sendSMS(mobile, phoneContent + "\r\n" + otpCode); + System.out.println("OTP sent successfully for " + mobile); + } else { + String updateQuery = String.format("UPDATE %s SET otp_code = %d, otp_expiry = %d , mobile_verified = %b WHERE mobile = '%s'", beneficiaryTable, otpCode, System.currentTimeMillis() + otpExpiry, false, mobile); + postgresService.execute(updateQuery); + smsService.sendSMS(mobile, phoneContent + "\r\n" + otpCode); + System.out.println("OTP sent successfully for " + mobile); + } + lastOTPSendTime = System.currentTimeMillis(); + otpSentThisMinute++; + } catch (ClientException e) { + throw new ClientException(e.getMessage()); + } + } + } + + private boolean isRateLimited() throws ClientException { + if (System.currentTimeMillis() - lastOTPSendTime >= 60000) { + otpSentThisMinute = 0; // Reset the count if a minute has passed + } + if (otpSentThisMinute >= otpSendPerMinute) { + System.out.println("Rate limit exceeded. Try again later."); + throw new ClientException(ErrorCodes.ERR_SENDING_OTP, "Rate limit exceeded. Try again later."); + } + return false; + } + + public ResponseEntity verifyOTP(Map requestBody) { + try { + String mobile = (String) requestBody.get(Constants.MOBILE); + int userEnteredOTP = Integer.parseInt((String) requestBody.get("otp_code")); + String query = String.format("SELECT * FROM %s WHERE mobile = '%s'", beneficiaryTable, mobile); + ResultSet resultSet = postgresService.executeQuery(query); + if (resultSet.next()) { + boolean isMobileVerified = resultSet.getBoolean("mobile_verified"); + int storedOTP = resultSet.getInt("otp_code"); + long otpExpire = resultSet.getLong("otp_expiry"); + if (isMobileVerified) { + return ResponseEntity.badRequest().body(response("Mobile Number already verified", mobile, "failed")); + } + if (userEnteredOTP != storedOTP || System.currentTimeMillis() > otpExpire) { + throw new io.hcxprotocol.exception.ClientException("Invalid OTP or OTP has expired"); + } + // Update mobile_verified status + String updateQuery = String.format("UPDATE %s SET mobile_verified = true WHERE mobile = '%s'", beneficiaryTable, mobile); + postgresService.execute(updateQuery); + return ResponseEntity.ok().body(response("verification is successful", mobile, "successful")); + } else { + return ResponseEntity.badRequest().body(response("Record does not exist in the database", mobile, "failed")); + } + } catch (Exception e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + + public Map response(String message , String mobile,String verification){ + Map response = new HashMap<>(); + response.put("message",message); + response.put("mobile",mobile); + response.put("verification",verification); + return response; + } + + public Map getPayloadMap(String requestID) throws ClientException, SQLException { + String searchQuery = String.format("SELECT * FROM %s WHERE request_id = '%s'", payorDataTable, requestID); + ResultSet resultSet = postgresService.executeQuery(searchQuery); + if (!resultSet.next()) { + throw new ClientException("Request does not exist in the database"); + } + Map payloadMap = new HashMap<>(); + payloadMap.put("request_fhir", resultSet.getString("request_fhir")); + payloadMap.put("correlation_id", resultSet.getString("correlation_id")); + payloadMap.put("workflow_id", resultSet.getString("workflow_id")); + return payloadMap; + } + + + public ResponseEntity getRequestListFromDatabase(Map requestBody) throws Exception { + String mobile = (String) requestBody.getOrDefault("mobile", ""); +// String countQuery = String.format("SELECT COUNT(*) AS count FROM %s WHERE mobile = '%s' AND action = 'coverageeligibility'", payorDataTable, mobile); +// ResultSet resultSet = postgresService.executeQuery(countQuery); + Map resp = new HashMap<>(); +// int count; +// if (resultSet.next()) { +// count = resultSet.getInt("count"); +// resp.put("count", count); +// System.out.println("Total count of the requests: " + count); +// } + + // Create a map to store entries grouped by workflow ID + Map>> groupedEntries = new HashMap<>(); + + String searchQuery = String.format("SELECT * FROM %s WHERE mobile = '%s' ORDER BY created_on DESC", payorDataTable, mobile); + ResultSet searchResultSet = postgresService.executeQuery(searchQuery); + + while (searchResultSet.next()) { + String workflowId = searchResultSet.getString("workflow_id"); + // Create a response map for each entry + Map responseMap = new HashMap<>(); + String fhirPayload = searchResultSet.getString("request_fhir"); + + if (searchResultSet.getString("action").equalsIgnoreCase("claim")) { + responseMap.put("supportingDocuments", getSupportingDocuments(fhirPayload)); + responseMap.put("billAmount", getAmount(fhirPayload)); + } + + responseMap.put("type", searchResultSet.getString("action")); + responseMap.put("status", searchResultSet.getString("status")); + responseMap.put("apiCallId", searchResultSet.getString("request_id")); + responseMap.put("claimType", "OPD"); + responseMap.put("date", searchResultSet.getString("created_on")); + responseMap.put("insurance_id", getInsuranceId(fhirPayload)); + responseMap.put("correlationId", searchResultSet.getString("correlation_id")); + responseMap.put("sender_code", searchResultSet.getString("sender_code")); + responseMap.put("recipient_code", searchResultSet.getString("recipient_code")); + responseMap.put("workflow_id", workflowId); + if (!groupedEntries.containsKey(workflowId)) { + groupedEntries.put(workflowId, new ArrayList<>()); + } + groupedEntries.get(workflowId).add(responseMap); + } + List> entries = new ArrayList<>(); + for (String key : groupedEntries.keySet()) { + Map entry = new HashMap<>(); + entry.put(key, groupedEntries.get(key)); + entries.add(entry); + } + + resp.put("entries", entries); + resp.put("count",entries.size()); + return new ResponseEntity<>(resp, HttpStatus.OK); + } + + + + + public ResponseEntity getDataFromWorkflowId(Map requestBody) throws ClientException, SQLException { + String workflowId = (String) requestBody.getOrDefault("workflow_id", ""); + List> entries = new ArrayList<>(); + Map resp = new HashMap<>(); + String searchQuery = String.format("SELECT * FROM %s WHERE workflow_id = '%s' AND (action = 'claim' OR action = 'preauth') ORDER BY created_on ASC", payorDataTable, workflowId); + ResultSet searchResultSet = postgresService.executeQuery(searchQuery); + while (searchResultSet.next()) { + Map responseMap = new HashMap<>(); + String fhirPayload = searchResultSet.getString("request_fhir"); + responseMap.put("type", getType(searchResultSet.getString("action"))); + responseMap.put("status", searchResultSet.getString("status")); + responseMap.put("apiCallId", searchResultSet.getString("request_id")); + responseMap.put("claimType", "OPD"); + responseMap.put("date", searchResultSet.getString("created_on")); + responseMap.put("correlationId", searchResultSet.getString("correlation_id")); + responseMap.put("sender_code", searchResultSet.getString("sender_code")); + responseMap.put("recipient_code", searchResultSet.getString("recipient_code")); + responseMap.put("billAmount", getAmount(fhirPayload)); + responseMap.put("supportingDocuments", getSupportingDocuments(fhirPayload)); + entries.add(responseMap); + } + resp.put("entries", entries); + return new ResponseEntity<>(resp, HttpStatus.OK); + } + + private String getType(String action) { + if ("claim".equalsIgnoreCase(action)) { + return "claim"; + } else { + return "preauth"; // You can add more handling as needed + } + } + + + public String getInsuranceId(String fhirPayload) { + IParser parser = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + Bundle parsed = parser.parseResource(Bundle.class, fhirPayload); + Coverage coverage = parser.parseResource(Coverage.class, parser.encodeResourceToString(parsed.getEntry().get(4).getResource())); + return coverage.getSubscriberId(); + } + + public String getAmount(String fhirPayload) { + IParser parser = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + Bundle parsed = parser.parseResource(Bundle.class, fhirPayload); + Claim claim = parser.parseResource(Claim.class, parser.encodeResourceToString(parsed.getEntry().get(0).getResource())); + return claim.getTotal().getValue().toString(); + } + + public List getSupportingDocuments(String fhirPayload) { + IParser parser = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + Bundle parsed = parser.parseResource(Bundle.class, fhirPayload); + Claim claim = parser.parseResource(Claim.class, parser.encodeResourceToString(parsed.getEntry().get(0).getResource())); + List documentUrls = new ArrayList<>(); + for (Claim.SupportingInformationComponent supportingInfo : claim.getSupportingInfo()) { + if (supportingInfo.hasValueAttachment() && supportingInfo.getValueAttachment().hasUrl()) { + String url = supportingInfo.getValueAttachment().getUrl(); + documentUrls.add(url); + } + } + return documentUrls; + } + + + public List> getDocumentUrls(List files, String mobile) throws ClientException, SQLException, IOException { + String query = String.format("SELECT bsp_reference_id FROM %s WHERE mobile = '%s'", beneficiaryTable, mobile); + ResultSet resultSet = postgresService.executeQuery(query); + String beneficiaryReferenceId = ""; + while (resultSet.next()) { + beneficiaryReferenceId = resultSet.getString("bsp_reference_id"); + } + List> responses = new ArrayList<>(); + for (MultipartFile file : files) { + String fileName = file.getOriginalFilename(); + cloudStorageClient.putObject(beneficiaryReferenceId, bucketName); + String pathToFile = "beneficiary-app" + "/" + beneficiaryReferenceId + "/" + fileName; + cloudStorageClient.putObject(bucketName, pathToFile, file); + Map response = new HashMap<>(); + response.put("url", cloudStorageClient.getUrl(bucketName, pathToFile).toString()); + response.put("reference_id", beneficiaryReferenceId); + responses.add(response); + } + return responses; + } + + public boolean checkCommunicationRequest(Map requestBody) throws ClientException, SQLException { + String requestId = (String) requestBody.get("request_id"); + String type = (String) requestBody.get("type"); + String query = String.format("SELECT %s FROM %s WHERE request_id = '%s'", type, payorDataTable, requestId); + ResultSet resultSet = postgresService.executeQuery(query); + String status; + if (resultSet.next()) { + status = resultSet.getString(type); + return status.equalsIgnoreCase("initiated"); + } else { + return false; + } + } + + +} diff --git a/src/main/java/org/swasth/hcx/service/CloudStorageClient.java b/src/main/java/org/swasth/hcx/service/CloudStorageClient.java new file mode 100644 index 0000000..c30f3a1 --- /dev/null +++ b/src/main/java/org/swasth/hcx/service/CloudStorageClient.java @@ -0,0 +1,48 @@ +package org.swasth.hcx.service; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import org.checkerframework.checker.index.qual.SearchIndexBottom; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.net.URL; + +@Service +public class CloudStorageClient { + + @Value("${certificates.accesskey}") + private String accessKey; + @Value("${certificates.secretKey}") + private String secretKey; + + public AmazonS3 getClient() { + return AmazonS3ClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey))) + .withRegion(Regions.AP_SOUTH_1) + .build(); + } + + public void putObject(String folderName, String bucketName) { + PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, folderName + "/", new ByteArrayInputStream(new byte[0]), new ObjectMetadata()); + putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead); + } + + public void putObject(String bucketName, String folderName, MultipartFile content) throws IOException { + getClient().putObject(bucketName, folderName, content.getInputStream(), new ObjectMetadata()); + } + + public URL getUrl(String bucketName, String path) { + return getClient().getUrl(bucketName, path); + } +} diff --git a/src/main/java/org/swasth/hcx/service/GenerateOutgoingRequest.java b/src/main/java/org/swasth/hcx/service/GenerateOutgoingRequest.java new file mode 100644 index 0000000..0ef6dfe --- /dev/null +++ b/src/main/java/org/swasth/hcx/service/GenerateOutgoingRequest.java @@ -0,0 +1,269 @@ +package org.swasth.hcx.service; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import com.amazonaws.services.dynamodbv2.xspec.S; +import io.hcxprotocol.init.HCXIntegrator; +import io.hcxprotocol.utils.Operations; +import kong.unirest.HttpResponse; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.r4.model.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.swasth.hcx.dto.Response; +import org.swasth.hcx.dto.ResponseError; +import org.swasth.hcx.exception.ClientException; +import org.swasth.hcx.exception.ErrorCodes; +import org.swasth.hcx.exception.ServerException; +import org.swasth.hcx.exception.ServiceUnavailbleException; +import org.swasth.hcx.fhirexamples.OnActionFhirExamples; +import org.swasth.hcx.utils.Constants; +import org.swasth.hcx.utils.HCXFHIRUtils; +import org.swasth.hcx.utils.HttpUtils; +import org.swasth.hcx.utils.JSONUtils; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.ResultSet; +import java.util.*; + +import static org.swasth.hcx.utils.Constants.CREATE_COMMUNICATION_REQUEST; +import static org.swasth.hcx.utils.Constants.MOBILE; + +@Service +public class GenerateOutgoingRequest { + + @Autowired + private BeneficiaryService beneficiaryService; + + @Autowired + private PostgresService postgresService; + @Value("${postgres.table.payerData}") + private String payorDataTable; + @Value("${phone.communication-content}") + private String communicationContent; + @Value("${beneficiary.protocol-base-path}") + private String protocolBasePath; + @Value("${beneficiary.participant-code}") + private String participantCode; + @Value("${beneficiary.username}") + private String userName; + @Value("${beneficiary.password}") + private String password; + @Value("${beneficiary.recipient-code}") + private String mockRecipientCode; + + public ResponseEntity createCoverageEligibilityRequest(Map requestBody, Operations operations) { + Response response = new Response(); + try { + HCXIntegrator hcxIntegrator = HCXIntegrator.getInstance(initializingConfigMap()); + IParser parser = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + CoverageEligibilityRequest ce = OnActionFhirExamples.coverageEligibilityRequestExample(); + Practitioner practitioner = OnActionFhirExamples.practitionerExample(); + Organization hospital = OnActionFhirExamples.providerOrganizationExample(); + hospital.setName((String) requestBody.getOrDefault("providerName", "")); + Patient patient = OnActionFhirExamples.patientExample(); + patient.getTelecom().add(new ContactPoint().setValue((String) requestBody.getOrDefault("mobile", "")).setSystem(ContactPoint.ContactPointSystem.PHONE)); + patient.getName().add(new HumanName().setText((String) requestBody.getOrDefault("patientName", ""))); + Organization insurerOrganization = OnActionFhirExamples.insurerOrganizationExample(); + insurerOrganization.setName((String) requestBody.getOrDefault("payor", "")); + Coverage coverage = OnActionFhirExamples.coverageExample(); + coverage.setSubscriberId((String) requestBody.getOrDefault("insuranceId", "")); + List domList = List.of(hospital, insurerOrganization, patient, coverage, practitioner); + Bundle bundleTest = new Bundle(); + try { + bundleTest = HCXFHIRUtils.resourceToBundle(ce, domList, Bundle.BundleType.COLLECTION, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-CoverageEligibilityRequestBundle.html", hcxIntegrator); + System.out.println("reosurceToBundle Coverage Eligibility Request \n" + parser.encodeResourceToString(bundleTest)); + } catch (Exception e) { + System.out.println("Error message " + e.getMessage()); + } + Map output = new HashMap<>(); + String workFlowId = UUID.randomUUID().toString(); + hcxIntegrator.processOutgoingRequest(parser.encodeResourceToString(bundleTest), operations, mockRecipientCode, "", "", workFlowId, new HashMap<>(), output); + System.out.println("The outgoing request has been successfully generated."); + return new ResponseEntity<>(response, HttpStatus.ACCEPTED); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("error " + e); + return exceptionHandler(response, e); + } + } + + public ResponseEntity createClaimRequest(Map requestBody, Operations operations) { + Response response = new Response(); + try { + HCXIntegrator hcxIntegrator = HCXIntegrator.getInstance(initializingConfigMap()); + IParser parser = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + Claim claim = OnActionFhirExamples.claimExample(); + String billAmount = (String) requestBody.getOrDefault("billAmount", 0); + claim.setTotal(new Money().setCurrency("INR").setValue(Long.parseLong(billAmount))); + // adding supporting documents (Bill/invoice or prescription) + if (requestBody.containsKey("supportingDocuments")) { + ArrayList> supportingDocuments = JSONUtils.convert(requestBody.get("supportingDocuments"), ArrayList.class); + for (Map document : supportingDocuments) { + String documentType = (String) document.get("documentType"); + List urls = (List) document.get("urls"); + if (urls != null && !urls.isEmpty()) { + for (String url : urls) { + claim.addSupportingInfo(new Claim.SupportingInformationComponent().setSequence(1).setCategory(new CodeableConcept(new Coding().setCode("POI").setSystem("http://hcxprotocol.io/codes/claim-supporting-info-categories").setDisplay("proof of identity"))).setValue(new Attachment().setUrl(url))); + } + } + } + } + Practitioner practitioner = OnActionFhirExamples.practitionerExample(); + Organization hospital = OnActionFhirExamples.providerOrganizationExample(); + hospital.setName((String) requestBody.getOrDefault("providerName", "")); + Patient patient = OnActionFhirExamples.patientExample(); + patient.getTelecom().add(new ContactPoint().setValue((String) requestBody.getOrDefault("mobile", "")).setSystem(ContactPoint.ContactPointSystem.PHONE)); + patient.getName().add(new HumanName().setText((String) requestBody.getOrDefault("patientName", ""))); + Organization insurerOrganization = OnActionFhirExamples.insurerOrganizationExample(); + insurerOrganization.setName((String) requestBody.getOrDefault("payor", "")); + Coverage coverage = OnActionFhirExamples.coverageExample(); + coverage.setSubscriberId((String) requestBody.getOrDefault("insuranceId", "")); + List domList = List.of(hospital, insurerOrganization, patient, coverage, practitioner); + Bundle bundleTest = new Bundle(); + try { + bundleTest = HCXFHIRUtils.resourceToBundle(claim, domList, Bundle.BundleType.COLLECTION, "https://ig.hcxprotocol.io/v0.7.1/StructureDefinition-Claim.html", hcxIntegrator); + System.out.println("resource To Bundle claim Request\n" + parser.encodeResourceToString(bundleTest)); + } catch (Exception e) { + System.out.println("Error message " + e.getMessage()); + } + Map output = new HashMap<>(); + String workflowId = ""; + if(!requestBody.containsKey("workflowId")) { + workflowId = UUID.randomUUID().toString(); + } else { + workflowId = (String) requestBody.getOrDefault("workflowId",""); + } + hcxIntegrator.processOutgoingRequest(parser.encodeResourceToString(bundleTest), operations, mockRecipientCode, "", "", workflowId, new HashMap<>(), output); + System.out.println("The outgoing request has been successfully generated."); + return new ResponseEntity<>(response, HttpStatus.ACCEPTED); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("error " + e); + return exceptionHandler(response, e); + } + } + + public ResponseEntity createCommunicationRequest(Map requestBody, Operations operations) { + Response response = new Response(); + try { + String requestId = (String) requestBody.get("request_id"); + System.out.println("---------requesr_id -------------"+ requestId); + validateMap(requestId, requestBody); + IParser parser = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + Map payloadMap = beneficiaryService.getPayloadMap(requestId); + Bundle parsed = parser.parseResource(Bundle.class, (String) payloadMap.get("request_fhir")); + String correlationId = (String) payloadMap.getOrDefault("correlation_id", ""); + Patient patient1 = parser.parseResource(Patient.class, parser.encodeResourceToString(parsed.getEntry().get(3).getResource())); + String mobile = patient1.getTelecom().get(0).getValue(); + System.out.println("mobile number of beneficiary: " + mobile); + HCXIntegrator hcxIntegrator = HCXIntegrator.getInstance(initializingConfigMapForPayor()); + CommunicationRequest communicationRequest = OnActionFhirExamples.communicationRequestExample(); + Patient patient = OnActionFhirExamples.patientExample(); + patient.getTelecom().add(new ContactPoint().setValue(mobile).setSystem(ContactPoint.ContactPointSystem.PHONE)); + if(requestBody.getOrDefault("type", "").equals("bank_details")){ + communicationRequest.getPayload().add(new CommunicationRequest.CommunicationRequestPayloadComponent().setContent(new StringType("Please provide the bank details for claim to be complete."))); + System.out.println("The Communication request has been sent successfully bank details."); + } else if (requestBody.getOrDefault("type","").equals("otp")){ + communicationRequest.getPayload().add(new CommunicationRequest.CommunicationRequestPayloadComponent().setContent(new StringType("Please verify the OTP sent to your mobile number to proceed."))); + beneficiaryService.sendOTP(mobile, communicationContent); + System.out.println("The otp has been sent for the beneficiary mobile to verify cliam."); + } + Map output = new HashMap<>(); + String workflowId = (String) payloadMap.getOrDefault("workflow_id",""); + System.out.println("-----------correlation id ----------------"+ correlationId); + hcxIntegrator.processOutgoingRequest(parser.encodeResourceToString(communicationRequest), operations, "testprovider1.apollo@swasth-hcx-dev", "", correlationId, workflowId , new HashMap<>(), output); + System.out.println("The outgoing request has been successfully generated." + output); + return new ResponseEntity<>(response, HttpStatus.ACCEPTED); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("error " + e); + return exceptionHandler(response, e); + } + } + + public ResponseEntity createCommunicationOnRequest(Map requestBody) throws ClientException { + String requestId = (String) requestBody.getOrDefault("request_id", ""); + if (StringUtils.equalsIgnoreCase((String) requestBody.get("type"), "otp")) { + ResponseEntity responseEntity = beneficiaryService.verifyOTP(requestBody); + System.out.println(responseEntity.getStatusCode() == HttpStatus.OK); + System.out.println(responseEntity.getStatusCode().is2xxSuccessful()); + if (responseEntity.getStatusCode() == HttpStatus.OK) { + String query = String.format("UPDATE %s SET otp_verification = '%s' WHERE request_id = '%s'", payorDataTable, "successful", requestId); + postgresService.execute(query); + } else { + throw new ClientException(Objects.requireNonNull(responseEntity.getBody()).toString()); + } + return responseEntity; + } else if(StringUtils.equalsIgnoreCase((String) requestBody.get("type"), "bank_details")){ + String accountNumber = (String) requestBody.getOrDefault("account_number", ""); + String ifscCode = (String) requestBody.getOrDefault("ifsc_code", ""); + String query = String.format("UPDATE %s SET account_number ='%s',ifsc_code = '%s' WHERE request_id = '%s'", payorDataTable, accountNumber, ifscCode, requestId); + postgresService.execute(query); + System.out.println("The bank details updated successfully to the request id " + requestId); + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } + return ResponseEntity.badRequest().body("Unable to update the details to database"); + } + + + public Map initializingConfigMap() throws IOException { + Map configMap = new HashMap<>(); + configMap.put("protocolBasePath", protocolBasePath); + configMap.put("participantCode", participantCode); + configMap.put("username", userName); + configMap.put("password", password); + String keyUrl = "https://raw.githubusercontent.com/Swasth-Digital-Health-Foundation/hcx-platform/main/hcx-apis/src/test/resources/examples/test-keys/private-key.pem"; + String certificate = IOUtils.toString(new URL(keyUrl), StandardCharsets.UTF_8); + configMap.put("encryptionPrivateKey", certificate); + configMap.put("signingPrivateKey", certificate); + return configMap; + } + + protected ResponseEntity exceptionHandler(Response response, Exception e) { + e.printStackTrace(); + if (e instanceof ClientException) { + return new ResponseEntity<>(errorResponse(response, ((ClientException) e).getErrCode(), e), HttpStatus.BAD_REQUEST); + } else if (e instanceof ServiceUnavailbleException) { + return new ResponseEntity<>(errorResponse(response, ((ServiceUnavailbleException) e).getErrCode(), e), HttpStatus.SERVICE_UNAVAILABLE); + } else if (e instanceof ServerException) { + return new ResponseEntity<>(errorResponse(response, ((ServerException) e).getErrCode(), e), HttpStatus.INTERNAL_SERVER_ERROR); + } else { + return new ResponseEntity<>(errorResponse(response, ErrorCodes.INTERNAL_SERVER_ERROR, e), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + protected Response errorResponse(Response response, ErrorCodes code, java.lang.Exception e) { + ResponseError error = new ResponseError(code, e.getMessage(), e.getCause()); + response.setError(error); + return response; + } + + protected void validateMap(String field, Map value) throws ClientException { + if (MapUtils.isEmpty(value)) + throw new ClientException("Missing required field " + field); + } + + public Map initializingConfigMapForPayor() throws IOException { + Map configMap = new HashMap<>(); + configMap.put("protocolBasePath", protocolBasePath); + configMap.put("participantCode", "wemeanhospital+mock_payor.yopmail@swasth-hcx-dev"); + configMap.put("username", "wemeanhospital+mock_payor@yopmail.com"); + configMap.put("password", "i6cA0R0EZHF3@"); + String keyUrl = "https://raw.githubusercontent.com/Swasth-Digital-Health-Foundation/hcx-platform/main/hcx-apis/src/test/resources/examples/test-keys/private-key.pem"; + String certificate = IOUtils.toString(new URL(keyUrl), StandardCharsets.UTF_8); + configMap.put("encryptionPrivateKey", certificate); + configMap.put("signingPrivateKey", certificate); + return configMap; + } + + +} diff --git a/src/main/java/org/swasth/hcx/service/HcxIntegratorService.java b/src/main/java/org/swasth/hcx/service/HcxIntegratorService.java index 0c590ba..c157860 100644 --- a/src/main/java/org/swasth/hcx/service/HcxIntegratorService.java +++ b/src/main/java/org/swasth/hcx/service/HcxIntegratorService.java @@ -67,6 +67,7 @@ public Map getConfig(String code, String username, String passwor configMap.put("password", password); configMap.put("encryptionPrivateKey", privateKey); configMap.put("fhirValidationEnabled", false); + configMap.put("signingPrivateKey", privateKey); return configMap; } diff --git a/src/main/java/org/swasth/hcx/service/PayerService.java b/src/main/java/org/swasth/hcx/service/PayerService.java index d5b9198..35184a6 100644 --- a/src/main/java/org/swasth/hcx/service/PayerService.java +++ b/src/main/java/org/swasth/hcx/service/PayerService.java @@ -26,20 +26,19 @@ public class PayerService { @Autowired private PostgresService postgres; - public void process(Request request, String reqFhirObj, String respFhirObj) throws ClientException, JsonProcessingException { Map info = new HashMap<>(); if(!request.getAction().contains("coverageeligibility")) { info.put("medical", Collections.singletonMap("status", PENDING)); info.put("financial", Collections.singletonMap("status", PENDING)); } - String query = String.format("INSERT INTO %s (request_id,sender_code,recipient_code,action,raw_payload,request_fhir,response_fhir,status,additional_info,created_on,updated_on) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s',%d,%d);", - table, request.getApiCallId(), request.getSenderCode(), request.getRecipientCode(), getEntity(request.getAction()), request.getPayload().getOrDefault("payload", ""), reqFhirObj, respFhirObj, PENDING, JSONUtils.serialize(info), System.currentTimeMillis(), System.currentTimeMillis()); + String query = String.format("INSERT INTO %s (request_id,sender_code,recipient_code,action,raw_payload,request_fhir,response_fhir,status,additional_info,created_on,updated_on,correlation_id,mobile,otp_verification,workflow_id,account_number,ifsc_code,bank_details) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s',%d,%d,'%s','%s','%s','%s','%s','%s','%s');", + table, request.getApiCallId(), request.getSenderCode(), request.getRecipientCode(), getEntity(request.getAction()), request.getPayload().getOrDefault("payload", ""), reqFhirObj, respFhirObj, PENDING, JSONUtils.serialize(info), System.currentTimeMillis(), System.currentTimeMillis(), request.getCorrelationId(), "", PENDING, request.getWorkflowId(), "", "", PENDING); postgres.execute(query); } private String getEntity(String action){ - Map actionMap = new HashMap(); + Map actionMap = new HashMap<>(); actionMap.put("/v0.7/coverageeligibility/check", "coverageeligibility"); actionMap.put("/v0.7/preauth/submit", "preauth"); actionMap.put("/v0.7/claim/submit", "claim"); diff --git a/src/main/java/org/swasth/hcx/service/SMSService.java b/src/main/java/org/swasth/hcx/service/SMSService.java new file mode 100644 index 0000000..c5df67d --- /dev/null +++ b/src/main/java/org/swasth/hcx/service/SMSService.java @@ -0,0 +1,50 @@ +package org.swasth.hcx.service; + + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.sns.AmazonSNS; +import com.amazonaws.services.sns.AmazonSNSClient; +import com.amazonaws.services.sns.model.PublishRequest; +import com.amazonaws.services.sns.model.PublishResult; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.net.URL; +import java.util.concurrent.CompletableFuture; + +@Service +public class SMSService { + + @Value("${aws.access-key}") + private String accessKey; + + @Value("${aws.access-secret}") + private String accessSecret; + + @Value("${aws.region}") + private String awsRegion; + + @Async + public CompletableFuture sendSMS(String phone, String content) { + String phoneNumber = "+91" + phone; // Ex: +91XXX4374XX + AmazonSNS snsClient = AmazonSNSClient.builder().withCredentials(new AWSCredentialsProvider() { + @Override + public AWSCredentials getCredentials() { + return new BasicAWSCredentials(accessKey, accessSecret); + } + + @Override + public void refresh() { + + } + }).withRegion(awsRegion).build(); + + PublishResult result = snsClient.publish(new PublishRequest() + .withMessage(content) + .withPhoneNumber(phoneNumber)); + return CompletableFuture.completedFuture(result.getMessageId()); + } +} \ No newline at end of file diff --git a/src/main/java/org/swasth/hcx/utils/Constants.java b/src/main/java/org/swasth/hcx/utils/Constants.java index 81c0f06..351aa9e 100644 --- a/src/main/java/org/swasth/hcx/utils/Constants.java +++ b/src/main/java/org/swasth/hcx/utils/Constants.java @@ -11,6 +11,15 @@ public class Constants { public static final String COVERAGE_ELIGIBILITY_CHECK = VERSION_PREFIX + "/coverageeligibility/check"; public static final String COVERAGE_ELIGIBILITY_ONCHECK = VERSION_PREFIX + "/coverageeligibility/on_check"; + public static final String CREATE_COVERAGEELIGIBILITY_REQUEST = "/create/coverageeligibility/check"; + public static final String CREATE_CLAIM_SUBMIT = "/create/claim/submit"; + public static final String CREATE_PRE_AUTH_SUBMIT = "/create/preauth/submit"; + public static final String CREATE_COMMUNICATION_REQUEST = "/create/communication/request"; + public static final String CREATE_COMMUNICATION_ON_REQUEST = "create/communication/on_request"; + public static final String BSP_REQUEST_LIST = "/bsp/request/list"; + public static final String SEND_OTP = "/send/otp"; + public static final String VERIFY_OTP = "verify/otp"; + public static final String UPLOAD_DOCUMENTS = "/upload/documents"; // Claims APIs public static final String PRE_AUTH_SUBMIT = VERSION_PREFIX + "/preauth/submit"; public static final String PRE_AUTH_ONSUBMIT = VERSION_PREFIX + "/preauth/on_submit"; @@ -30,7 +39,7 @@ public class Constants { public static final String HCX_ON_SEARCH = VERSION_PREFIX + "/hcx/on_search"; //Communication APIs - public static final String COMMUNICATION_REQUEST = VERSION_PREFIX + "/communication/request"; + public static final String COMMUNICATION_REQUEST = "/communication/request"; public static final String COMMUNICATION_ONREQUEST = VERSION_PREFIX + "/communication/on_request"; //Predetermination APIs diff --git a/src/main/java/org/swasth/hcx/utils/HCXFHIRUtils.java b/src/main/java/org/swasth/hcx/utils/HCXFHIRUtils.java new file mode 100644 index 0000000..8137259 --- /dev/null +++ b/src/main/java/org/swasth/hcx/utils/HCXFHIRUtils.java @@ -0,0 +1,127 @@ +package org.swasth.hcx.utils; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.ResultSeverityEnum; +import ca.uhn.fhir.validation.SingleValidationMessage; +import ca.uhn.fhir.validation.ValidationResult; +import io.hcxprotocol.dto.ResponseError; +import io.hcxprotocol.init.HCXIntegrator; +import io.hcxprotocol.validator.HCXFHIRValidator; +import org.apache.maven.artifact.versioning.ComparableVersion; +import org.hl7.fhir.r4.model.*; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +public class HCXFHIRUtils { + + //Initializing the FHIR parser + static IParser p = FhirContext.forR4().newJsonParser().setPrettyPrint(true); + + public static boolean validateVersion(String bundleURL) throws IOException { + //replace .html extension with .json + bundleURL = bundleURL.replace(".html", ".json"); + StructureDefinition bundleDef = (StructureDefinition) p.parseResource(new URL(bundleURL).openStream()); + ComparableVersion versionBundle = new ComparableVersion(bundleDef.getVersion()); + ComparableVersion versionBase = new ComparableVersion("0.7"); + return versionBundle.compareTo(versionBase) > 0; + } + + public static Bundle resourceToBundle(DomainResource res, List referencedResource, Bundle.BundleType type, String bundleURL, HCXIntegrator integrator) throws Exception { + + //checking for bundle version. We support bundle version v0.7.1 and above + ResponseError err = new ResponseError(); + try { + if (!validateVersion(bundleURL)) { + throw new Exception("IG version should be greater than 0.7.0"); + } + } catch (Exception e) { + throw new Exception("Could not read the Structure Defintion for the Bundle"); + } + + DomainResource resource = res.copy(); + Bundle bundle = new Bundle(); + bundle.setId(UUID.randomUUID().toString()); + Meta meta = new Meta(); + meta.getProfile().add(new CanonicalType(bundleURL)); + meta.setLastUpdated(new Date()); + bundle.setMeta(meta); + bundle.setIdentifier(new Identifier().setSystem("https://www.tmh.in/bundle").setValue(UUID.randomUUID().toString())); + bundle.setType((type)); + bundle.setTimestamp(new Date()); + //adding the main resource to the bundle entry + resource.getContained().clear(); + bundle.getEntry().add(new Bundle.BundleEntryComponent().setFullUrl(resource.getResourceType() + "/" + resource.getId().toString().replace("#", "")).setResource(resource)); + for (Resource refResource : referencedResource) { + String id = refResource.getId().toString().replace("#", ""); + refResource.setId(id); + bundle.getEntry().add(new Bundle.BundleEntryComponent().setFullUrl(refResource.getResourceType() + "/" + id).setResource(refResource)); + } + + //validating the bundle + FhirValidator validator = HCXFHIRValidator.getValidator(integrator.getConfig()); + ValidationResult result = validator.validateWithResult(bundle); + List messages = result.getMessages(); + List errMessages = new ArrayList<>(); + for (SingleValidationMessage message : messages) { + if (message.getSeverity() == ResultSeverityEnum.ERROR) { + errMessages.add(message.getMessage()); + } + } + if (!errMessages.isEmpty()) { + throw new Exception(String.valueOf(errMessages)); + } + return bundle; + } + + + public static DomainResource getPrimaryResource(Bundle resource, String resourceURL) throws Exception { + Bundle newBundle = resource.copy(); + for (int i = 0; i < newBundle.getEntry().size(); i++) { + Bundle.BundleEntryComponent par = newBundle.getEntry().get(i); + DomainResource dm = (DomainResource) par.getResource(); + if (dm.getMeta().getProfile().get(0).getValue() == resourceURL) { + return dm; + } + } + throw new Exception("No resource with the given URL found"); + } + + public static DomainResource getPrimaryResource(Bundle resource) { + Bundle newBundle = resource.copy(); + Bundle.BundleEntryComponent par = newBundle.getEntry().get(0); + DomainResource dm = (DomainResource) par.getResource(); + return dm; + } + + + public static List getReferencedResource(Bundle resource, String resourceURL) { + Bundle newBundle = resource.copy(); + List dmList = new ArrayList<>(); + for (int i = 0; i < newBundle.getEntry().size(); i++) { + Bundle.BundleEntryComponent par = newBundle.getEntry().get(i); + DomainResource dm = (DomainResource) par.getResource(); + if (dm.getMeta().getProfile().get(0).getValue() != resourceURL) { + dmList.add(dm); + } + } + return dmList; + } + + public static List getReferencedResource(Bundle resource) { + Bundle newBundle = resource.copy(); + List dmList = new ArrayList<>(); + for (int i = 1; i < newBundle.getEntry().size(); i++) { + Bundle.BundleEntryComponent par = newBundle.getEntry().get(i); + DomainResource dm = (DomainResource) par.getResource(); + dmList.add(dm); + } + return dmList; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a498fc7..c5b8f23 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -28,6 +28,7 @@ postgres: table: payerData: ${payer_data_table:payersystem_data} mockParticipant: ${mock_participant_table:mock_participant} + beneficiary: ${beneficiary_table:beneficiary_verification} #hcx headers protocol: @@ -73,3 +74,27 @@ mock_payer: password: ${mock_payer_password:Opensaber@123} participant_code: ${mock_payer_participant_code:1-29482df3-e875-45ef-a4e9-592b6f565782} +aws: + access-key: ${aws_accessKey:} + access-secret: ${aws_accessSecret:} + region: ${aws_region:} + +phone: + beneficiary-register : ${phone_beneficiary_register:The OTP for beneficiary app registration is:} + communication-content: ${phone_communication_content:The OTP for the communication request is:} + +otp: + expiry: ${verification_otp_expiry:300000} + send-per-minute: ${otp_sent_per_minute:5} + +beneficiary: + protocol-base-path: ${beneficiary_protocol_base_path:https://dev-hcx.swasth.app/api/v0.8} + participant-code: ${beneficiary_sender_code:testprovider1.apollo@swasth-hcx-dev} + username: ${beneficiary_username:testprovider1@apollo.com} + password: ${beneficiary_password:Opensaber@123} + recipient-code: ${beneficiary-config_recipient_code:testpayor1.icici@swasth-hcx-dev} + +certificates: + accesskey : ${certificates_accessKey:} + secretKey : ${certificates_accessSecret:} + bucketName : ${certificates_bucketName:} \ No newline at end of file