El flujo del pago
Análisis Funcional: Pago con documentos — parte 2 de 5. ← Resumen del alcance · Índice · Siguiente: Datos y documentos →
El orden de este flujo no lo inventamos: sale casi textual de la hoja Módulo caja del Excel de CIS-EC, filas 5 a 14. Lo que sigue es esa secuencia, con lo que la app hace en cada paso y por qué.
1.1 La ventanilla, de punta a punta
Los pasos 1 y 2 —identificación física, cotejo contra la carta y el rostro— ocurren fuera de la app. La app no los puede verificar del todo; desde ADR-008 (2026-09-12), sí deja de confiar ciegamente en que ocurrieron: exige que esos mismos documentos (Carta + identificación) se suban antes de confirmar el pago (paso G), no después. El resto de los soportes (papeleta, selfie, los de transferencia) siguen subiéndose después, sin bloquear nada — ver §1.4 más abajo.
1.2 El PIN es el MTCN, y por eso no se pide
Es la definición que más vueltas dio, y termina en una función que se construyó y se sacó. Vale dejar escrito el recorrido completo, porque el razonamiento sigue siendo válido para cualquiera que proponga volver a agregarla.
El análisis del material de CIS-EC (doc/plans/2026-08-07-analisis-definiciones-cis-ec.md, §3.6) leyó la carta al beneficiario y concluyó que el "PIN de Pago" era un control de seguridad separado del MTCN, que no teníamos modelado. Sobre esa lectura, el primer borrador del diseño técnico agregaba una columna pin a transactions.
Carlos lo corrigió el 2026-08-07: no son dos datos, es uno solo impreso dos veces.
Cuando llega una nota de Soterex, la app genera el MTCN y se lo responde. Soterex lo imprime en la carta que le manda al beneficiario, donde figura en dos renglones:
MTCN: C-5436014032
Payment PIN: 5436014032El mismo número, con y sin el prefijo C-. Por eso no se agregó ninguna columna: guardar el PIN aparte habría duplicado un dato que ya teníamos, con la garantía de que en algún momento los dos se desincronizan.
Por qué se sacó la verificación
Con esa corrección, se implementó igual un paso de verificación: el operador tipeaba el PIN y el botón de pagar no se habilitaba hasta que coincidiera con el MTCN.
Carlos lo probó el 2026-08-10 y encontró el problema: buscando por número de transacción, la pantalla le pedía volver a tipear el número que acababa de tipear. Un paso que no verifica nada.
El argumento con el que se había defendido era la búsqueda por nombre: por ese camino el operador nunca vio el MTCN antes —lo obtuvo del resultado— así que pedir el PIN confirmaba que la persona trae la carta de esa transacción y no de otra a nombre parecido. Protegía contra pagarle al homónimo equivocado, no contra un operador deshonesto (ese ya tiene el número en pantalla).
Ese argumento sólo se sostiene en el camino de búsqueda por nombre. En el otro —que es el habitual, porque el cliente llega con la carta en la mano— el control era puro rozamiento. Y un paso obligatorio que en el caso más frecuente no aporta nada es un paso que la gente aprende a completar en piloto automático, lo que además lo debilita donde sí serviría.
Así que se sacó entero: el endpoint, el método del modelo, el bloque de la pantalla y sus tests.
Si vuelve a discutirse, la forma correcta no es reponerlo como estaba: sería pedirlo sólo cuando el resultado vino de una búsqueda por nombre, que es el único caso donde verifica algo. Con esa forma el operador que llega por MTCN no ve el paso, y el que llega por nombre sí. Nadie lo pidió todavía; queda anotado por si el escenario del homónimo aparece en la operación real.
1.3 Una transacción ya pagada se muestra igual
Hasta E5a, el buscador de la pantalla Pago descartaba lo que no estuviera ACCEPTED. El material del cliente pide lo contrario:
"Si la tx está pagada, corrobora soportes del pago para eliminar posibilidades de error" — hoja Módulo caja, fila 11.
Tiene sentido operativo: el caso más común es un cliente que vuelve porque falta un papel, o un supervisor revisando un pago del día anterior. Que la transacción "desaparezca" apenas se paga convierte esa revisión en imposible.
Con la transacción en PAID, la pantalla muestra el formulario de datos y la lista de soportes en lugar de las acciones de pago.
1.4 Solo Carta + identificación bloquean el pago
Hasta el 2026-09-12 esta sección se llamaba "El pago no se bloquea por documentos faltantes", sin excepciones, y era la decisión de diseño más importante del paquete. Sigue siéndolo — lo que cambió es que ahora tiene tres excepciones puntuales, pedidas por Carlos y registradas en ADR-008: Carta de Soterex, Identificación (anverso) e Identificación (reverso) tienen que estar subidas para poder confirmar el pago. Todo lo demás —selfie, papeleta, los cuatro de transferencia, y los 11 campos de datosPago()— sigue exactamente igual que antes: no bloquea nada.
La razón original no cambió, y sigue siendo por qué el resto no bloquea: cuando el operador llega al paso de subir el resto de los escaneos, la plata ya salió del cajón. El cliente la contó, firmó la papeleta y se fue. Si la app se negara a registrar el pago porque falta, por ejemplo, la papeleta firmada, el resultado no sería "no se pagó": sería una caja con menos efectivo del que el sistema dice que tiene, y una transacción que figura disponible para cobrar por segunda vez.
Frenar el registro no evita el problema, lo convierte en un descuadre. La app tiene que reflejar lo que pasó en el mostrador, no lo que hubiera preferido que pasara.
Carta e identificación son distintas por una razón física, no de alcance: son exactamente lo que el operador coteja contra el beneficiario antes de entregarle la plata (§1.1, pasos 1-2), y ese cotejo hoy ocurre fuera de la app sin dejar rastro verificable hasta que alguien sube el escaneo — a veces mucho después, o nunca. Exigirlo antes de pagar no es agregarle fricción a un paso que ya existe en el mostrador: es hacerlo auditable. La Papeleta firmada, en cambio, no podría exigirse antes de pagar aunque quisiéramos: es el comprobante que el cliente firma después de recibir la plata, no puede existir todavía en ese momento.
El cliente había pedido la no-bloqueante en estos términos —"podría evaluarse la posibilidad de que la tx se quede pagada pero incompleta, si todos los soportes no se han guardado", hoja Módulo caja fila 14— y el diseño técnico de E5a lo adoptó tal cual para los nueve tipos. ADR-008 no lo contradice: separa "verificar identidad antes de pagar" (nuevo) de "completar el legajo después de pagar" (sin cambios).
La misma lógica de no bloquear se sigue aplicando a los datos: datosPago() no exige ningún campo, todos son nullable, y se pueden guardar antes o después del pago (TransactionController.php:461-481). Lo dice el propio comentario del método: "la plata ya se entregó en el mostrador".
Lo que sí hace la app, para el resto de los documentos, es no dejar que se olvide: la transacción queda marcada como incompleta, con la lista exacta de lo que falta, visible en la ficha y filtrable en el listado. Ver §2.4.
1.5 La secuencia completa, con actores
Notar el orden real: el pago ocurre antes que la carga de datos y del resto de los documentos, pero después de Carta + identificación (ADR-008, 2026-09-12). En la pantalla, el formulario de datos y la lista completa de soportes recién aparecen cuando la transacción está PAID (PagoView.vue) — pero el bloque de Carta + identificación aparece antes, mientras sigue ACCEPTED. Es coherente con §1.4: primero se verifica identidad, después se registra lo irreversible, después se completa el resto del legajo.
1.6 Permisos y alcance
Sin módulo de permisos nuevo: los documentos y los datos del pago son parte de la transacción.
Sobre el descuento del pago. El paso "descuenta" toca tres saldos con importes distintos —Holding
amount + fee, Paísamount, Cajaamounten la moneda de pago— pero valida uno solo: la caja del operador. El detalle está en Modelo de saldos §1.6.
| Acción | Permiso | Alcance |
|---|---|---|
| Completar datos del pago | transacciones E | País |
| Subir un soporte a una transacción ya resuelta | transacciones E | País y PDV |
Subir Carta/identificación a una ACCEPTED sin PDV todavía | transacciones E | País (bolsa compartida — ver más abajo) |
| Listar o descargar soportes | transacciones E | País y PDV (una vez asignado) |
| Emitir la papeleta | transacciones E | País |
| Borrar un soporte | — | No existe |
El acotamiento por PDV sobre los documentos es más estricto que el de la transacción en sí, y a propósito. En el listado general, una transacción ACCEPTED es bolsa compartida del país: cualquier PDV la puede tomar. Los documentos, en cambio, están siempre acotados por PDV — pero desde ADR-008 (2026-09-12) ya no puede asumirse que "siempre" significa "solo sobre transacciones ya resueltas": Carta e identificación se suben mientras la transacción puede seguir siendo ACCEPTED, así que hay que distinguir tres casos:
// backend/app/Http/Controllers/TransactionDocumentController.php — authorizeScope()
// ACCEPTED sin PDV asignado todavía: bolsa compartida, mismo criterio que
// TransactionController::authorizeScope() para la transacción misma.
if ($transaction->status === 'ACCEPTED' && $transaction->station_id === null) {
return $user;
}
// Ya resuelta, o ACCEPTED con PDV ya asignado por un upload anterior:
// acotamiento de siempre.
$stationIds = $this->permissions->allowedStationIds($user, self::MODULE, $action, $transaction->country_id);
if ($stationIds !== null && ! in_array($transaction->station_id, $stationIds, true)) {
abort(404);
}El primer documento subido sobre una ACCEPTED sin PDV le asigna el PDV del operador que lo sube (misma resolución que ya usa pay()) — desde ese momento, los documentos de esa transacción quedan acotados a ese PDV, aunque la transacción en sí siga siendo bolsa compartida hasta que se resuelva. pay()/cancel() no reasignan el PDV si un documento ya lo hizo, para no dejar esos documentos huérfanos si termina resolviéndola un operador de otro PDV.
Antes de ADR-008, este acotamiento siempre asumía "transacción ya resuelta" — es el mismo agujero que se encontró y corrigió en Caja el 2026-08-06 (ver Módulo Caja, Preguntas Abiertas #5): un permiso que decía acotar por PDV y validaba solo a nivel país. Acá se escribió cerrado desde el principio para transacciones resueltas, y ADR-008 extendió el mismo criterio al caso nuevo.
Y se responde 404, no 403, cuando el usuario no alcanza la transacción: confirmar que existe ya sería información (TransactionDocumentController.php).
Análisis Funcional: Pago con documentos — parte 2 de 5. ← Resumen del alcance · Índice · Siguiente: Datos y documentos →

