Skip to main content
Traducción. Autoritativo: fs-loy-0013-apply-discount.md.

Contexto

Hoy los efectos del motor acreditan puntos, emiten cupones, disparan webhooks y gatillan campañas. Todos ocurren después del hecho. apply_discount es distinto en naturaleza: modifica un precio en caliente, lo que lo sitúa en el camino crítico del checkout del tenant. Ese es el territorio que ocupa Talon.One, y la convergencia entre pagos e incentivos —Adyen adquiriéndolos en julio de 2026— es una señal de hacia dónde va la categoría, no un nicho. DEC-H4 lo ratificó para F2. El costo en F1 fue cero precisamente porque rule_effect_types quedó abierto: agregarlo es una fila más una implementación, nunca una migración. El problema nuevo que introduce es un modelo de carro. Todo efecto anterior operaba sobre un contacto; este opera sobre una canasta de ítems que existe solo mientras dura un checkout. Eso, más el control real de presupuesto en tiempo de request, es por qué es su propia feature y su propia fase.

Alcance (normativo)

  • Un modelo de carro/sesión: ítems, cantidades, precios, moneda — aceptado, nunca persistido como orden.
  • POST /v1/loyalty/qualifications: dado un carro y un member, devolver los efectos aplicables.
  • El tipo de efecto apply_discount: monto o porcentaje, con alcance de carro o de línea.
  • Verificación de presupuesto en tiempo de request, con reserva atómica.
  • Guardarraíles de margen: descuento máximo por carro y por línea.
  • Evaluación de acumulación (FS-LOY-0008) aplicada a los descuentos candidatos.
  • loyalty.qualification_sessions para auditoría y reconciliación contra lo que realmente se cobró.

Fuera de alcance (normativo)

  • Ejecutar un pago o persistir una orden. El checkout del tenant es dueño de ambos. Respondemos una pregunta; no nos convertimos en el sistema de comercio.
  • Impuestos. Los descuentos se calculan sobre los valores que el llamador entrega, y cómo interactúa el impuesto con ellos es la jurisdicción del tenant y su problema.
  • Inventario. Un descuento no reserva stock.

Comportamiento (normativo)

  1. El endpoint es mayormente de lectura: devuelve efectos y reserva presupuesto, pero nunca escribe una transacción del libro. Los puntos solo se acreditan cuando el tenant confirma la venta con una llamada aparte.
  2. La reserva de presupuesto tiene TTL. Un checkout abandonado libera su reserva automáticamente; sin eso, un carro abandonado por segundo agota una campaña en silencio.
  3. Los descuentos se calculan en unidades menores contra la moneda del carro (DEC-B5). Los descuentos porcentuales redondean a favor del member, y la regla de redondeo se documenta en la referencia pública de la API en vez de dejarse para que alguien la descubra.
  4. Los guardarraíles de margen son topes duros. Una regla que los excedería se acota y el recorte se reporta, nunca se aplica en silencio: el tenant debe poder ver que su regla quería más de lo que su guardarraíl permite.
  5. La acumulación se evalúa antes de responder. La respuesta contiene solo la combinación que efectivamente se honrará.
  6. Las sesiones se conservan para reconciliación y quedan sujetas a la política estándar de retención. No son órdenes y no llevan datos de pago.
  7. Presupuesto: p95 <300 ms. Esto está en un camino de checkout — excederlo le cuesta conversiones al tenant.
  8. PROHIBIDO: devolver un efecto que el tenant no pueda honrar, o uno cuyo presupuesto no se reservó.

Datos (normativo)

API (normativo)

Eventos (normativo)

loyalty.qualification.confirmed al confirmar, que es lo que dispara la acreditación de puntos por el camino normal de efectos. El request de calificación en sí no emite nada — un cliente navegando no es un evento de dominio.

Criterios de aceptación (normativo)

  1. Un carro de 3 ítems a 45 000 con una regla Gold del 10% devuelve un descuento de 4 500 y reserva presupuesto.
  2. Una sesión no confirmada libera su reserva al vencer el TTL, y el presupuesto de la campaña vuelve a su valor previo.
  3. La confirmación acredita puntos una vez; reprocesarla con la misma clave de idempotencia no acredita nada más.
  4. Una regla que excedería el guardarraíl de margen se acota, y la respuesta reporta tanto el monto solicitado como el aplicado.
  5. La acumulación se respeta: un par no combinable devuelve solo el descuento de mayor precedencia.
  6. p95 <300 ms bajo el perfil k6 de checkout.
  7. Negativo: la llamada de calificación no escribe ninguna transacción del libro — probado por el conteo de filas antes y después.

Ejecución

Comando síncrono. Endpoint en backend/api; la reserva de presupuesto usa las mismas primitivas de Upstash que el rate limiting; la liberación por TTL es un barrido programado.

Preguntas abiertas

Ninguna pendiente. Toda pregunta que llevaba este spec quedó respondida en el registro consolidado (../../../../design/open-questions-v1.md, v1.1) y se incorporó a las secciones normativas de arriba.

Changelog

Registro de entrega

Aún no implementado.