models

package
v1.0.7 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package models holds the GORM persistence models — the database schema expressed as Go structs. Each type maps to one table and carries the JSON and gorm struct tags that define its API shape and its columns. The repository layer reads and writes these; the service layers wrap them in their own DTOs.

The entities are User, Account, and Transaction, plus SecurityQuestion for the PIN-recovery flow. An Account is a Stellar child account belonging to a User; a Transaction optionally references a User and an Account. PIN material lives on the User (its hash, attempt count, and lockout time), and security-question answers are stored hashed.

Conventions

Every model follows the same rules. The primary key is a UUIDv7 assigned in a BeforeCreate hook, so IDs are unique and time-ordered without a database sequence. A TableName method pins the table name. Secret fields — PIN hash and security-answer hash — are tagged json:"-" so they never serialize into a response. User and Account carry a nullable, indexed DeletedAt that the repository stamps and clears by hand for reversible soft deletes; transactions are never deleted.

Canonical enums

The string constants defined here are the single source of truth for the values the service state machines validate against: KYC statuses, the shared user and account lifecycle statuses, and the transaction statuses, categories, and types. Referencing these constants rather than bare strings keeps the services and the stored rows in agreement.

Index

Constants

View Source
const (
	// Transaction Status
	TxStatusPending   = "pending"
	TxStatusSubmitted = "submitted"
	TxStatusSuccess   = "success"
	TxStatusFailed    = "failed"
	TxStatusCancelled = "cancelled"

	// Transaction Types — Loan Disbursement
	TxTypeVaultBorrow  = "vault_borrow"  // USDC borrowed from Stellar vault to user account
	TxTypeOffRamp      = "off_ramp"      // Off-ramp initiated (crypto-to-fiat via YellowCard)
	TxTypeFiatFailover = "fiat_failover" // Fiat failover after direct settlement refund
	TxTypeVaultRepay   = "vault_repay"   // USDC repaid from treasury back to Stellar vault
	TxTypeRefund       = "refund"        // USDC returned by an anchor after a cancelled off-ramp

	// TxTypeAnchorTransfer is the on-chain USDC leg of an anchor withdrawal:
	// treasury to the anchor's withdraw account. Distinct from TxTypeOffRamp,
	// which records the off-chain fiat leg the anchor pays out afterwards — a
	// cash-pickup disbursement produces both.
	TxTypeAnchorTransfer = "anchor_transfer"
)
View Source
const (
	TxCategoryOnChain  = "on_chain"
	TxCategoryOffChain = "off_chain"
)

Transaction categories. Derived from TxType rather than stored: every type is settled either on the Stellar ledger or off it, never both, so a column would only add a way for the two to disagree.

View Source
const (
	// ChainStatusPending is set at registration: the sponsored-creation
	// transaction has been dispatched but not confirmed.
	ChainStatusPending = "pending"

	// ChainStatusConfirmed means the account was observed on the network.
	ChainStatusConfirmed = "confirmed"

	// ChainStatusFailed means creation retries were exhausted. Lending is
	// blocked for these until EnsureOnChainAccount heals them.
	ChainStatusFailed = "failed"

	// ChainStatusUnknown marks rows that predate this column.
	ChainStatusUnknown = "unknown"
)

Account.ChainStatus values. The account row is committed before the Stellar account is submitted, so this is what distinguishes a row whose keypair exists on-chain from one whose creation never landed.

View Source
const (
	// User KYC Status
	KYCStatusPending  = "pending"
	KYCStatusVerified = "verified"
	KYCStatusRejected = "rejected"
	KYCStatusExpired  = "expired"

	// User/Account Status
	StatusActive    = "active"
	StatusSuspended = "suspended"
	StatusBlocked   = "blocked"
	StatusFrozen    = "frozen"
	StatusClosed    = "closed"
)

Constants for status enums

View Source
const PredefinedQuestionCount = 5

PredefinedQuestionCount is the total number of predefined security questions available for selection. Question IDs range from 1 to PredefinedQuestionCount.

Variables

This section is empty.

Functions

func TxCategoryFor added in v1.0.0

func TxCategoryFor(txType string) string

TxCategoryFor reports where a transaction type settles.

Unknown types report off_chain: a type this function has not been taught about has no ledger presence to claim.

Types

type Account

type Account struct {
	ID           string     `json:"id" gorm:"type:uuid;primaryKey"`
	UserID       string     `json:"user_id" gorm:"type:uuid;not null;index"`
	PublicKey    string     `json:"public_key" gorm:"type:varchar(56);uniqueIndex;not null"`
	AccountIndex int        `json:"account_index" gorm:"not null"`
	Status       string     `json:"status" gorm:"type:varchar(20);not null;default:'active'"`
	ChainStatus  string     `json:"chain_status" gorm:"type:varchar(20);not null;default:'pending'"`
	CreatedAt    time.Time  `json:"created_at" gorm:"autoCreateTime;not null"`
	UpdatedAt    time.Time  `json:"updated_at" gorm:"autoUpdateTime;not null"`
	DeletedAt    *time.Time `json:"deleted_at,omitempty" gorm:"index"`

	User User `gorm:"foreignKey:UserID"`
}

Account represents a Stellar child account

func (*Account) BeforeCreate

func (account *Account) BeforeCreate(tx *gorm.DB) error

BeforeCreate sets the ID before creating a new account

func (Account) TableName

func (Account) TableName() string

TableName specifies the table name for Account model

type Date added in v1.0.0

type Date struct{ time.Time }

func (Date) MarshalJSON added in v1.0.0

func (d Date) MarshalJSON() ([]byte, error)

func (*Date) Scan added in v1.0.0

func (d *Date) Scan(value any) error

Scan reads a DATE value from the database into d.

func (Date) Value added in v1.0.0

func (d Date) Value() (driver.Value, error)

Value renders the date as a DATE literal for the database driver. Without this, GORM serializes the embedded time.Time via String(), which Postgres rejects for a DATE column.

type SecurityQuestion

type SecurityQuestion struct {
	ID         string    `json:"id" gorm:"type:uuid;primaryKey"`
	UserID     string    `json:"user_id" gorm:"type:uuid;not null;index"`
	QuestionID int       `json:"question_id" gorm:"not null"`
	AnswerHash string    `json:"-" gorm:"type:varchar(72);not null"`
	CreatedAt  time.Time `json:"created_at" gorm:"autoCreateTime;not null"`
	UpdatedAt  time.Time `json:"updated_at" gorm:"autoUpdateTime;not null"`

	User User `gorm:"foreignKey:UserID"`
}

SecurityQuestion stores a hashed answer to a predefined security question for a given user. Each user may have multiple security questions, identified by QuestionID (an integer index into a predefined question list). The combination of (UserID, QuestionID) is unique.

func (*SecurityQuestion) BeforeCreate

func (sq *SecurityQuestion) BeforeCreate(tx *gorm.DB) error

BeforeCreate sets a UUIDv7 primary key before inserting a new row.

func (SecurityQuestion) TableName

func (SecurityQuestion) TableName() string

TableName specifies the table name for the SecurityQuestion model.

type Transaction

type Transaction struct {
	ID               string    `json:"id" gorm:"type:uuid;primaryKey"`
	UserID           *string   `json:"user_id,omitempty" gorm:"type:uuid;index"`
	AccountID        *string   `json:"account_id,omitempty" gorm:"type:uuid;index"`
	LoanID           *string   `json:"loan_id,omitempty" gorm:"type:uuid;index"`
	TxType           string    `json:"tx_type" gorm:"type:varchar(50);not null;index"`
	Amount           int64     `json:"amount" gorm:"type:bigint;not null"`
	Asset            string    `json:"asset" gorm:"type:varchar(20);not null;index"`
	StellarTxHash    *string   `json:"stellar_tx_hash,omitempty" gorm:"type:varchar(64);uniqueIndex"`
	StellarLedger    *int64    `json:"stellar_ledger,omitempty" gorm:"type:bigint"`
	ContractID       *string   `json:"contract_id,omitempty" gorm:"type:varchar(56);index"`
	ContractFunction *string   `json:"contract_function,omitempty" gorm:"type:varchar(100)"`
	ExternalID       *string   `json:"external_id,omitempty" gorm:"type:varchar(100);index"`
	ExternalProvider *string   `json:"external_provider,omitempty" gorm:"type:varchar(50);index"`
	ExternalStatus   *string   `json:"external_status,omitempty" gorm:"type:varchar(20)"`
	Description      *string   `json:"description,omitempty" gorm:"type:text"`
	Metadata         *string   `json:"metadata,omitempty" gorm:"type:jsonb"`
	Status           string    `json:"status" gorm:"type:varchar(20);not null;default:'pending';index"`
	CreatedAt        time.Time `json:"created_at" gorm:"autoCreateTime;not null;index"`
	UpdatedAt        time.Time `json:"updated_at" gorm:"autoUpdateTime;not null"`

	User    *User    `gorm:"foreignKey:UserID"`
	Account *Account `gorm:"foreignKey:AccountID"`
}

Transaction represents a blockchain or off-chain transaction

func (*Transaction) BeforeCreate

func (transaction *Transaction) BeforeCreate(tx *gorm.DB) error

BeforeCreate sets the ID before creating a new transaction

func (Transaction) TableName

func (Transaction) TableName() string

TableName specifies the table name for Transaction model

type User

type User struct {
	ID                string     `json:"id" gorm:"type:uuid;primaryKey"`
	MobileNumber      string     `json:"mobile_number" gorm:"type:varchar(20);uniqueIndex;not null"`
	CountryCode       string     `json:"country_code" gorm:"type:varchar(5);not null;default:'KE'"`
	MobileNetworkCode string     `json:"mobile_network_code" gorm:"type:varchar(6);not null;default:'99999'"`
	MomoNetworkCode   string     `json:"momo_network_code" gorm:"type:varchar(20);not null;default:'SANDBOX'"`
	MomoNetworkName   string     `json:"momo_network_name" gorm:"type:varchar(20);not null;default:'Sandbox Network'"`
	TelcoName         string     `json:"telco_name" gorm:"type:varchar(20);not null;default:'Athena'"`
	FullName          *string    `json:"full_name,omitempty" gorm:"type:varchar(255)"`
	BirthDate         *Date      `json:"birth_date,omitempty" gorm:"type:date"`
	Address           *string    `json:"address,omitempty" gorm:"type:varchar(255)"`
	City              *string    `json:"city,omitempty" gorm:"type:varchar(255)"`
	PostalCode        *string    `json:"postal_code,omitempty" gorm:"type:varchar(20)"`
	NationalID        *string    `json:"national_id,omitempty" gorm:"type:varchar(50);uniqueIndex"`
	KYCStatus         string     `json:"kyc_status" gorm:"type:varchar(20);not null;default:'pending'"`
	KYCVerifiedAt     *time.Time `json:"kyc_verified_at,omitempty" gorm:"type:timestamp"`
	PinHash           *string    `json:"-" gorm:"type:varchar(72)"`
	PinAttempts       int        `json:"-" gorm:"not null;default:0"`
	PinLockedUntil    *time.Time `json:"-" gorm:"type:timestamp"`
	PinSetAt          *time.Time `json:"-" gorm:"type:timestamp"`

	PreferredLanguage string     `json:"preferred_language" gorm:"type:varchar(10);not null;default:'en'"`
	Status            string     `json:"status" gorm:"type:varchar(20);not null;default:'active'"`
	Role              string     `json:"role" gorm:"type:varchar(20);not null;default:'user'"`
	CreatedAt         time.Time  `json:"created_at" gorm:"autoCreateTime;not null"`
	UpdatedAt         time.Time  `json:"updated_at" gorm:"autoUpdateTime;not null"`
	DeletedAt         *time.Time `json:"deleted_at,omitempty" gorm:"index"`
}

User represents a system user

func (*User) BeforeCreate

func (user *User) BeforeCreate(tx *gorm.DB) error

BeforeCreate sets the ID before creating a new user

func (User) TableName

func (User) TableName() string

TableName specifies the table name for User model

Jump to

Keyboard shortcuts

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