billing

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package billing defines the minimal typed external monetary host binding.

An external billing integration supplies one Binding: a stable identity and version plus typed ports for the cheap pre-route credit screen, the bounded quote and atomic exposure admission, the terminal evidence handoff with a durable acknowledgement, and explicit owned-resource lifecycle registration. Adapters translate these public DTOs to the existing internal billing services; the binding itself carries no SQL handles, executors, request bodies, or provider-shaped payloads.

The provider-neutral observation/sideband DTOs and the Rater, Quoter, StatementImporter, and ReconciliationReader contracts remain defined in pkg/lipsdk/metering and pkg/lipsdk/economics. This package reuses them and introduces no generic provider-shaped normalizer port and no service-map or any-typed payload.

Import DAG: billing -> economics -> metering, billing -> scope (no cycles).

Boundary rules:

  • Must not import internal/*, database/sql, net/http, Bun/driver types, or provider SDKs and concrete plugins.
  • DTOs are immutable-style public values using only public packages/types.
  • No any, no generic maps standing in for typed APIs.

Index

Constants

View Source
const (
	// MaxExecutionLimits bounds finite candidate/work quantities per admission.
	MaxExecutionLimits = 128
	// MaxUnitReservations bounds non-monetary unit reservations per admission.
	MaxUnitReservations = 128
)
View Source
const (
	// MaxOwnedResources bounds binding-owned resources per lifecycle.
	MaxOwnedResources = 64
	// MaxBorrowedRefs bounds declared borrowed handles per lifecycle.
	MaxBorrowedRefs = 64
)
View Source
const (
	// MaxBindingIDLength bounds the stable external binding identifier.
	MaxBindingIDLength = 128
	// MaxIdentityBytes bounds account, store, envelope, and resource IDs.
	MaxIdentityBytes = 512
	// MaxReasonBytes bounds human-readable decision reasons.
	MaxReasonBytes = 512
)
View Source
const (
	// TerminalEnvelopeVersionV1 is the first version of the terminal envelope.
	TerminalEnvelopeVersionV1 uint32 = 1
	// MaxObservationsPerEnvelope bounds neutral evidence per terminal handoff.
	MaxObservationsPerEnvelope = 1024
	// MaxExpectedBLegIDs bounds call-closure B-leg coverage per envelope.
	MaxExpectedBLegIDs = 1024
)
View Source
const (
	// BindingVersionV1 is the first version of the external billing binding.
	BindingVersionV1 uint32 = 1
)

Variables

View Source
var (
	// ErrInvalidAdmission identifies a malformed exposure-admission DTO.
	ErrInvalidAdmission = errors.New("billing: invalid exposure admission")
	// ErrQuoteMismatch identifies a structurally valid quote that names a
	// different subject, store, or frozen snapshot than requested. It must
	// be rejected before atomic admission, never forwarded.
	ErrQuoteMismatch = errors.New("billing: quote does not match requested subject")
	// ErrAdmissionMismatch identifies a structurally valid handle that names
	// a different store, call, or quote than admitted. It cannot authorize
	// the requested call.
	ErrAdmissionMismatch = errors.New("billing: admission handle does not match request")
)
View Source
var (
	// ErrInvalidBinding identifies a malformed binding envelope.
	ErrInvalidBinding = errors.New("billing: invalid binding")
	// ErrInvalidBindingID identifies a missing or malformed binding ID.
	ErrInvalidBindingID = errors.New("billing: invalid binding ID")
	// ErrUnsupportedBindingVersion identifies an unknown binding version.
	ErrUnsupportedBindingVersion = errors.New("billing: unsupported binding version")
	// ErrMissingCreditScreen identifies an absent credit-screen port.
	ErrMissingCreditScreen = errors.New("billing: credit screen port is required")
	// ErrMissingQuoter identifies an absent quote port.
	ErrMissingQuoter = errors.New("billing: quoter port is required")
	// ErrMissingAdmission identifies an absent exposure-admission port.
	ErrMissingAdmission = errors.New("billing: exposure admission port is required")
	// ErrMissingTerminal identifies an absent terminal handoff port.
	ErrMissingTerminal = errors.New("billing: terminal port is required")
	// ErrDuplicateMonetaryBinding rejects a second monetary binding. Ordinary
	// non-money authority registrations may coexist for distinct quotas, but
	// they must never create a second monetary authority.
	ErrDuplicateMonetaryBinding = errors.New("billing: duplicate monetary binding")
)
View Source
var (
	// ErrInvalidCreditScreen identifies a malformed credit-screen DTO.
	ErrInvalidCreditScreen = errors.New("billing: invalid credit screen")
	// ErrCreditResultMismatch identifies a structurally valid result that
	// answers a different store or account. It is never a decision for the
	// requested account, whatever its allow/deny value.
	ErrCreditResultMismatch = errors.New("billing: credit result does not match request")
)
View Source
var (
	// ErrInvalidTerminal identifies a malformed terminal handoff DTO.
	ErrInvalidTerminal = errors.New("billing: invalid terminal handoff")
	// ErrTerminalAckMismatch identifies a structurally valid acknowledgement
	// that answers a different envelope. Store and envelope identity must
	// match the sent envelope exactly; a foreign ack is never durable success.
	ErrTerminalAckMismatch = errors.New("billing: terminal acknowledgement does not match envelope")
)
View Source
var ErrInvalidBindingScope = errors.New("billing: invalid binding scope")

ErrInvalidBindingScope identifies a missing or malformed composition-time economics identity.

View Source
var ErrInvalidLifecycle = errors.New("billing: invalid lifecycle")

ErrInvalidLifecycle identifies a malformed lifecycle declaration.

Functions

func ValidateBindings

func ValidateBindings(bindings []Binding) error

ValidateBindings validates each candidate binding and enforces the single-monetary-authority rule: at most one monetary binding may be published. An empty set is valid and keeps the host non-money.

Types

type Binding

type Binding struct {
	ID           string
	Version      uint32
	Scope        BindingScope
	CreditScreen CreditScreener
	Quoter       economics.Quoter
	Admission    ExposureAdmitter
	Terminal     TerminalSink
	Lifecycle    Lifecycle
}

Binding is the minimal typed external monetary host binding. It carries a stable identity and version, complete cheap-screen/quote-admit/terminal ports, and explicit owned-resource lifecycle registration. Public rater and statement/reconciliation ports stay separate in pkg/lipsdk/economics; workers consume them after terminal persistence, not through this binding.

func (Binding) IsMonetary

func (b Binding) IsMonetary() bool

IsMonetary reports that the binding owns the monetary authority. It lets a future host composition reject a second monetary binding while ordinary non-money authority registrations coexist for distinct quotas.

func (Binding) Validate

func (b Binding) Validate() error

Validate rejects incomplete bindings before publication: invalid ID or version, nil or typed-nil monetary ports, and invalid lifecycle declarations.

type BindingScope

type BindingScope struct {
	StoreID string                      `json:"store_id"`
	Tariff  economics.RatingSnapshotRef `json:"tariff"`
	Policy  economics.PolicySnapshotRef `json:"policy"`
}

BindingScope carries the composition-time economics identity owned by the binding: the trusted store and the frozen tariff and policy snapshots the host binding translates into quote, admission, credit-screen, and terminal DTOs. Adapters never invent these identities; the binding declares them.

func (BindingScope) Clone

func (s BindingScope) Clone() BindingScope

Clone returns an independent copy of the scope.

func (BindingScope) Validate

func (s BindingScope) Validate() error

Validate requires the store identity and the frozen snapshot references.

type BorrowedRef

type BorrowedRef struct {
	ID   string
	Kind string
}

BorrowedRef declares one borrowed store or resource handle. Borrowed handles are never implicitly closed: the type offers no close entry point, so host cleanup cannot dispose a store it does not own.

func (BorrowedRef) Validate

func (r BorrowedRef) Validate() error

Validate requires borrowed identity with an optional bounded kind label.

type CreditDecision

type CreditDecision string

CreditDecision is the typed outcome of the cheap pre-route credit screen.

const (
	// CreditAllow permits route planning to proceed.
	CreditAllow CreditDecision = "allow"
	// CreditDeny stops the request before provider execution.
	CreditDeny CreditDecision = "deny"
	// CreditDegraded permits execution with explicitly reduced guarantees.
	CreditDegraded CreditDecision = "degraded"
)

func (CreditDecision) IsKnown

func (d CreditDecision) IsKnown() bool

IsKnown reports whether d is a documented credit decision.

type CreditScreenInput

type CreditScreenInput struct {
	Scope     scope.PrincipalScopeView    `json:"scope"`
	StoreID   string                      `json:"store_id"`
	AccountID string                      `json:"account_id"`
	Policy    economics.PolicySnapshotRef `json:"policy"`
}

CreditScreenInput carries the trusted customer scope, the account reference, and the frozen policy version for one cheap pre-route credit check.

func (CreditScreenInput) Clone

Clone returns an independent copy of the input.

func (CreditScreenInput) Validate

func (in CreditScreenInput) Validate() error

Validate requires trusted scope, store/account identity, and policy version.

type CreditScreenResult

type CreditScreenResult struct {
	Decision  CreditDecision `json:"decision"`
	Reason    string         `json:"reason,omitempty"`
	StoreID   string         `json:"store_id"`
	AccountID string         `json:"account_id"`
}

CreditScreenResult is the typed allow/deny/degraded outcome. Reason is a bounded human-readable label, never raw account state.

func (CreditScreenResult) Clone

Clone returns an independent copy of the result.

func (CreditScreenResult) MatchesInput

func (r CreditScreenResult) MatchesInput(in CreditScreenInput) bool

MatchesInput reports whether the result answers exactly the submitted store and account. The decision value is irrelevant to identity.

func (CreditScreenResult) Validate

func (r CreditScreenResult) Validate() error

Validate requires a known decision and store/account identity.

type CreditScreener

type CreditScreener interface {
	Check(ctx context.Context, in CreditScreenInput) (CreditScreenResult, error)
}

CreditScreener is the cheap pre-route credit gate. It performs a bounded account read and never posts financial state.

type ExposureAdmissionInput

type ExposureAdmissionInput struct {
	Subject          metering.SubjectRef      `json:"subject"`
	Scope            scope.PrincipalScopeView `json:"scope"`
	AccountID        string                   `json:"account_id"`
	Quote            economics.ExposureQuote  `json:"quote"`
	ExecutionLimits  []economics.Limit        `json:"execution_limits"`
	UnitReservations []economics.UnitBound    `json:"unit_reservations,omitempty"`
}

ExposureAdmissionInput carries one frozen quote, the call identity, the trusted customer scope and credited account, finite execution limits, and unit reservations for atomic exposure admission.

func (ExposureAdmissionInput) Clone

Clone returns an independent copy of the input.

func (ExposureAdmissionInput) Validate

func (in ExposureAdmissionInput) Validate() error

Validate requires call identity, trusted customer scope and credited account for per-customer allowance binding, a frozen quote bound to exactly the requested subject, and at least one finite execution limit.

type ExposureAdmitter

type ExposureAdmitter interface {
	Admit(ctx context.Context, in ExposureAdmissionInput) (ExposureHandle, error)
}

ExposureAdmitter is the atomic operational exposure admission authority. It consumes one frozen quote and finite execution limits and returns an admitted handle or a typed rejection.

type ExposureHandle

type ExposureHandle struct {
	StoreID       string `json:"store_id"`
	BillingCallID string `json:"billing_call_id"`
	ExposureID    string `json:"exposure_id"`
	QuoteID       string `json:"quote_id"`
}

ExposureHandle is the admitted-exposure identity returned by atomic admission. It carries store-scoped IDs only.

func (ExposureHandle) Clone

func (h ExposureHandle) Clone() ExposureHandle

Clone returns an independent copy of the handle.

func (ExposureHandle) MatchesAdmission

func (h ExposureHandle) MatchesAdmission(in ExposureAdmissionInput) bool

MatchesAdmission reports whether the handle authorizes exactly the admitted request: store and call must equal the admission subject, and a carried quote ID must equal the admitted quote. The quote-ID check applies only when the quote itself carries an ID.

func (ExposureHandle) Validate

func (h ExposureHandle) Validate() error

Validate requires complete store-scoped exposure identity.

type Lifecycle

type Lifecycle struct {
	Owned    []OwnedResource
	Borrowed []BorrowedRef
}

Lifecycle is the explicit owned-resource registration for one binding. Owned resources are started and closed once by the host; borrowed refs are declared for attribution and are never closed by host cleanup.

func (Lifecycle) Clone

func (l Lifecycle) Clone() Lifecycle

Clone returns an independent copy of the lifecycle. Callbacks are shared by value; slices are never aliased.

func (Lifecycle) Validate

func (l Lifecycle) Validate() error

Validate checks owned/borrowed declarations, duplicate identity, and owned/borrowed collisions. An empty lifecycle is valid: the binding simply owns no process resources.

type OwnedResource

type OwnedResource struct {
	ID    string
	Start func(ctx context.Context) error
	Close func(ctx context.Context) error
}

OwnedResource is one binding-owned resource registration. Owned resources start and close exactly once under host cleanup; a start failure unwinds already-started owned resources through the same host cleanup path.

func (OwnedResource) Validate

func (r OwnedResource) Validate() error

Validate requires resource identity and both start and close callbacks.

type TerminalAck

type TerminalAck struct {
	StoreID    string `json:"store_id"`
	EnvelopeID string `json:"envelope_id"`
	Revision   uint64 `json:"revision"`
}

TerminalAck is the idempotent durable acknowledgement for one envelope.

func (TerminalAck) Clone

func (a TerminalAck) Clone() TerminalAck

Clone returns an independent copy of the acknowledgement.

func (TerminalAck) MatchesEnvelope

func (a TerminalAck) MatchesEnvelope(env TerminalEnvelope) bool

MatchesEnvelope reports whether the acknowledgement answers exactly the sent envelope, identified by store and envelope ID.

func (TerminalAck) Validate

func (a TerminalAck) Validate() error

Validate requires the acknowledged envelope identity and a nonzero durable revision.

type TerminalEnvelope

type TerminalEnvelope struct {
	Version         uint32                      `json:"version"`
	EnvelopeID      string                      `json:"envelope_id"`
	StoreID         string                      `json:"store_id"`
	Scope           scope.PrincipalScopeView    `json:"scope"`
	Subject         metering.SubjectRef         `json:"subject"`
	Correlation     metering.CorrelationV2      `json:"correlation"`
	Outcome         TerminalOutcome             `json:"outcome"`
	AttemptSeq      uint64                      `json:"attempt_seq"`
	AccountID       string                      `json:"account_id,omitempty"`
	ALegID          string                      `json:"a_leg_id,omitempty"`
	SubmissionID    string                      `json:"submission_id,omitempty"`
	Workload        *TerminalWorkload           `json:"workload,omitempty"`
	ExpectedBLegIDs []string                    `json:"expected_b_leg_ids,omitempty"`
	Tariff          economics.RatingSnapshotRef `json:"tariff"`
	Policy          economics.PolicySnapshotRef `json:"policy"`
	Observations    []metering.Observation      `json:"observations,omitempty"`
	ParentWorkID    string                      `json:"parent_work_id,omitempty"`
	PayloadHash     string                      `json:"payload_hash"`
}

TerminalEnvelope is the public terminal evidence handoff. It carries format version, store/customer scope, call/attempt closure identity and outcome, the authoritative attempt sequence, frozen rate/policy references, neutral V2 observations, parent/workload coverage, and a payload hash. It contains no SQL driver, executor pointer, request body, or mutable service map.

func (TerminalEnvelope) Clone

Clone returns an independent copy of the envelope. Coverage order and the authoritative sequence are preserved as carried; only the slices and the workload pointer are deep-copied, never reordered.

func (TerminalEnvelope) Validate

func (e TerminalEnvelope) Validate() error

Validate checks envelope identity, call/leg lineage, frozen references, neutral evidence, closure coverage, and payload integrity.

Lineage rules: B-leg closures require A-leg identity and a positive authoritative attempt sequence; call closures require the customer account and A-leg identity but carry no leg sequence. Submission and workload are optional attribution present only when the closure carries them.

type TerminalOutcome

type TerminalOutcome string

TerminalOutcome classifies the execution closure carried by an envelope.

const (
	// TerminalCompleted marks successful execution closure.
	TerminalCompleted TerminalOutcome = "completed"
	// TerminalFailed marks failed execution closure; incurred usage remains.
	TerminalFailed TerminalOutcome = "failed"
	// TerminalCanceled marks canceled execution closure; incurred usage remains.
	TerminalCanceled TerminalOutcome = "canceled"
)

func (TerminalOutcome) IsKnown

func (o TerminalOutcome) IsKnown() bool

IsKnown reports whether o is a documented terminal outcome.

type TerminalSink

type TerminalSink interface {
	AppendTerminal(ctx context.Context, envelope TerminalEnvelope) (TerminalAck, error)
}

TerminalSink is the terminal evidence handoff. It durably persists the envelope and returns an idempotent acknowledgement; it is never a best-effort observer.

type TerminalWorkload

type TerminalWorkload struct {
	Class string `json:"class,omitempty"`
	Role  string `json:"role,omitempty"`
}

TerminalWorkload carries parent/workload correlation for one terminal envelope. Class and role are bounded safe labels, never request content. A nil workload means the closure carries no workload attribution.

func (TerminalWorkload) Clone

Clone returns an independent copy of the workload.

func (TerminalWorkload) IsZero

func (w TerminalWorkload) IsZero() bool

IsZero reports whether the workload carries no attribution.

func (TerminalWorkload) Validate

func (w TerminalWorkload) Validate() error

Validate checks the bounded workload labels.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL