Errori delle API, restituzione dei crediti e riconciliazione: come verificare l'addebito netto di una richiesta

Separa task, tentativi di chiamata, stati di consegna ed eventi del portafoglio per gestire rifiuti prima dell'addebito, risultati vuoti e restituzioni parziali di crediti senza doppi conteggi, con esempi offline basati su interi.

Perché «le richieste non riuscite non vengono addebitate» sia un impegno di prodotto vincolante, bisogna chiarire due cose: cosa si intende per richiesta non riuscita e come dimostrare che non c'è stato alcun addebito netto. Basarsi solo sui codici di stato HTTP fa perdere i risultati vuoti e le consegne parziali, mentre guardare solo le variazioni di saldo mescola richieste simultanee, depositi o rettifiche. Una riconciliazione affidabile richiede di tracciare ogni singola richiesta insieme agli eventi associati.

Questa nota spiega come i client API possono organizzare i record di riconciliazione e chiarisce i campi ambigui facendo riferimento al codebase attuale di EveryInfra. Il registro offline mostrato qui è un esempio di design applicativo, non un estratto conto per i clienti né una regola contabile di riconoscimento dei ricavi. Le condizioni di addebito delle singole azioni, le unità di valuta e gli estratti conto finali vanno verificati rispetto alle regole di addebito del prodotto corrispondente.

  • Task di business: l'obiettivo che l'utente vuole effettivamente raggiungere, ad esempio esaminare un insieme di recensioni di prodotto approvate. Un singolo task può comportare più chiamate API.
  • Tentativo di chiamata: una richiesta effettiva, con il proprio timestamp, la versione dei parametri, il modello o l'azione e l'eventuale ID richiesta.
  • Risultato della consegna: successo, risultato vuoto, consegna parziale, errore o ancora sconosciuto; è uno stato di business, non uno stato del portafoglio.
  • Evento del portafoglio: registra addebiti, rimborsi o altre rettifiche esplicite legate al tentativo e serve a spiegare le variazioni nette.

Collega questi quattro oggetti invece di comprimerli in un unico campo di successo. Se una richiesta raggiunge solo in parte il suo obiettivo, il business può tenere il contenuto consegnato e continuare a verificare l'addebito per la parte non completata; anche un timeout può lasciare indeterminate consegna e liquidazione, quindi non va registrato con leggerezza come costo zero.

Anche l'ID del task di business, l'ID richiesta, il job_id asincrono e il riferimento di addebito sono concetti distinti. Memorizza le relazioni effettivamente fornite dal servizio; non ricavare ID di addebito inesistenti da stringhe simili. Gli ID richiesta HTTP generati durante l'interrogazione dei task non devono mai sovrascrivere l'identificatore di associazione dell'invio iniziale del task.

Rifiuto prima dell'addebito e rimborso dopo l'addebito seguono percorsi diversi

Secondo la baseline del codice sorgente in produzione al 2026-09-04, i controlli di EveryInfra su identità, input, modello e permessi di funzionalità delle richieste testuali avvengono tutti prima dell'addebito; se mancano i messaggi può essere restituito un errore di input prima dell'autenticazione, quindi qui non si dichiara un ordine di precedenza uniforme per tutti gli errori. Nei percorsi in cui i messaggi sono validi e i controlli di identità vanno a buon fine, un modello sconosciuto produce 422 unknown_model prima di qualsiasi addebito. Questi errori sono rifiuti dell'esecuzione e non generano un addebito iniziale seguito da un rimborso; la vecchia descrizione del modello sconosciuto con 503 non va più usata.

Per le richieste che entrano nella fase di esecuzione e poi falliscono, può essere necessario rimborsare addebiti parziali. L'esistenza di un ramo di rimborso nell'implementazione non significa che il client ne abbia osservato il completamento. Se una risposta va persa, interroga prima i record disponibili tramite gli identificatori della richiesta; in mancanza di prove, mantieni lo stato «liquidazione da verificare» invece di modificare i registri finanziari sulla base del solo testo dell'errore.

Gli archivi del 2026-09-02 contengono esempi anonimizzati di rimborsi dopo un errore, oltre a esempi di risultati vuoti e di consegna parziale in batch. Mostrano che questi scenari meritano un'accettazione separata, non che esista oggi una garanzia universale per ogni funzionalità o tipo di errore. Questo ciclo copre solo il codice sorgente e la documentazione pubblica, senza addebiti reali, rimborsi di fatturazione o operazioni sugli account.

Leggi le definizioni dei campi prima di decidere come sommarli

Prendendo ad esempio le risposte sincrone dei dati, billing.charged indica lo stato di addebito nella risposta, billing.credits rappresenta il numero di unità addebitate riportato dalla risposta e billing.amount si usa per le visualizzazioni che richiedono una semantica monetaria. I valori residui della quota sono snapshot dell'account e non possono essere trattati come costo di una singola richiesta; i prezzi di listino del catalogo non possono sostituire i record effettivi delle richieste.

I rimborsi parziali sono la fonte più comune di errori di calcolo. L'attuale implementazione dei dati sottrae l'importo di rimborso calcolato dalla tariffa originale prima di scrivere la tariffa residua in billing.credits, e restituisce separatamente refunded_credits. Di conseguenza, non devi sottrarre di nuovo refunded_credits da billing.credits, altrimenti lo stesso rimborso viene sottratto due volte. Questa conclusione vale solo per il ramo sincrono dei dati verificato qui e non si applica automaticamente a tutti i prodotti.

In un esempio puramente sintetico, un addebito originale di 30 unità con un rimborso di 10 unità dà un addebito netto di 20. Se la risposta finale riporta già credits pari a 20 e refunded_credits pari a 10, allora 20 è già l'importo netto espresso da quella risposta. Per verificare gli eventi grezzi, calcola 30 meno 10; non sottrarre 10 da 20 arrivando a 10.

Ogni tentativo di polling per la stessa richiesta può riportare uno stato duplicato; non sommare i crediti di tutte le risposte. Memorizza separatamente nel database snapshot delle risposte, eventi di addebito e rettifiche di liquidazione, deduplicali prima in base alle rispettive identità stabili e solo dopo aggregali. Una nuova voce nel log HTTP non significa che ci sia stato un nuovo consumo.

Interpretare risultati vuoti e consegne parziali in base all'azione

Quando i risultati di ricerca sono vuoti, verifica se consegne valide per quello strumento possano comparire in altri campi. L'attuale implementazione della ricerca generale controlla contemporaneamente results e answer; guardare solo i results vuoti può far scambiare per completamente vuote risposte che contengono un answer. I client dovrebbero seguire la risposta documentata dello strumento ed evitare di indovinare il valore dei contenuti a livello finanziario.

Le richieste di dati in batch richiedono di confrontare il numero di target con le consegne effettive. L'attuale ramo di rimborso parziale fornisce total_targets, billed_targets e refunded_credits, mentre i risultati di business possono includere anche partial e missed. Questi campi aiutano a spiegare le differenze, ma i valori specifici devono provenire dalle risposte effettive; il numero di commenti non può essere trattato come numero di target riusciti.

Prima di rielaborare i target non completati, conserva gli oggetti consegnati e le richieste originali, chiarendo quali target restano sconosciuti. Reinviare un intero batch può duplicare i contenuti esistenti o generare nuovi tentativi di chiamata. La deduplicazione dei risultati lato client e la prevenzione degli addebiti duplicati lato server sono due operazioni distinte.

L'accettazione asincrona non equivale alla liquidazione finale

HTTP 202 e job_id indicano l'ingresso nella pipeline di elaborazione del task, non che l'utente abbia ottenuto i risultati. Conserva il task originale e interrogane lo stato finale; un'interrogazione riuscita indica solo che l'interrogazione stessa è stata completata, e devi verificare se il task è ancora in esecuzione o in attesa di finalizzazione. Una disconnessione non può essere interpretata automaticamente come un annullamento riuscito.

Le attuali interrogazioni dei task di dati restituiscono principalmente stato del task, risultati o informazioni sull'errore, invece di fornire ogni volta uno snapshot completo dell'addebito. Quando mancano i campi di addebito, verifica tramite i portali di fatturazione autorizzati; non inserire zeri e non trattare gli snapshot di creazione e completamento come nuovi addebiti separati. Questa nota non afferma che tutte le funzionalità asincrone usino la stessa tempistica di liquidazione.

Verifica i costi singoli con gli interi, non con decimali derivati da numeri in virgola mobile

La riconciliazione richiede innanzitutto di definire i portafogli, le unità di misura e gli ambiti delle richieste. Non sommare direttamente importi di più portafogli, valute diverse o tassi di cambio usati per la visualizzazione. Se le interfacce usano crediti interi, verifica preferibilmente in quell'unità originale; controlla che i valori ai limiti rientrino nell'intervallo degli interi sicuri e usa rappresentazioni intere esplicite quando i valori superano l'intervallo degli interi esatti di JavaScript.

Quello che segue mostra una struttura di eventi personalizzata: ogni voce appartiene allo stesso ambito di richiesta confermato, credits usa stringhe di interi positivi e kind indica addebiti o rimborsi. L'esempio non legge record di addebito reali, non si collega a database e non modifica saldi. Un insieme completo di eventi e l'autorizzazione ad accedere ai dati di fatturazione sono prerequisiti che il chiamante deve confermare in anticipo.

JavaScript offline: distingui totale addebitato, totale rimborsato e addebito netto
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; // duplicate snapshots of the same event are not counted again.
    }
    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"}
]));

Il risultato è un addebito di 30, un rimborso di 10 e un addebito netto di 20; gli eventi di rimborso duplicati che ricompaiono non vengono contati di nuovo. Se lo stesso ID evento corrisponde a contenuti in conflitto, l'esempio si interrompe invece di sovrascrivere silenziosamente. La verifica si ferma anche se i rimborsi superano gli addebiti acquisiti, cosa che può dipendere da ambiti o finestre temporali incompleti, e non va interpretata automaticamente come un rimborso eccessivo da parte del servizio.

Questo non è un algoritmo completo per il portafoglio. Versamenti, accrediti, prelievi, blocchi, storni e altre rettifiche non sono modellati in questo esempio; i sistemi reali devono gestirli secondo le rispettive definizioni degli eventi. Gli zeri calcolati da liste vuote indicano solo che non c'erano eventi in input e non dimostrano che una richiesta sconosciuta non abbia comportato addebiti.

I Number di JavaScript non sono interi a precisione arbitraria. Le indicazioni di RFC 8259 sugli intervalli di interoperabilità dei numeri JSON spiegano perché qui si conservano stringhe di interi prima di convertirle esplicitamente in BigInt; questo riduce solo gli errori di rappresentazione nel calcolo e non può compensare eventi di addebito mancanti.

Conserva gli eventi di apertura e quelli a cavallo tra periodi quando verifichi per finestra temporale

Le richieste possono iniziare un giorno e terminare il successivo; anche gli eventi di rimborso o storno possono essere registrati dopo l'addebito originale. Filtrare in base al timestamp di creazione della richiesta oppure a quello dell'evento del portafoglio produce insiemi diversi. I report dovrebbero specificare quale base temporale usano, poi conservare le associazioni di apertura, chiusura e a cavallo tra periodi, per evitare falsi allarmi dovuti a rimborsi rilevati senza gli addebiti del giorno precedente.

Le identità contabili del saldo devono tenere conto di tutti gli addebiti e gli accrediti nella finestra di quel portafoglio, non solo dei due tipi di eventi trattati qui. La differenza tra il saldo attuale e il saldo precedente a una certa chiamata può includere anche task simultanei o altre rettifiche; è utile per un controllo incrociato, non per dimostrare da sola il costo di una singola richiesta.

Consumo del portafoglio, depositi in denaro dei clienti, uso di crediti promozionali e ricavi contabili seguono definizioni diverse. Questa nota tratta solo l'abbinamento delle richieste alle prove del portafoglio e non fornisce conclusioni su riconoscimento dei ricavi o aspetti fiscali. Le metriche finanziarie esterne dovrebbero essere pubblicate solo dopo che i responsabili di prodotto e finanza ne hanno chiarito le definizioni.

La documentazione di Stripe sulle richieste idempotenti offre un termine di paragone: i nuovi tentativi sicuri richiedono che il servizio supporti esplicitamente il meccanismo di idempotenza corrispondente. Ciò non dimostra che EveryInfra accetti la stessa chiave di idempotenza. Gli esempi qui riportati deduplicano i record acquisiti per ID evento, non bloccano gli addebiti duplicati lato server; i due tipi di deduplicazione non vanno confusi.

Indirizza le discrepanze a code di revisione senza modificare automaticamente i registri

  • Record di addebito senza consegna collegabile: verifica se l'elaborazione asincrona non è terminata, se i risultati non sono stati salvati o se le associazioni sono andate perse.
  • Risultati di errore con stato di addebito poco chiaro: verifica le richieste originali e gli eventi successivi senza contrassegnarli di tua iniziativa come rimborsati o stornati.
  • Contenuti in conflitto per lo stesso evento: conserva le versioni originali e i timestamp di recupero, bloccando le sovrascritture silenziose.
  • Eventi di rimborso mostrati più volte: stabilisci se lo stesso evento è stato letto più volte; non contarlo più volte.
  • Unità valutarie o finestre temporali incoerenti: allinea gli ambiti di confronto prima di valutare le differenze.

Quando invii richieste di assistenza, fornisci solo gli ID richiesta, i timestamp, le funzionalità, gli stati e i riferimenti di addebito anonimizzati necessari. Non inviare chiavi API complete, il testo dei contenuti dei clienti o esportazioni integrali dell'account. Lo smistamento delle discrepanze e la modifica dei registri richiedono permessi separati; un'anomalia segnalata automaticamente non costituisce un'autorizzazione a storni o altre rettifiche contabili.

Una richiesta verificabile dovrebbe indicare cosa ha consegnato, quale base di costo si è applicata, se i rimborsi sono stati inclusi nell'importo netto e quali eventi restano da confermare. Chiarire queste relazioni fa sì che «le richieste non riuscite non comportano addebiti finali» sia più di uno slogan, ed evita che una doppia sottrazione dei rimborsi lato client crei nuove discrepanze di addebito.

Riferimento LF-14

Le nostre statistiche di visita interne sono sempre attive. Google Analytics viene caricato solo se scegli «Consenti analisi». Informativa sulla privacy