Facturare recurentă pentru SaaS în România: Stripe Subscriptions, SmartBill, ANAF, EU VAT OSS
Ghid recurring billing pentru SaaS RO: Stripe Subscriptions, SmartBill, e-Factura, EU VAT OSS, anulări și rambursări. Arhitectură și cod practic.
Cuprins
- Cum arată arhitectura facturării recurente cu Stripe și SmartBill?
- Cum configurezi Stripe Subscriptions pentru abonamente?
- Cum configurezi SmartBill pentru facturi recurente?
- Cum tratezi evenimentul invoice.payment_succeeded?
- Cum aplici EU VAT OSS pentru clienții din alte state UE?
- Cum tratezi anulările și rambursările?
- Care sunt erorile comune la facturarea recurentă?
- De ce crawlerra pentru facturarea recurentă?
Ghid practic pentru implementarea facturării recurente într-un SaaS care vinde în România, având clienți B2B și B2C din RO și din alte state UE. Combinația arhitecturală standard: Stripe Subscriptions pentru gestiunea plății recurente, SmartBill pentru emiterea facturii fiscale conform Codului Fiscal românesc, raportarea automată în e-Factura pentru clienții români B2B, plus gestiunea EU VAT OSS pentru clienți din alte state UE1.
Stripe Subscriptions este excelent la partea de plată recurentă (storing card data, smart retries, dunning, prorated upgrades), dar nu emite factură fiscală conformă Codului Fiscal RO. Acea parte rămâne în responsabilitatea ta, iar SmartBill (sau Oblio, FGO) este interfața practică spre regimul fiscal local. Pentru detaliile comparative ale celor trei SaaS-uri, vezi entry-ul despre factura proforma și comparația vendorilor.
Cum arată arhitectura facturării recurente cu Stripe și SmartBill?
Arhitectura este o buclă lunară: Stripe încasează abonamentul și livrează webhook-ul, handler-ul tău îl înregistrează, iar un job de fundal emite factura fiscală prin SmartBill, cu trimitere automată în e-Factura. Fluxul de bază:
- Clientul se înscrie pe platforma ta SaaS, alege un plan, atașează un card prin Stripe Checkout sau Payment Element.
- Stripe creează un
Customerși oSubscriptioncu prețul ales. Prima plată este procesată imediat. - Stripe livrează webhook-ul
invoice.payment_succeededla endpoint-ul tău. - Handler-ul tău verifică semnătura, înregistrează evenimentul pentru idempotență, pune un job în coadă pentru emiterea facturii fiscale.
- Job-ul de fundal apelează SmartBill API, emite factura fiscală cu numărul corespunzător, SmartBill o trimite în e-Factura.
- Pentru lunile următoare, Stripe re-livrează același webhook la fiecare reînnoire automată; flow-ul se repetă identic.
Decuplarea între webhook receiver (rapid, în memorie) și emiterea facturii (lent, prin coadă) este crucială. Stripe se așteaptă la răspuns în sub 30 secunde; SmartBill poate fi lent la rate limiting2. Coada absoarbe diferența.
Cum configurezi Stripe Subscriptions pentru abonamente?
În Stripe Subscriptions configurezi patru lucruri: Products și Prices, Smart Retries, dunning emails și webhook endpoint-ul:
- Products și Prices. Definește produsele (planurile tale: Basic, Pro, Enterprise) și prețurile (lunar, anual, în RON sau EUR). Stripe acceptă mai multe valute pe același produs.
- Smart Retries. În setări Stripe Billing, activează retry-urile pentru plăți eșuate (recomandare: 4 încercări pe parcursul a 2 săptămâni).
- Dunning emails. Email-uri automate către client la fiecare plată eșuată, plus alert final înainte de anulare.
- Webhook endpoint. URL public care primește evenimentele relevante:
invoice.payment_succeeded,invoice.payment_failed,customer.subscription.deleted,charge.refunded.
Webhook signing secret este stocat ca env var; verificarea semnăturii este prima operație în handler.
Cum configurezi SmartBill pentru facturi recurente?
În SmartBill configurezi o serie dedicată abonamentelor, șablonul de factură, trimiterea automată în e-Factura și validarea CUI-ului de client:
- Serie dedicată. O serie de facturi separată pentru abonamenți (de exemplu, „SUB" sau „ABO") simplifică reconcilierea ulterioară.
- Șablon de factură. Cu cota TVA corectă pentru piață (19% standard RO), termen de plată „achitată" (pentru că plata a fost deja procesată prin Stripe), și mențiunea explicită a perioadei de abonament acoperite („Abonament Pro, perioada iunie 2026").
- Trimitere automată în e-Factura. Activă pe serie pentru clienții B2B RO. Pentru B2C RO și pentru clienți UE, e-Factura nu se aplică, deci nu trebuie activată.
- Validare CUI client. Pentru B2B RO, SmartBill validează CUI-ul la emitere. Validează tu însuți preventiv prin API-ul ANAF de verificare contribuabili, ca să eviți respingerile la emitere.
Cum tratezi evenimentul invoice.payment_succeeded?
La fiecare plată reușită, handler-ul înregistrează evenimentul și declanșează emiterea facturii prin coadă. În Node.js cu Express:
app.post('/webhooks/stripe',
express.raw({type: 'application/json'}),
async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
if (event.type !== 'invoice.payment_succeeded') {
return res.json({received: true, ignored: true});
}
const exists = await db.events.findById(event.id);
if (exists) return res.json({received: true, dedupe: true});
await db.events.insert({id: event.id, type: event.type, ts: Date.now()});
const stripeInvoice = event.data.object;
const customer = await stripe.customers.retrieve(stripeInvoice.customer);
await queue.publish('emit-fiscal-invoice', {
eventId: event.id,
stripeInvoiceId: stripeInvoice.id,
customerId: customer.id,
amount: stripeInvoice.amount_paid,
currency: stripeInvoice.currency,
periodStart: stripeInvoice.period_start,
periodEnd: stripeInvoice.period_end,
taxContext: determineTaxContext(customer),
});
res.json({received: true});
}
);
Job-ul de fundal preia evenimentul, construiește payload-ul SmartBill având cota TVA corespunzătoare contextului fiscal, emite factura, înregistrează rezultatul în baza ta de date.
Cum aplici EU VAT OSS pentru clienții din alte state UE?
Pentru SaaS care vinde servicii digitale către consumatori finali din UE, regimul aplicabil este EU VAT OSS (One Stop Shop). Esența:
- Cota TVA aplicată este cea a țării clientului, nu a țării tale. Pentru un client din Germania, aplici 19% TVA Germania; pentru Franța, 20% TVA Franța; pentru Estonia, 22% TVA Estonia.
- Raportare trimestrială prin formularul OSS depus la ANAF, având totalurile de TVA defalcate pe țări UE.
- Pentru B2B UE, regimul este taxare inversă: validezi CUI-ul prin VIES API, emiți factură fără TVA, cu mențiunea „Reverse charge" și CUI-ul clientului. Clientul își aplică TVA-ul în țara lui.
- Pentru clienți non-UE, în general fără TVA, cu mențiunea „Export servicii".
SmartBill expune câmpuri pentru regim de TVA și pentru țara clientului care permit emiterea facturii având cota corectă. Validarea CUI UE prin VIES trebuie făcută în handler-ul tău preventiv; SmartBill acceptă CUI-ul ca șir, dar nu îl validează contra VIES.
Cum tratezi anulările și rambursările?
Cele două scenarii se tratează diferit: anularea la sfârșit de perioadă și anularea cu rambursare pro-rata:
- Anulare la sfârșit de perioadă (
customer.subscription.deletedcuat_period_end: true). Factura pentru perioada curentă rămâne valabilă; nu emiți storno. Abonamentul pur și simplu nu se mai reînnoiește. - Anulare cu rambursare pro-rata (
charge.refunded+ politică internă de pro-rata). Emiți storno pentru suma rambursată; factura originală rămâne, storno-ul anulează doar partea rambursată. Vezi entry-ul despre factura proforma și ghidul de storno pentru detalii structurale.
Pentru ambele scenarii, idempotența este crucială: webhook-ul de refund poate ajunge de mai multe ori, iar dublarea storno-ului produce probleme fiscale reale (TVA dublu deductibil). Folosește charge.id al refund-ului ca cheie de unicitate.
Care sunt erorile comune la facturarea recurentă?
Cele patru erori recurente sunt CUI-ul de client invalid la emitere, cota de TVA greșită pentru clienți UE, dublarea facturii la prima încercare eșuată și sumele nepotrivite în raportul OSS:
- CUI client invalid la emitere: SmartBill returnează 400 cu mesaj „CUI invalid". Validează preventiv prin API-ul ANAF (pentru RO) sau VIES (pentru UE).
- Cotă TVA greșită pentru cliente UE: ai aplicat 19% RO în loc de cota țării clientului. Re-emite factura având cota corectă, plus storno pentru cea greșită.
- Dublarea facturii la prima încercare eșuată: Stripe a livrat
invoice.payment_succeededde două ori; idempotența peevent.idprevine acest scenariu. - OSS report având sume nepotrivite: ai amestecat tranzacțiile B2B (cu taxare inversă) și cele B2C (având TVA țară client) la generarea OSS. Filtrează strict pe tipul fiscal al fiecărei facturi.
De ce crawlerra pentru facturarea recurentă?
Facturarea recurentă pentru SaaS RO este un proiect cu multe muchii: Stripe Subscriptions plus SmartBill plus e-Factura plus EU VAT OSS plus gestiunea anulărilor și rambursărilor. Pe crawlerra construim astfel de fluxuri ca module reutilizabile: handler webhook tipizat având idempotență built-in, client SmartBill cu retry exponențial, generator OSS report automatizat trimestrial, alertare la inconsistențe (CUI invalid, cotă TVA greșită) în Discord. Pentru contextul fiscal mai larg, vezi e-Factura și SAF-T; pentru autentificarea pe API-urile relevante, semantica JWT ajută.
- Stripe Subscriptions documentation: docs.stripe.com/billing/subscriptions. SmartBill API: api.smartbill.ro. EU VAT OSS: ec.europa.eu/taxation_customs/business/vat/oss. Combinația celor trei, plus regulile Codului Fiscal RO pentru factură fiscală și pentru raportarea în e-Factura, constituie sursa de adevăr pentru implementarea în producție.
[seo.recurring_saas_docs] - Caracteristicile rate limit-ului SmartBill observate în clientul nostru de producție: aproximativ 3 cereri pe secundă, depășirea returnează 403 (nu 429) pentru până la zece minute, fără headers care să declare limita. Clientul nostru tratează 403 ca semnal de rate limit și aplică un backoff de 60 secunde înainte de retransmitere.
[crawlerra.smartbill_backoff]
Întrebări frecvente
Pot folosi Stripe Subscriptions fără SmartBill pentru clienți RO?
Nu. Stripe Subscriptions gestionează plata recurentă, dar nu emite factură fiscală conformă Codului Fiscal românesc. Documentul PDF generat de Stripe nu are numerotare fiscală în seria firmei tale, nu este raportat în e-Factura și nu îndeplinește obligația contabilă RO. Pentru clienți B2B din România, factura fiscală trebuie emisă printr-un sistem care respectă regimul fiscal RO (SmartBill, Oblio, FGO sau implementare proprie), iar Stripe rămâne la rolul de procesator de plată.
Cum gestionez TVA-ul pentru clienți din alte state UE?
Folosești EU VAT OSS (One Stop Shop) pentru servicii digitale către consumatori finali, sau taxare inversă pentru servicii B2B. Pentru B2C UE, TVA-ul aplicat este cota din țara clientului, raportat trimestrial prin OSS. Pentru B2B UE (clientul are CUI valid), aplici taxare inversă (mențiune explicită pe factură, fără TVA aplicat). Pentru clienți non-UE, în general fără TVA cu mențiune de export servicii. Validează CUI-ul UE prin VIES API înainte de a aplica taxare inversă.
Ce se întâmplă cu factura când clientul își anulează abonamentul?
Facturile deja emise rămân valabile pentru perioada deja consumată; storno-ul intervine doar pentru perioada neutilizată (dacă politica ta acordă pro-rata refund). Stripe handle-uiește anularea logică (subscription canceled), dar emiterea facturii pentru ultima perioadă consumată plus eventualul storno pro-rata este responsabilitatea sistemului tău. Webhook-urile customer.subscription.deleted și charge.refunded sunt punctele de plecare pentru cele două flow-uri.
Cum gestionez plățile eșuate (failed payments) recurente?
Stripe oferă smart retries (retry-uri automate cu strategii de recuperare) și dunning emails pentru a anunța clientul. Pentru SaaS RO, configurează Stripe Smart Retries (3-4 încercări pe parcursul a 2 săptămâni), email-uri automate către client la fiecare eșec, plus webhook-ul invoice.payment_failed care îți permite să suspenzi accesul la serviciu după N eșecuri consecutive. Factura fiscală este emisă doar la plata reușită, nu la prima încercare.