Contrato común de entrada y salida
Mensajería remota del ciclo de Payment Order
Diseño objetivo del módulo. Su propósito es permitir que un sistema externo siga remotamente una Payment Order desde su creación hasta su resultado terminal. El lifecycle canónico pertenece a la Payment Order; el Checkout Web es el canal o sesión que la envuelve.
Estado de implementación: payment_order.created, payment_order.presented y payment_order.terminal están implementados y certificados como callbacks productivos del Web Checkout QR.
Contrato terminal público: payment_order.terminal usa únicamente result = pagado | cancelado | timeout.
1. payment_order.created
created— creada correctamente.rejected— rechazada por validación o regla de negocio.duplicate— operación equivalente ya existente por idempotencia.failed— no fue posible crearla.
{
"event": "payment_order.created",
"result": "created",
"message": "Payment Order created successfully.",
"external_transaction_id": "TX-12345",
"payment_order_uid": "po_...",
"checkout_uid": "chk_...",
"amount": "125.50",
"currency": "PEN",
"payment_method": "qr",
"channel": "web",
"occurred_at": "2026-08-28T03:39:50Z"
}
2. payment_order.presented
presented— presentado correctamente.presentation_failed— no pudo presentarse.expired_before_presentation— expiró antes de presentarse.cancelled_before_presentation— fue cancelada antes de presentarse.
{
"event": "payment_order.presented",
"result": "presented",
"message": "Payment method presented to customer.",
"external_transaction_id": "TX-12345",
"payment_order_uid": "po_...",
"payment_method": "qr",
"channel": "web",
"presented_at": "2026-08-28T03:40:02Z",
"occurred_at": "2026-08-28T03:40:02Z"
}
3. payment_order.terminal
pagado— pago confirmado.cancelado— operación cancelada.timeout— venció sin pago confirmado.timeout— la operación agotó su ventana operativa sin confirmación.- No existe
failedcomo cuarto resultado comercial terminal público. rejected— reservado para medios futuros que expongan rechazo terminal explícito.
{
"event": "payment_order.terminal",
"result": "pagado",
"message": "Payment confirmed.",
"external_transaction_id": "TX-12345",
"payment_order_uid": "po_...",
"amount": "125.50",
"currency": "PEN",
"payment_method": "qr",
"channel": "web",
"presented_at": "2026-08-28T03:40:02Z",
"paid_at": "2026-08-28T03:41:18Z",
"occurred_at": "2026-08-28T03:41:18Z"
}
Datos comunes de correlación
Los mensajes deben incluir, cuando corresponda, external_transaction_id, payment_order_uid, checkout_uid, amount, currency, payment_method, channel y occurred_at. Según el evento también pueden incluir checkout_created_at, presented_at, paid_at, timeout_seconds, result y message.
Flujo conceptual: payment_order.created → payment_order.presented → payment_order.terminal.
Contrato vigente de Web Checkout
POST /v1/checkouts requiere:
external_transaction_id: identificador de la transacción en el sistema del comercio.amount: monto.currency: moneda ISO de 3 letras, por ejemploPEN.
Campos opcionales:
payment_method: por defectoqr.payer.first_nameypayer.last_name: si el comercio conoce el nombre del pagador/cliente.timeout_seconds: vigencia solicitada por el comercio.
YUPY deriva internamente channel=web y resuelve client, counter, location, instrument y policy. Para correlación comercial del Web Checkout use external_transaction_id; source_reference no debe usarse como un segundo identificador comercial.
Hoy, cuando el comercio envía timeout_seconds, ese valor reemplaza el timeout fijo de policy para esa operación. Cuando exista el motor automático de cálculo de timeout, el valor calculado por YUPY tendrá precedencia.
{
"external_transaction_id": "TX-12345",
"amount": "125.50",
"currency": "PEN",
"payment_method": "qr",
"payer": {
"first_name": "Juan",
"last_name": "Perez"
},
"timeout_seconds": 240
}
Respuesta inmediata HTTP 201
{
"external_transaction_id": "TX-12345",
"checkout_uid": "chk_...",
"checkout_token": "cks_...",
"checkout_url": "https://api.yupy.us/v1/checkouts/browser#token=...",
"payment_order_uid": "po_...",
"status": "pending",
"amount": "125.50",
"currency": "PEN",
"payment_method": "qr",
"channel": "web",
"checkout_created_at": "2026-08-28T03:39:50Z",
"timeout_seconds": 240,
"delivery": {}
}
Esta respuesta confirma la creación del Checkout y de la Payment Order asociada. Las notificaciones posteriores de estado se documentan por separado.
Estado documental: Propuesto.
Este documento define el contrato público común para crear y seguir una intención de cobro en YUPY. La API pública no expone el contrato interno del Payment Order Orchestrator. El integrador envía la intención de cobro al YUPY API Engine; YUPY autentica al cliente, valida e idempotentiza la solicitud, normaliza el contrato y resuelve internamente el contexto operativo necesario antes de crear la Payment Order.
Principio contractual
Toda operación de cobro en YUPY se materializa internamente como una Payment Order. El caller selecciona el medio de pago requerido; YUPY resuelve el Counter, la policy y el instrumento concreto, prepara su presentación y mantiene la trazabilidad de la orden hasta su cierre. La exclusividad o capacidad runtime depende del tipo de Counter y es administrada internamente por YUPY.
Integrador
↓
YUPY API Engine
↓
autentica
identifica Cliente
valida request
controla Idempotency-Key
normaliza contrato
resuelve contexto interno
↓
Adapter del origen
↓
Payment Order Orchestrator
API pública ≠ contrato interno del Orchestrator. Campos como client_id, counter_id, QR ID, WhatsApp Account ID, Agencia o Reconciliation Target pertenecen a la infraestructura interna de YUPY y no forman parte del request público común.
Cabeceras HTTP
Las operaciones de creación deben usar:
Authorization: Bearer <token>
Idempotency-Key: <clave-unica-del-intento-http>
Authorization autentica al integrador. Idempotency-Key controla reintentos técnicos y duplicados HTTP. No reemplaza la identidad comercial del sistema origen.
Request común de creación para Web Checkout
El contrato productivo de Web Checkout crea la intención mediante POST /v1/checkouts. El caller expresa el medio de pago; YUPY resuelve la infraestructura interna aplicable.
{
"external_transaction_id": "ORDER-10482",
"amount": "85.50",
"currency": "PEN",
"payment_method": "qr",
"payer": {
"first_name": "María",
"last_name": "Ramos"
},
"business_context": {
"industry": "ground_transport",
"destination": "Arequipa",
"route": "Lima-Arequipa"
}
}
Campos mínimos de Web Checkout
| Campo | Requerido | Descripción |
|---|---|---|
external_transaction_id |
Sí | Identidad comercial de la operación en el sistema origen. |
amount |
Sí | Monto esperado. |
currency |
Sí | Moneda de la operación, por ejemplo PEN. |
Body mínimo de Web Checkout: external_transaction_id, amount y currency. payment_method es opcional y actualmente usa qr por defecto.
payment_method |
No | Opcional en Web Checkout; por defecto qr. Si se envía, selecciona la familia del medio solicitado dentro de las opciones públicas habilitadas. |
El caller selecciona el medio, no el instrumento interno
"payment_method": "qr"
El caller de Web Checkout no envía payment_instrument_type, instrument_id, QR ID, counter_id ni policy_config. YUPY deriva el cliente autenticado, resuelve el Counter virtual Web canónico y aplica la policy correspondiente a cliente + Counter + canal + medio de pago.
El instrumento QR concreto puede variar por cliente y configuración. Yape y Plin son mecanismos con los que el pagador puede utilizar un QR compatible; no forman parte del request como instrumentos administrados separados.
Identidades: tres conceptos separados
| Identificador | Quién lo genera | Uso |
|---|---|---|
external_transaction_id |
Sistema del cliente | Identidad comercial en el sistema origen. |
Idempotency-Key |
Caller | Control de reintentos técnicos de la operación HTTP/API. |
payment_order_uid |
YUPY | Identidad canónica de la Payment Order de punta a punta. |
La misma Idempotency-Key con el mismo payload debe representar el mismo intento técnico. external_transaction_id continúa siendo la referencia comercial externa. YUPY devuelve payment_order_uid como identificador canónico de la orden.
Buyer
buyer no es universalmente obligatorio. Para QR BBVA se recomienda enviar al menos:
"buyer": {
"first_name": "María",
"last_name": "Ramos"
}
Estos datos pueden ayudar posteriormente en correlación, conciliación o atención operativa. Los flujos que permitan comprador anónimo pueden omitirlos según su contrato específico.
Contexto del negocio
Los datos propios de cada industria no deben contaminar el núcleo universal de Payment Orders. Deben viajar dentro de business_context.
"business_context": {
"industry": "ground_transport",
"destination": "Arequipa",
"route": "Lima-Arequipa",
"seat": "12A"
}
Otros sectores pueden utilizar claves equivalentes, por ejemplo mesa, habitación, reserva, sede o referencia de atención.
Campos internos que el caller no envía
El API Engine y los adapters resuelven internamente, según el origen:
client_id
source_system
delivery_channel
request_uid / correlación técnica
counter_id
policy aplicable
instrumento concreto
destino de entrega
configuración de conciliación
En Web Checkout, el integrador no selecciona el Counter. YUPY resuelve el Counter virtual Web canónico del cliente y el Payment Order Orchestrator administra su capacidad/concurrency runtime. No existe un pool público de Counters que el integrador deba recorrer o administrar.
Respuesta común
La respuesta pública debe mantener una separación clara entre identidad, lifecycle y datos efectivos resueltos por YUPY. Como mínimo, una creación aceptada devuelve el identificador canónico:
{
"payment_order_uid": "pay_01JXYZ...",
"external_transaction_id": "ORDER-10482",
"payment_method": "qr",
"received_at": "2026-08-09T20:15:00-05:00",
"expires_at": "2026-08-09T20:20:00-05:00"
}
Los objetos específicos de presentación, checkout, evidencia, conciliación o canal pueden extender esta respuesta en sus contratos especializados. La API pública no expone identificadores internos de infraestructura salvo que exista una razón contractual explícita.
Timestamps y medición
Los tiempos tienen responsabilidades distintas:
| Timestamp | Responsable | Significado |
|---|---|---|
requested_at |
Caller | Momento en que el origen pidió iniciar el cobro. |
received_at |
YUPY | Momento en que YUPY recibió la solicitud. |
order_created_at |
YUPY | Creación de la Payment Order. |
instrument_resolved_at |
YUPY | Resolución del instrumento concreto. |
delivery_prepared_at |
YUPY | Payload o instrucción listos para el canal. |
presented_at |
Canal | La instrucción de pago fue realmente presentada al pagador. |
confirmed_at |
Conciliación | Momento en que el pago quedó confirmado financieramente. |
presented_at es distinto de haber creado una URL o preparado un payload. Ese momento se registra mediante el concepto público payment_instruction.presented.
Timeout y expiración
El caller no gobierna arbitrariamente el timeout de la Payment Order. Para Web Checkout, YUPY obtiene la vigencia efectiva de la policy canónica aplicable al cliente autenticado, Counter resuelto, canal y payment_method. El instrumento concreto se resuelve internamente y la configuración efectiva queda asociada a la operación:
client_id autenticado
+ counter_id resuelto
+ channel
+ payment_method
↓
policy canónica
↓
timeout_seconds
expires_at
Además, payment_order.expires_at y checkout_session.expires_at son conceptos distintos. La primera controla el lifecycle del cobro y la ocupación del Counter; la segunda controla la sesión o token Web. Pueden coincidir inicialmente, pero no deben tratarse como el mismo reloj.
Counters y capacidad
La semántica runtime depende del tipo de Counter. Un Counter físico puede conservar exclusividad operativa. El Counter virtual Web canónico permite que el Payment Order Orchestrator administre su capacidad/concurrency sin exponer al integrador un pool ni selección de Counters.
Lifecycle y confirmación financiera
Los estados terminales v1 son:
confirmed
expired
cancelled
failed
Al cerrar la orden se registra closed_at y se libera el Counter. El Payment Order Orchestrator no confirma financieramente por sí solo: la confirmación proviene de Reconciliation y se refleja en confirmed_at.
Señales como “Ya pagó”, fotografías o reclamos de pago solicitan verificación; no equivalen a pago confirmado. Del mismo modo, los pagos tardíos pueden detectarse después de que una orden expiró o fue cancelada sin reabrirla ni volver a ocupar el Counter.
Campos del contrato anterior que dejan de ser canónicos
Para este contrato común ya no son canónicos:
requested_payment_methods[]
payment_options[]
yape_qr
plin_qr
collection_profile_id como fuente del instrumento
expires_in_seconds controlado por el caller
yupy_transaction_id como identidad canónica pública
Para Web Checkout, la sustitución conceptual vigente es:
payment_method
↓
YUPY resuelve Counter + policy + instrumento
↓
timeout_seconds efectivo
payment_order_uid
Changelog
2026-08-26 — Web Checkout productivo: el contrato público de Web Checkout quedó alineado con POST /v1/checkouts y payment_method como selección del caller; YUPY resuelve internamente cliente, Counter virtual Web, policy, instrumento y timeout. Se retiró del contrato Web la selección pública de payment_instrument_type, el modelo de pool/Allocator visible y cualquier dependencia de un QR concreto como supuesto universal.