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 ¶
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" // TxTypeLoanRepayment is the off-chain leg: cash the borrower hands over // at a MoneyGram agent, or pays in over a mobile-money paybill. It is what // the borrower did, not what settled — the USDC leg is separate and can // lag it or fail. TxTypeLoanRepayment = "loan_repayment" // TxTypeAnchorDeposit is the on-chain USDC leg of an anchor deposit: // the anchor to the treasury. The mirror of TxTypeAnchorTransfer. TxTypeAnchorDeposit = "anchor_deposit" )
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.
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.
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
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
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 ¶
BeforeCreate sets the ID before creating a new account
type Date ¶ added in v1.0.0
func (Date) MarshalJSON ¶ added in v1.0.0
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 ¶
BeforeCreate sets the ID before creating a new user