Facturare
Facturare
API-ul de facturare încapsulează Stripe pentru ca Portalul (și orice alt client autentificat) să poată porni un checkout, să deschidă portalul client, să citească starea abonamentului, să anuleze sau să reia un abonament, să listeze facturi și — pe partea de Stripe — să primească evenimentele de webhook care actualizează planul organizației. Toate rutele sunt sub /api/v1/billing.
jwtAuth); endpoint-urile care modifică date mai cer ca utilizatorul apelant să aibă rolul de Owner sau Admin. Nu există nicio permisiune de cheie API care să dea acces la facturare — autentifică-te prin POST /api/v1/auth/login și folosește token-ul de acces returnat ca Authorization: Bearer <jwt>. Vezi Autentificare › Folosește autentificarea JWT.Formatul de eroare, limitele de rată și convențiile de paginare sunt documentate o singură dată pe Prezentarea API-ului; pe această pagină listăm doar codurile de eroare specifice fiecărui endpoint.
POST /api/v1/billing/create-subscription
Creează o sesiune Stripe Checkout pentru organizația utilizatorului autentificat. Dacă organizația are deja un client Stripe (stripeCustomerId), clientul existent este reutilizat; altfel se creează unul nou. Returnează sessionId-ul Stripe, url-ul găzduit (opțional) și clientSecret-ul (opțional) pentru checkout încorporat.
- Autentificare: JWT din Portal, rol Owner sau Admin.
Corpul cererii
| Câmp | Tip | Obligatoriu | Note |
|---|---|---|---|
priceId | string | nu | ID-ul de preț Stripe. Implicit este prețul Pro configurat (stripe.proPriceId); eșuează dacă niciuna nu e furnizată. |
successUrl | string | da | URL absolut către care Stripe redirecționează după un checkout reușit. |
cancelUrl | string | da | URL absolut către care Stripe redirecționează dacă utilizatorul renunță. |
Răspuns (200 OK)
{
"sessionId": "cs_test_a1b2c3",
"url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3",
"clientSecret": null
}
Exemplu
curl -X POST https://api.e-bon.ro/api/v1/billing/create-subscription \
-H "Authorization: Bearer <portal-jwt>" \
-H "Content-Type: application/json" \
-d '{
"successUrl": "https://app.e-bon.ro/billing/success",
"cancelUrl": "https://app.e-bon.ro/billing/cancel"
}'
Coduri de eroare
VALIDATION_ERROR(400) — corpul cererii nu a trecut validarea Zod (URL-uri lipsă/invalide,priceIdgol).BAD_REQUEST(400) — nu s-a furnizatpriceIdși nu există un preț Pro configurat implicit pe server.UNAUTHORIZED(401) — JWT lipsă sau invalid.FORBIDDEN(403) — apelantul nu are rolul Owner sau Admin.NOT_FOUND(404) — documentul organizației lipsește.
Catalogul HTTP complet este pe Prezentarea API-ului › Catalogul codurilor de eroare HTTP.
POST /api/v1/billing/portal
Creează o sesiune de Portal Client Stripe pentru ca utilizatorul să-și gestioneze singur abonamentul, să-și actualizeze metodele de plată și să-și vadă facturile pe pagina găzduită de Stripe.
- Autentificare: JWT din Portal, rol Owner sau Admin.
Corpul cererii
| Câmp | Tip | Obligatoriu | Note |
|---|---|---|---|
returnUrl | string | da | URL absolut către care Stripe redirecționează după ce utilizatorul termină. |
Răspuns (200 OK)
{
"url": "https://billing.stripe.com/p/session/abc123"
}
Exemplu
curl -X POST https://api.e-bon.ro/api/v1/billing/portal \
-H "Authorization: Bearer <portal-jwt>" \
-H "Content-Type: application/json" \
-d '{ "returnUrl": "https://app.e-bon.ro/billing" }'
Coduri de eroare
VALIDATION_ERROR(400) — corpul cererii nu a trecut validarea Zod (returnUrllipsă/invalid).BAD_REQUEST(400) — organizația nu are un abonament activ (stripeCustomerIdnu este setat).UNAUTHORIZED(401) — JWT lipsă sau invalid.FORBIDDEN(403) — apelantul nu are rolul Owner sau Admin.NOT_FOUND(404) — documentul organizației lipsește.
GET /api/v1/billing/subscription
Returnează starea curentă a abonamentului pentru organizația utilizatorului autentificat. Dacă organizația nu are încă un abonament Stripe, răspunsul este { "plan": "free", "status": null, "subscription": null } — ruta returnează tot 200 OK în acest caz.
- Autentificare: JWT din Portal (orice membru autentificat al organizației).
Răspuns (200 OK)
{
"plan": "pro",
"status": "active",
"subscription": {
"id": "sub_abc123",
"customerId": "cus_abc123",
"priceId": "price_pro",
"currentPeriodEnd": "2026-05-09T00:00:00.000Z",
"cancelAtPeriodEnd": false,
"status": "active"
}
}
Exemplu
curl https://api.e-bon.ro/api/v1/billing/subscription \
-H "Authorization: Bearer <portal-jwt>"
Coduri de eroare
UNAUTHORIZED(401) — JWT lipsă sau invalid.NOT_FOUND(404) — documentul organizației lipsește.
POST /api/v1/billing/cancel
Programează abonamentul organizației pentru anulare la sfârșitul perioadei de facturare curente. Planul rămâne utilizabil până la currentPeriodEnd.
- Autentificare: JWT din Portal, rol Owner sau Admin.
Răspuns (200 OK)
{
"subscription": {
"id": "sub_abc123",
"status": "active",
"cancelAtPeriodEnd": true,
"currentPeriodEnd": "2026-05-09T00:00:00.000Z"
}
}
Exemplu
curl -X POST https://api.e-bon.ro/api/v1/billing/cancel \
-H "Authorization: Bearer <portal-jwt>"
Coduri de eroare
BAD_REQUEST(400) — nu există un abonament activ care să poată fi anulat.UNAUTHORIZED(401) — JWT lipsă sau invalid.FORBIDDEN(403) — apelantul nu are rolul Owner sau Admin.NOT_FOUND(404) — documentul organizației lipsește.
POST /api/v1/billing/resume
Reia un abonament programat anterior pentru anulare. Resetează cancelAtPeriodEnd.
- Autentificare: JWT din Portal, rol Owner sau Admin.
Răspuns (200 OK)
{
"subscription": {
"id": "sub_abc123",
"status": "active",
"cancelAtPeriodEnd": false,
"currentPeriodEnd": "2026-05-09T00:00:00.000Z"
}
}
Exemplu
curl -X POST https://api.e-bon.ro/api/v1/billing/resume \
-H "Authorization: Bearer <portal-jwt>"
Coduri de eroare
BAD_REQUEST(400) — nu există un abonament activ care să poată fi reluat.UNAUTHORIZED(401) — JWT lipsă sau invalid.FORBIDDEN(403) — apelantul nu are rolul Owner sau Admin.NOT_FOUND(404) — documentul organizației lipsește.
GET /api/v1/billing/invoices
Returnează o listă paginată de facturi Stripe pentru organizație. Paginarea folosește cursorul startingAfter al Stripe (ID-ul ultimei facturi din pagina anterioară), nu convenția de cursor folosită de Bonuri. Dacă organizația nu are încă un client Stripe, răspunsul este { "invoices": [], "hasMore": false } cu 200 OK.
- Autentificare: JWT din Portal (orice membru autentificat al organizației).
Parametri query
| Parametru | Tip | Implicit | Note |
|---|---|---|---|
limit | număr | 10 | Mărimea paginii, 1–100. |
startingAfter | string | — | ID-ul facturii Stripe folosit ca cursor. Returnează facturi create după aceasta. |
Răspuns (200 OK)
{
"invoices": [
{
"id": "in_abc123",
"number": "ACME-0001",
"date": 1712649600,
"amountDue": 4900,
"currency": "ron",
"status": "paid",
"pdfUrl": "https://files.stripe.com/.../invoice.pdf",
"hostedInvoiceUrl": "https://invoice.stripe.com/i/acct_…"
}
],
"hasMore": true
}
Exemplu
curl "https://api.e-bon.ro/api/v1/billing/invoices?limit=20" \
-H "Authorization: Bearer <portal-jwt>"
Apoi pentru pagina următoare:
curl "https://api.e-bon.ro/api/v1/billing/invoices?limit=20&startingAfter=in_abc123" \
-H "Authorization: Bearer <portal-jwt>"
Coduri de eroare
VALIDATION_ERROR(400) — query invalid (limit > 100).UNAUTHORIZED(401) — JWT lipsă sau invalid.NOT_FOUND(404) — documentul organizației lipsește.
POST /api/v1/billing/webhook
Handler pentru webhook-urile inbound de la Stripe. Stripe apelează singur acest endpoint — nu îl invoca tu. Ruta nu trece prin middleware-ul obișnuit de autentificare, dar cere:
- Antet
Stripe-Signature. - Corpul brut al cererii (ruta depinde de parsing pe raw body — input-ul deja parsat ca JSON este respins).
Semnătura este verificată față de cheia secretă de webhook configurată în Stripe; dacă verificarea reușește, evenimentul actualizează plan-ul organizației, stripeCustomerId, stripeSubscriptionId și câmpurile asociate.
Răspuns (200 OK)
{ "received": true }
Coduri de eroare
BAD_REQUEST(400) — antetulStripe-Signaturelipsește, corpul cererii nu a fost primit caBuffersau verificarea semnăturii a eșuat față de cheia secretă de webhook configurată.
curl — folosește Stripe CLI (stripe listen --forward-to) când dezvolți local și configurează URL-ul de producție direct din Dashboard-ul Stripe.Vezi și
- Autentificare — fluxul de login JWT folosit de fiecare endpoint de facturare de mai sus.
- Prezentarea API-ului — URL de bază, plicul de eroare, limite de rată, idempotență, paginare.
- Webhook-uri — webhook-uri outbound de la e-bon către URL-ul tău (sistem separat de webhook-ul Stripe).
Webhook-uri
Endpoint-uri REST pentru gestionarea abonamentelor webhook — creare, listare, actualizare, ștergere, rotire chei de semnare, livrări de test și inspecția istoricului de livrări.
Instanțe ale aplicației
Endpoint REST pentru a inspecta ce instanțe ale aplicației mobile E-BON sunt în prezent conectate prin WebSocket și fac bridge la dispozitivele fiscale ale organizației.