Auto-provision default billing account
This commit is contained in:
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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`.
|
||||
|
||||
32
status/log/2026-04-07-billing-account-auto-provisioning.md
Normal file
32
status/log/2026-04-07-billing-account-auto-provisioning.md
Normal 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.
|
||||
Reference in New Issue
Block a user