Documentation
¶
Index ¶
- Variables
- func GetMintAddressForSymbol(symbol string) string
- func GetStablecoinSymbol(mintAddress string) string
- func IsStablecoin(mintAddress string) bool
- func RegisterAsset(asset Asset) error
- func ValidateStablecoinMint(mintAddress string) (string, error)
- type Asset
- type AssetMetadata
- type AssetType
- type Money
- func (m Money) Abs() Money
- func (m Money) Add(other Money) (Money, error)
- func (m Money) ApplyFixedDiscount(discount Money) (Money, error)
- func (m Money) ApplyPercentageDiscount(discountPercent float64) (Money, error)
- func (m Money) ApplyPercentageDiscountWithRounding(discountPercent float64, mode RoundingMode) (Money, error)
- func (m Money) Div(divisor int64) (Money, error)
- func (m Money) Equal(other Money) bool
- func (m Money) GreaterThan(other Money) bool
- func (m Money) IsNegative() bool
- func (m Money) IsPositive() bool
- func (m Money) IsZero() bool
- func (m Money) LessThan(other Money) bool
- func (m Money) MarshalJSON() ([]byte, error)
- func (m Money) Mul(multiplier int64) (Money, error)
- func (m Money) MulBasisPoints(basisPoints int64) (Money, error)
- func (m Money) MulBasisPointsWithRounding(basisPoints int64, mode RoundingMode) (Money, error)
- func (m Money) MulPercent(percent int64) (Money, error)
- func (m Money) MultiplyByFloat(multiplier float64) (Money, error)
- func (m Money) Negate() Money
- func (m Money) RoundUpToCents() Money
- func (m Money) String() string
- func (m Money) Sub(other Money) (Money, error)
- func (m Money) ToAtomic() string
- func (m Money) ToMajor() string
- func (m *Money) UnmarshalJSON(data []byte) error
- type MoneyJSON
- type MoneyRequest
- type MoneyResponse
- type RoundingMode
- type SPLAdapter
- type StripeAdapter
Constants ¶
This section is empty.
Variables ¶
var ( // ErrOverflow occurs when an operation would exceed int64 capacity. ErrOverflow = errors.New("money: arithmetic overflow") // ErrAssetMismatch occurs when operating on different assets. ErrAssetMismatch = errors.New("money: asset mismatch") // ErrNegativeAmount occurs when negative amount is invalid for operation. ErrNegativeAmount = errors.New("money: negative amount not allowed") // ErrInvalidFormat occurs when parsing fails. ErrInvalidFormat = errors.New("money: invalid format") // ErrDivisionByZero occurs when dividing by zero. ErrDivisionByZero = errors.New("money: division by zero") )
var KnownStablecoins = map[string]string{
"CASHx9KJUStyftLFWGvEVf59SGeG9sh5FfcnZMVPCASH": "CASH",
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "USDC",
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB": "USDT",
"2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo": "PYUSD",
}
KnownStablecoins maps Solana token mint addresses to their stablecoin symbols. These are the ONLY tokens that should be used for payments to ensure proper decimal handling (all stablecoins use 2-6 decimals and are pegged to $1).
WARNING: Using non-stablecoin tokens (SOL, BONK, etc.) will cause precision issues because the system rounds to 2 decimal places (cents).
Functions ¶
func GetMintAddressForSymbol ¶
GetMintAddressForSymbol returns the mint address for a stablecoin symbol. Returns empty string if symbol not found.
func GetStablecoinSymbol ¶
GetStablecoinSymbol returns the symbol for a stablecoin mint address. Returns empty string if not a known stablecoin.
func IsStablecoin ¶
IsStablecoin returns true if the mint address is a known stablecoin.
func RegisterAsset ¶
RegisterAsset adds a new asset to the registry (for testing or dynamic tokens).
func ValidateStablecoinMint ¶
ValidateStablecoinMint checks if a token mint address is a known stablecoin. Returns the stablecoin symbol if valid, or an error if not.
Why this matters:
- Typo in token mint = payments go to wrong token = permanent loss
- Non-stablecoins have unpredictable values (1 SOL ≠ $1, 1 BONK ≠ $1)
- System rounds to 2 decimal places assuming $1 peg
Types ¶
type Asset ¶
type Asset struct {
Code string // Asset code (USD, USDC, SOL, etc.)
Decimals uint8 // Number of decimal places (2 for USD, 6 for USDC, 9 for SOL)
Type AssetType
Metadata AssetMetadata
}
Asset represents a currency or token with its properties.
func MustGetAsset ¶
MustGetAsset retrieves an asset and panics if not found (for tests/constants).
func (Asset) GetSolanaMint ¶
GetSolanaMint returns the Solana mint address or error.
func (Asset) GetStripeCurrency ¶
GetStripeCurrency returns the Stripe currency code or error.
func (Asset) IsSPLToken ¶
IsSPLToken returns true if the asset is a Solana SPL token.
func (Asset) IsStripeCurrency ¶
IsStripeCurrency returns true if the asset is a Stripe fiat currency.
type AssetMetadata ¶
type AssetMetadata struct {
StripeCurrency string // Stripe currency code (lowercase: "usd", "eur")
SolanaMint string // Solana token mint address (base58)
}
AssetMetadata contains backend-specific information.
type Money ¶
type Money struct {
Asset Asset // The currency/token
Atomic int64 // Amount in smallest unit (cents, lamports, etc.)
}
Money represents a monetary amount in atomic units for a specific asset. All arithmetic is performed on int64 to avoid floating-point precision issues.
Examples:
- $10.50 USD = Money{Asset: USD, Atomic: 1050} // 1050 cents
- 1.5 USDC = Money{Asset: USDC, Atomic: 1500000} // 1.5 × 10^6
- 0.5 SOL = Money{Asset: SOL, Atomic: 500000000} // 0.5 × 10^9
func FromAtomic ¶
FromAtomic creates Money from an atomic units string.
Example:
- FromAtomic(USD, "1050") → $10.50
- FromAtomic(USDC, "1500000") → 1.5 USDC
func FromMajor ¶
FromMajor creates Money from a major unit string (e.g., "10.50"). Uses half-up rounding for fractional atomic units.
Examples:
- FromMajor(USD, "10.50") → 1050 cents
- FromMajor(USDC, "1.5") → 1500000 micro-USDC
func SumMoney ¶
SumMoney adds multiple Money values together. All values must be the same asset. Returns error if assets don't match or overflow occurs.
func (Money) Add ¶
Add returns the sum of two Money values. Returns error if assets don't match or overflow occurs.
func (Money) ApplyFixedDiscount ¶
ApplyFixedDiscount subtracts a fixed amount from the Money value. Returns zero if the discount exceeds the original amount.
func (Money) ApplyPercentageDiscount ¶
ApplyPercentageDiscount applies a percentage discount to the Money amount. The discount is a value between 0-100 (e.g., 10 for 10% off). Uses half-up rounding (standard) for the discount calculation. Returns the discounted amount (not the discount itself).
func (Money) ApplyPercentageDiscountWithRounding ¶
func (m Money) ApplyPercentageDiscountWithRounding(discountPercent float64, mode RoundingMode) (Money, error)
ApplyPercentageDiscountWithRounding applies a percentage discount with configurable rounding. The discount is a value between 0-100 (e.g., 10 for 10% off). Returns the discounted amount (not the discount itself).
func (Money) GreaterThan ¶
GreaterThan returns true if m > other (same asset required).
func (Money) IsNegative ¶
IsNegative returns true if amount is less than zero.
func (Money) IsPositive ¶
IsPositive returns true if amount is greater than zero.
func (Money) MarshalJSON ¶
MarshalJSON implements json.Marshaler for Money. Outputs atomic-only JSON:
{
"asset": "USD",
"atomic": "1050"
}
func (Money) MulBasisPoints ¶
MulBasisPoints multiplies Money by basis points (1/100th of a percent). Example: amount.MulBasisPoints(250) applies a 2.5% rate. Uses half-up rounding (standard).
func (Money) MulBasisPointsWithRounding ¶
func (m Money) MulBasisPointsWithRounding(basisPoints int64, mode RoundingMode) (Money, error)
MulBasisPointsWithRounding multiplies Money by basis points with configurable rounding. Example: amount.MulBasisPointsWithRounding(250, RoundingCeiling) applies a 2.5% rate with ceiling rounding.
func (Money) MulPercent ¶
MulPercent multiplies Money by a percentage (0-100). Example: amount.MulPercent(10) applies a 10% rate.
func (Money) MultiplyByFloat ¶
MultiplyByFloat multiplies Money by a float64 using precise decimal arithmetic. This is used for percentage-based calculations where the multiplier is not an integer. Uses big.Float for intermediate precision, then rounds half-up to nearest atomic unit. WARNING: Only use when necessary (e.g., calculating percentage discounts). Prefer integer operations when possible.
func (Money) RoundUpToCents ¶
RoundUpToCents rounds the amount up to the nearest cent (2 decimal places). This is used for pricing to ensure we never undercharge. Only affects assets with more than 2 decimals (like USDC with 6 decimals).
For positive amounts: rounds up (ceiling) - $0.184 → $0.19 For negative amounts: rounds towards zero (floor) - -$0.184 → -$0.18 This ensures consistent behavior for both payments and refunds.
func (Money) String ¶
String returns a human-readable representation. Example: Money{USD, 1050} → "$10.50 USD"
func (Money) ToMajor ¶
ToMajor converts Money to major unit string with proper decimal places.
Examples:
- Money{USD, 1050}.ToMajor() → "10.50"
- Money{USDC, 1500000}.ToMajor() → "1.500000"
- Money{USDC, 1500000}.ToMajor() → "1.500000"
func (*Money) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler for Money. Accepts atomic format only:
- {"asset":"USDC", "atomic":"1500000"} → Money{USDC, 1500000}
Returns error if:
- Asset code is missing or unknown
- Atomic field is missing
- Parsing fails
type MoneyJSON ¶
type MoneyJSON struct {
Asset string `json:"asset"` // Asset code (USD, USDC, SOL, etc.)
Atomic string `json:"atomic"` // Atomic units as string
}
MoneyJSON represents the JSON format for Money. Uses atomic units for precision:
{"asset":"USDC", "atomic":"1500000"}
type MoneyRequest ¶
type MoneyRequest Money
MoneyRequest is a helper type for API request parsing. Use this in request structs for clearer intent.
Example:
type PaymentRequest struct {
Amount MoneyRequest `json:"amount"`
}
func (MoneyRequest) MarshalJSON ¶
func (mr MoneyRequest) MarshalJSON() ([]byte, error)
MarshalJSON for MoneyRequest uses the same atomic-only format as Money.
func (MoneyRequest) ToMoney ¶
func (mr MoneyRequest) ToMoney() Money
ToMoney converts MoneyRequest to Money.
func (*MoneyRequest) UnmarshalJSON ¶
func (mr *MoneyRequest) UnmarshalJSON(data []byte) error
UnmarshalJSON for MoneyRequest uses the same parsing as Money.
type MoneyResponse ¶
type MoneyResponse Money
MoneyResponse is a helper type for API response formatting. Use this in response structs for clearer intent.
Example:
type QuoteResponse struct {
Total MoneyResponse `json:"total"`
}
func FromMoney ¶
func FromMoney(m Money) MoneyResponse
FromMoney creates a MoneyResponse from Money.
func (MoneyResponse) MarshalJSON ¶
func (mr MoneyResponse) MarshalJSON() ([]byte, error)
MarshalJSON for MoneyResponse uses the same atomic-only format as Money.
func (MoneyResponse) ToMoney ¶
func (mr MoneyResponse) ToMoney() Money
ToMoney converts MoneyResponse to Money.
func (*MoneyResponse) UnmarshalJSON ¶
func (mr *MoneyResponse) UnmarshalJSON(data []byte) error
UnmarshalJSON for MoneyResponse uses the same parsing as Money.
type RoundingMode ¶
type RoundingMode int
RoundingMode determines how fractional cents are rounded.
const ( // RoundingStandard uses half-up rounding (0.5 rounds up). // This matches Stripe's default behavior: $0.025 → $0.03, $0.024 → $0.02 RoundingStandard RoundingMode = iota // RoundingCeiling always rounds up to the next cent. // Example: $0.024 → $0.03, $0.001 → $0.01 RoundingCeiling )
func ParseRoundingMode ¶
func ParseRoundingMode(mode string) RoundingMode
ParseRoundingMode converts a string to RoundingMode. Accepts "standard", "ceiling", or empty string (defaults to standard).
type SPLAdapter ¶
type SPLAdapter struct{}
SPLAdapter converts Money to Solana SPL token format. SPL tokens use uint64 for amounts, while we use int64 internally.
func NewSPLAdapter ¶
func NewSPLAdapter() *SPLAdapter
NewSPLAdapter creates a new SPL token adapter.
func (*SPLAdapter) FromSPLAmount ¶
func (a *SPLAdapter) FromSPLAmount(mint string, amount uint64) (Money, error)
FromSPLAmount converts SPL token format to Money. Takes mint address and uint64 amount.
Example:
- ("EPjF...", 1500000) → Money{USDC, 1500000} // 1.5 USDC
- ("So11...", 500000000) → Money{SOL, 500000000} // 0.5 SOL
Returns error if:
- Mint address is not recognized
- Amount exceeds int64 max value (overflow)
func (*SPLAdapter) GetMintDecimals ¶
func (a *SPLAdapter) GetMintDecimals(mint string) (uint8, error)
GetMintDecimals returns the number of decimals for an SPL token mint. This is useful for external callers who have a mint address and need to know the token's decimal places.
func (*SPLAdapter) ToSPLAmount ¶
func (a *SPLAdapter) ToSPLAmount(m Money) (mint string, amount uint64, err error)
ToSPLAmount converts Money to SPL token format. Returns (mint address, amount) where:
- mint is the base58-encoded Solana token mint address
- amount is uint64 in token's atomic units (lamports, micro-USDC, etc.)
Example:
- Money{USDC, 1500000} → ("EPjF...", 1500000) // 1.5 USDC
- Money{SOL, 500000000} → ("So11...", 500000000) // 0.5 SOL
Returns error if:
- Asset is not an SPL token
- Amount is negative (SPL tokens use uint64, cannot represent negative)
- Amount exceeds uint64 max value
func (*SPLAdapter) ValidateSPLAmount ¶
func (a *SPLAdapter) ValidateSPLAmount(m Money) error
ValidateSPLAmount checks if a Money value is valid for SPL tokens. SPL token requirements:
- Must be an SPL token asset
- Amount must be non-negative (uint64 limitation)
- Amount must fit in uint64 (always true for non-negative int64)
type StripeAdapter ¶
type StripeAdapter struct{}
StripeAdapter converts Money to Stripe API format. Stripe expects amounts in the currency's smallest unit (cents for USD).
func NewStripeAdapter ¶
func NewStripeAdapter() *StripeAdapter
NewStripeAdapter creates a new Stripe adapter.
func (*StripeAdapter) FromStripeAmount ¶
func (a *StripeAdapter) FromStripeAmount(currency string, amount int64) (Money, error)
FromStripeAmount converts Stripe format to Money. Takes Stripe currency code (lowercase) and amount in smallest unit.
Example:
- ("usd", 1050) → Money{USD, 1050} // $10.50
- ("eur", 2500) → Money{EUR, 2500} // €25.00
Returns error if currency code is not recognized.
func (*StripeAdapter) ToStripeAmount ¶
func (a *StripeAdapter) ToStripeAmount(m Money) (currency string, amount int64, err error)
ToStripeAmount converts Money to Stripe format. Returns (currency, amount) where:
- currency is lowercase Stripe currency code (e.g., "usd", "eur")
- amount is int64 in smallest unit (cents)
Example:
- Money{USD, 1050} → ("usd", 1050) // $10.50
- Money{EUR, 2500} → ("eur", 2500) // €25.00
Returns error if asset is not a Stripe-supported fiat currency.
func (*StripeAdapter) ValidateStripeAmount ¶
func (a *StripeAdapter) ValidateStripeAmount(m Money) error
ValidateStripeAmount checks if a Money value is valid for Stripe. Stripe has specific requirements:
- Must be a fiat currency
- Amount must be non-negative
- Amount must be within Stripe's limits (0 to 99,999,999 for most currencies)