diff --git a/scripts/native/file/rec-service-issue-payment-status-1.0-native-quarkus-jdk17-runner b/scripts/native/file/rec-service-issue-payment-status-1.0-native-quarkus-jdk17-runner index e130ff1..a16b24c 100644 Binary files a/scripts/native/file/rec-service-issue-payment-status-1.0-native-quarkus-jdk17-runner and b/scripts/native/file/rec-service-issue-payment-status-1.0-native-quarkus-jdk17-runner differ diff --git a/src/main/java/com/banesco/common/application/helper/MessageHelper.java b/src/main/java/com/banesco/common/application/helper/MessageHelper.java index 2bf57bc..7d7e5bd 100644 --- a/src/main/java/com/banesco/common/application/helper/MessageHelper.java +++ b/src/main/java/com/banesco/common/application/helper/MessageHelper.java @@ -40,6 +40,14 @@ public class MessageHelper { this.errorMappings = initializeErrorMappings(); } + public Response handleSuccess(Object data, String statusCode) { + log.info( + "Respuesta exitosa controlada: {}", + statusCode + ); + return buildResponse(data, statusCode); + } + public Response handleException(HttpStatusCodeException exception) { log.error( "Error interno controlado: {} -> {}", @@ -106,8 +114,27 @@ public class MessageHelper { ); return Response.status(mapping.getHttpCode()) - .entity(new ApiResponse<>(status)) - .build(); + .entity(new ApiResponse<>(status)) + .build(); + } + + private Response buildResponse(Object data, String statusCode) { + ErrorMapping mapping = errorMappings.getOrDefault( + statusCode, errorMappings.getOrDefault( + statusCode, createDefaultMapping() + ) + ); + StatusResponse status = createError(mapping, null); + + log.error( + "[Success] Message {} -> {}", + statusCode, + status.getMessage() + ); + + return Response.status(mapping.getHttpCode()) + .entity(new ApiResponse<>(data, status)) + .build(); } private Map initializeErrorMappings() { diff --git a/src/main/java/com/banesco/common/application/helper/RequestValidatorHelper.java b/src/main/java/com/banesco/common/application/helper/RequestValidatorHelper.java index d330096..ee2fe63 100644 --- a/src/main/java/com/banesco/common/application/helper/RequestValidatorHelper.java +++ b/src/main/java/com/banesco/common/application/helper/RequestValidatorHelper.java @@ -24,7 +24,6 @@ public class RequestValidatorHelper { ) { required(request.getId(), "paymentStatusId"); required(request.getChannelCode(), "channelCode"); - required(request.getSignatureIdentifier(), "signatureIdentifier"); required(request.getCustomerReferenceFintechId(), "customerReferenceFintechId"); required(request.getAppId(), "appId"); } diff --git a/src/main/java/com/banesco/common/application/service/HttpClientService.java b/src/main/java/com/banesco/common/application/service/HttpClientService.java index 122e6e8..416a6a2 100644 --- a/src/main/java/com/banesco/common/application/service/HttpClientService.java +++ b/src/main/java/com/banesco/common/application/service/HttpClientService.java @@ -17,6 +17,8 @@ import lombok.extern.slf4j.Slf4j; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -34,56 +36,61 @@ public class HttpClientService implements HttpClientUseCase { @Override public T execute(HttpRequest request) { - return executeInternal(request); + return executeRequest(request); + } + + @Override + public Either executeEither(HttpRequest request) { + return executeEitherInternal(request, false); + } + + @Override + public Either, R> executeEitherList(HttpRequest request) { + return executeEitherInternal(request, true); } @Override public ApiResponse executeApiResponse(HttpRequest request) { - return executeInternal(request); + return executeRequest(request); } @Override - public ApiResponse> executeApiResponseList( - HttpRequest request - ) { - return executeInternal(request); + public ApiResponse> executeApiResponseList(HttpRequest request) { + return executeRequest(request); } @Override - public ApiPrivateResponse> executeApiPrivateResponse( - HttpRequest request - ) { - return executeInternal(request); + public ApiPrivateResponse> executeApiPrivateResponse(HttpRequest request) { + return executeRequest(request); } @Override - public ApiPrivateResponse, ApiPrivateError>> executeApiPrivateResponseList( - HttpRequest request - ) { - return executeInternal(request); + public ApiPrivateResponse, ApiPrivateError>> executeApiPrivateResponseList(HttpRequest request) { + return executeRequest(request); } - private T executeInternal(HttpRequest request) { - String finalUrl = buildFinalUrl(request); - - if (request.isLogRequestBody()) { - log.info("URL final: {}", finalUrl); - - if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { - log.info("Headers: {}", request.getHeaders()); - } - - if (request.getQueryParams() != null && !request.getQueryParams().isEmpty()) { - log.info("Query params: {}", request.getQueryParams()); - } - - if (request.getBody() != null) { - log.info("Body: {}", request.getBody()); - } - } - + private Either executeEitherInternal(HttpRequest request, boolean isList) { try (Client client = createClient(request.getConnectTimeout(), request.getReadTimeout())) { - WebTarget target = client.target(finalUrl); + WebTarget target = client.target(buildFinalUrl(request)); + Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON); + + if (request.getHeaders() != null) { + request.getHeaders().forEach(builder::header); + } + + Response response = buildRequest(builder, request); + return handleEitherResponse(request, response, isList); + + } catch (HttpStatusCodeException | HttpApiResponseException e) { + throw e; + } catch (Exception e) { + throw handleConnectionError(request, e); + } + } + + private T executeRequest(HttpRequest request) { + try (Client client = createClient(request.getConnectTimeout(), request.getReadTimeout())) { + WebTarget target = client.target(buildFinalUrl(request)); Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON); if (request.getHeaders() != null) { @@ -95,14 +102,122 @@ public class HttpClientService implements HttpClientUseCase { } catch (HttpStatusCodeException | HttpApiResponseException e) { throw e; } catch (Exception e) { - log.error("Error de conexion {}: {}", request.getMethod(), e.getMessage()); - throw HttpStatusCodeException.serviceUnavailable( - "503", - "Error de conexion con el servicio externo: " + e.getMessage() - ); + throw handleConnectionError(request, e); } } + @SuppressWarnings("unchecked") + private Either handleEitherResponse(HttpRequest request, Response response, boolean isList) { + int statusCode = response.getStatus(); + + try (response) { + String responseBody = response.readEntity(String.class); + logResponse(request, statusCode, responseBody); + + if (statusCode >= 200 && statusCode < 300) { + Object successData = isList + ? parseSuccessListResponse(request, responseBody) + : parseSuccessResponse(request, responseBody); + return Either.left((T) successData); + } else { + logErrorResponse(request, statusCode, responseBody); + R errorData = tryParseErrorResponse(request, responseBody); + + if (errorData != null) { + return Either.right(errorData); + } + + throw mapHttpStatusToException(statusCode, responseBody); + } + } catch (HttpStatusCodeException | HttpApiResponseException e) { + throw e; + } catch (Exception e) { + throw handleProcessingError(request, e); + } + } + + @SuppressWarnings("unchecked") + private T parseSuccessResponse(HttpRequest request, String responseBody) throws JsonProcessingException { + Type successType = extractSuccessType(request); + + if (successType != null) { + if (successType instanceof Class) { + return objectMapper.readValue(responseBody, (Class) successType); + } else if (successType instanceof ParameterizedType) { + JavaType javaType = objectMapper.getTypeFactory().constructType(successType); + return objectMapper.readValue(responseBody, javaType); + } + } + + if (request.getResponseType() != null && request.getResponseType() != Object.class) { + return objectMapper.readValue(responseBody, objectMapper.getTypeFactory().constructType(request.getResponseType())); + } + + return (T) objectMapper.readValue(responseBody, Object.class); + } + + @SuppressWarnings("unchecked") + private List parseSuccessListResponse(HttpRequest request, String responseBody) throws JsonProcessingException { + Type successType = extractSuccessType(request); + + if ( + successType instanceof ParameterizedType paramType && + paramType.getRawType() == List.class && + paramType.getActualTypeArguments().length > 0 + ) { + Type elementType = paramType.getActualTypeArguments()[0]; + if (elementType instanceof Class) { + JavaType javaType = objectMapper.getTypeFactory().constructCollectionType( + List.class, (Class) elementType + ); + return objectMapper.readValue(responseBody, javaType); + } + } + + return objectMapper.readValue(responseBody, List.class); + } + + private Type extractSuccessType(HttpRequest request) { + if ( + request.getComplexType() != null && + request.getComplexType() instanceof ParameterizedType paramType && + paramType.getRawType() == Either.class && + paramType.getActualTypeArguments().length > 0 + ) { + return paramType.getActualTypeArguments()[0]; + } + + if (request.getGenericType() != null) { + return request.getGenericType(); + } + + return request.getResponseType(); + } + + @SuppressWarnings("unchecked") + private R tryParseErrorResponse(HttpRequest request, String responseBody) { + if (responseBody == null || responseBody.trim().isEmpty()) { + return null; + } + + try { + if (request.getErrorType() != null) { + return (R) objectMapper.readValue(responseBody, request.getErrorType()); + } + + if (request.getComplexType() != null && request.getComplexType() instanceof ParameterizedType paramType) { + Type[] typeArgs = paramType.getActualTypeArguments(); + if (typeArgs.length >= 2 && typeArgs[1] instanceof Class) { + return objectMapper.readValue(responseBody, (Class) typeArgs[1]); + } + } + } catch (Exception e) { + log.error("No se pudo parsear la respuesta como error type: {}", e.getMessage()); + } + + return null; + } + private String buildFinalUrl(HttpRequest request) { String finalUrl = request.getUrl(); @@ -113,7 +228,11 @@ public class HttpClientService implements HttpClientUseCase { } } - return appendQueryParams(finalUrl, request.getQueryParams()); + String url = appendQueryParams(finalUrl, request.getQueryParams()); + + log.info("Url Final: {}", url); + + return url; } private String appendQueryParams(String url, Map queryParams) { @@ -132,20 +251,24 @@ public class HttpClientService implements HttpClientUseCase { urlBuilder.append("&"); } - urlBuilder.append(entry.getKey()) - .append("=") - .append(entry.getValue() != null ? entry.getValue() : ""); + String encodedKey = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8); + String encodedValue = entry.getValue() != null + ? URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8) + : ""; + + urlBuilder.append(encodedKey).append("=").append(encodedValue); } return urlBuilder.toString(); } - private Response buildRequest( - Invocation.Builder builder, - HttpRequest request - ) { + private Response buildRequest(Invocation.Builder builder, HttpRequest request) { log.info("Metodo HTTP: {}", request.getMethod().name()); + if(request.getBody() != null) { + log.info("Peticion Cuerpo: {}", request.getBody()); + } + return switch (request.getMethod()) { case GET -> builder.get(); case POST -> builder.post(Entity.entity(request.getBody(), MediaType.APPLICATION_JSON)); @@ -160,43 +283,26 @@ public class HttpClientService implements HttpClientUseCase { private Client createClient(int connectTimeout, int readTimeout) { return ClientBuilder.newBuilder() - .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) - .readTimeout(readTimeout, TimeUnit.MILLISECONDS) - .build(); + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) + .readTimeout(readTimeout, TimeUnit.MILLISECONDS) + .build(); } - private T handleResponse( - HttpRequest request, - Response response - ) { + private T handleResponse(HttpRequest request, Response response) { int statusCode = response.getStatus(); - log.info("Respuesta {} - Status: {}", request.getMethod(), statusCode); try (response) { String responseBody = response.readEntity(String.class); - - if (request.isLogResponseBody()) { - log.info("Respuesta Cuerpo: {}", responseBody); - } + logResponse(request, statusCode, responseBody); if (statusCode >= 200 && statusCode < 300) { if (request.getResponseType() == Void.class || request.getResponseType() == void.class) { return null; } - T result = responseResult(request, responseBody); - - log.debug("Respuesta exitosa {} {}: {}", request.getMethod(), request.getUrl(), result); - - return result; + return responseResult(request, responseBody); } else { - log.error( - "Error HTTP {} {} - Status: {} - Body: {}", - request.getMethod(), - request.getUrl(), - statusCode, - responseBody - ); + logErrorResponse(request, statusCode, responseBody); if (isApiResponseFormat(responseBody)) { ApiResponse apiResponse = deserializeApiResponse(responseBody, request); @@ -208,53 +314,72 @@ public class HttpClientService implements HttpClientUseCase { } catch (HttpStatusCodeException | HttpApiResponseException e) { throw e; } catch (Exception e) { - log.error( - "Error procesando respuesta {} {}: {}", - request.getMethod(), - request.getUrl(), - e.getMessage() - ); - throw HttpStatusCodeException.internalServer( - "500", "Error procesando respuesta del servicio externo: " + e.getMessage() - ); + throw handleProcessingError(request, e); } } - private T responseResult( - HttpRequest request, - String responseBody - ) throws JsonProcessingException { + private void logResponse(HttpRequest request, int statusCode, String responseBody) { + if (request.isLogResponseBody()) { + log.info("Respuesta {} - Status: {}", request.getMethod(), statusCode); + log.info("Respuesta Cuerpo: {}", responseBody); + } + } + + private void logErrorResponse(HttpRequest request, int statusCode, String responseBody) { + log.error( + "Error HTTP {} {} - Status: {} - Body: {}", + request.getMethod(), + request.getUrl(), + statusCode, + responseBody + ); + } + + private HttpStatusCodeException handleConnectionError(HttpRequest request, Exception e) { + log.error("Error de conexion {}: {}", request.getMethod(), e.getMessage()); + + return HttpStatusCodeException.serviceUnavailable( + "503", "Error de conexion con el servicio externo: " + e.getMessage() + ); + } + + private HttpStatusCodeException handleProcessingError(HttpRequest request, Exception e) { + log.error( + "Error procesando respuesta {} {}: {}", + request.getMethod(), + request.getUrl(), + e.getMessage() + ); + return HttpStatusCodeException.internalServer( + "500", "Error procesando respuesta del servicio externo: " + e.getMessage() + ); + } + + private T responseResult(HttpRequest request, String responseBody) throws JsonProcessingException { if (request.isApiPrivateResponse() && request.isEitherResponse()) { return handleApiPrivateResponseWithEither(request, responseBody); } - T result; - if (request.getResponseType() == ApiResponse.class) { - result = deserializeApiResponse(responseBody, request); + return deserializeApiResponse(responseBody, request); } else if (request.getComplexType() != null) { JavaType javaType = objectMapper.getTypeFactory().constructParametricType( request.getResponseType(), objectMapper.getTypeFactory().constructType(request.getComplexType()) ); - result = objectMapper.readValue(responseBody, javaType); + return objectMapper.readValue(responseBody, javaType); } else if (request.getGenericType() != null) { JavaType javaType = objectMapper.getTypeFactory().constructParametricType( request.getResponseType(), objectMapper.getTypeFactory().constructType(request.getGenericType()) ); - result = objectMapper.readValue(responseBody, javaType); + return objectMapper.readValue(responseBody, javaType); } else { - result = objectMapper.readValue( + return objectMapper.readValue( responseBody, objectMapper.getTypeFactory().constructType(request.getResponseType()) ); } - - return result; } - private T handleApiPrivateResponseWithEither( - HttpRequest request, - String responseBody - ) throws JsonProcessingException { + private T handleApiPrivateResponseWithEither(HttpRequest request, String responseBody) throws JsonProcessingException { JsonNode rootNode = objectMapper.readTree(responseBody); String status = rootNode.has("estatus") ? rootNode.get("estatus").asText() : null; String message = rootNode.has("mensaje") ? rootNode.get("mensaje").asText() : null; @@ -268,12 +393,7 @@ public class HttpClientService implements HttpClientUseCase { } @SuppressWarnings("unchecked") - private T handleSuccessResponse( - HttpRequest request, - String status, - String message, - JsonNode detailNode - ) { + private T handleSuccessResponse(HttpRequest request, String status, String message, JsonNode detailNode) { Object successData; if (request.isListResponse()) { @@ -295,10 +415,7 @@ public class HttpClientService implements HttpClientUseCase { } } - private Object handleListSuccess( - HttpRequest request, - JsonNode detailNode - ) { + private Object handleListSuccess(HttpRequest request, JsonNode detailNode) { Class elementType = getElementTypeFromRequest(request); JavaType listType = objectMapper.getTypeFactory().constructCollectionType(List.class, elementType); @@ -309,10 +426,7 @@ public class HttpClientService implements HttpClientUseCase { return List.of(); } - private Object handleObjectSuccess( - HttpRequest request, - JsonNode detailNode - ) { + private Object handleObjectSuccess(HttpRequest request, JsonNode detailNode) { Class elementType = getElementTypeFromRequest(request); if (detailNode != null && !detailNode.isNull()) { @@ -323,11 +437,7 @@ public class HttpClientService implements HttpClientUseCase { } @SuppressWarnings("unchecked") - private T handleErrorResponse( - String status, - String message, - JsonNode detailNode - ) { + private T handleErrorResponse(String status, String message, JsonNode detailNode) { ApiPrivateError error = buildApiPrivateError(detailNode, message); ApiPrivateResponse> response = new ApiPrivateResponse<>(); @@ -338,10 +448,7 @@ public class HttpClientService implements HttpClientUseCase { return (T) response; } - private ApiPrivateError buildApiPrivateError( - JsonNode detailNode, - String message - ) { + private ApiPrivateError buildApiPrivateError(JsonNode detailNode, String message) { if (detailNode != null && !detailNode.isNull()) { try { return objectMapper.convertValue(detailNode, ApiPrivateError.class); @@ -385,15 +492,11 @@ public class HttpClientService implements HttpClientUseCase { } @SuppressWarnings("unchecked") - private T deserializeApiResponse( - String responseBody, - HttpRequest request - ) { + private T deserializeApiResponse(String responseBody, HttpRequest request) { try { if (request.getGenericType() != null) { JavaType javaType = objectMapper.getTypeFactory().constructParametricType( - ApiResponse.class, - objectMapper.getTypeFactory().constructType(request.getGenericType()) + ApiResponse.class, objectMapper.getTypeFactory().constructType(request.getGenericType()) ); return objectMapper.readValue(responseBody, javaType); } else { @@ -427,10 +530,7 @@ public class HttpClientService implements HttpClientUseCase { } } - private HttpStatusCodeException mapHttpStatusToException( - int statusCode, - String errorBody - ) { + private HttpStatusCodeException mapHttpStatusToException(int statusCode, String errorBody) { String errorCode = "HTTP_" + statusCode; String defaultMessage = "Error en servicio externo: HTTP " + statusCode; String message = errorBody != null && !errorBody.isEmpty() diff --git a/src/main/java/com/banesco/common/application/usecase/HttpClientUseCase.java b/src/main/java/com/banesco/common/application/usecase/HttpClientUseCase.java index c7a0b7b..b55922c 100644 --- a/src/main/java/com/banesco/common/application/usecase/HttpClientUseCase.java +++ b/src/main/java/com/banesco/common/application/usecase/HttpClientUseCase.java @@ -8,6 +8,10 @@ public interface HttpClientUseCase { T execute(HttpRequest request); + Either executeEither(HttpRequest request); + + Either, R> executeEitherList(HttpRequest request); + ApiResponse executeApiResponse(HttpRequest request); ApiResponse> executeApiResponseList(HttpRequest request); diff --git a/src/main/java/com/banesco/common/domain/exception/BusinessException.java b/src/main/java/com/banesco/common/domain/exception/BusinessException.java deleted file mode 100644 index 6d44727..0000000 --- a/src/main/java/com/banesco/common/domain/exception/BusinessException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.banesco.common.domain.exception; - -public class BusinessException extends BaseApiException { - public BusinessException(String errorCode, String message, String fieldPath) { - super(errorCode, message, fieldPath, "business"); - } - - public BusinessException(String errorCode, String fieldPath) { - super(errorCode, fieldPath, "business"); - } -} diff --git a/src/main/java/com/banesco/common/domain/model/Device.java b/src/main/java/com/banesco/common/domain/model/Device.java new file mode 100644 index 0000000..8299a06 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/Device.java @@ -0,0 +1,17 @@ +package com.banesco.common.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class Device { + private String deviceType; + private String deviceDescription; + private String deviceIp; + private String deviceSessionReference; +} diff --git a/src/main/java/com/banesco/common/infrastructure/context/RequestContext.java b/src/main/java/com/banesco/common/infrastructure/context/RequestContext.java index 633fd0f..bf29549 100644 --- a/src/main/java/com/banesco/common/infrastructure/context/RequestContext.java +++ b/src/main/java/com/banesco/common/infrastructure/context/RequestContext.java @@ -6,7 +6,9 @@ public class RequestContext { private RequestContext() {} - private static final String REQUEST_ID = "requestId"; + public static final String REQUEST_ID = "requestId"; + public static final String DEVICE = "device"; + public static final String DEVICE_SESSION_REFERENCE = "deviceSessionReference"; public static String getRequestId() { return MDC.get(REQUEST_ID); diff --git a/src/main/java/com/banesco/common/infrastructure/filter/RequestIdFilter.java b/src/main/java/com/banesco/common/infrastructure/filter/RequestIdFilter.java index c6090ec..e14b02b 100644 --- a/src/main/java/com/banesco/common/infrastructure/filter/RequestIdFilter.java +++ b/src/main/java/com/banesco/common/infrastructure/filter/RequestIdFilter.java @@ -6,15 +6,88 @@ import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.ContainerResponseContext; import jakarta.ws.rs.container.ContainerResponseFilter; import jakarta.ws.rs.ext.Provider; +import lombok.extern.slf4j.Slf4j; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; import java.util.UUID; +@Slf4j @Provider public class RequestIdFilter implements ContainerRequestFilter, ContainerResponseFilter { @Override public void filter(ContainerRequestContext requestContext) { - RequestContext.setRequestId(UUID.randomUUID().toString().substring(0, 13)); + String requestId = requestContext.getHeaderString(RequestContext.DEVICE_SESSION_REFERENCE); + + if (isEmpty(requestId)) { + requestId = requestContext.getUriInfo() + .getQueryParameters() + .getFirst(RequestContext.DEVICE_SESSION_REFERENCE); + } + + if (isEmpty(requestId) && hasJsonBody(requestContext)) { + requestId = extractRequestIdFromBody(requestContext); + } + + if (isEmpty(requestId)) { + requestId = UUID.randomUUID().toString().substring(0, 13); + } + + RequestContext.setRequestId(requestId); + } + + private boolean isEmpty(String value) { + return value == null || value.trim().isEmpty(); + } + + private boolean hasJsonBody(ContainerRequestContext context) { + try { + String method = context.getMethod(); + String contentType = context.getHeaderString("Content-Type"); + + return ("POST".equals(method) || "PUT".equals(method)) + && contentType != null + && contentType.contains("application/json"); + } catch (Exception e) { + log.warn("La peticion no es un POST o PUT: {}", e.getMessage()); + return false; + } + } + + private String extractRequestIdFromBody(ContainerRequestContext context) { + try { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + context.getEntityStream().transferTo(buffer); + byte[] bodyBytes = buffer.toByteArray(); + + context.setEntityStream(new ByteArrayInputStream(bodyBytes)); + + String bodyString = new String(bodyBytes, StandardCharsets.UTF_8); + io.vertx.core.json.JsonObject jsonObject = new io.vertx.core.json.JsonObject(bodyString); + + if (jsonObject.containsKey(RequestContext.DEVICE)) { + io.vertx.core.json.JsonObject device = jsonObject.getJsonObject(RequestContext.DEVICE); + + if (device.containsKey(RequestContext.DEVICE_SESSION_REFERENCE)) { + return device.getString(RequestContext.DEVICE_SESSION_REFERENCE); + } + } + + if (jsonObject.containsKey(RequestContext.REQUEST_ID)) { + return jsonObject.getString(RequestContext.REQUEST_ID); + } + + if (jsonObject.containsKey(RequestContext.DEVICE_SESSION_REFERENCE)) { + return jsonObject.getString(RequestContext.DEVICE_SESSION_REFERENCE); + } + + return null; + } catch (Exception e) { + log.error("Error extrayendo el requestId del cuerpo de la peticion: {}", e.getMessage()); + return null; + } } @Override @@ -22,6 +95,10 @@ public class RequestIdFilter implements ContainerRequestFilter, ContainerRespons ContainerRequestContext requestContext, ContainerResponseContext responseContext ) { - RequestContext.clear(); + try { + RequestContext.clear(); + } catch (Exception e) { + log.error("Error limpiando el filtro: {}", e.getMessage()); + } } -} +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/banking_product/domain/model/BankingProductType.java b/src/main/java/com/banesco/module/banking_product/domain/model/BankingProductType.java index 4020738..445999d 100644 --- a/src/main/java/com/banesco/module/banking_product/domain/model/BankingProductType.java +++ b/src/main/java/com/banesco/module/banking_product/domain/model/BankingProductType.java @@ -7,4 +7,5 @@ public enum BankingProductType { BROKERED_PRODUCT, TERM_DEPOSIT_PRODUCT, MOBILE_PAYMENT, + NETUNO } diff --git a/src/main/java/com/banesco/module/instruction/domain/model/Instruction.java b/src/main/java/com/banesco/module/instruction/domain/model/Instruction.java index 160c7c9..136eb8d 100644 --- a/src/main/java/com/banesco/module/instruction/domain/model/Instruction.java +++ b/src/main/java/com/banesco/module/instruction/domain/model/Instruction.java @@ -1,14 +1,10 @@ package com.banesco.module.instruction.domain.model; -import com.banesco.common.domain.exception.HttpStatusCodeException; import com.banesco.common.domain.model.Identifier; import com.fasterxml.jackson.annotation.JsonInclude; import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.*; -import java.util.Arrays; -import java.util.Objects; - @Getter @ToString @Builder @@ -19,21 +15,12 @@ import java.util.Objects; public class Instruction { private InstructionIdentification instructionIdentifier; // Request JSON: "id" private String instructionDescription; // Request JSON: "signature" - private InstructionPurposeType instructionPurposeType; // Request JSON: "channelOrigin" (BOLE) + private String instructionPurposeType; // Request JSON: "channelOrigin" (BOLE) public static Instruction fromResource( String paymentStatusId, - String channelCode, - String signatureIdentifier + String channelCode ) { - boolean isChannelCodeValid = (Arrays.stream( - InstructionPurposeType.values() - ).anyMatch(type -> Objects.equals(type.name(), channelCode))); - - if(!isChannelCodeValid) { - throw HttpStatusCodeException.badRequest("VDE02", "channelCode"); - } - return Instruction.builder() .instructionIdentifier( InstructionIdentification.builder() @@ -45,8 +32,7 @@ public class Instruction { .identificationType(InstructionIdentificationType.INSTRUCTION_NUMBER) .build() ) - .instructionPurposeType(InstructionPurposeType.valueOf(channelCode)) - .instructionDescription(signatureIdentifier) + .instructionPurposeType(channelCode) .build(); } } diff --git a/src/main/java/com/banesco/module/instruction/domain/model/InstructionPurposeType.java b/src/main/java/com/banesco/module/instruction/domain/model/InstructionPurposeType.java deleted file mode 100644 index acf15e5..0000000 --- a/src/main/java/com/banesco/module/instruction/domain/model/InstructionPurposeType.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.banesco.module.instruction.domain.model; - -public enum InstructionPurposeType { - BOLE, - BOL, -} diff --git a/src/main/java/com/banesco/module/service_issue_payment_status/application/service/ServiceIssuePaymentStatusService.java b/src/main/java/com/banesco/module/service_issue_payment_status/application/service/ServiceIssuePaymentStatusService.java index 4378cd1..6d32622 100644 --- a/src/main/java/com/banesco/module/service_issue_payment_status/application/service/ServiceIssuePaymentStatusService.java +++ b/src/main/java/com/banesco/module/service_issue_payment_status/application/service/ServiceIssuePaymentStatusService.java @@ -3,17 +3,16 @@ package com.banesco.module.service_issue_payment_status.application.service; import com.banesco.common.application.helper.MessageHelper; import com.banesco.common.application.helper.RequestValidatorHelper; import com.banesco.common.domain.exception.HttpStatusCodeException; -import com.banesco.common.domain.model.*; +import com.banesco.common.domain.model.ApiResponse; import com.banesco.module.service_issue_payment_status.application.usecase.BusinessUseCase; import com.banesco.module.service_issue_payment_status.application.usecase.ServiceIssuePaymentStatusUseCase; import com.banesco.module.service_issue_payment_status.domain.dto.request.ServiceIssuePaymentStatusRequest; import com.banesco.module.service_issue_payment_status.domain.dto.response.ServiceIssuePaymentStatusResponse; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.ws.rs.core.Response; import lombok.extern.slf4j.Slf4j; -import java.util.Objects; - @Slf4j @ApplicationScoped public class ServiceIssuePaymentStatusService implements ServiceIssuePaymentStatusUseCase { @@ -34,33 +33,31 @@ public class ServiceIssuePaymentStatusService implements ServiceIssuePaymentStat } @Override - public ApiResponse execute( + public Response execute( ServiceIssuePaymentStatusRequest request ) { log.info("Iniciando ejecucion para el transaccion: {}", request.getId()); - validate(request); + Response response; try { - ApiResponse response = business(request); + validate(request); - if ( - !Objects.isNull(response.getData()) && - messageHelper.isSuccessStatusCode(response.getStatusResponse()) - ) { - return new ApiResponse<>(response.getData(), messageHelper.createStatusResponse( - response.getStatusResponse().getStatusCode() - )); - } + ApiResponse apiResponse = business(request); - throw HttpStatusCodeException.serviceUnavailable("503"); + response = messageHelper.handleSuccess( + apiResponse.getData(), + apiResponse.getStatusResponse().getStatusCode() + ); } catch (HttpStatusCodeException e) { log.error("Excepcion HTTP del api de negocio: {} - {}", e.getStatusCode(), e.getErrorCode()); - throw e; + response = messageHelper.handleException(e); } catch (Exception e) { log.error("Excepcion generica del api de negocio: {}", e.getMessage()); - throw e; + response = messageHelper.handleGenericException(e); } + + return response; } private void validate( diff --git a/src/main/java/com/banesco/module/service_issue_payment_status/application/usecase/ServiceIssuePaymentStatusUseCase.java b/src/main/java/com/banesco/module/service_issue_payment_status/application/usecase/ServiceIssuePaymentStatusUseCase.java index 0e188fb..69ab9ee 100644 --- a/src/main/java/com/banesco/module/service_issue_payment_status/application/usecase/ServiceIssuePaymentStatusUseCase.java +++ b/src/main/java/com/banesco/module/service_issue_payment_status/application/usecase/ServiceIssuePaymentStatusUseCase.java @@ -1,11 +1,10 @@ package com.banesco.module.service_issue_payment_status.application.usecase; -import com.banesco.common.domain.model.ApiResponse; import com.banesco.module.service_issue_payment_status.domain.dto.request.ServiceIssuePaymentStatusRequest; -import com.banesco.module.service_issue_payment_status.domain.dto.response.ServiceIssuePaymentStatusResponse; +import jakarta.ws.rs.core.Response; public interface ServiceIssuePaymentStatusUseCase { - ApiResponse execute( + Response execute( ServiceIssuePaymentStatusRequest request ); } diff --git a/src/main/java/com/banesco/module/service_issue_payment_status/domain/dto/request/ServiceIssuePaymentStatusRequest.java b/src/main/java/com/banesco/module/service_issue_payment_status/domain/dto/request/ServiceIssuePaymentStatusRequest.java index 8ecf468..fe9cf6c 100644 --- a/src/main/java/com/banesco/module/service_issue_payment_status/domain/dto/request/ServiceIssuePaymentStatusRequest.java +++ b/src/main/java/com/banesco/module/service_issue_payment_status/domain/dto/request/ServiceIssuePaymentStatusRequest.java @@ -1,6 +1,10 @@ package com.banesco.module.service_issue_payment_status.domain.dto.request; +import com.banesco.common.domain.model.Device; +import com.banesco.common.infrastructure.context.RequestContext; import com.banesco.module.instruction.domain.model.Instruction; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.netty.util.internal.StringUtil; import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.*; @@ -22,23 +26,43 @@ public class ServiceIssuePaymentStatusRequest { private String appId; @NonNull private Instruction procedureRequest; + @NonNull + private Device device; + @JsonIgnore public String getId() { - return getProcedureRequest().getInstructionIdentifier().getIdentification().getIdentifierValue(); + return procedureRequest + .getInstructionIdentifier() + .getIdentification() + .getIdentifierValue(); } + @JsonIgnore public String getChannelCode() { - return getProcedureRequest().getInstructionPurposeType().name(); + return procedureRequest + .getInstructionPurposeType(); } - public String getSignatureIdentifier() { - return getProcedureRequest().getInstructionDescription(); + @JsonIgnore + public Map toParams() { + return Map.ofEntries( + entry("paymentStatusId", Objects.toString(getId(), "")), + entry("channelCode", Objects.toString(getChannelCode(), "")) + ); } + @JsonIgnore public Map toQueryString() { return Map.ofEntries( entry("appId", Objects.toString(getAppId(), "")), - entry("customerReferenceFintechId", Objects.toString(getCustomerReferenceFintechId(), "")) + entry("customerReferenceFintechId", Objects.toString(getCustomerReferenceFintechId(), "")), + entry("deviceType", Objects.toString(getDevice().getDeviceType(), "")), + entry("deviceDescription", Objects.toString(getDevice().getDeviceDescription(), "")), + entry("deviceIp", Objects.toString(getDevice().getDeviceIp(), "")), + entry("deviceSessionReference", (!StringUtil.isNullOrEmpty(getDevice().getDeviceSessionReference())) + ? getDevice().getDeviceSessionReference() + : RequestContext.getRequestId() + ) ); } } \ No newline at end of file diff --git a/src/main/java/com/banesco/module/service_issue_payment_status/infrastructure/client/BusServiceIssuePaymentStatusClient.java b/src/main/java/com/banesco/module/service_issue_payment_status/infrastructure/client/BusServiceIssuePaymentStatusClient.java index 246a4fd..b0756fd 100644 --- a/src/main/java/com/banesco/module/service_issue_payment_status/infrastructure/client/BusServiceIssuePaymentStatusClient.java +++ b/src/main/java/com/banesco/module/service_issue_payment_status/infrastructure/client/BusServiceIssuePaymentStatusClient.java @@ -13,8 +13,6 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.extern.slf4j.Slf4j; -import java.util.Map; - @Slf4j @ApplicationScoped public class BusServiceIssuePaymentStatusClient implements BusinessUseCase { @@ -37,27 +35,20 @@ public class BusServiceIssuePaymentStatusClient implements BusinessUseCase { Class responseType ) { String paymentStatusId = params.getId(); - String channelCode = params.getChannelCode(); - String signatureIdentifier = params.getSignatureIdentifier(); - HttpRequest request = HttpRequest.builder() - .url(businessConfig.getUrl()) - .method(HttpRequest.HttpMethod.GET) - .pathParams(Map.of( - "paymentStatusId", paymentStatusId, - "channelCode", channelCode, - "signatureIdentifier", signatureIdentifier - )) - .queryParams(params.toQueryString()) - .responseType(ApiResponse.class) - .genericType(responseType) - .connectTimeout(businessConfig.getTimeout().getConnect()) - .readTimeout(businessConfig.getTimeout().getResponse()) - .build(); - - log.debug("Request configurado: {}", request); + HttpRequest request = HttpRequest.forApiResponse( + businessConfig.getUrl(), + HttpRequest.HttpMethod.GET, + responseType + ) + .withPathParams(params.toParams()) + .withQueryParams(params.toQueryString()) + .withTimeout( + businessConfig.getTimeout().getConnect(), + businessConfig.getTimeout().getResponse() + ); try { - ApiResponse response = httpClientUseCase.execute(request); + ApiResponse response = httpClientUseCase.executeApiResponse(request); log.info( "Solicitud del api de negocio exitoso: {}", diff --git a/src/main/java/com/banesco/module/service_issue_payment_status/infrastructure/resource/ServiceIssuePaymentStatusResource.java b/src/main/java/com/banesco/module/service_issue_payment_status/infrastructure/resource/ServiceIssuePaymentStatusResource.java index 9015f1d..89fc909 100644 --- a/src/main/java/com/banesco/module/service_issue_payment_status/infrastructure/resource/ServiceIssuePaymentStatusResource.java +++ b/src/main/java/com/banesco/module/service_issue_payment_status/infrastructure/resource/ServiceIssuePaymentStatusResource.java @@ -1,8 +1,8 @@ package com.banesco.module.service_issue_payment_status.infrastructure.resource; import com.banesco.common.application.helper.MessageHelper; -import com.banesco.common.domain.exception.HttpStatusCodeException; import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.domain.model.Device; import com.banesco.common.domain.model.StatusResponse; import com.banesco.module.instruction.domain.model.Instruction; import com.banesco.module.service_issue_payment_status.application.usecase.ServiceIssuePaymentStatusUseCase; @@ -31,19 +31,17 @@ import java.util.Objects; public class ServiceIssuePaymentStatusResource { private final ServiceIssuePaymentStatusUseCase useCase; - private final MessageHelper messageHelper; @Inject public ServiceIssuePaymentStatusResource( MessageHelper messageHelper, ServiceIssuePaymentStatusUseCase useCase ) { - this.messageHelper = messageHelper; this.useCase = useCase; } @GET - @Path("/retrieve/{paymentStatusId}/{channelCode}/{signatureIdentifier}") + @Path("/retrieve/{paymentStatusId}/{channelCode}") @Operation( summary = "Recuperar informacion de la transaccion", description = "Consulta de una trasanccion de pago por id de archivo" @@ -320,26 +318,40 @@ public class ServiceIssuePaymentStatusResource { @Parameter(description = "Codigo del canal (BOL, BOLE)", required = true, example = "BOLE") String channelCode, - @PathParam("signatureIdentifier") - @Parameter(description = "Firma del archivo", required = true, example = "add7c6375b8659a798a998258fb049b98119ea97d8116b95e1ee00cc9ffafa84") - String signatureIdentifier + @QueryParam("deviceType") + @Parameter(description = "Tipo de dispositivo", example = "Mobile") + String deviceType, + + @QueryParam("deviceDescription") + @Parameter(description = "Descripcion del dispositivo", example = "Xiaomi Note 11 PRO") + String deviceDescription, + + @QueryParam("deviceIp") + @Parameter(description = "Direccion IP del dispositivo", example = "127.0.0.1") + String deviceIp, + + @QueryParam("deviceSessionReference") + @Parameter(description = "Referencia de la peticion del dispositivo", example = "12345678901304") + String deviceSessionReference ) { log.info("Iniciando consulta para instruccion de archivo id: {}", paymentStatusId); - try { - return Response.ok(useCase.execute( - ServiceIssuePaymentStatusRequest.builder() - .customerReferenceFintechId(Objects.toString(customerReferenceFintechId, "")) - .appId(Objects.toString(appId, "")) - .procedureRequest(Instruction.fromResource( - paymentStatusId, channelCode, signatureIdentifier - )) - .build() - )).build(); - } catch (HttpStatusCodeException e) { - return messageHelper.handleException(e); - } catch (Exception e) { - return messageHelper.handleGenericException(e); - } + return useCase.execute( + ServiceIssuePaymentStatusRequest.builder() + .customerReferenceFintechId(Objects.toString(customerReferenceFintechId, "")) + .appId(Objects.toString(appId, "")) + .procedureRequest(Instruction.fromResource( + paymentStatusId, channelCode + )) + .device( + Device.builder() + .deviceType(deviceType) + .deviceDescription(deviceDescription) + .deviceIp(deviceIp) + .deviceSessionReference(deviceSessionReference) + .build() + ) + .build() + ); } } \ No newline at end of file diff --git a/src/main/java/com/banesco/module/transaction/domain/model/TransactionStatusType.java b/src/main/java/com/banesco/module/transaction/domain/model/TransactionStatusType.java index 6680ba4..a30f6d2 100644 --- a/src/main/java/com/banesco/module/transaction/domain/model/TransactionStatusType.java +++ b/src/main/java/com/banesco/module/transaction/domain/model/TransactionStatusType.java @@ -7,6 +7,7 @@ public enum TransactionStatusType { EXECUTED, CANCELLED, CONFIRMED, + APPROVED, SUSPENDED, PENDING, COMPLETED, @@ -15,4 +16,9 @@ public enum TransactionStatusType { REJECTED, EXPIRED, SENT, + RECEIVED, + ACTIVE, + READ, + PROGRAMED, + ATYPICAL_TRANSACTION, } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 8a5cab7..a0373d8 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -17,6 +17,6 @@ api: rec-service-issue-payment-status: messages: key: 'rec-service-issue-payment-status' - content: '[{"backendCode":"200","httpCode":200,"statusCode":"200","description":"Operacion exitosa"},{"backendCode":"R404","httpCode":404,"statusCode":"404","description":"Datos de validacion no encontrado."},{"backendCode":"503","httpCode":503,"statusCode":"503","description":"Uso interno"},{"backendCode":"422","httpCode":422,"statusCode":"422","description":"Uso interno"},{"backendCode":"500","httpCode":500,"statusCode":"500","description":"Uso interno"},{"backendCode":"100","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"OSB-382505","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"OSB-380002","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"ERROR","httpCode":400,"statusCode":"400","description":"Uso interno"},{"backendCode":"400","httpCode":400,"statusCode":"400","description":"Uso interno"},{"backendCode":"401","httpCode":401,"statusCode":"401","description":"Uso interno"},{"backendCode":"403","httpCode":403,"statusCode":"403","description":"Uso interno"},{"backendCode":"404","httpCode":404,"statusCode":"404","description":"Uso interno"},{"backendCode":"default","httpCode":409,"statusCode":"409","description":"Conflicto"},{"backendCode":"424","httpCode":424,"statusCode":"424","description":"Error de dependencia"},{"backendCode":"VDE01","httpCode":400,"statusCode":"VDE01","description":"VDE01 - Error en dato de entrada obligatorio: %s"},{"backendCode":"VDE02","httpCode":400,"statusCode":"VDE02","description":"VDE02 - Error en valor permitido para campo: %s"},{"backendCode":"VRN04","httpCode":"503","statusCode":"VRN04","description":"Servicio en horario de mantenimiento","status":"error"},{"backendCode":"204","httpCode":"200","statusCode":"200","description":"Cliente sin productos","status":"ok"}]' + content: '[{"backendCode":"200","httpCode":200,"statusCode":"200","description":"Operacion exitosa"},{"backendCode":"R404","httpCode":404,"statusCode":"404","description":"Datos de validacion no encontrado."},{"backendCode":"503","httpCode":503,"statusCode":"503","description":"Uso interno"},{"backendCode":"422","httpCode":422,"statusCode":"422","description":"Uso interno"},{"backendCode":"500","httpCode":500,"statusCode":"500","description":"Uso interno"},{"backendCode":"100","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"OSB-382505","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"OSB-380002","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"ERROR","httpCode":400,"statusCode":"400","description":"Uso interno"},{"backendCode":"400","httpCode":400,"statusCode":"400","description":"Uso interno"},{"backendCode":"401","httpCode":401,"statusCode":"401","description":"Uso interno"},{"backendCode":"403","httpCode":403,"statusCode":"403","description":"Uso interno"},{"backendCode":"404","httpCode":404,"statusCode":"404","description":"Uso interno"},{"backendCode":"default","httpCode":409,"statusCode":"409","description":"Conflicto"},{"backendCode":"424","httpCode":424,"statusCode":"424","description":"Error de dependencia"},{"backendCode":"VDE01","httpCode":400,"statusCode":"VDE01","description":"VDE01 - Error en dato de entrada obligatorio: %s"},{"backendCode":"VDE02","httpCode":400,"statusCode":"VDE02","description":"VDE02 - Error en valor permitido para campo: %s"},{"backendCode":"VRN04","httpCode":"503","statusCode":"VRN04","description":"Servicio en horario de mantenimiento","status":"error"},{"backendCode":"VRN02","httpCode":"204","statusCode":"VRN02","description":"Cliente sin productos"}]' rest-client: - bus-service-issue-payment-status: '{"url":"http://localhost:8082/service-issue-payment-status/retrieve/{paymentStatusId}/{channelCode}/{signatureIdentifier}","timeout":{"connect":15000,"response":15000},"config":{}}' \ No newline at end of file + bus-service-issue-payment-status: '{"url":"http://localhost:8082/service-issue-payment-status/retrieve/{paymentStatusId}/{channelCode}","timeout":{"connect":15000,"response":15000},"config":{}}' \ No newline at end of file