API Technical Docs

Mantente al día con las innovaciones tecnológicas que están transformando el mercado.

Idempotencia, reintentos y duplicados

Estado documental: Contrato propuesto. Las ventanas temporales, campos de comparación y mecanismo de confirmación deben validarse en implementación.

Tres controles diferentes

Idempotency-Key
→ protege contra reintentos técnicos

external_transaction_id
→ identifica la venta en el sistema del cliente

Detección de posible duplicado
→ identifica dos IDs diferentes que parecen representar la misma venta

Reintento técnico

Cada operación de creación o modificación debe enviar:

Idempotency-Key: <UUID_UNICO>

Misma clave y mismo payload

YUPY devuelve la misma operación.

HTTP/1.1 200 OK
Idempotency-Replayed: true
{
  "result": "existing_transaction",
  "idempotency_replayed": true,
  "yupy_transaction_id": "ypt_01JXYZ",
  "external_transaction_id": "ORDER-10482",
  "state": {
    "operational": "active",
    "financial": "awaiting_payment"
  }
}

Misma clave y contenido distinto

HTTP/1.1 409 Conflict
{
  "error": {
    "code": "idempotency_conflict",
    "message": "La clave de idempotencia ya fue utilizada con otro contenido.",
    "request_id": "req_01JXYZ"
  }
}

Identidad comercial

empresa
+ integración
+ ambiente
+ external_transaction_id

El timestamp de la solicitud no forma parte de la identidad.

ORDER-10482 enviado a las 10:00
ORDER-10482 enviado a las 10:01

→ es la misma operación

Mismo ID externo y mismos datos

{
  "result": "existing_transaction",
  "duplicate": true,
  "duplicate_type": "same_external_transaction",
  "yupy_transaction_id": "ypt_01JXYZ",
  "external_transaction_id": "ORDER-10482"
}

Mismo ID externo y datos críticos diferentes

HTTP/1.1 409 Conflict
{
  "error": {
    "code": "external_transaction_conflict",
    "message": "La transacción externa ya existe con datos diferentes.",
    "external_transaction_id": "ORDER-10482",
    "yupy_transaction_id": "ypt_01JXYZ",
    "conflicting_fields": [
      "amount"
    ],
    "request_id": "req_01JABC"
  }
}

YUPY no debe cambiar silenciosamente la operación original.

Posible duplicado con otro ID

YUPY puede comparar monto, moneda, nombre, turno, dispositivo, ruta, asiento, pedido, producto, servicio, referencias opcionales y proximidad temporal normalizada.

Los campos de contexto siguen siendo opcionales. Su ausencia no invalida la operación, pero reduce la cantidad de señales disponibles.

No bloquear únicamente por monto

Dos operaciones de S/ 80.00
≠ necesariamente la misma venta

YUPY no debe marcar una operación como duplicada solo porque el monto coincide.

Contexto que demuestra operaciones distintas

Orden A:
S/ 85.50
Ruta 184
Asiento 12A

Orden B:
S/ 85.50
Ruta 184
Asiento 12B

Estas operaciones pueden ser legítimamente distintas.

Contexto que eleva la sospecha

Orden A:
S/ 85.50
Ruta 184
Asiento 12A

Orden B:
S/ 85.50
Ruta 184
Asiento 12A

Con IDs externos diferentes y cercanía temporal, YUPY puede devolver una advertencia.

HTTP/1.1 409 Conflict
{
  "error": {
    "code": "possible_duplicate_transaction",
    "message": "Existe una operación reciente con características equivalentes.",
    "candidate": {
      "yupy_transaction_id": "ypt_01JXYZ",
      "external_transaction_id": "ORDER-10482",
      "amount": "85.50",
      "currency": "PEN",
      "state": {
        "operational": "active",
        "financial": "awaiting_payment"
      }
    },
    "matching_signals": [
      "amount",
      "currency",
      "buyer_name",
      "shift_id",
      "device_id",
      "custom_reference_1",
      "custom_reference_2"
    ],
    "duplicate_confirmation_token": "<OPAQUE_CONFIRMATION_TOKEN>",
    "resolution": "review_before_creating",
    "request_id": "req_01JABC"
  }
}

Confirmar que es una operación diferente

{
  "external_transaction_id": "ORDER-10483",
  "amount": "85.50",
  "buyer_name": "María Ramos",
  "integration_experience": "chat_pos",
  "shift_id": "SHIFT-44",
  "device_id": "DEVICE-03",
  "context": {
    "custom_reference_1": "ROUTE-184",
    "custom_reference_2": "SEAT-12A"
  },
  "duplicate_confirmation_token": "<OPAQUE_CONFIRMATION_TOKEN>"
}

La confirmación debe quedar auditada.

Reintento después de timeout

Correcto:

Primer intento:
external_transaction_id = ORDER-10482
Idempotency-Key = abc-123

Timeout

Segundo intento:
external_transaction_id = ORDER-10482
Idempotency-Key = abc-123

Incorrecto:

Primer intento:
ORDER-10482

Timeout

Segundo intento:
ORDER-10483

Cambiar el ID después de un timeout puede convertir accidentalmente un reintento en una nueva venta.

Matriz de comportamiento

Situación Resultado propuesto
Misma Idempotency-Key, mismo payload Devuelve la misma operación
Misma Idempotency-Key, payload distinto 409 idempotency_conflict
Mismo ID externo, mismos datos Devuelve la operación existente
Mismo ID externo, datos críticos distintos 409 external_transaction_conflict
ID diferente, señales equivalentes 409 possible_duplicate_transaction
Solo coincide el monto No se bloquea automáticamente
Otro asiento o referencia Puede considerarse una operación diferente
Confirmación explícita con token Crea una nueva operación auditable

Criterios de aceptación

  1. Los reintentos utilizan Idempotency-Key.
  2. El timestamp no define la identidad comercial.
  3. El mismo ID externo no crea dos órdenes.
  4. Un cambio de monto genera conflicto.
  5. YUPY detecta posibles duplicados con IDs distintos.
  6. El contexto es opcional.
  7. El asiento ayuda a distinguir ventas.
  8. La coincidencia de monto por sí sola no bloquea.
  9. La creación forzada requiere confirmación explícita.
  10. La confirmación queda auditada.