Krizaka
Toutes les briques

krizaka-billing

Facturation & crédits

Crédits, portefeuilles, plans, une grille tarifaire versionnée et le comptage : réserver → régler → libérer.

Le problème qu'elle supprime

Débiter après, et le travail tourne à crédit. Débiter avant, et les échecs sont facturés.

Facturer un travail coûteux échoue de deux façons : débiter après, et le travail a tourné sur un crédit que personne n'avait ; débiter avant, et un job échoué est facturé. Puis un message « job terminé » est relivré et le même job est débité deux fois.

  • Un utilisateur qui lance dix jobs coûteux d'un coup avec le crédit d'un seul.
  • Un rendu échoué facturé à son estimation.
  • Un règlement relivré qui débite deux fois.

Ce qu'elle fait

  • hold sur l'estimation avant le travail, puis settleMeasured ou release.
  • Chaque débit porte une clé d'idempotence : un message relivré n'est débité qu'une fois.
  • Plans, abonnements, packs et portefeuilles autour d'un grand livre en ajout seul.

Un service et un client typé : l'appelant réserve les crédits estimés avant un travail coûteux, puis règle le coût mesuré ou libère la réservation. Les réservations jamais réglées expirent. Autour : plans, abonnements, packs, portefeuilles et statistiques d'usage.

Le flamant est payé pour ce qui a été fait, une fois — et personne ne paie un travail qui a échoué. Un job en échec libère sa réservation ; chaque débit porte une clé d'idempotence.

La règle du flamant

Décisions et compromis

  1. Nous avons choisi

    Réserver → régler → libérer, avec une expiration sur les réservations.

    Nous avons refusé

    Débiter après le travail, et prépayer sans réservation.

    Parce que

    Un travail ne démarre jamais sans le crédit pour le couvrir, et un échec ne coûte rien.

    Ce que cela vous coûte

    Un appel synchrone (la réservation) sur le chemin de la requête ; régler et libérer en sont hors.

  2. Nous avons choisi

    Un grand livre en ajout seul où chaque débit porte une clé d'idempotence, en plus de la déduplication des messages.

    Nous avons refusé

    Se fier à la seule déduplication.

    Parce que

    Deux gardes indépendantes : une relivraison qui passe l'une débite quand même une seule fois.

    Ce que cela vous coûte

    Le grand livre ne fait que grandir ; une correction est une nouvelle écriture.

  3. Nous avons choisi

    Les prix et la durée de vie d'une réservation sont des lignes d'une grille tarifaire versionnée ; une réservation fige la version à laquelle elle a été tarifée.

    Nous avons refusé

    Des prix dans le code ou la configuration.

    Parce que

    Changer un prix ne change jamais ce que coûte un job en cours, et ne demande aucun déploiement.

    Ce que cela vous coûte

    Un changement de prix est une migration de données que vous relisez.

  4. Nous avons choisi

    Un adaptateur neutre quand krizaka.billing.enabled vaut false.

    Nous avons refusé

    Un if (billingEnabled) à chaque appel.

    Parce que

    Le même code tourne avec et sans comptage.

    Ce que cela vous coûte

    Rien qui vaille d'être cité.

En code

javaRenderService.java
CreditHoldResponse hold = credits.hold(new CreditHoldCommand(
    userId, BillableCapability.IMAGE, "sdxl", requestId, jobId, estimate));
    // InsufficientCreditsException carries what a structured 402 needs

try {
  Render render = renderer.run(job);
  // The ledger prices what was measured, at the price-book version the hold pinned.
  credits.settleMeasured(hold.holdId(), render.consumption(), jobId); // jobId = idempotency key
} catch (RuntimeException failed) {
  credits.release(hold.holdId(), "render failed"); // a failed job is never billed
  throw failed;
}

Ne l'utilisez pas quand

  • Il vous faut des factures, des taxes ou des paiements par carte : billing compte des crédits ; encaisser reste à vous (une recharge crédite un portefeuille).
  • Vos capacités ne sont pas du travail d'IA — aujourd'hui. Le contrat nomme encore celles d'Orazaka (CHAT, IMAGE, AUDIO, VIDEO, AGENT) et ses mesures (jetons, secondes GPU…) ; voir plus bas.

Où elle en est

0.1.0 sur Maven Central (api, client). La 0.2.0 — l'outbox implémente OutboxStore.append, chaque événement a son JSON Schema — est fusionnée et sort en novembre.

Publié

En cours

  • Décidé : capacités et unités deviennent des clés déclarées dans la grille tarifaire, les mesures une table de quantités ; Orazaka garde ses enums chez Orazaka. Une brique de facturation réutilisable ne peut pas parler le vocabulaire d'un seul produit.krizaka-billing#7
  • Le BOM 0.2.0 gère cette brique en 0.2.0, non publiée : déclarez 0.1.0 explicitement jusqu'au BOM 0.3.0.krizaka-build#11

L'adopter

xmlpom.xml · application.yml
<dependency>
  <groupId>com.krizaka</groupId>
  <artifactId>krizaka-billing-client</artifactId>
  <version>0.1.0</version>
</dependency>

<!-- krizaka.billing.enabled: false wires a no-op adapter: no call site branches on "is billing on" -->

Dites-nous où ça coince.

Une brique est juste quand elle survit à votre code, pas au nôtre. Posez votre question dans le fil de la brique, proposez un changement comme idée, ou signalez un bug sur son dépôt — chaque décision de cette page reste ouverte à un meilleur argument.

Les autres briques