quotation v2 service

This commit is contained in:
Stephan D
2026-02-18 22:06:51 +01:00
parent a33be56247
commit 1c5d3d202b
30 changed files with 799 additions and 231 deletions

View File

@@ -27,6 +27,18 @@ func ensureComputedQuote(src *ComputedQuote, item *QuoteComputationPlanItem) *Co
if src.TotalCost == nil {
src.TotalCost = deriveTotalCost(src.DebitAmount, src.FeeLines)
}
if src.ResolvedSettlementMode == paymentv1.SettlementMode_SETTLEMENT_UNSPECIFIED {
src.ResolvedSettlementMode = item.ResolvedSettlementMode
}
if src.ResolvedSettlementMode == paymentv1.SettlementMode_SETTLEMENT_UNSPECIFIED {
src.ResolvedSettlementMode = paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE
}
if src.ResolvedFeeTreatment == quotationv2.FeeTreatment_FEE_TREATMENT_UNSPECIFIED {
src.ResolvedFeeTreatment = item.ResolvedFeeTreatment
}
if src.ResolvedFeeTreatment == quotationv2.FeeTreatment_FEE_TREATMENT_UNSPECIFIED {
src.ResolvedFeeTreatment = resolvedFeeTreatmentForSettlementMode(src.ResolvedSettlementMode)
}
return src
}

View File

@@ -0,0 +1,40 @@
package quote_computation_service
import (
"strings"
"github.com/tech/sendico/payments/storage/model"
paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1"
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
)
func resolvedSettlementModeFromModel(mode model.SettlementMode) paymentv1.SettlementMode {
switch mode {
case model.SettlementModeFixSource:
return paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE
case model.SettlementModeFixReceived:
return paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED
default:
return paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE
}
}
func resolvedSettlementModeFromRouteModelValue(value string) paymentv1.SettlementMode {
switch strings.ToUpper(strings.TrimSpace(value)) {
case "FIX_RECEIVED", "SETTLEMENT_FIX_RECEIVED", "SETTLEMENT_MODE_FIX_RECEIVED":
return paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED
case "FIX_SOURCE", "SETTLEMENT_FIX_SOURCE", "SETTLEMENT_MODE_FIX_SOURCE":
return paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE
default:
return paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE
}
}
func resolvedFeeTreatmentForSettlementMode(mode paymentv1.SettlementMode) quotationv2.FeeTreatment {
switch mode {
case paymentv1.SettlementMode_SETTLEMENT_FIX_RECEIVED:
return quotationv2.FeeTreatment_FEE_TREATMENT_DEDUCT_FROM_DESTINATION
default:
return quotationv2.FeeTreatment_FEE_TREATMENT_ADD_TO_SOURCE
}
}

View File

@@ -103,6 +103,6 @@ func modelSettlementMode(mode transfer_intent_hydrator.QuoteSettlementMode) mode
case transfer_intent_hydrator.QuoteSettlementModeFixReceived:
return model.SettlementModeFixReceived
default:
return model.SettlementModeUnspecified
return model.SettlementModeFixSource
}
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/tech/sendico/pkg/model/account_role"
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
paymentv1 "github.com/tech/sendico/pkg/proto/common/payment/v1"
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
quotationv2 "github.com/tech/sendico/pkg/proto/payments/quotation/v2"
"go.mongodb.org/mongo-driver/v2/bson"
@@ -34,15 +35,17 @@ type BuildQuoteInput struct {
// ComputedQuote is a canonical quote payload for v2 processing.
type ComputedQuote struct {
QuoteRef string
DebitAmount *moneyv1.Money
CreditAmount *moneyv1.Money
TotalCost *moneyv1.Money
FeeLines []*feesv1.DerivedPostingLine
FeeRules []*feesv1.AppliedRule
FXQuote *oraclev1.Quote
Route *quotationv2.RouteSpecification
ExecutionConditions *quotationv2.ExecutionConditions
QuoteRef string
DebitAmount *moneyv1.Money
CreditAmount *moneyv1.Money
TotalCost *moneyv1.Money
FeeLines []*feesv1.DerivedPostingLine
FeeRules []*feesv1.AppliedRule
FXQuote *oraclev1.Quote
Route *quotationv2.RouteSpecification
ExecutionConditions *quotationv2.ExecutionConditions
ResolvedSettlementMode paymentv1.SettlementMode
ResolvedFeeTreatment quotationv2.FeeTreatment
}
// QuoteComputationPlan is an orchestration plan for quote computations.
@@ -58,16 +61,18 @@ type QuoteComputationPlan struct {
// QuoteComputationPlanItem is one quote-computation unit.
type QuoteComputationPlanItem struct {
Index int
IdempotencyKey string
IntentRef string
Intent model.PaymentIntent
QuoteInput BuildQuoteInput
Steps []*QuoteComputationStep
Funding *gateway_funding_profile.QuoteFundingGate
Route *quotationv2.RouteSpecification
ExecutionConditions *quotationv2.ExecutionConditions
BlockReason quotationv2.QuoteBlockReason
Index int
IdempotencyKey string
IntentRef string
Intent model.PaymentIntent
QuoteInput BuildQuoteInput
Steps []*QuoteComputationStep
Funding *gateway_funding_profile.QuoteFundingGate
Route *quotationv2.RouteSpecification
ExecutionConditions *quotationv2.ExecutionConditions
ResolvedSettlementMode paymentv1.SettlementMode
ResolvedFeeTreatment quotationv2.FeeTreatment
BlockReason quotationv2.QuoteBlockReason
}
// QuoteComputationStep is one planner step in a generic execution graph.

View File

@@ -152,6 +152,8 @@ func (s *QuoteComputationService) buildPlanItem(
Route: cloneRouteSpecification(route),
ExecutionConditions: cloneExecutionConditions(conditions),
}
resolvedSettlementMode := resolvedSettlementModeFromModel(modelIntent.SettlementMode)
resolvedFeeTreatment := resolvedFeeTreatmentForSettlementMode(resolvedSettlementMode)
intentRef := strings.TrimSpace(modelIntent.Ref)
if intentRef == "" {
@@ -159,16 +161,18 @@ func (s *QuoteComputationService) buildPlanItem(
}
return &QuoteComputationPlanItem{
Index: index,
IdempotencyKey: itemIdempotencyKey,
IntentRef: intentRef,
Intent: modelIntent,
QuoteInput: quoteInput,
Steps: steps,
Funding: funding,
Route: route,
ExecutionConditions: conditions,
BlockReason: blockReason,
Index: index,
IdempotencyKey: itemIdempotencyKey,
IntentRef: intentRef,
Intent: modelIntent,
QuoteInput: quoteInput,
Steps: steps,
Funding: funding,
Route: route,
ExecutionConditions: conditions,
ResolvedSettlementMode: resolvedSettlementMode,
ResolvedFeeTreatment: resolvedFeeTreatment,
BlockReason: blockReason,
}, nil
}

View File

@@ -13,7 +13,7 @@ func buildRouteSettlement(
network string,
hops []*quotationv2.RouteHop,
) *quotationv2.RouteSettlement {
modelValue := normalizeSettlementModel(settlementModelString(intent.SettlementMode))
modelValue := strings.ToLower(strings.TrimSpace(settlementModelString(intent.SettlementMode)))
asset := buildRouteSettlementAsset(intent, network, hops)
if asset == nil && modelValue == "" {
return nil

View File

@@ -77,11 +77,11 @@ func buildExecutionConditions(
func settlementModelString(mode model.SettlementMode) string {
switch mode {
case model.SettlementModeFixSource:
return "FIX_SOURCE"
return "fix_source"
case model.SettlementModeFixReceived:
return "FIX_RECEIVED"
return "fix_received"
default:
return "UNSPECIFIED"
return "fix_source"
}
}