Quick Start • Problem • Solution • Project Structure • Configuration • API Reference • Plans & Pricing • Data Model • Development • Testing • Troubleshooting • Architecture • Contributing • License
| Tool | Version | Purpose |
|---|---|---|
| Go | 1.23+ | API Gateway binary |
| Docker & Docker Compose | Latest | Postgres, Redis, sidecar services |
| Python 3.10+ | 3.10+ | Factur-X validation sidecar |
| Make | any | Task runner |
git clone https://github.com/yawo/onefacture.git
cd onefactureCopy the default environment file and edit if needed:
cp .env.example .envStart all services:
make devThis launches:
| Service | Port | Purpose |
|---|---|---|
| onefacture API | 8080 |
Main API Gateway |
| PostgreSQL | 5432 |
Primary data store |
| Redis | 6379 |
Event bus / streams |
| Validation sidecar | 9091 |
Factur-X XML validation (Python) |
make migrate-up# The sandbox route provisions credentials automatically
curl -X POST http://localhost:8080/v1/sandbox/credentials
# Or manually via the API with an admin key
curl -X POST http://localhost:8080/v1/admin/api-keys \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"organization_id": "org-uuid-here", "label": "my-key"}'The response includes an X-API-Key value — use this for all subsequent requests.
curl -X POST http://localhost:8080/v1/invoices \
-H "X-API-Key: $ONEFACTURE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile": "EN16931",
"type_code": "380",
"number": "INV-2026-0001",
"currency": "EUR",
"issue_date": "2026-06-01T00:00:00Z",
"due_date": "2026-07-01T00:00:00Z",
"seller": {
"name": "Acme SAS",
"siren": "732829320",
"address": { "line1": "1 rue Cler", "postal_code": "75007", "city": "Paris", "country_code": "FR" }
},
"buyer": {
"name": "Globex SAS",
"siren": "552120222",
"address": { "line1": "2 avenue Foch", "postal_code": "75116", "city": "Paris", "country_code": "FR" }
},
"lines": [
{ "description": "Consulting services", "quantity": 10, "unit_code": "HUR", "unit_price": 150, "tax_rate": 20, "tax_category": "S" }
]
}'Open http://localhost:8080/docs for the Scalar API reference.
Starting September 1st, 2026, French law mandates that all domestic B2B transactions subject to VAT must be issued, transmitted, and received in electronic format. This isn't simply exchanging PDFs — it requires strict data formats (Factur-X, UBL, CII) and routing through a complex network of Plateformes de Dématérialisation Partenaires (PDP) and the Portail Public de Facturation (PPF) (the "Y-scheme").
For ERP vendors, SaaS platforms, and in-house systems, this creates:
- Fragmentation: 100+ certified platforms (Sage, Pennylane, Docaposte, Cegid, Qonto, etc.), each with their own proprietary API.
- Normative Complexity: Generating PDF/A-3 files with embedded XML (Factur-X) and validating against hundreds of business rules (Schematron / AFNOR) requires heavy tooling.
- Vendor Lock-in: Connecting directly to a single PDP ties your invoicing logic to their specific infrastructure.
onefacture is a unified, open-source API Gateway that abstracts all complexity of the French electronic invoicing ecosystem.
Instead of developing dozens of point-to-point integrations, your application communicates with a single REST API. onefacture handles: Factur-X generation, strict EN 16931 validation, dynamic PDP routing, and lifecycle tracking.
| Standard | Description |
|---|---|
| XP Z12-012 | Invoice message formats and profiles |
| XP Z12-013 | Standard API for business ↔ PA interfacing |
| XP Z12-014 | B2B usage cases |
| EN 16931 | European invoicing standard |
| Factur-X 1.08 | French PDF+XML hybrid format |
onefacture/
├── cmd/
│ ├── api/ # Main API server entrypoint
│ │ └── main.go # Wire-up, graceful shutdown
│ └── genopenapi/ # OpenAPI spec generator
├── internal/
│ ├── adapters/ # PA (Platform Partner) adapters
│ │ ├── chorus/ # Chorus Pro / PPF
│ │ ├── docaposte/ # Docaposte SERES
│ │ ├── pennylane/ # Pennylane
│ │ ├── cegid/ # Cegid
│ │ ├── qonto/ # Qonto
│ │ ├── mock/ # Mock adapter for tests
│ │ ├── sandbox/ # Sandbox mode client
│ │ ├── registry/ # Adapter registry
│ │ └── types.go # PAAdapter interface definition
│ ├── billing/ # Subscription management, usage tracking, Stripe integration
│ │ ├── service.go # Billing service (register, auth, usage checks, Stripe webhooks)
│ │ ├── stripe.go # Stripe checkout / portal / webhook client
│ │ ├── tiers.go # Plan tiers (Free, Starter, Advanced, Enterprise) & limits
│ │ └── password.go # bcrypt password hashing
│ ├── config/ # Configuration loading from env
│ ├── core/
│ │ ├── invoice/ # Core domain: Invoice model, state, totals, formats
│ │ └── facturx/ # Factur-X PDF/XML generation
│ ├── dir/ # PA directory lookup
│ ├── events/ # Event bus (Redis Streams)
│ ├── gateway/
│ │ ├── routes/ # HTTP handlers (CRUD, submission, inbox, validation, billing, auth)
│ │ ├── middleware/ # API key auth, JWT auth, rate limiting, logging, usage tracking
│ │ ├── openapi/ # OpenAPI spec generation
│ │ ├── problem/ # RFC 7807 problem details
│ │ └── server.go # Chi router assembly
│ ├── metrics/ # Prometheus metrics
│ ├── jurisdiction/ # Multi-jurisdiction tax rules
│ ├── reliability/ # Circuit breaker, retry with jitter
│ ├── security/ # KMS/BYOK encryption, AES-256-GCM
│ ├── storage/ # PostgreSQL persistence layer
│ │ ├── migrations/ # SQL migrations (init + SaaS tables)
│ │ ├── users.go # User model & repo
│ │ ├── subscriptions.go # Subscription model & repo
│ │ ├── usage.go # Usage tracking repo
│ │ └── ...
│ ├── validation/ # Validation client (Python sidecar)
│ ├── webhooks/ # Webhook delivery engine (retry, HMAC signing)
│ └── workers/ # Background workers (status polling)
├── sidecar/
│ ├── app/ # Python sidecar: Factur-X validation (lxml, Schematron)
│ └── tests/ # Sidecar tests
├── scripts/ # Utility scripts
├── docs/ # Documentation assets
├── docker-compose.yml # Dev environment
└── Makefile # Build, test, dev commands
All configuration is via environment variables with the ONEFACTURE_ prefix.
| Variable | Required | Default | Description |
|---|---|---|---|
ONEFACTURE_ENV |
no | development |
Runtime environment |
ONEFACTURE_HTTP_ADDR |
no | :8080 |
API listen address |
ONEFACTURE_PUBLIC_BASE_URL |
no | http://localhost:8080 |
Public-facing base URL (CORS, JWT, webhooks) |
ONEFACTURE_DB_DSN |
yes | — | PostgreSQL connection string |
ONEFACTURE_REDIS_ADDR |
yes | localhost:6379 |
Redis address |
ONEFACTURE_SIDECAR_URL |
yes | http://localhost:9091 |
Validation sidecar URL |
ONEFACTURE_LOG_LEVEL |
no | info |
Log level (debug, info, warn, error) |
| Variable | Default | Description |
|---|---|---|
ONEFACTURE_DB_MAX_CONNS |
20 |
Max pool connections |
ONEFACTURE_DB_CONNECT_TIMEOUT |
5s |
Connection timeout |
ONEFACTURE_DB_STATEMENT_CACHE |
true |
Enable prepared statement cache |
| Variable | Description |
|---|---|
ONEFACTURE_ENCRYPTION_KEY_ID |
Local key identifier (for static key mode) |
ONEFACTURE_ENCRYPTION_KEY |
AES-256 key as hex string (64 hex chars) |
ONEFACTURE_KMS_URL |
HTTP KMS broker base URL (for production) |
ONEFACTURE_KMS_TOKEN |
Workload auth token for KMS broker |
| Variable | Default | Description |
|---|---|---|
ONEFACTURE_RATE_LIMIT_PER_MIN |
600 |
Requests per minute per API key |
| Variable | Required | Description |
|---|---|---|
ONEFACTURE_BOOTSTRAP_API_KEY |
no | Initial admin API key for first-time setup |
ONEFACTURE_HASH_PEPPER |
yes (non-dev) | Pepper for API key bcrypt hashing |
| Variable | Required | Default | Description |
|---|---|---|---|
ONEFACTURE_STRIPE_SECRET_KEY |
no | — | Stripe secret key (sk_live_xxx) |
ONEFACTURE_STRIPE_WEBHOOK_SECRET |
no | — | Stripe webhook signing secret (whsec_xxx) |
ONEFACTURE_JWT_SECRET |
no | dev-jwt-secret-change-in-production |
JWT signing key for auth tokens |
ONEFACTURE_PRICE_FREE |
no | — | Stripe price ID for Free tier |
ONEFACTURE_PRICE_STARTER |
no | — | Stripe price ID for Starter tier |
ONEFACTURE_PRICE_ADVANCED |
no | — | Stripe price ID for Advanced tier |
ONEFACTURE_PRICE_ENTERPRISE |
no | — | Stripe price ID for Enterprise tier |
| Variable | Default | Description |
|---|---|---|
ONEFACTURE_DISPLAY_PRICE_FREE |
0 €/mois |
Display price for Free tier |
ONEFACTURE_DISPLAY_PRICE_STARTER |
49 €/mois |
Display price for Starter tier |
ONEFACTURE_DISPLAY_PRICE_ADVANCED |
199 €/mois |
Display price for Advanced tier |
ONEFACTURE_DISPLAY_PRICE_ENTERPRISE |
999 €/mois |
Display price for Enterprise tier |
Overrides per-tier monthly limits. 0 or unset = use hardcoded defaults (50/1k free, 500/10k starter, 5k/100k advanced, unlimited enterprise).
| Variable | Description |
|---|---|
ONEFACTURE_TIER_FREE_INVOICES |
Max invoices/month for Free |
ONEFACTURE_TIER_FREE_API_CALLS |
Max API calls/month for Free |
ONEFACTURE_TIER_STARTER_INVOICES |
Max invoices/month for Starter |
ONEFACTURE_TIER_STARTER_API_CALLS |
Max API calls/month for Starter |
ONEFACTURE_TIER_ADVANCED_INVOICES |
Max invoices/month for Advanced |
ONEFACTURE_TIER_ADVANCED_API_CALLS |
Max API calls/month for Advanced |
ONEFACTURE_TIER_ENTERPRISE_INVOICES |
Max invoices/month for Enterprise |
ONEFACTURE_TIER_ENTERPRISE_API_CALLS |
Max API calls/month for Enterprise |
Each PA adapter has its own prefix:
| Variable | Description |
|---|---|
ONEFACTURE_CHORUS_CLIENT_ID |
Chorus Pro OAuth2 client ID |
ONEFACTURE_CHORUS_CLIENT_SECRET |
Chorus Pro OAuth2 client secret |
ONEFACTURE_CHORUS_BASE_URL |
Chorus Pro API base URL |
ONEFACTURE_PENNYLANE_API_KEY |
Pennylane API key |
ONEFACTURE_PENNYLANE_BASE_URL |
Pennylane API base URL |
ONEFACTURE_DOCAPOSTE_API_KEY |
Docaposte SERES API key |
ONEFACTURE_DOCAPOSTE_BASE_URL |
Docaposte SERES API base URL |
API requests use one of two authentication methods:
API Key — Pass via the X-API-Key header. Used for most endpoints.
curl -H "X-API-Key: $ONEFACTURE_API_KEY" http://localhost:8080/v1/invoicesJWT Bearer Token — Used for billing and user-facing endpoints. Obtain via /auth/login.
curl -H "Authorization: Bearer $JWT_TOKEN" http://localhost:8080/billing/usageAPI keys are organization-scoped. Create one via the sandbox endpoint or admin API.
| Method | Path | Description |
|---|---|---|
POST |
/auth/register |
Register a new organization + admin user |
POST |
/auth/login |
Login with email/password, receive JWT |
GET |
/auth/me |
Get current user and organization info |
| Method | Path | Description |
|---|---|---|
GET |
/billing/plans |
List available pricing plans and their limits |
GET |
/billing/subscription |
Get current subscription tier and status |
POST |
/billing/checkout |
Create a Stripe Checkout session for upgrade |
GET |
/billing/portal |
Open Stripe Customer Portal to manage subscription |
GET |
/billing/usage |
Get current monthly usage vs plan limits |
POST |
/billing/stripe-webhook |
Stripe event webhook (signature-verified, public) |
| Method | Path | Description |
|---|---|---|
POST |
/v1/invoices |
Create and optionally submit an invoice |
GET |
/v1/invoices/{id} |
Get invoice details and current status |
GET |
/v1/invoices/{id}/timeline |
Full lifecycle event history |
POST |
/v1/invoices/{id}/submit |
Submit a DRAFT invoice to the PA |
POST |
/v1/invoices/{id}/retry |
Resolve a rejection and resubmit |
POST |
/v1/invoices/{id}/cancel |
Cancel an invoice |
| Method | Path | Description |
|---|---|---|
GET |
/v1/inbox |
List invoices received from the network |
GET |
/v1/inbox/{id} |
Get a received invoice |
POST |
/v1/inbox/{id}/approve |
Approve a received invoice |
POST |
/v1/inbox/{id}/reject |
Reject a received invoice |
| Method | Path | Description |
|---|---|---|
POST |
/v1/validate |
Validate a Factur-X/UBL/CII file (multipart upload) |
| Method | Path | Description |
|---|---|---|
POST |
/v1/webhooks |
Register a webhook endpoint |
GET |
/v1/webhooks |
List registered endpoints |
PUT |
/v1/webhooks/{id} |
Update a webhook endpoint |
DELETE |
/v1/webhooks/{id} |
Delete a webhook endpoint |
GET |
/v1/webhooks/deliveries |
List delivery logs |
| Method | Path | Description |
|---|---|---|
GET |
/v1/directory/lookup |
Look up a recipient's PA by SIREN |
GET |
/v1/platforms |
List supported PAs and their health status |
| Method | Path | Description |
|---|---|---|
POST |
/v1/sandbox/credentials |
Provision sandbox API credentials |
All errors follow RFC 7807 (Problem Details):
{
"type": "https://onefacture.io/errors/validation-failed",
"title": "Validation Failed",
"status": 422,
"detail": "Invoice validation failed: line 1 tax rate must be positive",
"instance": "/v1/invoices",
"errors": [
{ "field": "lines[0].tax_rate", "code": "INVALID", "message": "must be positive" }
]
}Based on Factur-X 1.08 / EN 16931.
| Field | Type | Description |
|---|---|---|
id |
uuid |
Unique invoice identifier |
status |
string |
DRAFT, SUBMITTED, RECEIVED, REJECTED, PAID, CANCELLED |
profile |
string |
MINIMUM, BASIC, EN16931, EXTENDED |
type_code |
string |
380 (invoice), 381 (credit note), 384 (corrective) |
number |
string |
Invoice number from the issuer |
currency |
string |
ISO 4217 currency code (default: EUR) |
issue_date |
date |
Date of issue |
due_date |
date |
Payment due date |
seller |
object |
Seller information (name, siren, address, VAT ID, email) |
buyer |
object |
Buyer information |
lines |
array |
Invoice lines |
totals |
object |
Computed totals (net, tax, gross) |
notes |
array |
Structured notes (credit note reason, corrections) |
DRAFT ──→ SUBMITTED ──→ RECEIVED ──→ APPROVED ──→ PAID
│ │
↓ ↓
REJECTED REJECTED
│
↓
CANCELLED
Invoices generate webhook events on every status transition:
| Event | Description |
|---|---|
invoice.submitted |
Invoice submitted to PA |
invoice.received |
Invoice received via network |
invoice.approved |
Invoice approved by buyer |
invoice.rejected |
Invoice rejected |
invoice.paid |
Invoice marked paid |
invoice.cancelled |
Invoice cancelled |
make dev # Start all services with Docker Compose
make build # Build the API binary
make test # Run unit tests
make test-short # Run tests excluding integration (no Docker needed)
make test-race # Run tests with race detector
make lint # Run golangci-lint
make migrate-up # Run database migrations
make migrate-down # Rollback migrations
make coverage # Generate coverage report
make clean # Stop services and clean build artifactsThe Python validation sidecar runs on port 9091:
cd sidecar
pip install -r requirements.txt
python app/main.py- Go 1.23+ — Standard library preferred
- Error wrapping — Always use
fmt.Errorf("context: %w", err) - Testing — Unit tests live alongside source (
*_test.go), integration tests use//go:build integrationtag - Secrets — Never hardcode; always use
ONEFACTURE_*env vars - Lint —
golangci-lintmust pass without warnings
We follow Conventional Commits:
feat: add Docaposte adapter
fix: correct tax rounding in totals calculation
chore: update dependencies
docs: add webhook delivery documentation
| Tag | Command | Requirements | Coverage |
|---|---|---|---|
| Unit (no tag) | go test ./... |
None | Core logic, handlers, validation rules |
| Integration | go test -tags=integration ./... |
Docker (Postgres, Redis) | DB, event bus, full workflows |
# Quick check (no Docker needed)
go test -short ./...
# Full suite with race detection
go test -race -short ./...
# Integration tests (Docker required)
make test
# For CI without Docker
go test -short ./... # Skips all integration tests automatically- Add
//go:build integrationat the top of the test file - Use
testcontainers-gofor Postgres/Redis in tests - Tagged files are excluded from
go test ./...andgo test -short ./...
# Ensure Go 1.23+
go version
# Clear build cache if you see stale errors
go clean -cache
# Verify dependencies
go mod tidy# Check Docker is running
docker ps
# Rebuild and restart
docker compose down -v
make dev
# Check logs
docker compose logs postgres redis# Run migrations explicitly
make migrate-up
# Reset database
docker compose down -v
docker compose up -d postgres
sleep 3
make migrate-up# Check sidecar is running
curl http://localhost:9091/health
# View sidecar logs
docker compose logs validator
# Run sidecar directly (for debugging)
cd sidecar
pip install -r requirements.txt
python -m pytest tests/ -v
python app/main.py --port 9091# Provision a new sandbox key
curl -X POST http://localhost:8080/v1/sandbox/credentials
# Verify the key works
curl -H "X-API-Key: <key>" http://localhost:8080/v1/platforms┌─────────────────────────────────────────────────┐
│ Client / ERP / SaaS │
│ (Sage, Odoo, custom app...) │
└───────────────────┬─────────────────────────────┘
│ REST API onefacture (OpenAPI 3.1)
▼
┌──────────────────────────────────────────────────────┐
│ onefacture API Gateway │
│ ┌──────────┐ ┌───────────┐ ┌────────────────────┐ │
│ │ Auth │ │ Router │ │ Validation Pipeline │ │
│ │ (API Key)│ │ (Chi) │ │ (Sidecar gRPC) │ │
│ └──────────┘ └───────────┘ └────────────────────┘ │
│ ┌──────────┐ ┌───────────┐ ┌────────────────────┐ │
│ │ Rate │ │ OpenAPI │ │ Adapter Registry │ │
│ │ Limiter │ │ Docs │ │ & Router │ │
│ └──────────┘ └───────────┘ └────────────────────┘ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Reliability Layer (Retry / Circuit Breaker)│ │
│ └──────────────────────────────────────────────┘ │
└──────────┬──────────────────────────────┬────────────┘
│ │
┌──────▼──────┐ ┌─────▼──────┐
│ PostgreSQL │ │ Redis │
│ + pgvector │ │ Streams │
└─────────────┘ └────────────┘
│ │
┌──────▼──────────────────────────────▼──────┐
│ Validation Sidecar (Python / lxml) │
│ │ XSD │ Schematron │ Business Rules │ │
└─────────────────────────────────────────────┘
| Layer | Technology | Purpose |
|---|---|---|
| API Gateway | Go 1.23+, Chi router | High-concurrency HTTP server |
| Validation | Python sidecar (lxml, Schematron) | Factur-X/UBL/CII validation |
| Database | PostgreSQL + pgx v5 | Multi-tenant persistence |
| Event Bus | Redis Streams (go-redis v9) | Async webhook delivery |
| Encryption | AES-256-GCM, BYOK/KMS | At-rest document encryption |
| Documentation | OpenAPI 3.1 + Scalar | Interactive API docs |
- Circuit Breaker — 3 consecutive failures → open for 30s (applies to all PA adapter calls)
- Retry with jitter — Up to 3 attempts, exponential backoff (50ms base → 2s max) with ±25% jitter
- Dead Letter Queue — Rejected submissions are persisted for manual review and replay
- Webhook delivery — Up to 8 retries with exponential backoff, HMAC-SHA256 signing
- API Key authentication — Organization-scoped keys with bcrypt hashing at rest
- Rate limiting — Per-key Redis-based sliding window
- Encryption at rest — AES-256-GCM envelope encryption for invoice XML/PDF payloads
- Webhook signing — HMAC-SHA256 signatures on all outgoing webhooks
- IP allowlisting — Optional IP allowlist per webhook endpoint
- mTLS — Optional mutual TLS for webhook endpoints
type PAAdapter interface {
Name() string
Submit(ctx context.Context, inv *Invoice) (*SubmitResult, error)
GetStatus(ctx context.Context, paRef string) (*LifecycleEvent, error)
Webhook(ctx context.Context, payload []byte) (*WebhookEvent, error)
HealthCheck(ctx context.Context) error
}Each adapter is wrapped with the reliability layer (retry + circuit breaker) automatically.
| PA | Status | Auth |
|---|---|---|
| Chorus Pro / PPF | OAuth2 client credentials | |
| Pennylane | API Key | |
| Docaposte (SERES) | API Key | |
| Cegid | API Key | |
| Qonto | API Key |
- Create a new package under
internal/adapters/<name>/ - Implement the
PAAdapterinterface - Register it in
internal/adapters/registry/ - Document env vars required for configuration
onefacture validates invoices through 6 layers:
PDF/A-3 → XML Extraction → XSD Schema → Schematron → Business → Score
- PDF/A-3 — Validate the PDF container (if Factur-X format)
- Extraction — Extract embedded CII or UBL XML
- XSD — Validate against EN 16931 schemas
- Schematron — Apply AFNOR business rules (XP Z12-012)
- Business — SIREN coherence, VAT structure, total calculations
- Score — Structured error report (RFC 7807)
{
"profile": "EN16931",
"type_code": "381",
"number": "AV-2026-0001",
"currency": "EUR",
"issue_date": "2026-05-22T00:00:00Z",
"seller": { "name": "Acme SAS", "siren": "732829320", "address": { "line1": "1 rue Cler", "postal_code": "75007", "city": "Paris", "country_code": "FR" } },
"buyer": { "name": "Globex SAS", "siren": "552120222", "address": { "line1": "2 avenue Foch", "postal_code": "75116", "city": "Paris", "country_code": "FR" } },
"lines": [{ "description": "Credit note", "quantity": 1, "unit_code": "C62", "unit_price": 250, "tax_rate": 20, "tax_category": "S" }],
"notes": [{ "subject": "credit_note", "content": "Credit for INV-2026-0007" }]
}curl -X POST "http://localhost:8080/v1/invoices/{invoice_id}/retry" \
-H "X-API-Key: $ONEFACTURE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"resolution_hint": "SIREN acheteur corrige dans ERP"}'onefacture offers four subscription tiers with usage-based limits enforced per organization.
| Tier | Price | Invoices/mo | API Calls/mo | Support | SLA | Dashboard | Onboarding |
|---|---|---|---|---|---|---|---|
| Free | 0 €/mois | 50 | 1 000 | Community | none | — | Self-serve |
| Starter | 49 €/mois | 500 | 10 000 | 99.5% | Self-serve | ||
| Advanced | 199 €/mois | 5 000 | 100 000 | Priority | 99.9% | ||
| Enterprise | 999 €/mois | Illimité | Illimité | Premium | 99.99% | Dédié |
Usage is tracked monthly — when a limit is reached, the API returns 429 Too Many Requests. Upgrade via the Stripe Checkout session (POST /billing/checkout) or manage from the Customer Portal.
- Registration —
POST /auth/registercreates an organization + admin user + Free subscription. - Authentication —
POST /auth/loginreturns a JWT used for billing endpoints. - Check usage —
GET /billing/usageshows current month consumption vs plan limits. - Upgrade —
POST /billing/checkoutwith{"tier": "starter", "success_url": "...", "cancel_url": "..."}redirects to Stripe. - Manage —
GET /billing/portalopens Stripe Customer Portal for plan changes / payment methods.
All subscriptions sync in real-time via Stripe webhooks (POST /billing/stripe-webhook).
- Fork the repository
- Create a feature branch (
git checkout -b feat/my-feature) - Make your changes
- Run tests (
go test -short ./...) - Run linter (
golangci-lint run) - Commit with Conventional Commits (
feat:,fix:,docs:, etc.) - Open a pull request
-
go build ./...passes -
go vet ./...passes -
go test -short ./...passes -
golangci-lint runpasses - Code follows project style (error wrapping, naming conventions)
- Tests cover new functionality
- Integration tests are tagged with
//go:build integration
Ce projet est distribué sous licence Server Side Public License (SSPL). Voir le fichier LICENSE pour plus de détails.
This project is licensed under the Server Side Public License (SSPL). See the LICENSE file for details.
onefacture — Unified e-invoicing for France. Built with in Paris.
