Auto-provision default billing account

This commit is contained in:
2026-04-07 08:13:15 -05:00
parent 0015623121
commit 5ddef5e84c
5 changed files with 64 additions and 4 deletions

View File

@ -3,10 +3,12 @@ package provisioning
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"strings"
"unicode"
"git.coopcloud.tech/wiki-cafe/member-console/internal/billing"
"git.coopcloud.tech/wiki-cafe/member-console/internal/entitlements"
"git.coopcloud.tech/wiki-cafe/member-console/internal/identity"
"git.coopcloud.tech/wiki-cafe/member-console/internal/organization"
@ -30,6 +32,7 @@ type Result struct {
Workspace organization.Workspace
Pool entitlements.ResourcePool
PoolAssignment entitlements.PoolAssignment
BillingAccount billing.Account
}
// AutoProvision creates all governance structures for a new user within a
@ -143,6 +146,18 @@ func AutoProvision(ctx context.Context, db *sql.DB, claims OIDCClaims) (*Result,
return nil, fmt.Errorf("create pool assignment: %w", err)
}
// 10. Create default billing account for the organization
billQ := billing.New(tx)
billingAccount, err := billQ.CreateBillingAccount(ctx, billing.CreateBillingAccountParams{
OrgID: org.OrgID,
Name: "Default",
Status: "active",
Metadata: json.RawMessage("{}"),
})
if err != nil {
return nil, fmt.Errorf("create billing account: %w", err)
}
if err := tx.Commit(); err != nil {
return nil, fmt.Errorf("commit transaction: %w", err)
}
@ -155,6 +170,7 @@ func AutoProvision(ctx context.Context, db *sql.DB, claims OIDCClaims) (*Result,
Workspace: workspace,
Pool: pool,
PoolAssignment: poolAssignment,
BillingAccount: billingAccount,
}, nil
}

View File

@ -2,13 +2,13 @@
## Purpose
Defines the automatic provisioning of governance structures (person, organization, workspace) when a user first authenticates via OIDC.
Defines the automatic provisioning of governance structures (person, organization, workspace, billing account) when a user first authenticates via OIDC.
## Requirements
### Requirement: Auto-provision governance structures on first login
The system SHALL create a complete set of governance structures when a user authenticates for the first time. The following records SHALL be created within a single database transaction: a `users` record, a `persons` record, a personal `organizations` record (with `org_type = 'personal'`), an `org_members` record with the `owner` system role, a default `workspaces` record, a default `resource_pools` record (with `pool_type = 'default'` and `is_auto_managed = true`), and a `pool_assignments` record linking the workspace to the pool (with `is_primary = true`).
The system SHALL create a complete set of governance structures when a user authenticates for the first time. The following records SHALL be created within a single database transaction: a `users` record, a `persons` record, a personal `organizations` record (with `org_type = 'personal'`), an `org_members` record with the `owner` system role, a default `workspaces` record, a default `resource_pools` record (with `pool_type = 'default'` and `is_auto_managed = true`), a `pool_assignments` record linking the workspace to the pool (with `is_primary = true`), and a default `billing.accounts` record (with `status = 'active'`) belonging to the organization.
#### Scenario: First-time OIDC authentication
@ -20,6 +20,7 @@ The system SHALL create a complete set of governance structures when a user auth
- **AND** the system SHALL create a `workspaces` record named "default" in that organization
- **AND** the system SHALL create a `resource_pools` record with `pool_type = 'default'`, `is_auto_managed = true`, belonging to the organization
- **AND** the system SHALL create a `pool_assignments` record with `is_primary = true` linking the workspace to the resource pool
- **AND** the system SHALL create a `billing.accounts` record named "Default" with `status = 'active'` belonging to the organization
- **AND** all records SHALL be created within a single database transaction
#### Scenario: Transaction atomicity
@ -31,7 +32,7 @@ The system SHALL create a complete set of governance structures when a user auth
#### Scenario: Returning user login does not re-provision
- **WHEN** a user completes OIDC authentication and a `users` record already exists for their OIDC subject
- **THEN** the system SHALL NOT create any new organizations, memberships, workspaces, resource pools, or pool assignments
- **THEN** the system SHALL NOT create any new organizations, memberships, workspaces, resource pools, pool assignments, or billing accounts
### Requirement: Personal organization naming

View File

@ -46,3 +46,12 @@ The system SHALL provide a `metadata JSONB` column on billing accounts for exten
#### Scenario: Billing account metadata defaults to empty
- **WHEN** a billing account is created without specifying metadata
- **THEN** the metadata column contains an empty JSON object
### Requirement: Billing account is auto-provisioned with organization
The system SHALL automatically create a default billing account when a new organization is auto-provisioned during first-time user authentication.
#### Scenario: Default billing account created on first login
- **WHEN** a user authenticates for the first time and auto-provisioning creates their personal organization
- **THEN** the system SHALL create a `billing.accounts` record named "Default" with `status = 'active'`
- **AND** the billing account SHALL belong to the newly created organization via `org_id`
- **AND** the billing account creation SHALL occur within the same transaction as other provisioning steps

View File

@ -75,6 +75,8 @@ Labels: `design-feedback`
Labels: `design-feedback`
The integration architecture models `provider_configs` as per-organization configuration. Member-console runs a single Stripe account for the cooperative — there's no multi-org Stripe Connect topology. `provider_configs` should be a single-row app-level config (or env-based), not org-scoped. Discovered during Stripe integration planning (2026-04-02).
### Default billing account not auto-provisioned at first login
### ~~Default billing account not auto-provisioned at first login~~ ✅ RESOLVED 2026-04-07
Labels: `design-feedback`, `billing`
`design/identity/architecture.md` specifies that auto-provisioning should create a default billing account alongside the personal org, workspace, and resource pool. The implementation (`internal/provisioning/provisioning.go`) creates the org, workspace, pool, and pool assignment but does **not** create a billing account. This means no billing account exists until manually created. Subscription creation (M4 phase 1c) requires a billing account as a precondition — either this gap is closed or the subscription flow must create one on demand. Discovered during subscription creation planning (2026-04-04).
**Resolution:** Fixed in commit on 2026-04-07. `AutoProvision` now creates a default billing account as step 10 of the provisioning flow, using `billing.CreateBillingAccountParams` with `Name: "Default"`, `Status: "active"`, and the organization's `org_id`.

View File

@ -0,0 +1,32 @@
# 2026-04-07 — Billing Account Auto-Provisioning
## What was done
Fixed the gap where auto-provisioning did not create a billing account for new users.
### Problem
The design spec (`design/identity/architecture.md`) specified that auto-provisioning should create a default billing account alongside the personal org, workspace, and resource pool. However, the implementation only created the org, workspace, pool, and pool assignment — no billing account. This blocked seamless Stripe subscription flows since subscriptions require a billing account as a precondition.
### Solution
Modified `internal/provisioning/provisioning.go` to add billing account creation as step 10 of the auto-provisioning transaction:
```go
// 10. Create default billing account for the organization
billQ := billing.New(tx)
billingAccount, err := billQ.CreateBillingAccount(ctx, billing.CreateBillingAccountParams{
OrgID: org.OrgID,
Name: "Default",
Status: "active",
Metadata: json.RawMessage("{}"),
})
```
### Files changed
- `internal/provisioning/provisioning.go` — Added billing account creation
- `openspec/specs/auto-provisioning/spec.md` — Updated requirements and scenarios to include billing account
- `openspec/specs/billing-accounts/spec.md` — Added auto-provisioning requirement
- `openspec/specs/price-management/spec.md` — Fixed "TBD" purpose (cleanup)
- `status/issues.md` — Marked issue as resolved
### Result
New users now have a complete governance structure on first login, including a billing account ready for Stripe subscription operations.