Implement the ux-first-run change: a state-derived setup checklist on /operator/setup with a landing region that recedes once required steps are done, and empty states that distinguish blocked from empty across operator and member surfaces. Also add production deployment and environment reference docs, plus a config-key completeness test.
235 lines
6.7 KiB
Go
235 lines
6.7 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.29.0
|
|
// source: prices.sql
|
|
|
|
package billing
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
)
|
|
|
|
const anyActivePrice = `-- name: AnyActivePrice :one
|
|
SELECT EXISTS(SELECT 1 FROM core.prices WHERE is_active = TRUE)
|
|
`
|
|
|
|
// Setup-checklist existence probe.
|
|
func (q *Queries) AnyActivePrice(ctx context.Context) (bool, error) {
|
|
row := q.db.QueryRowContext(ctx, anyActivePrice)
|
|
var exists bool
|
|
err := row.Scan(&exists)
|
|
return exists, err
|
|
}
|
|
|
|
const clearDefaultPrice = `-- name: ClearDefaultPrice :exec
|
|
UPDATE core.prices
|
|
SET is_default = FALSE
|
|
WHERE product_id = $1 AND is_default
|
|
`
|
|
|
|
// First half of the make-default handshake; run in the same transaction as
|
|
// MarkDefaultPrice (clear before set, so the partial unique index never sees
|
|
// two defaults for one product).
|
|
func (q *Queries) ClearDefaultPrice(ctx context.Context, productID string) error {
|
|
_, err := q.db.ExecContext(ctx, clearDefaultPrice, productID)
|
|
return err
|
|
}
|
|
|
|
const createPrice = `-- name: CreatePrice :one
|
|
INSERT INTO core.prices (product_id, currency, unit_amount, recurring_interval, trial_period_days, is_active, is_default)
|
|
VALUES ($1, $2, $3, $4, $5, TRUE,
|
|
NOT EXISTS (SELECT 1 FROM core.prices p WHERE p.product_id = $1 AND p.is_default))
|
|
RETURNING price_id, product_id, currency, unit_amount, recurring_interval, trial_period_days, is_active, created_at, updated_at, is_default, usage_type
|
|
`
|
|
|
|
type CreatePriceParams struct {
|
|
ProductID string `json:"product_id"`
|
|
Currency string `json:"currency"`
|
|
UnitAmount int32 `json:"unit_amount"`
|
|
RecurringInterval sql.NullString `json:"recurring_interval"`
|
|
TrialPeriodDays sql.NullInt32 `json:"trial_period_days"`
|
|
}
|
|
|
|
// The first price a product gets becomes its default automatically (there is
|
|
// nothing else to offer members); subsequent prices are created non-default
|
|
// and promoted explicitly via Clear/MarkDefaultPrice. The NOT EXISTS probe
|
|
// also self-heals a product that somehow lost its default: the next price
|
|
// created becomes it. The partial unique index
|
|
// (idx_prices_one_default_per_product) backstops concurrent creation races.
|
|
func (q *Queries) CreatePrice(ctx context.Context, arg CreatePriceParams) (Price, error) {
|
|
row := q.db.QueryRowContext(ctx, createPrice,
|
|
arg.ProductID,
|
|
arg.Currency,
|
|
arg.UnitAmount,
|
|
arg.RecurringInterval,
|
|
arg.TrialPeriodDays,
|
|
)
|
|
var i Price
|
|
err := row.Scan(
|
|
&i.PriceID,
|
|
&i.ProductID,
|
|
&i.Currency,
|
|
&i.UnitAmount,
|
|
&i.RecurringInterval,
|
|
&i.TrialPeriodDays,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.IsDefault,
|
|
&i.UsageType,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const deactivatePrice = `-- name: DeactivatePrice :one
|
|
UPDATE core.prices
|
|
SET is_active = FALSE
|
|
WHERE price_id = $1 AND is_default = FALSE
|
|
RETURNING price_id, product_id, currency, unit_amount, recurring_interval, trial_period_days, is_active, created_at, updated_at, is_default, usage_type
|
|
`
|
|
|
|
// Handlers must refuse to deactivate the default price (members would lose
|
|
// the purchase path); the is_default guard here backstops that rule.
|
|
func (q *Queries) DeactivatePrice(ctx context.Context, priceID string) (Price, error) {
|
|
row := q.db.QueryRowContext(ctx, deactivatePrice, priceID)
|
|
var i Price
|
|
err := row.Scan(
|
|
&i.PriceID,
|
|
&i.ProductID,
|
|
&i.Currency,
|
|
&i.UnitAmount,
|
|
&i.RecurringInterval,
|
|
&i.TrialPeriodDays,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.IsDefault,
|
|
&i.UsageType,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getDefaultPriceByProduct = `-- name: GetDefaultPriceByProduct :one
|
|
SELECT price_id, product_id, currency, unit_amount, recurring_interval, trial_period_days, is_active, created_at, updated_at, is_default, usage_type FROM core.prices
|
|
WHERE product_id = $1 AND is_default = TRUE
|
|
`
|
|
|
|
func (q *Queries) GetDefaultPriceByProduct(ctx context.Context, productID string) (Price, error) {
|
|
row := q.db.QueryRowContext(ctx, getDefaultPriceByProduct, productID)
|
|
var i Price
|
|
err := row.Scan(
|
|
&i.PriceID,
|
|
&i.ProductID,
|
|
&i.Currency,
|
|
&i.UnitAmount,
|
|
&i.RecurringInterval,
|
|
&i.TrialPeriodDays,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.IsDefault,
|
|
&i.UsageType,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getPrice = `-- name: GetPrice :one
|
|
SELECT price_id, product_id, currency, unit_amount, recurring_interval, trial_period_days, is_active, created_at, updated_at, is_default, usage_type FROM core.prices
|
|
WHERE price_id = $1
|
|
`
|
|
|
|
func (q *Queries) GetPrice(ctx context.Context, priceID string) (Price, error) {
|
|
row := q.db.QueryRowContext(ctx, getPrice, priceID)
|
|
var i Price
|
|
err := row.Scan(
|
|
&i.PriceID,
|
|
&i.ProductID,
|
|
&i.Currency,
|
|
&i.UnitAmount,
|
|
&i.RecurringInterval,
|
|
&i.TrialPeriodDays,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.IsDefault,
|
|
&i.UsageType,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listPricesByProduct = `-- name: ListPricesByProduct :many
|
|
SELECT price_id, product_id, currency, unit_amount, recurring_interval, trial_period_days, is_active, created_at, updated_at, is_default, usage_type FROM core.prices
|
|
WHERE product_id = $1 AND is_active = TRUE
|
|
ORDER BY created_at ASC, price_id ASC
|
|
`
|
|
|
|
func (q *Queries) ListPricesByProduct(ctx context.Context, productID string) ([]Price, error) {
|
|
rows, err := q.db.QueryContext(ctx, listPricesByProduct, productID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []Price{}
|
|
for rows.Next() {
|
|
var i Price
|
|
if err := rows.Scan(
|
|
&i.PriceID,
|
|
&i.ProductID,
|
|
&i.Currency,
|
|
&i.UnitAmount,
|
|
&i.RecurringInterval,
|
|
&i.TrialPeriodDays,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.IsDefault,
|
|
&i.UsageType,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const markDefaultPrice = `-- name: MarkDefaultPrice :one
|
|
UPDATE core.prices
|
|
SET is_default = TRUE
|
|
WHERE price_id = $1 AND product_id = $2 AND is_active = TRUE
|
|
RETURNING price_id, product_id, currency, unit_amount, recurring_interval, trial_period_days, is_active, created_at, updated_at, is_default, usage_type
|
|
`
|
|
|
|
type MarkDefaultPriceParams struct {
|
|
PriceID string `json:"price_id"`
|
|
ProductID string `json:"product_id"`
|
|
}
|
|
|
|
// Second half of the make-default handshake. Scoped to (price_id, product_id)
|
|
// so a stale/forged price ID from another product cannot be promoted; the
|
|
// is_active guard keeps a deactivated price from becoming the default.
|
|
func (q *Queries) MarkDefaultPrice(ctx context.Context, arg MarkDefaultPriceParams) (Price, error) {
|
|
row := q.db.QueryRowContext(ctx, markDefaultPrice, arg.PriceID, arg.ProductID)
|
|
var i Price
|
|
err := row.Scan(
|
|
&i.PriceID,
|
|
&i.ProductID,
|
|
&i.Currency,
|
|
&i.UnitAmount,
|
|
&i.RecurringInterval,
|
|
&i.TrialPeriodDays,
|
|
&i.IsActive,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.IsDefault,
|
|
&i.UsageType,
|
|
)
|
|
return i, err
|
|
}
|