Help us shape the future of Go! Take the 2026 Go Developer Survey

money

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: MIT Imports: 7 Imported by: 0

README

go-money

go-money

A simple, idiomatic Go library for working with monetary values, implementing Martin Fowler's Money pattern.

Amounts are stored as int64 minor units (e.g. cents) to avoid floating-point precision issues. All operations return new values — no mutation, no pointers, no panics.

Fowler's Money pattern compliance

This library implements Martin Fowler's Money pattern from Patterns of Enterprise Application Architecture:

Requirement Implementation
Store amount as integer minor units int64 (cents, pence, etc.)
Pair amount with currency Money struct holds both
Currency-aware arithmetic Add, Subtract return error on mismatch
Multiplication Multiply(int)
Allocation without loss Allocate and Split distribute remainders as single units
Value Object equality Equals(Money) (bool, error)
Comparison GreaterThan, LessThan return (bool, error)
Value semantics Money is a plain struct — no pointers, all operations return new values

Installation

go get github.com/tiagomelo/go-money

Usage

Creating a monetary value

Amounts are in minor units (cents, pence, etc.).

price := money.NewMoney(1999, money.USD) // $19.99
vat   := money.NewMoney(400,  money.EUR) // €4.00
Arithmetic
a := money.NewMoney(1000, money.USD)
b := money.NewMoney(250,  money.USD)

sum, err := a.Add(b)       // $12.50
diff, err := a.Subtract(b) // $7.50
doubled := a.Multiply(2)   // $20.00

Operations on different currencies return an error:

usd := money.NewMoney(100, money.USD)
eur := money.NewMoney(100, money.EUR)

_, err := usd.Add(eur) // err: "cannot add EUR to USD"
Comparison
a := money.NewMoney(1000, money.USD)
b := money.NewMoney(500,  money.USD)

eq,  err := a.Equals(b)      // false, nil
gt,  err := a.GreaterThan(b) // true,  nil
lt,  err := a.LessThan(b)    // false, nil

same := a.SameCurrency(b) // true

Comparing different currencies returns an error:

usd := money.NewMoney(100, money.USD)
eur := money.NewMoney(100, money.EUR)

_, err := usd.GreaterThan(eur) // err: "currency mismatch: USD and EUR"
Absolute value
m := money.NewMoney(-500, money.USD)
abs := m.Absolute() // $5.00
Splitting

Divides an amount into n equal parts. Any remainder is distributed one unit at a time to the first parts, ensuring no value is lost.

m := money.NewMoney(101, money.USD) // $1.01

parts, err := m.Split(4)
// parts[0] = $0.26  (gets the leftover cent)
// parts[1] = $0.25
// parts[2] = $0.25
// parts[3] = $0.25
// sum = $1.01 ✓
Allocation

Distributes an amount according to ratios. Remainder is distributed one unit at a time to the first allocations, ensuring no value is lost.

m := money.NewMoney(100, money.USD) // $1.00

// 2:1 split
parts, err := m.Allocate(2, 1)
// parts[0] = $0.67
// parts[1] = $0.33
// sum = $1.00 ✓

// Equal three-way split with a remainder
m2 := money.NewMoney(101, money.USD) // $1.01
parts, err = m2.Allocate(1, 1, 1)
// parts[0] = $0.34  (gets the leftover cent)
// parts[1] = $0.34
// parts[2] = $0.33
// sum = $1.01 ✓
Display
m := money.NewMoney(123456, money.USD)
fmt.Println(m.Display())        // $1,234.56
fmt.Println(m.AsMajorUnits())  // 1234.56 (float64, display only)

AsMajorUnits returns a float64 and should never be used for arithmetic.

JSON

Money implements json.Marshaler and json.Unmarshaler. The amount is serialized in minor units.

m := money.NewMoney(1999, money.USD)

b, err := json.Marshal(m)
// {"amount":1999,"currency":"USD"}

var got money.Money
err = json.Unmarshal(b, &got)
// got == m

// Unknown currency codes are rejected:
err = json.Unmarshal([]byte(`{"amount":100,"currency":"XYZ"}`), &got)
// err: "unknown currency code: XYZ"
Database storage

Money and Currency both implement driver.Valuer and sql.Scanner, so they work directly with database/sql and any compatible ORM.

The recommended schema stores amount and currency in two separate columns:

CREATE TABLE products (
    id            SERIAL PRIMARY KEY,
    price_amount  BIGINT      NOT NULL,  -- minor units (e.g. cents)
    price_currency VARCHAR(3) NOT NULL   -- ISO 4217 code (e.g. 'USD')
);

Writing:

m := money.NewMoney(1999, money.USD) // $19.99

amountVal, _ := m.Value()           // int64(1999)
currencyVal, _ := m.Currency.Value() // "USD"

db.Exec(
    `INSERT INTO products (price_amount, price_currency) VALUES ($1, $2)`,
    amountVal, currencyVal,
)

Reading:

var m money.Money

row := db.QueryRow(`SELECT price_amount, price_currency FROM products WHERE id = $1`, id)
err := row.Scan(&m.Amount, &m.Currency)
// m is fully reconstructed, including all currency formatting metadata

Currency.Scan validates the code and returns an error for unknown values:

var c money.Currency
err := c.Scan("XYZ") // err: "unknown currency code: XYZ"
Supported currencies

All ISO 4217 currencies are supported as package-level variables:

money.USD  money.EUR  money.GBP  money.JPY
money.BRL  money.CAD  money.AUD  money.CHF
// ... and 150+ more

Design notes

  • Minor units: amounts are always int64 minor units. NewMoney(100, money.USD) is $1.00, not $100.
  • Value semantics: Money is a plain struct, not a pointer. Assigning it copies it.
  • No panics: all error conditions (currency mismatch, invalid split/allocate arguments, unknown currency) return error.
  • Type-safe currencies: currency codes are a dedicated unexported type; only the predefined package variables (money.USD, money.EUR, etc.) are valid — you cannot pass an arbitrary string.
  • Remainder-safe arithmetic: Split and Allocate guarantee that sum(parts) == original, distributing any remainder as single units to the first elements.
  • Database-ready: Money and Currency implement driver.Valuer and sql.Scanner. Store them as two columns — amount (BIGINT) and currency code (VARCHAR(3)). Unknown currency codes are rejected on scan.

Running tests

make test

With coverage report:

make coverage

License

MIT. See LICENSE.

Documentation

Overview

Package money provides a simple and efficient way to handle monetary values in Go.

Index

Constants

This section is empty.

Variables

View Source
var (
	AED = currencyCode{"AED"}
	AFN = currencyCode{"AFN"}
	ALL = currencyCode{"ALL"}
	AMD = currencyCode{"AMD"}
	ANG = currencyCode{"ANG"}
	AOA = currencyCode{"AOA"}
	ARS = currencyCode{"ARS"}
	AUD = currencyCode{"AUD"}
	AWG = currencyCode{"AWG"}
	AZN = currencyCode{"AZN"}
	BAM = currencyCode{"BAM"}
	BBD = currencyCode{"BBD"}
	BDT = currencyCode{"BDT"}
	BGN = currencyCode{"BGN"}
	BHD = currencyCode{"BHD"}
	BIF = currencyCode{"BIF"}
	BMD = currencyCode{"BMD"}
	BND = currencyCode{"BND"}
	BOB = currencyCode{"BOB"}
	BRL = currencyCode{"BRL"}
	BSD = currencyCode{"BSD"}
	BTN = currencyCode{"BTN"}
	BWP = currencyCode{"BWP"}
	BYN = currencyCode{"BYN"}
	BYR = currencyCode{"BYR"}
	BZD = currencyCode{"BZD"}
	CAD = currencyCode{"CAD"}
	CDF = currencyCode{"CDF"}
	CHF = currencyCode{"CHF"}
	CLF = currencyCode{"CLF"}
	CLP = currencyCode{"CLP"}
	CNY = currencyCode{"CNY"}
	COP = currencyCode{"COP"}
	CRC = currencyCode{"CRC"}
	CUC = currencyCode{"CUC"}
	CUP = currencyCode{"CUP"}
	CVE = currencyCode{"CVE"}
	CZK = currencyCode{"CZK"}
	DJF = currencyCode{"DJF"}
	DKK = currencyCode{"DKK"}
	DOP = currencyCode{"DOP"}
	DZD = currencyCode{"DZD"}
	EEK = currencyCode{"EEK"}
	EGP = currencyCode{"EGP"}
	ERN = currencyCode{"ERN"}
	ETB = currencyCode{"ETB"}
	EUR = currencyCode{"EUR"}
	FJD = currencyCode{"FJD"}
	FKP = currencyCode{"FKP"}
	GBP = currencyCode{"GBP"}
	GEL = currencyCode{"GEL"}
	GGP = currencyCode{"GGP"}
	GHC = currencyCode{"GHC"}
	GHS = currencyCode{"GHS"}
	GIP = currencyCode{"GIP"}
	GMD = currencyCode{"GMD"}
	GNF = currencyCode{"GNF"}
	GTQ = currencyCode{"GTQ"}
	GYD = currencyCode{"GYD"}
	HKD = currencyCode{"HKD"}
	HNL = currencyCode{"HNL"}
	HRK = currencyCode{"HRK"}
	HTG = currencyCode{"HTG"}
	HUF = currencyCode{"HUF"}
	IDR = currencyCode{"IDR"}
	ILS = currencyCode{"ILS"}
	IMP = currencyCode{"IMP"}
	INR = currencyCode{"INR"}
	IQD = currencyCode{"IQD"}
	IRR = currencyCode{"IRR"}
	ISK = currencyCode{"ISK"}
	JEP = currencyCode{"JEP"}
	JMD = currencyCode{"JMD"}
	JOD = currencyCode{"JOD"}
	JPY = currencyCode{"JPY"}
	KES = currencyCode{"KES"}
	KGS = currencyCode{"KGS"}
	KHR = currencyCode{"KHR"}
	KMF = currencyCode{"KMF"}
	KPW = currencyCode{"KPW"}
	KRW = currencyCode{"KRW"}
	KWD = currencyCode{"KWD"}
	KYD = currencyCode{"KYD"}
	KZT = currencyCode{"KZT"}
	LAK = currencyCode{"LAK"}
	LBP = currencyCode{"LBP"}
	LKR = currencyCode{"LKR"}
	LRD = currencyCode{"LRD"}
	LSL = currencyCode{"LSL"}
	LTL = currencyCode{"LTL"}
	LVL = currencyCode{"LVL"}
	LYD = currencyCode{"LYD"}
	MAD = currencyCode{"MAD"}
	MDL = currencyCode{"MDL"}
	MGA = currencyCode{"MGA"}
	MKD = currencyCode{"MKD"}
	MMK = currencyCode{"MMK"}
	MNT = currencyCode{"MNT"}
	MOP = currencyCode{"MOP"}
	MUR = currencyCode{"MUR"}
	MRU = currencyCode{"MRU"}
	MVR = currencyCode{"MVR"}
	MWK = currencyCode{"MWK"}
	MXN = currencyCode{"MXN"}
	MYR = currencyCode{"MYR"}
	MZN = currencyCode{"MZN"}
	NAD = currencyCode{"NAD"}
	NGN = currencyCode{"NGN"}
	NIO = currencyCode{"NIO"}
	NOK = currencyCode{"NOK"}
	NPR = currencyCode{"NPR"}
	NZD = currencyCode{"NZD"}
	OMR = currencyCode{"OMR"}
	PAB = currencyCode{"PAB"}
	PEN = currencyCode{"PEN"}
	PGK = currencyCode{"PGK"}
	PHP = currencyCode{"PHP"}
	PKR = currencyCode{"PKR"}
	PLN = currencyCode{"PLN"}
	PYG = currencyCode{"PYG"}
	QAR = currencyCode{"QAR"}
	RON = currencyCode{"RON"}
	RSD = currencyCode{"RSD"}
	RUB = currencyCode{"RUB"}
	RUR = currencyCode{"RUR"}
	RWF = currencyCode{"RWF"}
	SAR = currencyCode{"SAR"}
	SBD = currencyCode{"SBD"}
	SCR = currencyCode{"SCR"}
	SDG = currencyCode{"SDG"}
	SEK = currencyCode{"SEK"}
	SGD = currencyCode{"SGD"}
	SHP = currencyCode{"SHP"}
	SKK = currencyCode{"SKK"}
	SLE = currencyCode{"SLE"}
	SLL = currencyCode{"SLL"}
	SOS = currencyCode{"SOS"}
	SRD = currencyCode{"SRD"}
	SSP = currencyCode{"SSP"}
	STD = currencyCode{"STD"}
	STN = currencyCode{"STN"}
	SVC = currencyCode{"SVC"}
	SYP = currencyCode{"SYP"}
	SZL = currencyCode{"SZL"}
	THB = currencyCode{"THB"}
	TJS = currencyCode{"TJS"}
	TMT = currencyCode{"TMT"}
	TND = currencyCode{"TND"}
	TOP = currencyCode{"TOP"}
	TRL = currencyCode{"TRL"}
	TRY = currencyCode{"TRY"}
	TTD = currencyCode{"TTD"}
	TWD = currencyCode{"TWD"}
	TZS = currencyCode{"TZS"}
	UAH = currencyCode{"UAH"}
	UGX = currencyCode{"UGX"}
	USD = currencyCode{"USD"}
	UYU = currencyCode{"UYU"}
	UZS = currencyCode{"UZS"}
	VEF = currencyCode{"VEF"}
	VES = currencyCode{"VES"}
	VND = currencyCode{"VND"}
	VUV = currencyCode{"VUV"}
	WST = currencyCode{"WST"}
	XAF = currencyCode{"XAF"}
	XAG = currencyCode{"XAG"}
	XAU = currencyCode{"XAU"}
	XCD = currencyCode{"XCD"}
	XCG = currencyCode{"XCG"}
	XDR = currencyCode{"XDR"}
	XOF = currencyCode{"XOF"}
	XPF = currencyCode{"XPF"}
	YER = currencyCode{"YER"}
	ZAR = currencyCode{"ZAR"}
	ZMW = currencyCode{"ZMW"}
	ZWD = currencyCode{"ZWD"}
	ZWL = currencyCode{"ZWL"}
)

Functions

This section is empty.

Types

type Currencies

type Currencies map[currencyCode]Currency

Currencies is a map of currency codes to their corresponding currency details.

type Currency

type Currency struct {
	Code        currencyCode
	NumericCode string
	Fraction    int
	Grapheme    string
	Template    string
	Decimal     string
	Thousand    string
}

Currency represents the details of a Currency.

func (Currency) Formatter

func (c Currency) Formatter() Formatter

Formatter returns a Formatter struct based on the currency's formatting details.

func (*Currency) Scan

func (c *Currency) Scan(src any) error

Scan implements sql.Scanner for database deserialization of Currency.

func (Currency) Value

func (c Currency) Value() (driver.Value, error)

Value implements driver.Valuer for database serialization of Currency.

type Formatter

type Formatter struct {
	Fraction int
	Decimal  string
	Thousand string
	Grapheme string
	Template string
}

Formatter stores Money formatting information.

func (Formatter) Format

func (f Formatter) Format(amount int64) string

Format returns a string representation of the amount using the currency formatting rules.

func (Formatter) ToMajorUnits

func (f Formatter) ToMajorUnits(amount int64) float64

ToMajorUnits returns the amount as a float64 in major units (e.g. cents -> dollars). Use for display purposes only, never for arithmetic.

type Money

type Money struct {
	Amount   int64
	Currency Currency
}

Money represents a monetary value. Fields are exported for convenience. Prefer using the provided methods over direct field mutation to ensure correct behavior.

func NewMoney

func NewMoney(amount int64, currencyCode currencyCode) Money

NewMoney creates a new Money instance with the given amount and currency code.

func (Money) Absolute

func (m Money) Absolute() Money

Absolute returns a new Money instance with the absolute value of the amount.

func (Money) Add

func (m Money) Add(om Money) (Money, error)

Add adds another Money instance to this Money instance and returns the result. It returns an error if the currencies of the two Money instances are different.

func (Money) Allocate

func (m Money) Allocate(ratios ...int) ([]Money, error)

Allocate divides the amount of this Money instance according to the given ratios and returns a slice of Money instances. It returns an error if no ratios are specified or if any ratio is negative.

func (Money) AsMajorUnits

func (m Money) AsMajorUnits() float64

AsMajorUnits returns the amount as a float64 in major units (e.g. cents -> dollars). Use for display purposes only, never for arithmetic.

func (Money) Display

func (m Money) Display() string

Display returns a string representation of the amount using the currency formatting rules.

func (Money) Equals

func (m Money) Equals(om Money) (bool, error)

Equals checks if this Money instance is equal to another Money instance.

func (Money) GreaterThan

func (m Money) GreaterThan(om Money) (bool, error)

GreaterThan checks if this Money instance is greater than another Money instance.

func (Money) LessThan

func (m Money) LessThan(om Money) (bool, error)

LessThan checks if this Money instance is less than another Money instance.

func (Money) MarshalJSON

func (m Money) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface to allow Money instances to be serialized to JSON.

func (Money) Multiply

func (m Money) Multiply(mul int) Money

Multiply multiplies the amount of this Money instance by a given multiplier and returns the result.

func (Money) SameCurrency

func (m Money) SameCurrency(other Money) bool

SameCurrency checks if the currency of this Money instance is the same as another Money instance.

func (*Money) Scan

func (m *Money) Scan(src any) error

Scan implements sql.Scanner for database deserialization. Expects the amount as int64 — store currency code separately.

func (Money) Split

func (m Money) Split(n int) ([]Money, error)

Split divides the amount of this Money instance into n equal parts and returns a slice of Money instances. It returns an error if n is less than or equal to zero.

func (Money) Subtract

func (m Money) Subtract(om Money) (Money, error)

Subtract subtracts another Money instance from this Money instance and returns the result. It returns an error if the currencies of the two Money instances are different.

func (*Money) UnmarshalJSON

func (m *Money) UnmarshalJSON(b []byte) error

UnmarshalJSON implements the json.Unmarshaler interface to allow Money instances to be deserialized from JSON.

func (Money) Value

func (m Money) Value() (driver.Value, error)

Value implements driver.Valuer for database serialization of Money amount.

Jump to

Keyboard shortcuts

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