currencyx

package
v1.0.0-beta.232 Latest Latest
Warning

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

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

README

currencyx

currencyx contains OpenMeter's shared currency primitives. It keeps fiat behavior compatible with GOBL/ISO currency definitions while allowing product and ledger code to pass configured custom currencies through the same Currency interface.

Fiat Currency

Use Code directly for fiat currencies. Fiat precision comes from the GOBL currency definition and fiat rounding remains half-away-from-zero.

calculator, err := currencyx.Code("USD").Calculator()
if err != nil {
    return err
}

amount := calculator.RoundToPrecision(alpacadecimal.RequireFromString("1.235"))
// amount == 1.24

Custom Currency

Use NewCustomCurrency when the currency is not a known fiat code. Custom currencies carry explicit precision and default to bankers rounding (half-even). Use NewCustomCurrencyWithRounding to opt into half-away-from-zero.

credits, err := currencyx.NewCustomCurrency(currencyx.Code("CREDITS"), 6)
if err != nil {
    return err
}

calculator, err := currencyx.NewCalculator(credits)
if err != nil {
    return err
}

amount := calculator.RoundToPrecision(alpacadecimal.RequireFromString("1.2345678"))
// amount == 1.234568

Allocation

Allocation helpers use the calculator's precision and distribute residual units with a deterministic largest-remainder method. Provide a CompareKey function when equal remainders need a stable domain-specific tie-breaker.

Documentation

Index

Constants

View Source
const (
	CustomCurrencyCodeMinLength = 4
	CustomCurrencyCodeMaxLength = 24
)
View Source
const CustomCurrencyMaxPrecision uint32 = 12

Variables

This section is empty.

Functions

This section is empty.

Types

type AmountAllocation

type AmountAllocation[T any] struct {
	Key    T
	Amount alpacadecimal.Decimal
}

AmountAllocation is the allocated currency amount for one key.

func AllocateByAmount

func AllocateByAmount[T any](currency Currency, input AmountAllocationInput[T]) ([]AmountAllocation[T], error)

AllocateByAmount allocates a currency amount across currency amount buckets using the largest remainder quota method. Each item amount is both its proportional weight and its allocation cap.

type AmountAllocationInput

type AmountAllocationInput[T any] struct {
	Amount alpacadecimal.Decimal
	Items  []AmountAllocationItem[T]

	// CompareKey is used as a deterministic tie-breaker when two buckets have
	// the same fractional remainder. If nil, the original item order is used.
	CompareKey func(left, right T) int
}

AmountAllocationInput defines a proportional allocation across currency amount buckets.

type AmountAllocationItem

type AmountAllocationItem[T any] struct {
	Key    T
	Amount alpacadecimal.Decimal
}

AmountAllocationItem defines one currency amount bucket that can receive a proportional allocation. The amount is both the allocation weight and the maximum amount that can be allocated to the key.

type Code

type Code currency.Code

Code represents a fiat or custom currency code. Code values used directly as Currency values are treated as fiat currencies for backwards compatibility.

func (Code) Equal

func (c Code) Equal(other Code) bool

func (Code) IsCustom

func (c Code) IsCustom() bool

func (Code) IsFiat

func (c Code) IsFiat() bool

func (Code) String

func (c Code) String() string

func (Code) Type

func (c Code) Type() CurrencyType

func (Code) Validate

func (c Code) Validate() error

type CostBasis

type CostBasis struct {
	// FiatCode is the target fiat currency code (e.g. USD, EUR) that the rate converts to.
	FiatCode Code `json:"fiat_code"`
	// Rate is the exchange rate: one unit of the custom currency equals this many units of FiatCode.
	Rate alpacadecimal.Decimal `json:"rate"`
	// EffectiveFrom is the start of the period during which this rate applies (inclusive).
	EffectiveFrom time.Time `json:"effective_from"`
	// EffectiveTo is the end of the period during which this rate applies (exclusive).
	// Nil means the rate is open-ended and currently active.
	EffectiveTo *time.Time `json:"effective_to,omitempty"`
}

CostBasis defines the exchange rate from a custom currency to a fiat currency over a specific time period. It is used to convert custom currency amounts (e.g. credits, tokens) into monetary values for billing and invoicing.

type Currency

type Currency interface {
	models.Validator
	CurrencyCalculator
	CurrencyFormatter

	Type() CurrencyType
	Details() CurrencyDetails

	AsFiat() (*FiatCurrency, error)
	AsCustom() (*CustomCurrency, error)

	Definition() *currency.Def
}

type CurrencyBuilder

type CurrencyBuilder struct {
	// contains filtered or unexported fields
}

func NewCurrencyBuilder

func NewCurrencyBuilder(currencyType CurrencyType) *CurrencyBuilder

func (*CurrencyBuilder) Build

func (b *CurrencyBuilder) Build() (Currency, error)

func (*CurrencyBuilder) WithCode

func (b *CurrencyBuilder) WithCode(code Code) *CurrencyBuilder

func (*CurrencyBuilder) WithDecimalMark

func (b *CurrencyBuilder) WithDecimalMark(decimalMark string) *CurrencyBuilder

func (*CurrencyBuilder) WithName

func (b *CurrencyBuilder) WithName(name string) *CurrencyBuilder

func (*CurrencyBuilder) WithPrecision

func (b *CurrencyBuilder) WithPrecision(precision uint32) *CurrencyBuilder

func (*CurrencyBuilder) WithSymbol

func (b *CurrencyBuilder) WithSymbol(symbol string) *CurrencyBuilder

func (*CurrencyBuilder) WithThousandsSeparator

func (b *CurrencyBuilder) WithThousandsSeparator(thousandsSeparator string) *CurrencyBuilder

type CurrencyCalculator

type CurrencyCalculator interface {
	RoundToPrecision(amount alpacadecimal.Decimal) alpacadecimal.Decimal
	IsRoundedToPrecision(amount alpacadecimal.Decimal) bool

	RoundUp(amount alpacadecimal.Decimal) alpacadecimal.Decimal
	RoundDown(amount alpacadecimal.Decimal) alpacadecimal.Decimal

	Unit() alpacadecimal.Decimal
}

type CurrencyDetails

type CurrencyDetails struct {
	Code               Code   `json:"code"`
	Name               string `json:"name"`
	Symbol             string `json:"symbol,omitempty"`
	Precision          uint32 `json:"precision"`
	DecimalMark        string `json:"decimal_mark,omitempty"`
	ThousandsSeparator string `json:"thousands_separator,omitempty"`
}

type CurrencyFormatter

type CurrencyFormatter interface {
	FormatAmount(amount alpacadecimal.Decimal) string
}

type CurrencyType

type CurrencyType string
const (
	CurrencyTypeFiat   CurrencyType = "fiat"
	CurrencyTypeCustom CurrencyType = "custom"
)

func (CurrencyType) Validate

func (t CurrencyType) Validate() error

type CustomCurrency

type CustomCurrency struct {
	// contains filtered or unexported fields
}

func (*CustomCurrency) AsCustom

func (c *CustomCurrency) AsCustom() (*CustomCurrency, error)

func (*CustomCurrency) AsFiat

func (c *CustomCurrency) AsFiat() (*FiatCurrency, error)

func (*CustomCurrency) Definition

func (c *CustomCurrency) Definition() *currency.Def

func (*CustomCurrency) Details

func (c *CustomCurrency) Details() CurrencyDetails

func (*CustomCurrency) FormatAmount

func (c *CustomCurrency) FormatAmount(amount alpacadecimal.Decimal) string

func (*CustomCurrency) IsRoundedToPrecision

func (c *CustomCurrency) IsRoundedToPrecision(amount alpacadecimal.Decimal) bool

func (*CustomCurrency) RoundDown

func (*CustomCurrency) RoundToPrecision

func (c *CustomCurrency) RoundToPrecision(amount alpacadecimal.Decimal) alpacadecimal.Decimal

func (*CustomCurrency) RoundUp

func (*CustomCurrency) Type

func (c *CustomCurrency) Type() CurrencyType

func (*CustomCurrency) Unit

func (*CustomCurrency) Validate

func (c *CustomCurrency) Validate() error

func (*CustomCurrency) ValidateWith

func (c *CustomCurrency) ValidateWith(v ...models.ValidatorFunc[Currency]) error

type FiatCode

type FiatCode currency.Code

func (FiatCode) AsFiatCurrency

func (c FiatCode) AsFiatCurrency() (*FiatCurrency, error)

func (FiatCode) Equal

func (c FiatCode) Equal(other FiatCode) bool

func (FiatCode) String

func (c FiatCode) String() string

func (FiatCode) Validate

func (c FiatCode) Validate() error

type FiatCurrency

type FiatCurrency struct {
	// contains filtered or unexported fields
}

func NewFiatCurrency

func NewFiatCurrency[T ~string](code T) (*FiatCurrency, error)

func (*FiatCurrency) AsCustom

func (f *FiatCurrency) AsCustom() (*CustomCurrency, error)

func (*FiatCurrency) AsFiat

func (f *FiatCurrency) AsFiat() (*FiatCurrency, error)

func (*FiatCurrency) Definition

func (f *FiatCurrency) Definition() *currency.Def

func (*FiatCurrency) Details

func (f *FiatCurrency) Details() CurrencyDetails

func (*FiatCurrency) FormatAmount

func (f *FiatCurrency) FormatAmount(amount alpacadecimal.Decimal) string

func (*FiatCurrency) GetFiatCode

func (f *FiatCurrency) GetFiatCode() FiatCode

func (*FiatCurrency) IsRoundedToPrecision

func (f *FiatCurrency) IsRoundedToPrecision(amount alpacadecimal.Decimal) bool

func (*FiatCurrency) RoundDown

func (*FiatCurrency) RoundToPrecision

func (f *FiatCurrency) RoundToPrecision(amount alpacadecimal.Decimal) alpacadecimal.Decimal

func (*FiatCurrency) RoundUp

func (*FiatCurrency) Type

func (f *FiatCurrency) Type() CurrencyType

func (*FiatCurrency) Unit

func (*FiatCurrency) Validate

func (f *FiatCurrency) Validate() error

func (*FiatCurrency) ValidateWith

func (f *FiatCurrency) ValidateWith(v ...models.ValidatorFunc[Currency]) error

type WeightedAllocation

type WeightedAllocation[T any] struct {
	Key    T
	Amount alpacadecimal.Decimal
}

WeightedAllocation is the allocated currency amount for one key.

func AllocateByWeight

func AllocateByWeight[T any](currency Currency, input WeightedAllocationInput[T]) ([]WeightedAllocation[T], error)

AllocateByWeight allocates a currency amount across keys using their weights and the largest remainder quota method at the currency precision.

type WeightedAllocationInput

type WeightedAllocationInput[T any] struct {
	Amount alpacadecimal.Decimal
	Items  []WeightedAllocationItem[T]

	// CompareKey is used as a deterministic tie-breaker when two items have
	// the same fractional remainder. If nil, the original item order is used.
	CompareKey func(left, right T) int
}

WeightedAllocationInput defines a proportional currency allocation.

type WeightedAllocationItem

type WeightedAllocationItem[T any] struct {
	Key    T
	Weight alpacadecimal.Decimal
}

WeightedAllocationItem defines one key that can receive a proportional allocation from a currency amount. Weight is dimensionless; it does not need to be a currency amount.

Jump to

Keyboard shortcuts

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