offramp

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: 4 Imported by: 0

Documentation

Overview

Package offramp defines the off-ramp Provider contract that every provider in the platform satisfies.

Provider is the single mandatory interface (ID + Initiate). Optional capabilities — StatusReader, Quoter, Directory, MobileMoneyDirectory, BalanceReporter — are separate small interfaces; consumers type-assert for what they need and providers implement only what they genuinely support.

Request and Result carry only cross-provider fields. Provider-specific extras travel on typed payloads via the ProviderOptions / ProviderPayload marker interfaces; the concrete types live in each provider's own package.

Registry resolves a Request to a concrete Provider, first by inspecting req.Options.ProviderID(), otherwise by aliasing req.PayoutMethod to a registered ProviderID. NoOpProvider in noop.go is a stand-in for tests.

Index

Constants

View Source
const (
	PayoutMethodMobileMoney = "mobile_money"
	PayoutMethodCashPickup  = "cash_pickup"
)

PayoutMethod selects which provider handles a request when multiple are wired in via the registry. Empty string is treated as PayoutMethodMobileMoney.

Variables

This section is empty.

Functions

func Fraction

func Fraction(f float64) *float64

Fraction returns a pointer to f, for populating optional buffer settings from literals in configuration and tests.

func LocalCurrency added in v1.1.2

func LocalCurrency(countryCode string) string

LocalCurrency maps a country to the currency its rails settle in, or "" when the corridor is unsupported.

Types

type BalanceReporter

type BalanceReporter interface {
	AvailableBalance(ctx context.Context) (float64, error)
}

BalanceReporter reports a pre-funded balance held with the provider. Not implemented by providers that settle per-transaction (e.g. MoneyGram).

type Directory

type Directory interface {
	SupportedProviders(ctx context.Context, countryCode string) ([]ProviderInfo, error)
}

Directory advertises which provider configurations are available for a given country (e.g. YC's per-channel info, MG's cash-pickup option).

type ExchangeRate

type ExchangeRate struct {
	FromCurrency string
	ToCurrency   string
	SellRate     float64
	BuyRate      float64
	RateID       string
	Locale       string
	UpdatedAt    time.Time
}

ExchangeRate is the canonical quote shape returned by Quoter.

type MobileMoneyDirectory

type MobileMoneyDirectory interface {
	Networks(ctx context.Context, countryCode string) ([]MobileMoneyNetwork, error)
}

MobileMoneyDirectory lists the MoMo operators available in a country. Only implemented by providers that actually operate on mobile-money rails.

type MobileMoneyNetwork

type MobileMoneyNetwork struct {
	ID     string
	Name   string
	Code   string
	Status string
}

MobileMoneyNetwork represents a MoMo operator.

type NoOpProvider

type NoOpProvider struct{}

NoOpProvider is a minimal Provider stub for tests and bootstrap wiring. It returns a mock Result and nothing else — callers that need richer behaviour should depend on a real provider or a hand-rolled fake.

func (NoOpProvider) ID

func (NoOpProvider) ID() ProviderID

ID returns a sentinel provider ID.

func (NoOpProvider) Initiate

func (NoOpProvider) Initiate(_ context.Context, req Request) (*Result, error)

Initiate returns a mock off-ramp result.

type Provider

type Provider interface {
	ID() ProviderID
	Initiate(ctx context.Context, req Request) (*Result, error)
}

Provider is the single mandatory capability — every off-ramp must register and initiate. Optional behaviour is split into the other capability interfaces below; consumers type-assert for what they need.

type ProviderID

type ProviderID string

ProviderID identifies a concrete off-ramp implementation in the registry.

const (
	ProviderYellowCard ProviderID = "yellowcard"
	ProviderMoneyGram  ProviderID = "moneygram"
	ProviderFonbnk     ProviderID = "fonbnk"
)

Known provider IDs. New providers should add their constant here.

type ProviderInfo

type ProviderInfo struct {
	ID                      string
	Name                    string
	SupportedMethods        []string
	MinAmount               float64
	MaxAmount               float64
	Currency                string
	Status                  string
	FeeUSD                  float64
	FeeLocal                float64
	EstimatedSettlementTime int
}

ProviderInfo describes an available off-ramp provider for a country.

type ProviderOptions

type ProviderOptions interface {
	ProviderID() ProviderID
}

ProviderOptions is the marker interface implemented by per-provider request extras (e.g. yellowcard.Options.SettlementMethod, moneygram.Options.BirthDate). Callers attach a concrete implementation to Request.Options; the receiving adapter type-asserts to its own concrete type.

type ProviderPayload

type ProviderPayload interface {
	ProviderID() ProviderID
}

ProviderPayload is the marker interface implemented by per-provider result extras (e.g. yellowcard.DirectSettlementPayload, moneygram.CashPickupPayload). Adapters set Result.Provider to a concrete implementation; consumers type- assert to read provider-specific output.

type ProviderRef

type ProviderRef struct {
	ID       string
	Provider ProviderID
	Extra    map[string]any
}

ProviderRef identifies an in-flight transaction enough to look it up. Extra is provider-scoped; document the expected keys in each provider's adapter so callers know what to pass.

type QuoteRequest

type QuoteRequest struct {
	Currency string
}

QuoteRequest carries the inputs a provider needs to quote a corridor. Future fields (amount, originating country, service option) can be added without breaking the interface.

type Quoter

type Quoter interface {
	Quote(ctx context.Context, q QuoteRequest) (*ExchangeRate, error)
}

Quoter exposes the provider's view of the FX rate for a corridor. The quote response may be cached upstream; the orchestration of multi-source quoting lives outside this interface.

type RateBuffer

type RateBuffer struct {
	// contains filtered or unexported fields
}

RateBuffer is a fractional safety margin deducted from a quoted FX rate to hedge drift between the moment a borrower is quoted and the moment the off-ramp settles.

func NewRateBuffer

func NewRateBuffer(pct *float64, def float64) RateBuffer

NewRateBuffer resolves a configured fraction against a default.

pct is a pointer because zero is a meaningful setting — quote raw, no margin — and has to stay distinguishable from "not configured", which takes def instead.

func (RateBuffer) Apply

func (b RateBuffer) Apply(rate float64) float64

Apply returns rate reduced by the margin, clamped at zero.

func (RateBuffer) Pct

func (b RateBuffer) Pct() float64

Pct returns the resolved fraction, where 0.01 is 1 %. Zero means rates are quoted raw. Persist it alongside the rate so drift is auditable after the fact.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry maps ProviderIDs to Provider implementations and resolves an incoming Request to the right provider. The resolution order is:

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty Registry.

func (*Registry) Alias

func (r *Registry) Alias(payoutMethod string, id ProviderID) error

Alias maps a PayoutMethod string (e.g. PayoutMethodMobileMoney) to a registered ProviderID. The provider must already be registered.

func (*Registry) All

func (r *Registry) All() []Provider

All returns every registered Provider in unspecified order. Useful for fanning out menu rendering across providers (Directory capability).

func (*Registry) Get

func (r *Registry) Get(id ProviderID) (Provider, bool)

Get returns the Provider for an explicit ID. Useful for callers that already know which provider they want (e.g. a poller bound to MG loans).

func (*Registry) Register

func (r *Registry) Register(p Provider) error

Register adds a Provider to the registry, keyed by its ID(). Returns an error if the provider is nil, has an empty ID, or duplicates an existing registration — boot-time misconfigurations should fail loudly.

func (*Registry) Resolve

func (r *Registry) Resolve(req Request) (Provider, error)

Resolve picks the Provider for a Request. See Registry doc-comment for the resolution order. Returns a wrapped error when no provider matches so callers can distinguish misconfiguration from runtime failure.

type Request

type Request struct {
	LoanID           string
	UserID           string
	RecipientName    string
	AmountUSD        float64
	AmountStroops    int64
	DestinationPhone string
	CountryCode      string
	IdempotencyKey   string
	NetworkCode      string
	NetworkName      string

	// PayoutMethod is consulted by the registry to pick a provider when the
	// caller doesn't pin one via Options. Empty defaults to mobile money.
	PayoutMethod string

	// Options carries per-provider extras (yellowcard.Options,
	// moneygram.Options). nil is acceptable; each adapter documents its
	// expectations for missing/wrong types.
	Options ProviderOptions
}

Request contains the cross-provider data needed to initiate an off-ramp. Provider-specific extras live on Options.

type Result

type Result struct {
	RequestID        string
	SequenceID       string
	Status           string
	AmountUSD        float64
	AmountLocal      float64
	LocalCurrency    string
	ExchangeRate     float64
	Fee              float64
	FeeLocal         float64
	EstimatedTime    int
	CreatedAt        time.Time
	SettlementMethod string // tag for downstream persistence: "direct"|"fiat"|"cash_pickup"

	// Provider is the typed payload returned by the adapter. Consumers
	// type-assert to the concrete payload type owned by the provider package.
	Provider ProviderPayload
}

Result is what Provider.Initiate returns. Cross-provider summary fields stay on the struct; provider-specific output lives in Provider.

type Status

type Status struct {
	RequestID     string
	SequenceID    string
	Status        string
	AmountLocal   float64
	LocalCurrency string
	CompletedAt   *time.Time
	FailureReason *string
}

Status contains status information for an in-flight off-ramp.

type StatusReader

type StatusReader interface {
	Status(ctx context.Context, ref ProviderRef) (*Status, error)
}

StatusReader looks up an in-flight off-ramp by ProviderRef. ProviderRef carries the transaction ID plus provider-scoped extras (e.g. MoneyGram needs the child memo to authenticate the SEP-10 session).

type TreasuryTransfer

type TreasuryTransfer interface {
	SendUSDC(ctx context.Context, destination string, memo string, amount int64) (txHash string, err error)
	CheckUSDCTrustline(ctx context.Context, address string) (hasTrustline bool, err error)
}

TreasuryTransfer abstracts sending USDC from the custodial treasury to an external Stellar address. Implemented by Stellar service adapters; injected into off-ramp providers that need to settle on-chain.

Jump to

Keyboard shortcuts

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