> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pay-me.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Certificación y salida a producción

> Casos de prueba, evidencias y checklist de certificación para Pay-me Transit PinPAD.

La certificación debe validar el contrato HTTP, la interacción física con el Wiseasy P5L, la decisión de negocio y la recuperación sin doble cobro. Las tablas describen **resultados esperados**, no pruebas ejecutadas. Registra el resultado real y su evidencia antes de marcar un criterio como cumplido.

## Preparación

Antes de ejecutar casos:

* registra la versión de Pay-me PinPAD instalada;
* confirma la asociación entre sistema central, punto de atención y Wiseasy P5L;
* configura una IP estable y el puerto del terminal;
* ejecuta `GET /health` desde el sistema central;
* completa la verificación de llaves en el terminal;
* configura 110 segundos para los POST y 20 segundos para la consulta del cobro; abre una conexión por petición, sin keep-alive;
* habilita persistencia y logs sanitizados;
* acuerda referencias, montos y medios de pago de prueba.

## Casos de contrato

| Caso | Estímulo | Resultado esperado |
| - | - | - |
| Disponibilidad | `GET /health` | HTTP `200`, `status: "UP"`, `service: "Alignet ECR"`. |
| Solicitud mínima | Solo `operationNumber` y `amount` válidos | Se procesa con moneda `604` y método `CARD` predeterminados. |
| Solicitud completa | Incluye campos obligatorios, `tollData` y `additionalFields` con valores String | La autorización se procesa sin error de validación. |
| Referencia numérica | `operationNumber: "44992528"` | Se acepta como String numérico. |
| Referencia alfanumérica | `operationNumber: "CAJA-01_A"` | Se acepta: 1–64 caracteres sin espacios; no se exige que sea numérica. |
| Límites de referencia | Cadenas de 1 y 64 caracteres; vacía, de 65 o con espacios | Aceptar las dos primeras; las otras devuelven `400 BAD_REQUEST`. |
| Monto como cadena | `amount: "100"` | Se acepta como String numérico en unidades menores. |
| Monto como número JSON | `amount: 100` | Se acepta como entero positivo en unidades menores. |
| Moneda como cadena | `currency: "604"` | Se acepta como String numérico de tres dígitos. |
| Moneda como número JSON | `currency: 604` | Se acepta como número de tres dígitos. |
| Moneda ausente | No incluye `currency` | Se usa `604`. |
| Método de pago | Ejecuta solicitudes independientes con `paymentMethod: "CARD"` y `paymentMethod: "QR"` | Cada valor se acepta por separado. |
| Método de pago ausente | No incluye `paymentMethod` | Se usa `CARD`. |
| Método sin distinguir mayúsculas | `card` o `qr` en solicitudes independientes | Se acepta cada valor. |
| Métodos de pago simultáneos | Envía `CARD` y `QR` juntos, como arreglo o valor combinado | HTTP `400` con `BAD_REQUEST`. |
| Método de pago inválido | Envía un valor distinto de `CARD` o `QR` | HTTP `400` con `BAD_REQUEST`. |
| Referencia inválida | Vacía, de más de 64 caracteres o con espacios | HTTP `400` con `BAD_REQUEST`. |
| Monto inválido | Cero, negativo, decimal, no numérico o mayor de 12 dígitos | HTTP `400` con `BAD_REQUEST`. |
| Moneda inválida | No contiene exactamente tres dígitos | HTTP `400` con `BAD_REQUEST`. |
| Colisión de datos | Misma clave con valores diferentes en `tollData` y `additionalFields` | Prevalece el valor de `tollData` en los campos fusionados. |
| Campo adicional inválido | Valor que no es String | HTTP `400` con `BAD_REQUEST`. |

## Casos transaccionales

| Caso | Resultado esperado | Decisión del sistema central |
| - | - | - |
| Pago aprobado | HTTP `200`, `success: true`, `resultCode: "00"`, `result.state: "AUTORIZADO"`, sin `status` propio. | Confirmar y aplicar la regla de apertura. |
| Pago rechazado | HTTP `200`, `success: false`, código SDK distinto de `00` y `result.state` crudo de la pasarela. | No confirmar. |
| Extorno explícito CARD aprobado | HTTP `200`, `REVERSED` con `00`, `16` o `17`. | Registrar extorno confirmado o éxito equivalente. |
| Extorno pendiente | HTTP `202`; `SEARCHING`, `PROCESSING` o `PENDING`. `P22` se clasifica como `PENDING`. | Esperar `Retry-After: 2` y consultar `GET /reversals/{operationNumber}`. |
| Extorno denegado | HTTP `200`, `DENIED`. | No registrar como extornado ni reintentar automáticamente. |
| Cobro sin confirmar | HTTP `202`, `success: false`, `WAITING_RESULT`, `result: null`, sin `status`. | Seguir consultando con la misma referencia, sin otro cobro. |
| Extorno sin confirmar | HTTP `502`, `UNKNOWN`: la solicitud terminó con error. El cuerpo incluye solo `operationNumber`, `status` y `message`. | Revisar si se ejecutó el extorno; no registrar éxito ni reenviar automáticamente. |
| Método incorrecto | Usar un método no admitido en una ruta válida. | HTTP `405`, `METHOD_NOT_ALLOWED`. |
| Consulta inválida | Consultar `/payments` o `/reversals` con segmento vacío o inválido. | HTTP `400` según el error de ruta; no inferir un resultado financiero. |
| Headers de respuesta | Todas incluyen `Connection: close` y `Cache-Control: no-store`; HTTP `202` añade `Retry-After: 2`. | Comprobar headers y respetar el intervalo de consulta. |
| Espera agotada | Autorización: HTTP `202`, `success: false`, `WAITING_RESULT`, `result: null`. | Consultar, sin nuevo cobro. |
| Terminal ocupado | Otra referencia durante un cobro activo devuelve `409 POS_BUSY`. | Esperar y reintentar cuando se libere. |
| Terminal no listo | HTTP `503 POS_NOT_AVAILABLE`. | Recuperar disponibilidad antes de reintentar. |
| Referencia inexistente | La consulta del cobro devuelve `404/05`, `success: false`, `transaction: null`. | Verificar la referencia; no asumir rechazo financiero. |

## Consulta del cobro en la pasarela

Comprueba esta forma en todas las respuestas: `operationNumber`, `success`, `resultCode`, `resultMessage` y `transaction`, sin `status` propio. El GET no devuelve `202` ni estados locales.

| Caso | Resultado esperado | Decisión |
| - | - | - |
| Operación encontrada | `200/00`, `success: true`, `transaction.state` crudo | `00` significa encontrada. `AUTORIZADO` confirma aprobación; `CANCELADO` confirma cancelación del cobro. |
| Operación no encontrada | `404/05`, `success: false`, `transaction: null` | Verificar referencia y terminal; no confirmar rechazo ni habilitar otro cobro. |
| SDK no inicializado o terminal sin disponibilidad | `503/P04` o `POS_NOT_AVAILABLE`, `success: false`, `transaction: null` | Restablecer disponibilidad y consultar de nuevo. |
| Espera interna agotada | `504/SEARCH_TIMEOUT`, `success: false`, `transaction: null` | Cobro no verificado; consultar de nuevo o escalar. |
| Servicio incierto | `200/03`, `success: false`, `transaction: null` | No confirmar rechazo ni iniciar otro cobro. |
| Consulta lenta | Respuesta en aproximadamente 12–15 segundos | El timeout de ejemplo de 20 segundos permite leerla; una desconexión conserva la operación sin resolver. |

## Validación de cancelación

Aplica estos casos a CARD y QR. El servicio valida ejecución y correspondencia con la operación; el cierre visual no acredita éxito.

| Caso | Estímulo | Resultado esperado |
| - | - | - |
| Cancelación abandonada con `14/15` | Recibir `DENIED/14` o `DENIED/15`. | No reconocer cancelación exitosa; consultar el cobro original. |
| Cancelación confirmada | Cancelar una autorización activa y obtener un resultado de esa operación. | `CANCELLED` con `00`, `16` o `17`, validado por el servicio; reconocer el resultado sin consulta obligatoria. |
| Éxito equivalente de cancelación | Recibir HTTP `200`, `CANCELLED` con `16` o `17`. | Reconocer cancelación validada; conservar el código SDK sin convertirlo a `00`. |
| Cancelación no ejecutada | Simular un fallo que impida ejecutar la cancelación. | Respuesta de error, sin informar éxito; consultar el cobro original. No se anticipan HTTP, código ni mensaje del nuevo error. |
| Resultado de otra operación | Entregar al servicio un resultado que no corresponda al `operationNumber` solicitado. | Rechazarlo con una respuesta de error; no cerrar la operación como cancelada. |
| Cancelación denegada | Obtener `DENIED` con `14`, `15`, `18`, `19`, `20`, `01`, `03` u otro código SDK. | Cancelación rechazada o abandonada; recuperar el cobro original sin asumir su resultado. |
| Segunda cancelación concurrente | Enviar otro `/cancel` para la misma referencia mientras se procesa el primero. | HTTP `409`, `ERROR`, `CANCELLATION_IN_PROGRESS`; no repetir el POST. |
| Venta no encontrada | Obtener `05` o `07` del SDK en cancelación. | HTTP `404`; no inferir el resultado financiero del cobro. El cuerpo usa `status: "ERROR"`, `resultCode: "NOT_FOUND"`. |
| Timeout o incertidumbre | Perder la respuesta o recibir HTTP `202`, `PENDING` con `P22`. | Mostrar **Cancelación no confirmada**; consultar el cobro original, respetando `Retry-After: 2` en HTTP `202`. |

Comprueba que `GET /payments/{operationNumber}` informa el cobro original, no el resultado de la solicitud de cancelación. Consulta la pasarela, sin `status` propio ni HTTP `202`. Ante `200/03`, `404/05`, `503` o `504`, conserva la referencia y no habilites otro cobro. Los ciclos repiten solo el GET.

## Cancelación CARD

| Caso | Estímulo | Resultado esperado |
| - | - | - |
| Cobro aprobado tras cancelación fallida | Recuperar `200/00` con `transaction.state: "AUTORIZADO"` en el cobro original. | Evaluar un extorno independiente con `POST /reversals` y el `operationNumber` de la venta, sin duplicados. Reconocer éxito con `REVERSED` y `00`, `16` o `17`; consultar `/reversals/{operationNumber}` si no está confirmado. |

No debe generarse un extorno automático ante cualquier error de cancelación. Presenta la tarjeta cuando el PinPAD la solicite en el extorno.

## Cancelación QR

| Caso | Estímulo | Resultado esperado |
| - | - | - |
| QR pendiente | Generar el QR sin pagar. | Ya existe la transacción pendiente asociada al `operationNumber`; mostrar o escanear no confirma el pago. |
| Cancelación validada | Cancelar o inhabilitar el QR pendiente. | Reconocer `CANCELLED` con `00`, `16` o `17`, sujeto a la validación del servicio. |
| Pago aprobado tras cancelación fallida | Recuperar `200/00` con `transaction.state: "AUTORIZADO"` en el cobro original. | Conservar el pago aprobado y registrar la discrepancia. Ni `/cancel` ni refund devuelven el monto; no enviar `/reversals`. |

## Casos de idempotencia y recuperación

| Caso | Validación esperada |
| - | - |
| Misma referencia durante el proceso | El segundo envío no inicia otro cobro y devuelve `202 WAITING_RESULT`, `success: false`, `result: null`, sin `status` propio. |
| Misma referencia finalizada | Devuelve `200` con el resultado guardado sin otro cargo. |
| Otra referencia concurrente | Devuelve `409 POS_BUSY`. |
| Timeout del sistema central | El sistema central consulta la referencia original antes de aceptar otro intento. |
| Pérdida de red | Al reconectar, el sistema central consulta las operaciones abiertas. |
| Reinicio del sistema central | Recupera las referencias guardadas y consulta los cobros cuyo resultado aún no conoce. |
| Reinicio del P5L durante el cobro | Consultar la pasarela con la referencia original; no anticipar un estado local de respuesta ni concluir rechazo. |
| `202` de POST o de consulta local de extorno | Respetar `Retry-After: 2`; el GET del cobro no devuelve `202`. |
| Extorno exitoso o en curso repetido | `REVERSED` devuelve `200` guardado; `SEARCHING`/`PROCESSING`/`PENDING` devuelve `202`, sin otro intento. |
| Extorno previo `NOT_FOUND` | Otro POST devuelve `404` sin ejecutar otro intento. |
| Nuevo extorno tras `DENIED`, `UNKNOWN` o `ERROR` final | Tras consulta y decisión controlada, el POST puede ejecutar otro intento real. No activar reintento automático por incertidumbre. |

## Verificaciones de seguridad

<Check>
  El puerto del terminal no es accesible desde redes no autorizadas ni desde Internet.
</Check>

<Check>
  `operationNumber` no contiene datos personales.
</Check>

<Check>
  Las solicitudes, respuestas y evidencias no contienen PAN completo, CVV, PIN, PIN Block, pistas ni datos EMV sensibles.
</Check>

<Check>
  El acceso y la retención de logs están controlados.
</Check>

## Lista de verificación para producción

<Check>
  Cada Wiseasy P5L tiene IP fija o reserva DHCP y puerto documentado.
</Check>

<Check>
  El sistema central distingue cancelación validada de recuperación del cobro ante fallo o incertidumbre, para CARD y QR.
</Check>

<Check>
  El timeout definitivo del sistema central es mayor que la espera interna confirmada de Pay-me PinPAD.
</Check>

<Check>
  La frecuencia, el backoff y el límite de consulta están acordados.
</Check>

<Check>
  La autorización se confirma con `200`, `success: true`, `00` y `result.state: "AUTORIZADO"`; en consulta `200/00`, se interpreta `transaction.state`.
</Check>

<Check>
  La lógica de idempotencia, concurrencia, timeout y reinicio superó las pruebas.
</Check>

<Check>
  La decisión de negocio para resultados inciertos y estados propios de cancelación/extorno está aprobada.
</Check>

<Check>
  Los datos del peaje permanecen en `tollData`; las claves complementarias, monedas y métodos están confirmados.
</Check>

<Check>
  Las clasificaciones de cobro, cancelación y extorno respetan los códigos SDK sin normalizaciones adicionales.
</Check>

<Check>
  Existen monitoreo, contactos de soporte y un procedimiento de escalamiento.
</Check>

<Check>
  El integrador y Alignet aprobaron las evidencias de integración y UAT.
</Check>

<Check>
  Se valida el extorno CARD independiente tras cancelación fallida y se conservan pagos QR aprobados sin devolución.
</Check>

## Criterio de aceptación

No habilites producción mientras exista riesgo de interpretar una consulta incierta, un timeout o `404/05` como rechazo y generar un cobro adicional. La salida requiere demostrar que toda operación incierta conserva su referencia original hasta resolverse o escalarse.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.