Desenvolupem la teva web presencial SPA DES DE 300€. Si, es una bogeria. Web SPA DES DE 300€ — Si, es una bogeria. Parlem →

Com muntar un sistema de crèdits i quotes de consum per al teu SaaS

Com muntar un sistema de crèdits i quotes de consum per al teu SaaS

Un pla amb límit d'ús sembla la funcionalitat més simple d'un SaaS: sumes el consumit i ho compares amb el topall. Aquest compte aguanta fins al dia que véns la teva primera recàrrega, i a partir d'aquí toca migrar en calent. Aquí veuràs com es modela des del principi perquè això no passi, on són les curses entre peticions concurrents i com es resolen. Els exemples van en Laravel, que és una de les nostres especialitats, però el model de dades és el mateix en qualsevol stack.

El saldo no es calcula, es desa

La primera versió que escriu tothom suma el consum del període i el resta del límit del pla. Funciona perfectament fins al dia que vens una recàrrega.

Perquè una recàrrega no és consum, així que no es pot expressar com una resta del que s'ha gastat. Quan un client pot comprar crèdits solts a més dels que porta el seu pla, el saldo deixa de ser una operació aritmètica sobre l'històric i passa a ser un estat que cal emmagatzemar.

Això no és una optimització, és el model. I com abans ho assumeixis, menys migracions doloroses.

Quatre taules, i cadascuna respon una pregunta diferent

L'estructura que ens funciona separa quatre coses que la gent sol barrejar en una:

El compte desa el que no caduca: en quin pla està i quants crèdits comprats li queden.

Les finestres desen el que el pla ja ha cobert en cada període. Una fila per compte i per finestra.

Els dipòsits són les entrades: compres, regals, reemborsaments, correccions.

El consum són les sortides, i és una taula que només creix. Mai s'edita ni s'esborra.

La separació importa perquè una recàrrega i un consum són esdeveniments de naturalesa diferent, i posar-los a la mateixa taula amb el signe canviat t'obliga a interpretar cada fila per saber què era.

Un pla concedeix una xifra, i cada finestra en pren la seva part

Un pla gairebé mai no té un sol límit: en té un de mensual, un altre de diari i de vegades un per sessió. Com es relacionin entre ells decideix si pujar un client de pla és canviar un número o revisar tres taules.

L'intuïtiu és declarar un límit per període: tant al mes, tant a la setmana, tant per sessió. El problema apareix en pujar de pla, perquè cal recordar pujar-los tots tres, i el dia que se n'oblida un, el client ha pagat més i continua topat on estava.

El que funciona millor és una sola xifra i un percentatge per finestra. Cinquanta mil al mes són dotze mil cinc-cents a la setmana i dos mil per sessió, i pujar el pla puja les tres alhora. Un pla nou es declara amb un número.

Un detall que sorprèn el primer cop: els percentatges no han de sumar u. Cada finestra calcula el seu sostre pel seu compte sobre la mateixa xifra base. El mensual pot ser el cent per cent i el setmanal un vint-i-cinc, i no es contradiuen: són dos topalls diferents aplicats alhora.

Les finestres es reinicien soles, sense cron

Gairebé totes les implementacions casolanes acaben amb una tasca programada que reinicia comptadors a mitjanit. És una peça més que pot fallar, i quan falla ningú se n'assabenta fins que un client reclama.

No cal. N'hi ha prou amb desar quan va començar la finestra i comprovar-ho en el moment de cobrar: si ja ha vençut, es reinicia allà mateix i es continua. El reinici és mandrós. No hi ha res a sincronitzar, cap webhook, cap tasca que es quedi penjada.

I hi ha dos comportaments que convé distingir. Una finestra ancorada avança en graella fixa: el mes comença quan li toca, hi hagis entrat o no. Una finestra rodant comença a comptar des que l'usuari torna, que és el que vols per a un topall per sessió.

Andie recomana

Consultar el saldo no ha d'obrir una finestra. Sona obvi i és fàcil equivocar-se: si l'objecte que representa la finestra es crea en llegir-lo, un usuari que entra a mirar quant li queda engega la seva finestra de sessió sense haver consumit res. Les lectures han de respondre sense escriure ni una sola fila.

Primer el pla, després el comprat, i això canvia la pantalla

L'ordre de cobrament sembla un detall comptable i té una conseqüència visible.

Es cobra primer contra l'assignació del pla i només quan aquesta s'esgota es tira dels crèdits comprats. I el més important: el que paguen els crèdits comprats no compta contra les finestres. Sobreviuen a tots els reinicis i no consumeixen el percentatge del període.

L'efecte pràctic és que el comptador mensual no reflecteix el consum total. Un client que ha gastat la seva assignació i continua treballant amb recàrregues veu la seva barra al cent per cent mentre continua produint. És correcte, i cal explicar-ho a la interfície o generes suport.

Un segon efecte, també correcte i també sorprenent: la finestra més estreta mana. Si el topall setmanal deixa cent crèdits i el mensual cinc mil, un càrrec de tres-cents només pot treure cent del pla, i la resta va contra el comprat. El mensual només avança cent.

El cobrament no ha de rebutjar, la comprovació prèvia sí

Hi ha dos moments en què es pot dir que no: abans de començar i en acabar. Sembla que sigui igual i no ho és, perquè entre l'un i l'altre la feina ja s'ha fet i algú l'ha pagada.

Quan arriba el moment de cobrar, l'operació ja està a mitges. El document s'ha generat, la crida al model ha sortit, el correu s'ha enviat. Rebutjar el cobrament allà no desfà res, només deixa el sistema sense registre d'una cosa que sí que va passar.

Així que cobrar no rebutja mai: registra el consum, i si no hi havia saldo ho apunta com a descobert. El lloc per frenar és abans, amb una comprovació explícita que a més pot dir alguna cosa útil a l'usuari.

Això converteix "preguntar primer" en la part que ha de ser fàcil d'escriure. Si comprovar el saldo és incòmode, la gent se'l salta, i llavors el descobert deixa de ser una xarxa i passa a ser el camí normal.

On són les curses, que és el que ningú preveu

Si véns això, dues peticions simultànies no poden gastar el mateix crèdit. I allà només hi ha un lloc on posar el pany: la fila del compte.

El cobrament obre transacció, bloqueja aquesta fila en exclusiva, rellegeix les finestres dins de la transacció i només llavors calcula quant hi cap. El segon procés espera el primer i veu el saldo ja mogut. Sense la rellegida dins del bloqueig, el segon calcularia sobre dades d'abans i tindries l'actualització perduda de manual.

Hi ha una segona cursa que gairebé tothom passa per alt: la creació del mateix compte. Dues peticions concurrents del primer dia d'un client poden intentar crear-lo alhora. Es resol amb un índex únic i rellegint quan l'insert xoca, no comprovant abans si existeix.

I una que convé acceptar en comptes de tancar: comprovar i cobrar no són atòmics. Dues peticions poden passar totes dues la comprovació i cobrar totes dues. Tancar aquesta finestra exigiria mantenir el bloqueig des d'abans de començar l'operació fins després d'acabar-la, i això serialitza tota la teva aplicació. Com que cobrar no rebutja, el resultat és un petit descobert registrat, no una operació a mitges. És el compromís correcte.

Un reemborsament no torna el mateix que es va gastar

Tornar crèdits sembla l'operació inversa de gastar-los, i amb un sol tipus de saldo ho seria. Així que conviuen l'assignació del pla i les recàrregues comprades, desfer un cobrament obliga a decidir a quina de les dues torna el diner.

Si una operació es va pagar amb l'assignació mensual i falla, el reemborsament natural és un dipòsit. Però un dipòsit entra al saldo comprat, no descompta el comptador de la finestra. El client recupera els seus crèdits al bucket que no caduca, i la seva barra de consum mensual continua marcant la despesa.

No és un error, és la conseqüència que les dues coses visquin en llocs diferents. Però si la teva interfície parla de percentatges, allà hi ha una conversa amb l'usuari que convé preveure.

Què hi ha al mercat i en què es diferencia

A Laravel la referència per cobrar és Cashier, i no competeix amb això: són peces complementàries. Cashier respon què ha pagat el client. Un mesurador respon quant ha gastat del que va pagar. Als nostres projectes conviuen al mateix model.

Hi ha una diferència d'enfocament que sí que importa. La facturació mesurada de les passarel·les reporta l'ús al proveïdor i factura després. Un mesurador local frena abans, sense sortir a la xarxa, que és el que necessites quan el cost de l'operació el poses tu per endavant.

I hi ha un motiu per desacoblar el rellotge de facturació del rellotge de quota: una subscripció anual normalment concedeix una assignació mensual. Una finestra ancorada al període de facturació la reiniciaria un cop l'any.

Larameter, que és el que mantenim, va sortir d'això. A Crowd Legal va substituir un mesurador fet a mà, i el repartiment setmanal del vint-i-cinc per cent es va triar precisament per reproduir el que aquell ja feia. No té res a veure amb IA: allà es cobren crèdits per crear un formulari, generar un document, llançar una enquesta, verificar una identitat i enviar un correu, i cap d'aquestes coses crida un model.

I si no treballes amb Laravel

Les decisions són les mateixes i cap depèn del llenguatge:

Desa el saldo, no el calculis, tan bon punt venguis recàrregues. Separa entrades de sortides en taules diferents. Una xifra per pla i un percentatge per finestra, no un número per període. Reinicia en cobrar, no amb un cron. Bloqueja la fila del compte i rellegeix dins de la transacció. I que cobrar no rebutgi, posant la porta abans de començar l'operació.

Si tot això hi és, el paquet que facis servir dona bastant igual.

On és el codi

El paquet està publicat a Packagist, el codi a GitHub i la documentació del paquet recull totes les opcions. És en producció a Crowd Legal i a Abodara, aquesta última venent recàrregues amb passarel·la.

Si estàs muntant un producte per subscripció amb límits d'ús, és de les parts que més car surt refer. Ho treballem a SaaS a mida i aplicacions web, i si ens expliques el model et diem on són les trampes.

Escrit per
Edu Lazaro
Edu Lazaro
Founder & Lead Developer en AndorraDev

Desenvolupador full-stack amb més de 15 anys d'experiència en Laravel, React, Node.js i arquitectures cloud. Ajudo empreses a Andorra a construir la seva presència digital.

Partner de diseño · ionospace.
Necessites ajuda? ×
Andie by AndorraDev
Assistent IA + equip humà
Assistent IA d'AndorraDev
Andie
Hola! Soc Andie, l'assistent IA d'AndorraDev. En què et puc ajudar? Si necessites parlar amb Edu, només demana-ho.
17:28