Documentation
¶
Overview ¶
Package webhook reacts to off-ramp settlement outcomes. It has two halves: a service that processes inbound payment-provider webhook events, and a poller that chases disbursements stuck awaiting a refund.
Both depend only on consumer-defined interfaces — DisbursementUpdater, PaymentLookup, TransactionRecorder, AlertService — which the loan/disbursement service implements. That keeps this package free of any dependency on the lending module while still driving loan state.
Event service ¶
Service (webhook_service.go) implements WebhookEventHandler. The HTTP layer verifies a webhook's signature and hands the event here; the service maps it to a disbursement status change and the side effects that follow — notifying the user, recording the final financials, and repaying the vault on completion. Failures are branched by settlement method: a FAILED event on a direct settlement becomes refund-pending, while one on a fiat settlement is terminal, a distinction the service resolves with IsDirectSettlement rather than relying on the provider to flag it.
Refund poller ¶
RefundPoller (refund_poller.go) runs in the background on an interval, fetching disbursements left in the refund-pending state — direct settlements whose USDC was returned to the treasury — and attempting a fiat failover so the borrower is still paid. It marks the loan's settlement method so the eventual completion event takes the vault-repay path, and alerts ops when a case needs a human.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AlertService ¶
AlertService is the interface for sending operational alerts.
type PaymentLookup ¶ added in v1.0.0
type PaymentLookup interface {
LookupPayment(ctx context.Context, paymentID string) (*yellowcard.PaymentDetails, error)
}
PaymentLookup fetches final payment details (amounts, fees) at completion.
type RefundPendingFetcher ¶
type RefundPendingFetcher interface {
// GetRefundPendingDisbursements returns (sequenceID, paymentID) pairs for all
// disbursements in DisbursementRefundPending status.
GetRefundPendingDisbursements(ctx context.Context) ([]RefundPendingRecord, error)
}
RefundPendingFetcher retrieves loans that are awaiting crypto refund from YellowCard.
type RefundPendingRecord ¶
type RefundPendingRecord struct {
SequenceID string // YC sequenceId / idempotency key
PaymentID string // YC payment ID
LoanID string // Loan ID for tracking
LoanReference string // Human-facing loan reference, for alerts
UserID string // User ID for tracking
RecipientName string // Recipient name for fiat disbursement
AmountUSD float64 // Amount in USD
AmountStroops int64 // Amount in stroops (USDC * 10^7)
RampFiatAmount int64 // Original off-ramp fiat amount in cents (local currency)
RampFiatCurrency string // Original off-ramp fiat currency (e.g. "KES")
DestinationPhone string // Recipient phone number
CountryCode string // ISO country code
NetworkCode string // MoMo network code
NetworkName string // MoMo network name
}
RefundPendingRecord identifies a disbursement awaiting refund, including the data needed to attempt a fiat failover.
type RefundPoller ¶
type RefundPoller struct {
// contains filtered or unexported fields
}
RefundPoller periodically checks YellowCard for refund status on disbursements that failed after USDC was sent (direct settlement F3 failover).
func NewRefundPoller ¶
func NewRefundPoller( ycAdapter *yellowcard.YellowcardAdapter, offRamp offramp.Provider, fetcher RefundPendingFetcher, disbursement contracts.DisbursementUpdater, alerts AlertService, transactions TransactionRecorder, config RefundPollerConfig, ) *RefundPoller
NewRefundPoller creates a new RefundPoller.
func (*RefundPoller) Start ¶
func (p *RefundPoller) Start(ctx context.Context)
Start runs the RefundPoller in a background goroutine. It polls until the context is cancelled (graceful shutdown).
type RefundPollerConfig ¶
RefundPollerConfig configures the refund polling behavior.
func DefaultRefundPollerConfig ¶
func DefaultRefundPollerConfig() RefundPollerConfig
DefaultRefundPollerConfig returns sensible defaults.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service processes incoming payment provider webhook events.
func NewService ¶
func NewService(disbursements contracts.DisbursementUpdater, alerts AlertService, transactions TransactionRecorder, payments PaymentLookup) *Service
NewService creates a new webhook processing service.
func (*Service) ProcessYellowCardEvent ¶
func (s *Service) ProcessYellowCardEvent(ctx context.Context, event yellowcard.WebhookEvent) error
ProcessYellowCardEvent handles a single YellowCard webhook event by mapping it to the appropriate disbursement status update and side effects.
Event to Action mapping:
DISBURSEMENT.COMPLETE / PAYMENT.COMPLETE / SEND.COMPLETE to DisbursementComplete + notify user DISBURSEMENT.FAILED / PAYMENT.FAILED / SEND.FAILED to if direct: DisbursementRefundPending; if fiat: DisbursementFailed + alert ops PENDING_LIQUIDITY to alert ops (YC balance low, auto-retries for 2hrs) REFUNDED to DisbursementRefundReceived (RefundPoller handles fiat failover) REFUND_FAILED to DisbursementFailed + alert ops EXPIRED / CANCELLED to treat as FAILED PROCESSING / PENDING / PROCESS to DisbursementProcessing
type TransactionRecorder ¶
type TransactionRecorder interface {
// UpdateOffRampTransaction syncs a loan's off-ramp row with the provider's
// reported status. Keyed by loan rather than by external ID, which no longer
// identifies a single row now that every leg of an anchor transaction shares
// the provider's request ID.
UpdateOffRampTransaction(ctx context.Context, loanID string, status string, externalStatus string) error
// RecordFiatFailover records a fiat failover transaction after a direct settlement refund.
RecordFiatFailover(ctx context.Context, rec RefundPendingRecord, newRequestID string) error
}
TransactionRecorder records and updates transaction records for disbursement events.
type WebhookEventHandler ¶
type WebhookEventHandler interface {
// ProcessYellowCardEvent handles a single webhook event, mapping it to the
// appropriate disbursement status update and triggering any side effects.
ProcessYellowCardEvent(ctx context.Context, event yellowcard.WebhookEvent) error
}
WebhookEventHandler processes incoming YellowCard webhook events.