Pular para o conteúdo principal

Endpoints de cobrança

Grupo /billing/* e /platform/organizations/{id}/billing/* — consulta de planos, overview de uso/quota, geração de sessão de checkout e acesso ao portal do cliente.

A implementação atrás pode ser Stripe, o gateway mock (para dev), ou disabled — depende de NATIVE_WAZUH_BILLING_GATEWAY.

Tabela de endpoints

MétodoCaminhoAuthDescrição
GET/billing/plans/publicPúblicaLista planos visíveis ao público.
POST/billing/webhooks/stripeAssinatura StripeInterno — não chamado pelo cliente.
GET/platform/organizations/{organization_id}/billingSessãoOverview do assinatura + uso.
POST/platform/organizations/{organization_id}/billing/checkoutSessãoCria sessão de checkout (upgrade).
POST/platform/organizations/{organization_id}/billing/portalSessãoCria URL para o portal do cliente.

GET /billing/plans/public

Lista planos disponíveis. Rota pública — útil para uma página de pricing que não exige login.

Response 200: array de BillingPlanRecord:

[
{
"id": "plan_starter",
"code": "starter",
"name": "Starter",
"description": "Para times pequenos começando com RuleForge.",
"currency": "usd",
"monthly_price_cents": 4900,
"annual_price_cents": 49000,
"minimum_seats": 1,
"maximum_seats": 10,
"status": "active",
"is_public": true,
"contact_sales": false,
"display_order": 1,
"features": {
"api_access": true,
"ai_assistant": false,
"sso": false,
"audit_log_retention_days": 30
},
"limits": {
"projects": 3,
"analysis_runs_per_month": 10000,
"api_requests_per_minute": 120,
"webhooks": 5
},
"metadata": {},
"created_at": "…",
"updated_at": "…"
},
{ "id": "plan_team", "code": "team", "...": "..." },
{ "id": "plan_enterprise", "code": "enterprise", "contact_sales": true, "...": "..." }
]

Planos com contact_sales=true não têm preço público — use portal ou contato direto com sales para acionar.

GET /.../billing

Overview completo da cobrança da organização.

Auth: sessão do usuário.

Response 200 (OrganizationBillingOverviewResponse):

{
"organization_id": "org_...",
"organization_name": "Acme Security",
"subscription": {
"id": "sub_...",
"organization_id": "org_...",
"plan_id": "plan_team",
"status": "active",
"billing_interval": "month",
"seat_count": 5,
"gateway_provider": "stripe",
"gateway_subscription_id": "sub_Nz…",
"trial_ends_at": null,
"current_period_start": "2026-04-01T00:00:00Z",
"current_period_end": "2026-05-01T00:00:00Z",
"cancel_at": null,
"canceled_at": null,
"created_at": "…",
"updated_at": "…"
},
"plan": { "...": "BillingPlanRecord" },
"available_plans": [ "BillingPlanRecord[]" ],
"usage": [
{
"metric": "analysis_runs",
"period_start": "2026-04-01T00:00:00Z",
"period_end": "2026-05-01T00:00:00Z",
"consumed": 2741,
"limit": 10000
},
{
"metric": "projects",
"consumed": 4,
"limit": 10
}
],
"gateway": {
"provider": "stripe",
"status": "connected",
"publishable_key": "pk_live_..."
}
}
  • subscription.statusunpaid.
  • usage[] — métricas consumidas no período atual. limit=null significa ilimitado.
  • gateway.publishable_key só vem preenchido quando o gateway é Stripe.

POST /.../billing/checkout

Cria uma sessão de checkout (normalmente Stripe Checkout) para o usuário concluir a assinatura/upgrade.

Request body (BillingCheckoutRequest):

{
"plan_id": "plan_team",
"billing_interval": "month",
"seat_count": 5
}
  • plan_id — ID do plano alvo.
  • billing_interval"month" ou "year".
  • seat_count — 1 a 100 000.

Response 200 (BillingCheckoutResponse):

{
"provider": "stripe",
"url": "https://checkout.stripe.com/c/pay/cs_...",
"session_id": "cs_..."
}

Fluxo típico do cliente:

  1. Frontend chama este endpoint.
  2. Recebe url e redireciona o usuário.
  3. Usuário conclui o pagamento na Stripe.
  4. Stripe chama de volta /billing/webhooks/stripe (não documentado aqui — é o webhook interno).
  5. O RuleForge atualiza o subscription.status para active.
  6. O frontend recebe ?checkout=success (configurável em NATIVE_WAZUH_BILLING_SUCCESS_PATH).

Erros:

  • 400 bad_request — plano não existe ou não está público.
  • 402 payment_required — gateway não configurado ou indisponível.
  • 403 forbidden — organização em estado canceled.

POST /.../billing/portal

Cria uma URL para o portal do cliente (Stripe Customer Portal), onde o usuário pode:

  • Ver histórico de faturas.
  • Atualizar método de pagamento.
  • Cancelar assinatura.
  • Fazer downgrade.

Request body (BillingPortalRequest):

{ "return_path": "/settings/billing" }
  • return_path — caminho absoluto (começa com /) para onde voltar após sair do portal.

Response 200 (BillingPortalResponse):

{
"provider": "stripe",
"url": "https://billing.stripe.com/p/session/bps_..."
}

Cliente redireciona o usuário para url.

Webhook interno da Stripe

POST /billing/webhooks/stripe recebe eventos da Stripe (invoice.paid, customer.subscription.updated, etc.) para manter o estado local sincronizado. A autenticação é via assinatura Stripe-Signature verificada com o NATIVE_WAZUH_STRIPE_WEBHOOK_SECRET.

Você não precisa chamar este endpoint — é configurado no dashboard da Stripe pelo operador da plataforma.

Estruturas compartilhadas

BillingPlanRecord

Ver exemplo acima em GET /billing/plans/public.

OrganizationSubscriptionRecord

{
"id": "string",
"organization_id": "string",
"plan_id": "string",
"status": "trialing|active|past_due|canceled|incomplete|incomplete_expired|unpaid",
"billing_interval": "month|year",
"seat_count": 1,
"gateway_provider": "string",
"gateway_subscription_id": "string|null",
"trial_ends_at": "ISO 8601|null",
"current_period_start": "ISO 8601|null",
"current_period_end": "ISO 8601|null",
"cancel_at": "ISO 8601|null",
"canceled_at": "ISO 8601|null",
"created_at": "ISO 8601",
"updated_at": "ISO 8601"
}

BillingUsageMetric

{
"metric": "string",
"period_start": "ISO 8601",
"period_end": "ISO 8601",
"consumed": 0,
"limit": 0
}

Métricas comuns: analysis_runs, projects, api_requests, webhooks, members.

Erros relacionados

codeQuando
plan_quota_exceededAlguma operação atinge o limite do plano (analysis_runs, webhooks, etc.).
billing_gateway_disabledNATIVE_WAZUH_BILLING_GATEWAY=disabled.
billing_subscription_requiredOperação exige plano ativo e a organização está sem subscription válida.