Build in Public · LF-14
Conciliación de fallos, reembolsos y API: cómo verificar el cargo neto de una solicitud
Separa las tareas de negocio, los intentos de llamada, los estados de entrega y los eventos de la cartera para explicar los rechazos previos al cargo, los resultados vacíos y los reembolsos parciales sin restar un mismo reembolso dos veces, y verifica una única solicitud con ejemplos enteros sin conexión.
Para que la premisa de no cobrar por fallos constituya un compromiso de producto ejecutable, define qué se considera fallo y cómo se demuestra la ausencia de un cargo neto. Observar únicamente el estado HTTP omite los resultados vacíos y las entregas parciales, mientras que revisar solo los cambios de saldo mezcla solicitudes simultáneas, recargas o ajustes. Una conciliación fiable requiere analizar una solicitud individual y sus eventos asociados.
Este artículo analiza cómo las integraciones de la API organizan los registros de conciliación y explica varios campos confusos utilizando el código actual de EveryInfra. El libro mayor sin conexión es un ejemplo de diseño de aplicación, no una factura real de cliente ni una regla de reconocimiento de ingresos contables. Comprueba las condiciones de tarificación, las unidades de importe y la factura final de cada acción en su contrato de producto correspondiente.
Separa primero los cuatro objetos
- Tarea de negocio: el objetivo que el usuario desea completar, como comprobar un conjunto de comentarios de productos aprobados. Una tarea puede incluir múltiples llamadas a la API.
- Intento de llamada: una solicitud real que conserva su marca temporal, versión de parámetros, modelo o acción e identificador de solicitud disponible.
- Resultado de entrega: éxito, resultado vacío, entrega parcial, fallo o estado desconocido; se trata de un estado de negocio y no equivale al estado de la cartera.
- Evento de la cartera: un cargo, reembolso u otro ajuste explícito relacionado con dicho intento para explicar el cambio neto.
Relaciona estos cuatro objetos en lugar de agruparlos en un único campo de éxito. Si una solicitud completa parcialmente su objetivo, la aplicación puede conservar el contenido entregado y continuar verificando la tarificación de la parte pendiente; un tiempo de espera agotado también puede dejar sin determinar la entrega y la liquidación, por lo que no debe registrarse directamente con costo cero.
El identificador de tarea de negocio, el identificador de solicitud, el id de trabajo asíncrono y la referencia contable no son el mismo concepto. Conserva las relaciones que el servicio proporciona realmente y evita generar identificadores contables inexistentes mediante cadenas similares. El identificador de la solicitud HTTP generado al consultar una tarea nunca debe sobrescribir el identificador de correlación con el que se envió la tarea inicialmente.
El rechazo previo al cargo y el reembolso posterior siguen rutas distintas
Según la línea base del código fuente implementado en 2026-09-04, las comprobaciones de identidad, entrada, modelo y permisos de capacidad de las solicitudes de texto EveryInfra se ejecutan antes de la tarificación; si faltan mensajes, se puede devolver un error de entrada antes de la autenticación, por lo que no se declara un orden de prioridad unificado entre todos los errores. Cuando los mensajes son válidos y la comprobación de identidad se supera, un modelo desconocido genera 422 unknown_model antes del cargo. Este tipo de fallo rechaza la ejecución y no genera un cargo final previo al reembolso; la antigua descripción de 503 ya no debe utilizarse.
Para las solicitudes que han entrado en la fase de ejecución y posteriormente fallan, puede ser necesario reembolsar la parte cobrada. La presencia de una rama de reembolso en el código no significa que el cliente ya haya observado su finalización. Si se pierde la respuesta, consulta los registros disponibles mediante el identificador de solicitud; a falta de evidencia, mantén la conciliación pendiente de liquidación y no modifiques tus registros financieros basándote únicamente en el texto del error.
El archivo de 2026-09-02 incluye muestras anonimizadas de reembolsos tras fallos específicos, así como muestras de resultados vacíos y entregas parciales por lotes. Estas muestras indican que tales situaciones merecen una validación individual y no constituyen una garantía uniforme para cada capacidad o fallo actual. Esta revisión abarca únicamente el código fuente y la documentación pública, sin añadir nuevos cargos reales, reembolsos contables ni operaciones de cuenta.
Lee primero las definiciones de los campos antes de decidir cómo sumar
Tomando como ejemplo una respuesta de datos síncronos, billing.charged indica el estado de tarificación en la respuesta, billing.credits representa la cantidad de unidades de cargo notificadas en dicha respuesta y billing.amount se emplea para mostrar semánticas monetarias. El valor restante de la cuota pertenece a una instantánea de cuenta y no debe considerarse un costo unitario, del mismo modo que el precio de catálogo no sustituye a los registros de solicitudes reales.
El cálculo más propenso a errores es el reembolso parcial. La implementación de datos actual resta primero el importe reembolsado calculado del costo original, escribe el costo restante en billing.credits y devuelve refunded_credits por separado. Por lo tanto, no restes billing.credits menos refunded_credits de nuevo, ya que duplicarías el descuento del mismo reembolso. Esta conclusión se limita a la rama de datos síncronos verificada aquí y no se aplica automáticamente a todos los productos.
En un ejemplo puramente sintético, un cargo original de 30 unidades y un reembolso de 10 unidades dan como resultado un cargo neto de 20. Si la respuesta final ya indica unos créditos de 20 y un valor refunded_credits de 10, entonces 20 representa el importe neto expresado por dicha respuesta. Para verificar el evento original, calcula 30 menos 10; no restes 20 menos 10 para obtener 10.
Cada sondeo de una misma solicitud puede incluir estados repetidos, por lo que no deben sumarse los créditos de cada respuesta. Almacena por separado las instantáneas de respuesta, los eventos de tarificación y los ajustes de liquidación; elimina los duplicados según sus identidades estables antes de consolidar. Visualizar un nuevo registro HTTP no implica que se haya producido un nuevo consumo.
Explica los resultados vacíos y las entregas parciales según la acción
Cuando los resultados de búsqueda estén vacíos, confirma primero si la entrega válida de dicha herramienta aún puede aparecer en otros campos. La implementación de búsqueda general actual comprueba simultáneamente results y answer; limitarse a observar un campo results vacío podría hacer que una respuesta con respuesta se catalogue erróneamente como un resultado completamente vacío. Los clientes deben atenerse al contrato de la herramienta y evitar adivinar el valor del contenido en la capa financiera.
Las solicitudes de datos por lotes requieren contrastar el número de objetivos con la entrega real. La rama actual de reembolsos parciales puede proporcionar total_targets, billed_targets y refunded_credits, mientras que el resultado de negocio también puede incluir partial y missed. Estos campos ayudan a explicar las discrepancias, pero sus valores específicos deben obtenerse de la respuesta real; el recuento de comentarios no debe tomarse como el número de objetivos correctos.
Antes de volver a procesar los objetivos pendientes, conserva los objetos entregados junto con la solicitud original e identifica cuáles siguen sin resolverse. Reenviar un lote completo puede duplicar el contenido ya obtenido o generar nuevos intentos de llamada. La deduplicación de resultados por parte del cliente y la prevención de cargos duplicados por parte del servidor son dos cuestiones distintas.
La aceptación asíncrona no equivale a la liquidación final
HTTP 202 y job_id indican que se ha entrado en el flujo de procesamiento de tareas, pero no que el usuario haya obtenido un resultado. Conserve la tarea original y consulte su estado final; una consulta de tarea exitosa solo significa que la consulta en sí ha concluido, por lo que también debe comprobarse si la tarea sigue ejecutándose o espera su finalización. Una desconexión de red tampoco justifica automáticamente una cancelación exitosa.
La consulta de tareas de datos actual devuelve principalmente el estado, los resultados o la información de error de la tarea, y no siempre proporciona una instantánea completa de billing. Si faltan campos financieros, consúltalos en el punto de acceso de facturación autorizado; no introduzcas valores cero ni trates dos instantáneas diferentes (la de creación y la de finalización) como cargos nuevos. Este documento no afirma que todas las capacidades asíncronas utilicen el mismo momento de liquidación.
Verifica los costos unitarios con números enteros sin deducirlos mediante decimales
Para conciliar, fija primero la cartera, la unidad de medida y el ámbito de las solicitudes. No sumes directamente importes procedentes de múltiples carteras, diferentes divisas o tipos de cambio de visualización. Si la interfaz utiliza créditos enteros, prioriza la verificación con las unidades originales; valida los enteros seguros en los límites y emplea una representación entera explícita cuando se supere el rango numérico preciso de JavaScript.
A continuación se muestra una estructura de eventos personalizada: cada uno pertenece al mismo ámbito de solicitudes confirmadas, los créditos utilizan cadenas numéricas enteras positivas y kind indica un cargo o un reembolso. Este ejemplo no lee facturas reales, no se conecta a bases de datos ni modifica saldos. El conjunto completo de eventos y la autorización financiera constituyen requisitos previos que el invocador debe confirmar de antemano.
function reconcileAttempt(entries) {
const seen = new Map();
let debited = 0n, refunded = 0n;
for (const e of entries) {
if (typeof e.id !== "string" || !e.id.trim()
|| !["debit", "refund"].includes(e.kind)
|| typeof e.credits !== "string" || !/^[1-9][0-9]*$/.test(e.credits)) {
throw new Error("invalid synthetic ledger entry");
}
const fingerprint = JSON.stringify([e.kind, e.credits]);
if (seen.has(e.id)) {
if (seen.get(e.id) !== fingerprint) throw new Error("conflicting entry");
continue; // las instantáneas duplicadas del mismo evento no se cuentan de nuevo.
}
seen.set(e.id, fingerprint);
if (e.kind === "debit") debited += BigInt(e.credits);
else refunded += BigInt(e.credits);
}
if (refunded > debited) throw new Error("check scope or missing debit evidence");
return {
debited: debited.toString(), refunded: refunded.toString(),
netCharged: (debited - refunded).toString()
};
}
console.log(reconcileAttempt([
{id: "synthetic-debit", kind: "debit", credits: "30"},
{id: "synthetic-refund", kind: "refund", credits: "10"},
{id: "synthetic-refund", kind: "refund", credits: "10"}
]));El resultado es un cargo de 30, un reembolso de 10 y un importe neto de 20; los eventos de reembolso repetidos idénticos no se contabilizan de nuevo. Si un mismo identificador de evento contiene contenido diferente, el ejemplo se detiene en lugar de sobrescribir de forma silenciosa. La conciliación también se detiene si el reembolso supera el cargo obtenido, lo que puede deberse a un ámbito o ventana temporal incompleta, sin que deba asumirse directamente que el servicio ha devuelto un importe excesivo.
Este planteamiento no constituye un algoritmo completo de cartera. Las recargas, bonificaciones, retiradas, bloqueos, correcciones y otros ajustes no están modelados en este ejemplo; los sistemas reales deben gestionarse según sus respectivas definiciones de eventos. Un valor cero calculado a partir de una lista vacía solo indica la ausencia de eventos de entrada y no demuestra que una solicitud desconocida carezca de cargos.
El tipo Number de JavaScript no representa enteros de precisión arbitraria. La explicación de la RFC 8259 sobre el rango de interoperabilidad de los números en JSON constituye el motivo por el cual se mantienen cadenas de enteros y se convierten explícitamente a BigInt en este caso; esto reduce los errores de representación de cálculo, pero no subsana eventos financieros ausentes.
Conserva los eventos iniciales y de periodos cruzados al conciliar por ventanas temporales
Una solicitud puede iniciarse en un día y completarse al siguiente; asimismo, los eventos de reembolso o corrección pueden registrarse con posterioridad al cargo original. Filtrar por fecha de creación de la solicitud o por fecha de ocurrencia de los eventos de cartera genera conjuntos distintos. Los informes deben especificar primero el criterio temporal empleado y conservar los saldos iniciales, finales y las relaciones cruzadas para evitar percibir únicamente reembolsos y no el cargo del día anterior, previniendo falsas alarmas de anomalías.
La identidad contable de la cartera exige contabilizar todos los abonos y cargos dentro de dicha ventana y no limitarse a los dos tipos de eventos descritos en este artículo. La diferencia entre el saldo actual y el saldo anterior a una llamada también puede incluir tareas simultáneas u otros ajustes; dicha diferencia es útil como comprobación cruzada, pero no resulta adecuada para demostrar por sí sola el costo de una solicitud.
El consumo de cartera, los depósitos en efectivo de clientes, el uso de créditos de bonificación y los ingresos contables responden a criterios diferentes. Este artículo se limita a analizar la correspondencia entre las solicitudes y los indicios de la cartera, sin proporcionar conclusiones sobre el reconocimiento de ingresos o cuestiones fiscales. Los indicadores financieros externos deben ser publicados por el personal de producto y finanzas responsable tras definir claramente sus criterios.
La documentación de solicitudes idempotentes de Stripe ofrece un punto de comparación: la retransmisión segura requiere que el servicio sea compatible de forma explícita con el mecanismo de idempotencia correspondiente. Esto no demuestra que EveryInfra acepte la misma clave de idempotencia. La deduplicación de registros obtenidos mediante el identificador de eventos en el ejemplo de este artículo se realiza en el cliente y no evita nuevos cargos en el servidor; ambos tipos de deduplicación no deben confundirse.
Dirige las discrepancias a una lista de pendientes antes de modificar los registros contables automáticamente
- Registro con cargos pero sin entrega asociable: comprueba primero si hay tareas asíncronas pendientes, resultados sin guardar o pérdida de correlación.
- Resultado de fallo sin estado financiero claro: contrasta la solicitud original con los eventos posteriores sin marcar de forma autónoma reembolsos ni correcciones.
- Conflicto de contenido en un mismo evento: guarda la versión original y la hora de obtención, deteniendo la sobrescritura silenciosa.
- Visualización repetida de eventos de reembolso: determina si se ha leído el mismo evento múltiples veces para evitar contabilizarlo de forma duplicada.
- Inconsistencia en unidades monetarias o ventanas temporales: unifica el rango de comparación antes de evaluar la diferencia.
Al enviar una solicitud de soporte, aporta únicamente el identificador de solicitud, la marca temporal, la capacidad, el estado y las referencias de facturación anonimizadas que sean estrictamente necesarios. No envíes claves completas, contenido de clientes ni exportaciones de cuentas enteras. La resolución de discrepancias y las modificaciones contables exigen permisos diferentes; una marca de anomalía generada automáticamente no constituye una autorización para realizar correcciones ni otros ajustes financieros.
Una solicitud verificable debe detallar qué se ha entregado, cuál es el fundamento del costo, si el reembolso ya se ha incluido en el importe neto y qué eventos quedan pendientes de confirmación. Al aclarar estas relaciones de antemano, la afirmación de que el fallo no genera un cargo final deja de ser un mero eslogan y se evita que el cliente genere nuevas discrepancias de facturación al restar un mismo reembolso de forma repetida.