Fecha: 2026-01-19 Esfuerzo: 15-30 minutos Tipo: Quick Win
La app N1co necesita mostrar mensajes específicos según el estado de la solicitud de crédito del cliente:
- "Necesitamos que subas tu AFP" (apelación pendiente)
- "Tu solicitud está en revisión" (apelación completada)
- "Tu solicitud fue rechazada" (rechazado manualmente)
Actualmente solo recibimos el código burofax_request_appeal sin detalles del estado.
Extender el objeto error_data en GET /api/v2/products para incluir el estado detallado de EquifaxRequest.
El código actual ya consulta EquifaxRequest y ya retorna códigos de apelación. Solo agregamos más campos al objeto que ya se está usando.
Archivo: api-laravel/app/Services/Product/ProductService.php
Antes:
if (blank($assignedAmount->approved_amount) && filled($equifaxRequest)) {
$error = true;
$errorData = [
'code' => ResponseCode::BUROFAX_REQUEST_APPEAL,
'appeal_url' => get_setting('appeal_url')
];
}Después:
if (blank($assignedAmount->approved_amount) && filled($equifaxRequest)) {
$error = true;
$errorData = [
'code' => ResponseCode::BUROFAX_REQUEST_APPEAL,
'appeal_url' => get_setting('appeal_url'),
'application_status' => [
'step' => $equifaxRequest->step,
'final_decision' => $equifaxRequest->final_decision,
'rejection_reason' => $equifaxRequest->rejection_reason,
'appeal_needed' => $equifaxRequest->appeal_needed,
'appeal_completed' => $equifaxRequest->appeal_completed,
]
];
}Antes:
if ($equifaxRequest->final_decision === 'denied') {
$errorData = [
'code' => ResponseCode::BUROFAX_REQUEST_APPEAL,
'appeal_url' => get_setting('appeal_url')
];
}Después:
if ($equifaxRequest->final_decision === 'denied') {
$errorData = [
'code' => ResponseCode::BUROFAX_REQUEST_APPEAL,
'appeal_url' => get_setting('appeal_url'),
'application_status' => [
'step' => $equifaxRequest->step,
'final_decision' => $equifaxRequest->final_decision,
'rejection_reason' => $equifaxRequest->rejection_reason,
'appeal_needed' => $equifaxRequest->appeal_needed,
'appeal_completed' => $equifaxRequest->appeal_completed,
]
];
}{
"error": true,
"error_data": {
"code": "burofax_request_appeal",
"appeal_url": "https://onboarding.mpay.com?uuid=...",
"application_status": {
"step": "appeal",
"final_decision": "denied",
"rejection_reason": "customer_not_found",
"appeal_needed": true,
"appeal_completed": false
}
},
"data": [...]
}Mensaje en UI: "Para continuar, necesitamos que subas tu constancia AFP"
{
"error": true,
"error_data": {
"code": "burofax_request_appeal",
"appeal_url": "https://onboarding.mpay.com?uuid=...",
"application_status": {
"step": "appeal",
"final_decision": "denied",
"rejection_reason": "customer_not_found",
"appeal_needed": true,
"appeal_completed": true
}
},
"data": [...]
}Mensaje en UI: "Tu solicitud está siendo revisada. Te notificaremos pronto."
{
"error": true,
"error_data": {
"code": "burofax_request_appeal",
"appeal_url": "https://onboarding.mpay.com?uuid=...",
"application_status": {
"step": "evaluation",
"final_decision": "rejected",
"rejection_reason": "manual_rejection",
"appeal_needed": false,
"appeal_completed": false
}
},
"data": [...]
}Mensaje en UI: "Tu solicitud no fue aprobada. Contáctanos para más información."
| Campo | Tipo | Valores Posibles | Descripción |
|---|---|---|---|
step |
string | "appeal", "evaluation", "search", etc. | Paso actual del proceso |
final_decision |
string | "approved", "denied", "rejected", null | Decisión final |
rejection_reason |
string | Ver tabla abajo | Razón del rechazo |
appeal_needed |
boolean | true/false | Requiere apelación |
appeal_completed |
boolean | true/false | Cliente ya subió AFP |
| Código | Significado |
|---|---|
customer_not_found |
No encontrado en buró de crédito |
minimum_age |
No cumple edad mínima |
maximum_age |
Excede edad máxima |
minimum_salary |
Ingreso insuficiente |
minimum_score |
Score crediticio bajo |
invalid_bureau_rating |
Calificación bancaria inválida |
manual_rejection |
Rechazado por agente de riesgo |
public class ProductErrorInfo
{
public string Code { get; set; }
public string AppealUrl { get; set; }
[JsonProperty("application_status")]
public CreditApplicationStatus ApplicationStatus { get; set; }
}
public class CreditApplicationStatus
{
public string Step { get; set; }
public string FinalDecision { get; set; }
public string RejectionReason { get; set; }
public bool AppealNeeded { get; set; }
public bool AppealCompleted { get; set; }
}public static string GetKycMessage(ProductErrorInfo errorInfo)
{
if (errorInfo.Code != "burofax_request_appeal")
return null;
var status = errorInfo.ApplicationStatus;
if (status == null)
return "Procesando tu solicitud...";
// Cliente debe subir AFP
if (status.AppealNeeded && !status.AppealCompleted)
return "Para continuar, necesitamos que subas tu constancia AFP";
// En revisión manual
if (status.AppealNeeded && status.AppealCompleted)
return "Tu solicitud está siendo revisada. Te notificaremos pronto.";
// Rechazado manualmente
if (status.FinalDecision == "rejected")
return "Tu solicitud no fue aprobada. Contáctanos para más información.";
// Rechazado automáticamente
if (status.FinalDecision == "denied")
return GetAutoRejectionMessage(status.RejectionReason);
return "Procesando tu solicitud de crédito...";
}
private static string GetAutoRejectionMessage(string reason)
{
return reason switch
{
"customer_not_found" => "No encontramos tu registro en el buró de crédito",
"minimum_age" => "No cumples con la edad mínima requerida",
"minimum_salary" => "Tu ingreso mensual no cumple con el mínimo",
"minimum_score" => "Tu perfil crediticio no cumple los requisitos",
_ => "Tu solicitud no fue aprobada en este momento"
};
}| Escenario | Verificar |
|---|---|
| Cliente con crédito aprobado | error_data = null |
| No encontrado en buró | appeal_needed=true, appeal_completed=false |
| Cliente subió AFP | appeal_needed=true, appeal_completed=true |
| Rechazado por agente | final_decision="rejected", rejection_reason="manual_rejection" |
| Rechazado por edad | final_decision="denied", rejection_reason="minimum_age" |
# Test con cliente que necesita apelación
curl -X GET "http://localhost/api/v2/products?customer={UUID}&amount=500" \
-H "Authorization: Bearer {TOKEN}" | jq '.error_data.application_status'
# Verificar que retorne:
{
"step": "appeal",
"final_decision": "denied",
"rejection_reason": "customer_not_found",
"appeal_needed": true,
"appeal_completed": false
}✅ Cambio seguro:
- Campos nuevos son opcionales
- N1co puede ignorar
application_statussi no lo necesita - Código actual sigue funcionando sin cambios
MPAY (30 min):
- Modificar
ProductService.phplíneas 61-67 - Modificar
ProductService.phplíneas 142-146 - Probar localmente con Postman
- Commit:
feat: add application_status to products error_data
N1co (1-2 horas):
- Actualizar
ProductErrorInfoyCreditApplicationStatusDTOs - Implementar
GetKycMessage()helper - Testing end-to-end
- Deploy a staging
| Condición | Estado en UI |
|---|---|
appeal_needed=true + appeal_completed=false |
"Sube tu AFP" |
appeal_needed=true + appeal_completed=true |
"En revisión" |
final_decision="rejected" |
"Rechazado" |
final_decision="denied" |
Ver razón específica |
final_decision="approved" |
Crédito disponible |
- MPAY:
api-laravel/app/Services/Product/ProductService.php - Modelo:
api-laravel/app/Models/EquifaxRequest.php - Enums:
api-laravel/app/Enums/Equifax/