contracts

package
v1.6.2 Latest Latest
Warning

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

Go to latest
Published: Sep 29, 2026 License: AGPL-3.0 Imports: 3 Imported by: 0

Documentation

Overview

Package contracts holds the interface seams between the platform's modules, together with the data types those interfaces exchange. It is a dependency-free boundary layer: producers and consumers both import contracts and depend on its abstractions rather than on each other, which keeps implementations swappable and avoids import cycles.

The interfaces

Lender (lending.go) is the loan port — eligibility assessment, origination, and retrieval. The package that originates loans implements it; handlers and jobs depend only on the interface.

LoanNotifier (lending.go) and AccountNotifier (pin.go) are the notification ports. They are implemented by the notification layer, which may deliver over SMS, push, or any other transport, and are consumed by the services that need to tell a user about loan lifecycle events (approved, disbursed, off-ramp failed, cash-pickup ready, repayment received) or account and PIN events (registration, wrong-PIN attempts, lockout, PIN change and reset).

The data types

EligibilityRequest and EligibilityResult frame an eligibility check; CreateLoanRequest and LoanRecord cover origination. A LoanRecord tracks both sides of a loan: the on-chain vault disbursement and the off-ramp settlement, including the settlement method, disbursement status, and provider reference. LoanNotification and AccountNotification carry the fields each notification method needs — not every field applies to every method. Monetary amounts are in stroops (USDC times ten million); display amounts and currencies travel alongside them on the notification types. Sentinel errors live in lending_errors.go.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotEligible           = errors.New("user is not eligible for a loan")
	ErrInsufficientLiquidity = errors.New("insufficient vault liquidity")
	ErrLoanNotFound          = errors.New("loan not found")
	ErrLoanNotActive         = errors.New("loan is not in an active state")
	ErrAccountNotFound       = errors.New("account not found")
	ErrAccountNotOwned       = errors.New("account is not owned by the user")
)

Functions

This section is empty.

Types

type AccountNotification

type AccountNotification struct {
	// UserID is the unique identifier of the user being notified.
	UserID string

	// PhoneNumber is the destination for SMS delivery (E.164 format).
	PhoneNumber string

	// FullName is used in registration welcome messages.
	FullName string

	// RemainingAttempts indicates how many PIN attempts remain before lockout.
	// Used by NotifyPINWrongAttempt.
	RemainingAttempts int

	// LockedUntil is a human-readable duration string (e.g. "15 minutes")
	// indicating how long until the lockout expires. Used by NotifyAccountLocked.
	LockedUntil string

	// Reason provides a human-readable explanation for failure notifications.
	Reason string

	// Language optionally pins the SMS language (ISO code: en/sw/fr). When
	// empty the notifier resolves it from the recipient's stored preference.
	Language string
}

AccountNotification carries the data needed to notify a user about account and PIN lifecycle events. Not all fields are used by every notification method; see individual method documentation for which fields are relevant.

type AccountNotifier

type AccountNotifier interface {
	// NotifyRegistrationSuccess informs a user that their account has been
	// created and their PIN is set.
	NotifyRegistrationSuccess(ctx context.Context, n AccountNotification) error

	// NotifyRegistrationFailed informs a user that their registration could
	// not be completed, along with a human-readable reason.
	NotifyRegistrationFailed(ctx context.Context, n AccountNotification) error

	// NotifyPINWrongAttempt warns a user that an incorrect PIN was entered
	// and includes the number of remaining attempts before lockout.
	NotifyPINWrongAttempt(ctx context.Context, n AccountNotification) error

	// NotifyAccountLocked informs a user that their account has been
	// temporarily locked due to repeated failed PIN attempts.
	NotifyAccountLocked(ctx context.Context, n AccountNotification) error

	// NotifyPINChanged confirms that the user's PIN was changed successfully.
	NotifyPINChanged(ctx context.Context, n AccountNotification) error

	// NotifyPINChangeFailed alerts a user that a PIN change attempt was
	// unsuccessful, along with the reason.
	NotifyPINChangeFailed(ctx context.Context, n AccountNotification) error

	// NotifyPINReset confirms that the user's PIN was reset successfully
	// via the recovery flow.
	NotifyPINReset(ctx context.Context, n AccountNotification) error

	// NotifyPINResetFailed alerts a user that a PIN reset attempt failed,
	// along with the reason.
	NotifyPINResetFailed(ctx context.Context, n AccountNotification) error
}

AccountNotifier defines the interface for sending account and PIN lifecycle notifications to users. Implementations may deliver via SMS, push, email, or any other transport. Methods must be safe for concurrent use.

type CreateLoanRequest

type CreateLoanRequest struct {
	UserID            string
	AccountID         string
	PrincipalAmount   int64  // In stroops
	PrincipalAsset    string // e.g. "USDC"
	InterestRateBps   int32  // Basis points
	DurationDays      int
	RepaymentSchedule string // "daily", "weekly", "bi_weekly", "monthly", "lump_sum"
}

CreateLoanRequest contains the data needed to create a new loan.

type EligibilityRequest

type EligibilityRequest struct {
	UserID       string
	Amount       int64 // In stroops (USDC * 10^7)
	DurationDays int
}

EligibilityRequest contains the data needed to assess loan eligibility.

type EligibilityResult

type EligibilityResult struct {
	Approved     bool
	Reason       string
	MaxAmount    int64   // Maximum eligible amount in stroops
	InterestRate float64 // Annual interest rate as a decimal (e.g. 0.12 = 12%)
}

EligibilityResult contains the outcome of an eligibility assessment.

type Lender

type Lender interface {
	AssessEligibility(ctx context.Context, req EligibilityRequest) (*EligibilityResult, error)
	CreateLoan(ctx context.Context, req CreateLoanRequest) (*LoanRecord, error)
	GetLoan(ctx context.Context, loanID string) (*LoanRecord, error)
	GetUserLoans(ctx context.Context, userID string) ([]*LoanRecord, error)
}

Lender defines the interface for loan origination and retrieval.

type LoanNotification

type LoanNotification struct {
	LoanID           string
	LoanReference    string
	UserID           string
	PhoneNumber      string
	Amount           int64   // Raw amount (stroops/cents) for consumers that need it
	DisplayAmount    float64 // Display-ready (e.g. 5000.00)
	DisplayCurrency  string  // e.g. "KES", "USD"
	Status           string
	Reason           string     // For rejection notifications
	RemainingBalance float64    // For repayment confirmations
	DueDate          *time.Time // For repayment reminders

	// InteractiveURL is the MoneyGram SEP-24 webview URL sent in the
	// cash-pickup initiated SMS. Empty for non-MG notifications.
	InteractiveURL string

	// CashPickupRef is the reference number the user quotes at the MG agent
	// when collecting cash. Populated once MG locks the payout.
	CashPickupRef string

	// CashPickupInfoURL is MoneyGram's support deep-link for the transaction,
	// sent alongside the reference so a borrower with a problem at the agent
	// has somewhere to go. Unlike InteractiveURL it stays valid after the
	// withdrawal settles.
	CashPickupInfoURL string

	// PaybillNumber is the M-Pesa collection shortcode the borrower pays under.
	// Only set on the paybill-instructions notification.
	PaybillNumber string

	// RepaymentExpiresAt is when an opened cash deposit lapses. Only set on
	// the repayment notifications; nil elsewhere.
	RepaymentExpiresAt *time.Time

	// Language optionally pins the SMS language (ISO code: en/sw/fr). When
	// empty the notifier resolves it from the recipient's stored preference.
	Language string
}

LoanNotification contains data for loan lifecycle notifications.

type LoanNotifier

type LoanNotifier interface {
	NotifyLoanApproved(ctx context.Context, n LoanNotification) error
	NotifyLoanRejected(ctx context.Context, n LoanNotification) error
	NotifyLoanDisbursed(ctx context.Context, n LoanNotification) error
	NotifyLoanFailed(ctx context.Context, n LoanNotification) error
	// NotifyLoanOffRampFailed reports that vault borrow succeeded but fiat
	// disbursement could not be completed and the USDC has been returned to
	// the vault. Distinct from NotifyLoanFailed, which is a credit-default
	// notice.
	NotifyLoanOffRampFailed(ctx context.Context, n LoanNotification) error
	// NotifyLoanCashPickupApproved acknowledges approval of a cash-pickup
	// loan without implying a push disbursement is on the way. A subsequent
	// NotifyLoanCashPickupInitiated carries the MoneyGram interactive URL.
	NotifyLoanCashPickupApproved(ctx context.Context, n LoanNotification) error
	// NotifyLoanRepaid confirms the treasury-to-vault leg confirmed and the
	// loan is closed. Distinct from NotifyRepaymentReceived, which fires
	// earlier — once cash reached the treasury but before the vault leg is
	// known to have settled.
	NotifyLoanRepaid(ctx context.Context, n LoanNotification) error
	NotifyRepaymentReceived(ctx context.Context, n LoanNotification) error
	NotifyRepaymentReminder(ctx context.Context, n LoanNotification) error

	// NotifyRepaymentFailed reports that a cash deposit could not be opened.
	//
	// The USSD screen has already told the borrower to expect an SMS by the
	// time this fires — initiation runs in the background because it is far
	// slower than a USSD session lives — so silence here would strand them
	// waiting for a message that is never coming.
	NotifyRepaymentFailed(ctx context.Context, n LoanNotification) error

	// NotifyRepaymentReference carries the reference MoneyGram issues once the
	// borrower commits in the webview. It is what they quote at the agent
	// counter to hand cash over, so without it the repayment cannot complete
	// however ready everything else is.
	NotifyRepaymentReference(ctx context.Context, n LoanNotification) error

	// NotifyRepaymentMoreInfo carries MoneyGram's transaction page when no
	// reference has been issued yet.
	//
	// SEP-24 defines external_transaction_id as the ID of the external
	// transaction that "started the deposit", so for a cash-in it only exists
	// once the borrower has paid — after the point the code would have been
	// useful. more_info_url is the field the spec designates for telling a user
	// how to start a deposit, and it is populated from the first poll. Read from
	// InteractiveURL.
	NotifyRepaymentMoreInfo(ctx context.Context, n LoanNotification) error

	// NotifyRepaymentPaybill sends the paybill instructions for a mobile-money
	// repayment: shortcode, account reference, and amount. Read from
	// PaybillNumber, LoanReference, and DisplayAmount/DisplayCurrency.
	NotifyRepaymentPaybill(ctx context.Context, n LoanNotification) error

	// NotifyRepaymentInitiated carries the MoneyGram interactive URL for a
	// borrower-initiated cash repayment. The USSD session ends before the
	// borrower can act on it, so this SMS is the only way the link reaches
	// them.
	//
	// Distinct from NotifyLoanCashPickupInitiated, which carries a link to
	// collect money. This one carries a link to hand money over — the opposite
	// message, and the borrower must not confuse the two.
	NotifyRepaymentInitiated(ctx context.Context, n LoanNotification) error

	// NotifyRepaymentWindowExpiring warns that an opened cash deposit is about
	// to lapse. Sent once, and distinct from NotifyRepaymentReminder, which is
	// driven by the loan's due date rather than by a deposit window.
	NotifyRepaymentWindowExpiring(ctx context.Context, n LoanNotification) error

	// NotifyRepaymentExpired tells the borrower an opened deposit lapsed
	// unused and that they can start again. The loan is untouched: nothing was
	// paid, so this is not a default notice.
	NotifyRepaymentExpired(ctx context.Context, n LoanNotification) error

	// NotifyLoanStatement sends the borrower a record of one loan —
	// reference, amount and due date. Unlike the other methods this is
	// user-initiated rather than lifecycle-driven: it answers a "My Loans"
	// request, so it is sent on demand and repeats are expected.
	NotifyLoanStatement(ctx context.Context, n LoanNotification) error

	// NotifyLoanCashPickupInitiated sends the MoneyGram interactive URL to
	// the user. The locked payout amount is unknown at this point and
	// confirmed when the user opens the link; the template should make that
	// disclosure explicit.
	NotifyLoanCashPickupInitiated(ctx context.Context, n LoanNotification) error

	// NotifyLoanCashPickupReady sends the cash-pickup reference number once
	// MoneyGram has locked the payout. Template should include the
	// reference, the locked amount, and the currency.
	NotifyLoanCashPickupReady(ctx context.Context, n LoanNotification) error

	// NotifyLoanCashPickupCancelled tells the borrower their cash pickup was
	// cancelled and the funds returned. Distinct from NotifyLoanOffRampFailed:
	// the usual cause is the borrower cancelling in MoneyGram's own app, so
	// this reads as an acknowledgement rather than a failure.
	NotifyLoanCashPickupCancelled(ctx context.Context, n LoanNotification) error
}

LoanNotifier defines the interface for sending loan lifecycle notifications.

type LoanRecord

type LoanRecord struct {
	ID                 string
	LoanReference      string
	UserID             string
	AccountID          string
	PrincipalAmount    int64
	PrincipalAsset     string
	InterestRateBps    int32
	TotalAmount        int64
	DurationDays       int
	RepaymentSchedule  string
	DueDate            *time.Time
	Status             string // "pending", "approved", "disbursed", "repaid", "defaulted", "cancelled"
	VaultTxHash        string
	RampProvider       string
	RampRequestID      string
	RampSequenceID     string
	RampFiatAmount     int64
	RampFiatCurrency   string
	SettlementMethod   string // "direct" or "fiat"
	DisbursementStatus string // "pending", "crypto_sent", "processing", "complete", "refund_pending", "failed"
	CreatedAt          time.Time
	UpdatedAt          time.Time
}

LoanRecord represents a loan with vault and ramp disbursement tracking.

Jump to

Keyboard shortcuts

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