Cheaf Docs
Orders/Cancelación de Pedidos

Modal de Cancelación de Pedidos - CancelOrderModal

Documentación completa del componente [CancelOrderModal] que gestiona la experiencia de usuario al cancelar pedidos en la aplicación móvil. El sistema implementa políticas diferenciadas por país (Chile/Argentina/México con CANCELLATION-2-2 vs otros países con sistema anti-fraude), evalúa condiciones de alto valor (HIGH_BASKET_SIZE), y determina qué mensaje mostrar al usuario basado en su historial de cancelaciones, método de pago, y configuraciones de fraude del backend.

Modal de Cancelación de Pedidos - CancelOrderModal

Documentación completa del componente CancelOrderModal que gestiona la experiencia de usuario al cancelar pedidos en la aplicación móvil. El sistema implementa políticas diferenciadas por país (Chile/Argentina con CANCELLATION-2-2 vs otros países con sistema anti-fraude), evalúa condiciones de alto valor (HIGH_BASKET_SIZE), y determina qué mensaje mostrar al usuario basado en su historial de cancelaciones, método de pago, y configuraciones de fraude del backend.

¿Qué es este sistema?

Cuando un usuario intenta cancelar un pedido, la aplicación muestra diferentes mensajes dependiendo de las condiciones técnicas evaluadas por el sistema de fraude y políticas de cancelación.


CHILE Y ARGENTINA ÚNICAMENTE - Política CANCELLATION-2-2

🟢 Mensaje Normal (DefaultMessageChile)

Lo que ve el usuario: "El monto de tu pedido se te devolverá automáticamente en créditos Cheaf"

Cuándo aparece:

  • País es Chile ('CL') o Argentina ('AR') únicamente
  • Y NO se cumple la condición de CANCELLATION-2-2

Condición técnicacountryCode === 'CL' || countryCode === 'AR' Y condiciones normales

Devolución de créditoswillRefundCredits = true

Origen de datosauthState.ipDetails.countryCode

🔴 Cancelación con Costo (CancellationChileMessage)

Lo que ve el usuario: "Por tratarse de una cancelación tardía, se te cobrará el monto total del pedido"

Cuándo aparece:

  • País es Chile o Argentina únicamente
  • policyType === 'CANCELLATION-2-2'
  • isApplicable === true

Condiciones específicas de la Regla 2-2 (Backend):

  1. Condiciones Temporales (AMBAS deben cumplirse):
    • Faltan menos de 2 horas para el cierrenow_time_plus_2 > collect_before > now.time()
    • El pedido fue creado hace más de 1 horacreated_at < now_time (donde now_time = now - 1 hora)
  2. Condiciones Adicionales para Penalización:
    • Monto altoorder.total() >= basket_size (≥200 configurado en BASKET_SIZE)
    • Pago en efectivoorder.payment_type == "1"
    • Política aplicableis_applicable === true

Consecuencias:

  • Se genera deuda por el costo total del pedido
  • La orden se marca como STATUS_LATE_CANCELLED
  • Sin devolución de créditoswillRefundCredits = false

Condición técnicacountryCode === 'CL' || countryCode === 'AR' Y policyType === 'CANCELLATION-2-2' && isApplicable === true

Origen de datosorderPolicies.cancellationPolicy (evaluado por el backend según reglas temporales)


TODOS LOS DEMÁS PAÍSES - Sistema Anti-Fraude

🚫 Usuario Baneado (BannedMessage)

Lo que ve el usuario: "Has cancelado muchos pedidos en los últimos 90 días, si cancelas este pedido no recibirás devolución"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • Y (isBanned === true O fraudStatus.details.warningCancellationPolicy === 'inflicts')

Qué significa 'inflicts': El backend asigna este valor cuando:

  • Órdenes efectivas ≤ 8 Y cancelaciones = 5, O
  • Órdenes efectivas > 8 Y cancelaciones ≥ 5 Y tasa de cancelación ≥ 25%

Condición técnica!(countryCode === 'CL' || countryCode === 'AR') Y (isBanned || warningCancellationPolicy === 'inflicts')

Devolución de créditoswillRefundCredits = false

Origen de datos:

  • isBannedauthState.cancellationBanned
  • warningCancellationPolicy: API /orders/cancel-order/${orderId}/check_for_fraud/

⚠️ Advertencia de Fraude (FraudWarningMessage)

Lo que ve el usuario: "Has cancelado muchos pedidos, no podrás pagar en sucursal. Tus créditos se te devolverán en un periodo de 24 horas"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • HIGH_BASKET_SIZE === false
  • fraudStatus.isFraudAttempt === true
  • usePromos === true (tiene cupones o créditos > 0)
  • fraudStatus.details.warningCancellationPolicy === 'warning'

Qué significa 'warning': El backend asigna este valor cuando:

  • Cancelaciones = 4 (una menos que el límite de 5), O
  • inflicts_cancellation_policy = true Y last_opportunity_cancellation_policy = true

Condición técnica!(countryCode === 'CL' || countryCode === 'AR') Y !HIGH_BASKET_SIZE && isFraudAttempt && usePromos && warningCancellationPolicy === 'warning'

Devolución de créditoswillRefundCredits = true (con retraso de 24h)

Origen de datos: API /orders/cancel-order/${orderId}/check_for_fraud/ + usePromos calculado como coupon || credits > 0

🔍 Fraude Simple (FraudMessage)

Lo que ve el usuario: "Tus créditos o cupones se te devolverán en un periodo de 24 horas"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • HIGH_BASKET_SIZE === false
  • fraudStatus.isFraudAttempt === true
  • usePromos === true
  • fraudStatus.details.warningCancellationPolicy !== 'warning'

Condición técnica!(countryCode === 'CL' || countryCode === 'AR') Y !HIGH_BASKET_SIZE && isFraudAttempt && usePromos && warningCancellationPolicy !== 'warning'

Devolución de créditoswillRefundCredits = true (con retraso de 24h)

Origen de datos: API /orders/cancel-order/${orderId}/check_for_fraud/

📦⚠️ Alto Valor + Advertencia (HighBasketWarningMessage)

Lo que ve el usuario: "La tienda ha reservado este pedido para ti. Si decides cancelar [generarás un adeudo/no podremos realizar un reembolso]"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • fraudStatus.details.userCancelledOrdersCount >= 1
  • fraudStatus.details.warningCancellationPolicy === 'not_inflicts'
  • HIGH_BASKET_SIZE === true

Qué significa 'not_inflicts': El backend asigna este valor cuando el usuario NO cumple las condiciones para 'inflicts' o 'warning' (es decir, está en rango normal pero con algunas cancelaciones)

Condición técnica!(countryCode === 'CL' || countryCode === 'AR') Y userCancelledOrdersCount >= 1 && warningCancellationPolicy === 'not_inflicts' && HIGH_BASKET_SIZE

Devolución de créditoswillRefundCredits = false

Origen de datos: API /orders/cancel-order/${orderId}/check_for_fraud/ + cálculo HIGH_BASKET_SIZE

⚠️ Primera Advertencia (WarningMessage)

Lo que ve el usuario: "Si tu alta tasa de cancelación persiste perderás la posibilidad de pagar en sucursal"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • fraudStatus.details.userCancelledOrdersCount >= 1
  • fraudStatus.details.warningCancellationPolicy === 'not_inflicts'
  • HIGH_BASKET_SIZE === false

Condición técnica!(countryCode === 'CL' || countryCode === 'AR') Y userCancelledOrdersCount >= 1 && warningCancellationPolicy === 'not_inflicts' && !HIGH_BASKET_SIZE

Devolución de créditoswillRefundCredits = true

Origen de datos: API /orders/cancel-order/${orderId}/check_for_fraud/

🚨📦 Pre-Baneo + Alto Valor (HighBasketPreBannedMessage)

Lo que ve el usuario: "Has cancelado muchos pedidos, no podrás pagar en sucursal. Si decides cancelar [generarás un adeudo/no podremos realizar un reembolso]"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • fraudStatus.details.warningCancellationPolicy === 'warning'
  • HIGH_BASKET_SIZE === true

Condición técnica!(countryCode === 'CL' || countryCode === 'AR') Y warningCancellationPolicy === 'warning' && HIGH_BASKET_SIZE

Devolución de créditoswillRefundCredits = false

Origen de datos: API /orders/cancel-order/${orderId}/check_for_fraud/ + cálculo HIGH_BASKET_SIZE

🚨 Pre-Baneo (PreBannedMessage)

Lo que ve el usuario: "Has cancelado muchos pedidos en los últimos 90 días, si cancelas este pedido no podrás pagar en sucursal"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • fraudStatus.details.warningCancellationPolicy === 'warning'
  • HIGH_BASKET_SIZE === false

Condición técnica!(countryCode === 'CL' || countryCode === 'AR') Y warningCancellationPolicy === 'warning' && !HIGH_BASKET_SIZE

Devolución de créditoswillRefundCredits = true

Origen de datos: API /orders/cancel-order/${orderId}/check_for_fraud/

📦 Solo Alto Valor (HighBasketMessage)

Lo que ve el usuario: "La tienda ha reservado este pedido para ti. Si decides cancelar [generarás un adeudo/no podremos realizar un reembolso]"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • HIGH_BASKET_SIZE === true
  • Y ninguna de las condiciones anteriores se cumple

Condición técnica!(countryCode === 'CL' || countryCode === 'AR') Y solo HIGH_BASKET_SIZE === true

Devolución de créditoswillRefundCredits = false

Origen de datos: Cálculo HIGH_BASKET_SIZE en el componente

🟢 Mensaje Normal (DefaultMessage)

Lo que ve el usuario: "Si pagaste con tarjeta, créditos o cupones, se te devolverá el monto de tu pedido en créditos"

Cuándo aparece:

  • País NO es Chile ni Argentina
  • Y ninguna de las condiciones anteriores se cumple

Condición técnica: Caso por defecto para países que no son CL/AR

Devolución de créditoswillRefundCredits = true


Variables Clave del Sistema

Política CANCELLATION-2-2

Países que aplican en Frontend:

  • Chile (CL) únicamente
  • Argentina (AR) únicamente

Nota: México (MX) tiene la política en backend pero NO se maneja en este componente frontend

Otros países: Usan sistema anti-fraude por defecto

Significado de "2-2":

  • 2 horas: Tiempo límite antes del cierre de la tienda
  • 2 horas: Tiempo total (1 hora mínima del pedido + 1 hora de buffer)

Condiciones Backend para Activación:


# Condiciones temporales (AMBAS deben cumplirse)
now_time_plus_2 > collect_before > now.time()  # Menos de 2h para cierre
created_at < now_time  # Pedido creado hace más de 1h

# Condiciones adicionales para penalización
order.total() >= basket_size  # >= 200 (BASKET_SIZE)
order.payment_type == "1"     # Pago en efectivo
is_applicable == true         # Política activa

HIGH_BASKET_SIZE (Frontend)

Cálculo técnico:

HIGH_BASKET_SIZE = cantCancelByTime && subtotal >= basketSize;
cantCancelByTime =
  now + 2 >= closeTime ? deltaTimestamp < 60 * 60 * 1000 : false;

Componentes:

  • subtotal: Monto total del pedido (calculado en OrderDetail)
  • basketSize: Límite configurado (default: 190, desde authState.fraudSettings.basketSize)
  • closeTime: Hora de cierre de la tienda (schedule.collectBefore)
  • deltaTimestamp: Tiempo transcurrido desde creación del pedido
  • now + 2: Hora actual + 2 horas de buffer

willRefundCredits (Lógica de Devolución)

Basado en el código del componente:

// Chile/Argentina únicamente
if (countryCode === "CL" || countryCode === "AR") {
  if (policyType === "CANCELLATION-2-2" && isApplicable) return false;
  else return true;
}
// Otros países
else if (isBanned || warningCancellationPolicy === "inflicts") return false;
else if (!HIGH_BASKET_SIZE && isFraudAttempt && usePromos)
  return true; // Ambos casos
else if (
  userCancelledOrdersCount >= 1 &&
  warningCancellationPolicy === "not_inflicts"
) {
  if (HIGH_BASKET_SIZE) return false;
  else return true;
} else if (warningCancellationPolicy === "warning") {
  if (HIGH_BASKET_SIZE) return false;
  else return true;
} else if (HIGH_BASKET_SIZE) return false;
return true; // Caso por defecto

warningCancellationPolicy (Backend)

Calculado por el sistema backend basado en configuración AWS S3:

'inflicts' (Política se aplica - más severo):

  • Órdenes efectivas ≤ 8 Y cancelaciones = 5
  • Órdenes efectivas > 8 Y cancelaciones ≥ 5 Y tasa ≥ 25%

'warning' (Advertencia - estado intermedio):

  • Cancelaciones = 4 (una menos que el límite)
  • inflicts_cancellation_policy = true Y last_opportunity_cancellation_policy = true

'not_inflicts' (No aplica política - normal):

  • No cumple condiciones anteriores

Configuración AWS S3

Archivos3://cheaf-configuration/fraud_cancellation_policy/fraud_and_cancellation_configuration.json

Variables clave:

  • EFFECTIVE_ORDERS_CANCELLATION_POLICY8 (órdenes efectivas mínimas)
  • CANCEL_ORDERS_CANCELLATION_POLICY5 (límite de cancelaciones)
  • CANCEL_RATE_CANCELLATION_POLICY25% (tasa límite de cancelación)
  • DAYS_RANGE_CANCELLATION_POLICY90 (días para evaluar política)
  • BASKET_SIZE200 (límite para CANCELLATION-2-2 en backend)

Flujo Completo del Sistema

  1. Usuario hace clic "Cancelar" → Se abre CancelOrderModal
  2. Frontend consulta → /orders/cancel-order/${orderId}/check_for_fraud/
  3. Backend ejecuta → CancellationOrdersPolicy con configuración AWS S3
  4. Backend calcula:
    • Cancelaciones en últimos 90 días (userCancelledOrdersCount)
    • Tasa de cancelación vs órdenes efectivas
    • Asigna warningCancellationPolicy según reglas
    • Evalúa isFraudAttempt si hay promociones
  5. Frontend evalúa:
    • Primero: País (Chile/Argentina vs otros)
    • Si otros paísesHIGH_BASKET_SIZE (timing + monto)
    • Si otros países: Combinación de variables de fraude
  6. Se muestra mensaje correspondiente según matriz de decisión

Referencia Técnica para Desarrolladores

// Flujo de decisión principal en renderMessage() - líneas 689-732
if (countryCode === "CL" || countryCode === "AR") {
  if (policyType === "CANCELLATION-2-2" && isApplicable) {
    return <CancellationChileMessage />; // Sin devolución
  }
  return <DefaultMessageChile />; // Con devolución
} else if (
  isBanned ||
  fraudStatus?.details?.warningCancellationPolicy === "inflicts"
) {
  return <BannedMessage />; // Sin devolución
} else if (!HIGH_BASKET_SIZE && fraudStatus?.isFraudAttempt && usePromos) {
  if (fraudStatus?.details?.warningCancellationPolicy === "warning") {
    return <FraudWarningMessage />; // Con devolución (24h)
  } else {
    return <FraudMessage />; // Con devolución (24h)
  }
} else if (
  fraudStatus?.details?.userCancelledOrdersCount >= 1 &&
  fraudStatus?.details?.warningCancellationPolicy === "not_inflicts"
) {
  if (HIGH_BASKET_SIZE) {
    return <HighBasketWarningMessage payWithCash={payWithCash} />; // Sin devolución
  } else {
    return <WarningMessage />; // Con devolución
  }
} else if (fraudStatus?.details?.warningCancellationPolicy === "warning") {
  if (HIGH_BASKET_SIZE) {
    return <HighBasketPreBannedMessage payWithCash={payWithCash} />; // Sin devolución
  } else {
    return <PreBannedMessage />; // Con devolución
  }
} else if (HIGH_BASKET_SIZE) {
  return <HighBasketMessage payWithCash={payWithCash} />; // Sin devolución
}
return <DefaultMessage />; // Con devolución - caso por defecto