W2 · Pago a contacto
# W2 · Pago a contacto
Esta sección cubre el envío de dinero a `contact-demo`. La aplicación debe mostrar el monto, el destinatario y el estado final, además de permitir consultar el historial y el comprobante.
## Flujo de pago
1. Consulta `GET /wallets/wallet-demo` para ver el saldo contable, reservado y disponible. El saldo disponible descuenta las reservas de los pagos pendientes.
2. Envía `POST /payments` con `Content-Type: application/json` e `Idempotency-Key: contacto-001`. Usa `{"walletId":"wallet-demo","recipient":"contact-demo","amountCents":1500,"currency":"PEN","scenario":"APPROVED"}` para pagar S/ 15.00.
3. Guarda el ID del pago. Mientras esté en `PENDING`, muestra que la confirmación está en proceso.
4. Entrega el evento del pago como se explica en "Cómo se confirma un pago".
5. Consulta `GET /payments/{id}` y `GET /payments/{id}/receipt`. Para mostrar el historial de esa wallet, usa `GET /payments?walletId=wallet-demo`.
## Qué debes comprobar
- `APPROVED` termina en `SUCCEEDED` después del evento. El saldo se descuenta una sola vez y el comprobante queda disponible.
- `REJECTED` termina en `REJECTED` después del evento. Se libera la reserva sin cobrar el monto.
- `DELAYED_APPROVAL` permanece pendiente hasta que envíes `POST /control/provider/{id}/complete` con `{"status":"SUCCEEDED"}` y entregues el evento generado. En esta ruta usa el ID del pago.
- Un monto mayor al saldo disponible devuelve `409` antes de enviar la operación al proveedor.
Los importes son enteros en céntimos. `1500` significa S/ 15.00. Los destinatarios son externos y el mock no acredita otra wallet local.
## APIs de esta historia
### `POST /payments`
Crea una intención PENDING y reserva saldo; responde 202. Requiere Idempotency-Key de 1 a 200 caracteres, única por wallet. Misma clave y contenido devuelve 200 con el pago actual; contenido distinto devuelve 409 IDEMPOTENCY_CONFLICT. APPROVED y REJECTED generan evento; PENDING y DELAYED_APPROVAL esperan control manual. TIMEOUT_AFTER_PROCESSING devuelve 504 con paymentId, pero ya creó el pago y el proveedor lo aprobó. PROVIDER_ERROR devuelve 503 sin operación ni reserva. Confirma por callback o /resolve. Moneda predeterminada PEN, escenario APPROVED. 409 WALLET_NOT_ACTIVE o INSUFFICIENT_FUNDS; 404 por wallet, destinatario o QR inexistente; 410 QR_EXPIRED; 409 QR_PAYMENT_MISMATCH si difieren comercio o monto fijo. Solo se permiten contact-demo y merchant-demo. qrId es opcional: sin él, el pago es directo.
Campos del cuerpo JSON:
- `walletId`: string, obligatorio. ID de wallet. Para pagos con fondos usa wallet-demo.
- `recipient`: string, obligatorio. Destinatario externo de prueba. Valores: contact-demo, merchant-demo.
- `amountCents`: integer, obligatorio. Importe en céntimos de PEN. 1500 = S/ 15.00.
- `currency`: string, opcional. Solo PEN. Predeterminado PEN. Valores: PEN.
- `scenario`: string, opcional. Escenario del proveedor simulado. Predeterminado APPROVED. Valores: APPROVED, REJECTED, PENDING, DELAYED_APPROVAL, TIMEOUT_AFTER_PROCESSING, PROVIDER_ERROR.
- `qrId`: string, opcional. ID del QR creado en este grupo. Opcional, valida comercio, monto y vigencia.
Respuestas HTTP: `200`, `202`, `400`, `404`, `409`, `410`, `503`, `504`, `413`, `429`.
Campos de respuesta: `id`, `wallet_id`, `recipient`, `amount`, `status`, `scenario`, `idem`, `fingerprint`.
### `GET /payments`
Lista el historial de una wallet. walletId es opcional y vale wallet-demo por defecto. Una wallet sin pagos o inexistente produce []. Sin paginación ni filtros de fechas. No lista todas las wallets del grupo.
Respuestas HTTP: `200`.
Respuesta: lista de objetos con `id`, `wallet_id`, `recipient`, `amount`, `status`, `scenario`, `idem`, `fingerprint`. Una lista vacía se representa como `[]`.
### `GET /payments/{id}`
Consulta el estado local del pago. No consulta al proveedor ni confirma eventos. Pago inexistente: 404 NOT_FOUND.
Respuestas HTTP: `200`, `404`.
Campos de respuesta: `id`, `wallet_id`, `recipient`, `amount`, `status`, `scenario`, `idem`, `fingerprint`.
### `GET /payments/{id}/receipt`
Obtiene el comprobante cuando el pago está SUCCEEDED o REVERSED. En PENDING o REJECTED devuelve 409 RECEIPT_NOT_READY. No genera PDF. Pago inexistente: 404.
Respuestas HTTP: `200`, `404`, `409`.
Campos de respuesta: `receiptId`, `paymentId`, `amountCents`, `currency`, `recipient`, `status`.
### `GET /provider/payments/{id}`
Consulta el estado del proveedor simulado usando el mismo ID del pago. Puede diferir del estado local si falta el callback. No aplica el resultado a la wallet. ID inexistente: 404.
Respuestas HTTP: `200`, `404`.
Campos de respuesta: `id`, `amount`, `status`.
## Material relacionado
[Leer la historia de usuario](/dia-05/packages/user-stories/payer-low-value-payment-confirmation.sp.html)
[Conectar y confirmar pagos](/dia-04/mocks/)
[Controles para preparar escenarios](/dia-04/mocks/controles.html)
[Cobertura frente a los criterios del reto](/dia-04/mocks/alcance.html)
Conectar con las APIs
Selecciona tu grupo. Sus ocho URLs son fijas: todos los integrantes del mismo grupo usan la misma dirección.
Esta sección cubre el envío de dinero a contact-demo. La aplicación debe mostrar el monto, el destinatario y el estado final, además de permitir consultar el historial y el comprobante.
Flujo de pago
- Consulta
GET /wallets/wallet-demopara ver el saldo contable, reservado y disponible. El saldo disponible descuenta las reservas de los pagos pendientes. - Envía
POST /paymentsconContent-Type: application/jsoneIdempotency-Key: contacto-001. Usa{"walletId":"wallet-demo","recipient":"contact-demo","amountCents":1500,"currency":"PEN","scenario":"APPROVED"}para pagar S/ 15.00. - Guarda el ID del pago. Mientras esté en
PENDING, muestra que la confirmación está en proceso. - Entrega el evento del pago como se explica en "Cómo se confirma un pago".
- Consulta
GET /payments/{id}yGET /payments/{id}/receipt. Para mostrar el historial de esa wallet, usaGET /payments?walletId=wallet-demo.
Qué debes comprobar
APPROVEDtermina enSUCCEEDEDdespués del evento. El saldo se descuenta una sola vez y el comprobante queda disponible.REJECTEDtermina enREJECTEDdespués del evento. Se libera la reserva sin cobrar el monto.DELAYED_APPROVALpermanece pendiente hasta que envíesPOST /control/provider/{id}/completecon{"status":"SUCCEEDED"}y entregues el evento generado. En esta ruta usa el ID del pago.- Un monto mayor al saldo disponible devuelve
409antes de enviar la operación al proveedor.
Los importes son enteros en céntimos. 1500 significa S/ 15.00. Los destinatarios son externos y el mock no acredita otra wallet local.
APIs de esta historia
POST /payments
Crea una intención PENDING y reserva saldo; responde 202. Requiere Idempotency-Key de 1 a 200 caracteres, única por wallet. Misma clave y contenido devuelve 200 con el pago actual; contenido distinto devuelve 409 IDEMPOTENCY_CONFLICT. APPROVED y REJECTED generan evento; PENDING y DELAYED_APPROVAL esperan control manual. TIMEOUT_AFTER_PROCESSING devuelve 504 con paymentId, pero ya creó el pago y el proveedor lo aprobó. PROVIDER_ERROR devuelve 503 sin operación ni reserva. Confirma por callback o /resolve. Moneda predeterminada PEN, escenario APPROVED. 409 WALLET_NOT_ACTIVE o INSUFFICIENT_FUNDS; 404 por wallet, destinatario o QR inexistente; 410 QR_EXPIRED; 409 QR_PAYMENT_MISMATCH si difieren comercio o monto fijo. Solo se permiten contact-demo y merchant-demo. qrId es opcional: sin él, el pago es directo.
Campos del cuerpo JSON:
walletId: string, obligatorio. ID de wallet. Para pagos con fondos usa wallet-demo.recipient: string, obligatorio. Destinatario externo de prueba. Valores: contact-demo, merchant-demo.amountCents: integer, obligatorio. Importe en céntimos de PEN. 1500 = S/ 15.00.currency: string, opcional. Solo PEN. Predeterminado PEN. Valores: PEN.scenario: string, opcional. Escenario del proveedor simulado. Predeterminado APPROVED. Valores: APPROVED, REJECTED, PENDING, DELAYED_APPROVAL, TIMEOUT_AFTER_PROCESSING, PROVIDER_ERROR.qrId: string, opcional. ID del QR creado en este grupo. Opcional, valida comercio, monto y vigencia.
Respuestas HTTP: 200, 202, 400, 404, 409, 410, 503, 504, 413, 429.
Campos de respuesta: id, wallet_id, recipient, amount, status, scenario, idem, fingerprint.
GET /payments
Lista el historial de una wallet. walletId es opcional y vale wallet-demo por defecto. Una wallet sin pagos o inexistente produce []. Sin paginación ni filtros de fechas. No lista todas las wallets del grupo.
Respuestas HTTP: 200.
Respuesta: lista de objetos con id, wallet_id, recipient, amount, status, scenario, idem, fingerprint. Una lista vacía se representa como [].
GET /payments/{id}
Consulta el estado local del pago. No consulta al proveedor ni confirma eventos. Pago inexistente: 404 NOT_FOUND.
Respuestas HTTP: 200, 404.
Campos de respuesta: id, wallet_id, recipient, amount, status, scenario, idem, fingerprint.
GET /payments/{id}/receipt
Obtiene el comprobante cuando el pago está SUCCEEDED o REVERSED. En PENDING o REJECTED devuelve 409 RECEIPT_NOT_READY. No genera PDF. Pago inexistente: 404.
Respuestas HTTP: 200, 404, 409.
Campos de respuesta: receiptId, paymentId, amountCents, currency, recipient, status.
GET /provider/payments/{id}
Consulta el estado del proveedor simulado usando el mismo ID del pago. Puede diferir del estado local si falta el callback. No aplica el resultado a la wallet. ID inexistente: 404.
Respuestas HTTP: 200, 404.
Campos de respuesta: id, amount, status.