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
- func TxCategoryFor(txType string) string
- type Account
- type AddressScreening
- type AirtelTransaction
- type AirtelTransactionConfirmVia
- type AirtelTransactionSource
- type Counterparty
- type CounterpartyAddress
- type CounterpartyAddressOnchainState
- type CounterpartyAddressStatus
- type CounterpartyKYBStatus
- type Date
- type MpesaNumberValidation
- type MpesaTransaction
- type MpesaTransactionConfirmVia
- type MpesaTransactionReversal
- type MpesaTransactionSource
- type SecurityQuestion
- type Transaction
- type User
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 AddressScreening ¶ added in v1.5.0
type AddressScreening struct {
ID string `json:"id" gorm:"type:uuid;primaryKey"`
AddressID string `json:"address_id" gorm:"column:address_id;type:uuid;not null;index"`
// EllipticAnalysisID/EllipticScreeningID let this screening be fetched
// from Elliptic directly later, independent of RawPayload.
EllipticAnalysisID string `json:"elliptic_analysis_id,omitempty" gorm:"column:elliptic_analysis_id"`
EllipticScreeningID string `json:"elliptic_screening_id,omitempty" gorm:"column:elliptic_screening_id"`
ScreeningSource string `json:"screening_source" gorm:"column:screening_source;type:varchar(30);not null"`
// RiskScore is nullable and never defaulted to zero — see
// compliance.Screening.RiskScore's doc comment on why a null score is
// not the same fact as a clean score.
RiskScore *float64 `json:"risk_score,omitempty" gorm:"column:risk_score;type:numeric"`
Sanctioned bool `json:"sanctioned" gorm:"not null;default:false"`
Verdict string `json:"verdict" gorm:"type:varchar(20);not null"`
// RawPayload is Elliptic's whole response. Kept because a compliance
// record has to be reproducible years later, and a struct designed
// today will not have a field for whatever Elliptic adds later.
RawPayload datatypes.JSON `json:"raw_payload" gorm:"column:raw_payload;type:jsonb;not null"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;not null"`
}
AddressScreening is one screening performed on a CounterpartyAddress. Append-only — there is deliberately no Update path anywhere in this codebase for this table. A compliance record that changes in place cannot answer "what did we know on the day we approved this," which is the only question that matters after an incident. See the source design doc §12.
func (*AddressScreening) BeforeCreate ¶ added in v1.5.0
func (s *AddressScreening) BeforeCreate(g *gorm.DB) error
func (AddressScreening) TableName ¶ added in v1.5.0
func (AddressScreening) TableName() string
type AirtelTransaction ¶ added in v1.6.0
type AirtelTransaction struct {
ID string `json:"id" gorm:"type:uuid;primaryKey"`
// PartnerTxnID is the id we generated and sent. Unique-indexed, and the
// idempotency key — Airtel's own receipt cannot be, because it does not
// exist until the transaction succeeds.
PartnerTxnID string `json:"partner_txn_id" gorm:"column:partner_txn_id;type:varchar(64);uniqueIndex;not null"`
// AirtelMoneyID is Airtel's receipt, minted only on TS. It is the only
// key a refund accepts, which is why a refund is impossible until an
// enquiry or a callback has disclosed it.
AirtelMoneyID *string `json:"airtel_money_id,omitempty" gorm:"column:airtel_money_id;type:varchar(64)"`
Source AirtelTransactionSource `json:"source" gorm:"type:varchar(20);not null;index"`
StatusCode string `json:"status_code" gorm:"type:varchar(4);not null"`
Confirmed bool `json:"confirmed" gorm:"not null;default:false"`
ConfirmedVia *AirtelTransactionConfirmVia `json:"confirmed_via,omitempty" gorm:"type:varchar(20)"`
// HashVerified records whether the callback's HmacSHA256 checked out.
// HashVariant records which reading of "the callback body" matched, so
// the answer the portal does not give is recorded from live traffic
// rather than inferred.
HashVerified bool `json:"hash_verified" gorm:"not null;default:false"`
HashVariant *string `json:"hash_variant,omitempty" gorm:"type:varchar(20)"`
Reference string `json:"reference" gorm:"type:varchar(25);index"`
LoanID *string `json:"loan_id,omitempty" gorm:"type:uuid;index"`
// AmountMinor is in minor units throughout. The M-Pesa rail's equivalent
// column is named for shillings and stores them; naming this one for the
// unit it holds removes the question.
AmountMinor int64 `json:"amount_minor" gorm:"column:amount_minor;not null"`
// AppliedStroops is set once, the first time this row is converted to
// USDC and credited toward LoanID's repayment progress.
AppliedStroops *int64 `json:"applied_stroops,omitempty" gorm:"column:applied_stroops"`
// Msisdn is the payer's number in Airtel's national form. Nullable PII;
// never log it. Airtel does not mask it the way a C2B confirmation does,
// so there is no masked counterpart and no safe-to-log version.
Msisdn *string `json:"msisdn,omitempty" gorm:"type:varchar(25)"`
PayerName *string `json:"payer_name,omitempty" gorm:"type:varchar(100)"`
TransTime time.Time `json:"trans_time" gorm:"not null"`
// RawPayload is the bytes as received. It is not optional: when an
// enquiry disagrees with a callback, the bytes settle it — and with two
// callbacks per transaction, which bytes arrived when is the only record
// of the intermediate state.
RawPayload datatypes.JSON `json:"raw_payload" gorm:"type:jsonb;not null"`
NextPollAt *time.Time `json:"next_poll_at,omitempty" gorm:"index"`
PollAttempts int `json:"poll_attempts" gorm:"not null;default:0"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;not null"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime;not null"`
}
AirtelTransaction is the observation log for inbound Airtel Money notifications.
A row is never evidence of a payment. A verified hash proves Airtel sent the callback; it does not prove the payment settled, and an intermediate callback looks exactly like a final one. The row becomes a payment only once ConfirmedVia names an enquiry.
func (*AirtelTransaction) BeforeCreate ¶ added in v1.6.0
func (tx *AirtelTransaction) BeforeCreate(_ *gorm.DB) error
BeforeCreate sets the ID before creating a new AirtelTransaction.
func (AirtelTransaction) TableName ¶ added in v1.6.0
func (AirtelTransaction) TableName() string
TableName specifies the table name for AirtelTransaction.
type AirtelTransactionConfirmVia ¶ added in v1.6.0
type AirtelTransactionConfirmVia string
AirtelTransactionConfirmVia is how an observation was independently verified. A callback is not on this list, and that is the point: Airtel's callback carries intermediate or final status with nothing to tell them apart, so it can never be the thing that confirms.
const ( AirtelConfirmViaEnquiry AirtelTransactionConfirmVia = "enquiry" AirtelConfirmViaSummary AirtelTransactionConfirmVia = "summary" )
The confirmation paths.
type AirtelTransactionSource ¶ added in v1.6.0
type AirtelTransactionSource string
AirtelTransactionSource is where an observation came from.
const ( AirtelSourceCallback AirtelTransactionSource = "callback" AirtelSourceEnquiry AirtelTransactionSource = "enquiry" AirtelSourceSummary AirtelTransactionSource = "summary" )
The inbound sources.
type Counterparty ¶ added in v1.5.0
type Counterparty struct {
ID string `json:"id" gorm:"type:uuid;primaryKey"`
LegalName string `json:"legal_name" gorm:"type:varchar(200);not null"`
RegistrationNumber string `json:"registration_number,omitempty" gorm:"type:varchar(100)"`
Jurisdiction string `json:"jurisdiction,omitempty" gorm:"type:varchar(100)"`
KYBStatus CounterpartyKYBStatus `json:"kyb_status" gorm:"column:kyb_status;type:varchar(20);not null;default:'pending';index"`
// EllipticCustomerReference is sent as customer_reference on every
// screening call for this counterparty's addresses — set once at
// creation, never changed. See the source design doc §4.
EllipticCustomerReference string `json:"elliptic_customer_reference" gorm:"column:elliptic_customer_reference;type:varchar(100);uniqueIndex;not null"`
KYBApprovedAt *time.Time `json:"kyb_approved_at,omitempty" gorm:"column:kyb_approved_at"`
// KYBApprovedBy is the approving admin's Stellar public key, as text —
// see counterparty_repository.go's doc comment on why this is text and
// not a user-UUID FK.
KYBApprovedBy *string `json:"kyb_approved_by,omitempty" gorm:"column:kyb_approved_by"`
Addresses []CounterpartyAddress `json:"addresses,omitempty" gorm:"foreignKey:CounterpartyID"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;not null"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime;not null"`
}
Counterparty is an institutional vault depositor undergoing KYB — external and self-custodied, the opposite of the custodial borrower accounts in models.Account. See elliptic-compliance-integration.md §1.
func (*Counterparty) BeforeCreate ¶ added in v1.5.0
func (c *Counterparty) BeforeCreate(g *gorm.DB) error
BeforeCreate assigns an ID only if the caller hasn't already set one — unlike every other model's BeforeCreate in this codebase, which always overwrites. Callers that need the ID before the insert (to derive EllipticCustomerReference from it, so the row is never briefly written with an empty value that would collide with the column's unique index) pre-generate it with uuid.NewV7() themselves.
func (Counterparty) TableName ¶ added in v1.5.0
func (Counterparty) TableName() string
type CounterpartyAddress ¶ added in v1.5.0
type CounterpartyAddress struct {
ID string `json:"id" gorm:"type:uuid;primaryKey"`
CounterpartyID string `json:"counterparty_id" gorm:"column:counterparty_id;type:uuid;not null;index"`
// Address is a Stellar G... account, 56 characters, matching
// accounts.public_key.
Address string `json:"address" gorm:"type:varchar(56);uniqueIndex;not null"`
Status CounterpartyAddressStatus `json:"status" gorm:"type:varchar(20);not null;default:'pending';index"`
LastScreeningID *string `json:"last_screening_id,omitempty" gorm:"column:last_screening_id;type:uuid"`
ScreenedAt *time.Time `json:"screened_at,omitempty" gorm:"column:screened_at"`
ExpiresAt *time.Time `json:"expires_at,omitempty" gorm:"column:expires_at;index"`
OnchainState CounterpartyAddressOnchainState `json:"onchain_state" gorm:"column:onchain_state;type:varchar(20);not null;default:'absent';index"`
ApprovedBy *string `json:"approved_by,omitempty" gorm:"column:approved_by"`
ApprovedAt *time.Time `json:"approved_at,omitempty" gorm:"column:approved_at"`
RevokedBy *string `json:"revoked_by,omitempty" gorm:"column:revoked_by"`
RevokedAt *time.Time `json:"revoked_at,omitempty" gorm:"column:revoked_at"`
OverrideReason *string `json:"override_reason,omitempty" gorm:"column:override_reason"`
Counterparty *Counterparty `json:"counterparty,omitempty" gorm:"foreignKey:CounterpartyID"`
// LatestScreening is a belongs-to on last_screening_id — an ordinary FK,
// so Preload("LatestScreening") works the same as any other association.
LatestScreening *AddressScreening `json:"latest_screening,omitempty" gorm:"foreignKey:LastScreeningID;references:ID"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;not null"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime;not null"`
}
CounterpartyAddress is a wallet address a counterparty has submitted for screening — the allowlist's source of truth, and the row an approval or revocation is actually attached to.
func (*CounterpartyAddress) BeforeCreate ¶ added in v1.5.0
func (a *CounterpartyAddress) BeforeCreate(g *gorm.DB) error
func (CounterpartyAddress) TableName ¶ added in v1.5.0
func (CounterpartyAddress) TableName() string
type CounterpartyAddressOnchainState ¶ added in v1.5.0
type CounterpartyAddressOnchainState string
CounterpartyAddressOnchainState tracks whether the vault contract's on-chain allowlist actually reflects this row — a database write and a contract write are two systems, and one can fail after the other succeeds. See the source design doc §12.
const ( OnchainStateAbsent CounterpartyAddressOnchainState = "absent" OnchainStatePending CounterpartyAddressOnchainState = "pending" OnchainStateApproved CounterpartyAddressOnchainState = "approved" OnchainStateRevoked CounterpartyAddressOnchainState = "revoked" )
type CounterpartyAddressStatus ¶ added in v1.5.0
type CounterpartyAddressStatus string
CounterpartyAddressStatus is the address's current compliance state — the §10 verdict states, plus "expired" (a staleness state no single screening produces; it's derived from expires_at). Deliberately its own type rather than reusing compliance.Verdict: this column is what we currently believe about the address, which "expired" can describe even when the last screening's own verdict was "approved".
const ( AddressStatusPending CounterpartyAddressStatus = "pending" AddressStatusApproved CounterpartyAddressStatus = "approved" AddressStatusRejected CounterpartyAddressStatus = "rejected" AddressStatusReview CounterpartyAddressStatus = "review" AddressStatusUnscreenable CounterpartyAddressStatus = "unscreenable" AddressStatusExpired CounterpartyAddressStatus = "expired" )
type CounterpartyKYBStatus ¶ added in v1.5.0
type CounterpartyKYBStatus string
CounterpartyKYBStatus is where a KYB entity stands in its own onboarding, independent of any address's screening status.
const ( CounterpartyKYBPending CounterpartyKYBStatus = "pending" CounterpartyKYBApproved CounterpartyKYBStatus = "approved" CounterpartyKYBRejected CounterpartyKYBStatus = "rejected" CounterpartyKYBExpired CounterpartyKYBStatus = "expired" )
type Date ¶ added in v1.0.0
func (Date) MarshalJSON ¶ added in v1.0.0
type MpesaNumberValidation ¶ added in v1.4.1
type MpesaNumberValidation struct {
ID string `json:"id" gorm:"type:uuid;primaryKey"`
// IdentityHash is sha256(msisdn|idType|idNumber), hex-encoded.
IdentityHash string `json:"identity_hash" gorm:"column:identity_hash;type:varchar(64);uniqueIndex;not null"`
Matched bool `json:"matched" gorm:"not null"`
ResponseCode string `json:"response_code" gorm:"column:response_code;type:varchar(10);not null"`
CheckedAt time.Time `json:"checked_at" gorm:"column:checked_at;not null"`
}
MpesaNumberValidation caches the verdict of a Mobile Number Validation call, keyed on a hash of the (msisdn, idType, idNumber) tuple so the cache itself never stores the PII it exists to avoid re-checking.
func (*MpesaNumberValidation) BeforeCreate ¶ added in v1.4.1
func (v *MpesaNumberValidation) BeforeCreate(g *gorm.DB) error
BeforeCreate sets the ID before creating a new MpesaNumberValidation.
func (MpesaNumberValidation) TableName ¶ added in v1.4.1
func (MpesaNumberValidation) TableName() string
TableName specifies the table name for MpesaNumberValidation.
type MpesaTransaction ¶ added in v1.4.1
type MpesaTransaction struct {
ID string `json:"id" gorm:"type:uuid;primaryKey"`
// TransID is the M-Pesa receipt. Unique-indexed. The idempotency key.
TransID string `json:"trans_id" gorm:"column:trans_id;type:varchar(20);uniqueIndex;not null"`
Source MpesaTransactionSource `json:"source" gorm:"type:varchar(20);not null;index"`
Confirmed bool `json:"confirmed" gorm:"not null;default:false"`
ConfirmedVia *MpesaTransactionConfirmVia `json:"confirmed_via,omitempty" gorm:"type:varchar(20)"`
BillRefNumber string `json:"bill_ref_number" gorm:"type:varchar(20);index"`
LoanID *string `json:"loan_id,omitempty" gorm:"type:uuid;index"`
AmountKes int64 `json:"amount_kes" gorm:"column:amount_kes;not null"` // minor units
// AppliedStroops is set once, the first time this row is converted to
// USDC and credited toward LoanID's repayment progress — nil means not
// yet applied. See the migration's comment for why this lives on the
// row rather than a running total elsewhere.
AppliedStroops *int64 `json:"applied_stroops,omitempty" gorm:"column:applied_stroops"`
// MsidnMasked comes from a C2B callback; MsidnFull comes from the Pull
// reconciler. Full is nullable PII; never log it.
MsidnMasked string `json:"msisdn_masked,omitempty" gorm:"column:msisdn_masked"`
MsidnFull *string `json:"msisdn_full,omitempty" gorm:"column:msisdn_full"`
// PayerName comes from the C2B callback or a reversal result. Nullable PII.
PayerName *string `json:"payer_name,omitempty" gorm:"type:varchar(100)"`
TransTime time.Time `json:"trans_time" gorm:"not null"`
// ThirdPartyTransID is our own correlation handle, echoed validation →
// confirmation.
ThirdPartyTransID *string `json:"third_party_trans_id,omitempty"`
CheckoutRequestID *string `json:"checkout_request_id,omitempty" gorm:"index"` // STK
MerchantRequestID *string `json:"merchant_request_id,omitempty"` // STK
SequenceID *string `json:"sequence_id,omitempty" gorm:"type:varchar(200);index"`
// RawPayload is the bytes as received. It is not optional: when a
// reconciliation disagrees with a callback, the bytes settle it.
RawPayload datatypes.JSON `json:"raw_payload" gorm:"type:jsonb;not null"`
Reversal MpesaTransactionReversal `json:"reversal_state" gorm:"column:reversal_state;type:varchar(20);not null;default:'none'"`
NextPollAt *time.Time `json:"next_poll_at,omitempty" gorm:"index"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;not null"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime;not null"`
}
MpesaTransaction is the observation log for inbound M-Pesa notifications. Every callback lands here before anything else happens, so the confirm-before-credit discipline is enforceable.
A row is never evidence of a payment. Daraja signs nothing, so a well-formed callback proves only that something posted to a URL. It becomes a payment only after ConfirmedVia names an independent check.
func (*MpesaTransaction) BeforeCreate ¶ added in v1.4.1
func (tx *MpesaTransaction) BeforeCreate(g *gorm.DB) error
BeforeCreate sets the ID before creating a new MpesaTransaction
func (MpesaTransaction) TableName ¶ added in v1.4.1
func (MpesaTransaction) TableName() string
TableName specifies the table name for MpesaTransaction model
type MpesaTransactionConfirmVia ¶ added in v1.4.1
type MpesaTransactionConfirmVia string
MpesaTransactionConfirmVia is how an observation was independently verified.
const ( MpesaConfirmViaSTKQuery MpesaTransactionConfirmVia = "stk_query" MpesaConfirmViaPull MpesaTransactionConfirmVia = "pull" MpesaConfirmViaTransactionStatus MpesaTransactionConfirmVia = "transaction_status" )
The confirmation paths.
type MpesaTransactionReversal ¶ added in v1.4.1
type MpesaTransactionReversal string
MpesaTransactionReversal is the state of a reversal for this transaction.
const ( MpesaReversalNone MpesaTransactionReversal = "none" MpesaReversalProposed MpesaTransactionReversal = "proposed" MpesaReversalApproved MpesaTransactionReversal = "approved" MpesaReversalSent MpesaTransactionReversal = "sent" MpesaReversalComplete MpesaTransactionReversal = "complete" MpesaReversalFailed MpesaTransactionReversal = "failed" )
The reversal states.
type MpesaTransactionSource ¶ added in v1.4.1
type MpesaTransactionSource string
MpesaTransactionSource is where an inbound notification came from. It decides which confirmation path can verify it.
const ( MpesaSourceSTKCallback MpesaTransactionSource = "stk_callback" MpesaSourceC2BConfirmation MpesaTransactionSource = "c2b_confirmation" MpesaSourcePull MpesaTransactionSource = "pull" MpesaSourceStatusResult MpesaTransactionSource = "status_result" MpesaSourceSTKQuery MpesaTransactionSource = "stk_query" )
The inbound sources.
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