200 lines
6.8 KiB
Go
200 lines
6.8 KiB
Go
package quotation
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/tech/sendico/payments/quotation/internal/service/quotation/gateway_funding_profile"
|
|
"github.com/tech/sendico/payments/quotation/internal/service/quotation/quotation_service_v2"
|
|
"github.com/tech/sendico/payments/quotation/internal/service/quotation/quote_computation_service"
|
|
"github.com/tech/sendico/payments/quotation/internal/service/quotation/transfer_intent_hydrator"
|
|
"github.com/tech/sendico/payments/storage/model"
|
|
quotestorage "github.com/tech/sendico/payments/storage/quote"
|
|
payecon "github.com/tech/sendico/pkg/payments/economics"
|
|
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"
|
|
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func newQuotationServiceV2(core *Service) *quotation_service_v2.QuotationServiceV2 {
|
|
if core == nil {
|
|
return nil
|
|
}
|
|
|
|
return quotation_service_v2.New(quotation_service_v2.Dependencies{
|
|
Logger: core.logger,
|
|
QuotesStore: quoteStore(core),
|
|
Hydrator: transfer_intent_hydrator.New(nil),
|
|
Computation: newQuoteComputationService(core),
|
|
})
|
|
}
|
|
|
|
func quoteStore(core *Service) quotestorage.QuotesStore {
|
|
if core == nil || core.storage == nil {
|
|
return nil
|
|
}
|
|
return core.storage.Quotes()
|
|
}
|
|
|
|
func newQuoteComputationService(core *Service) *quote_computation_service.QuoteComputationService {
|
|
opts := make([]quote_computation_service.Option, 0, 3)
|
|
if core != nil && core.storage != nil {
|
|
if routes := core.storage.Routes(); routes != nil {
|
|
opts = append(
|
|
opts,
|
|
quote_computation_service.WithRouteStore(routes),
|
|
quote_computation_service.WithLogger(core.logger),
|
|
)
|
|
}
|
|
}
|
|
if core != nil && core.deps.gatewayRegistry != nil {
|
|
opts = append(opts, quote_computation_service.WithGatewayRegistry(core.deps.gatewayRegistry))
|
|
}
|
|
if resolver := newManagedWalletNetworkResolver(core); resolver != nil {
|
|
opts = append(opts, quote_computation_service.WithManagedWalletNetworkResolver(resolver))
|
|
}
|
|
if resolver := newLedgerAccountCurrencyResolver(core); resolver != nil {
|
|
opts = append(opts, quote_computation_service.WithLedgerAccountCurrencyResolver(resolver))
|
|
}
|
|
if resolver := fundingProfileResolver(core); resolver != nil {
|
|
opts = append(opts, quote_computation_service.WithFundingProfileResolver(resolver))
|
|
}
|
|
return quote_computation_service.New(legacyQuoteComputationCore{core: core}, opts...)
|
|
}
|
|
|
|
func fundingProfileResolver(core *Service) gateway_funding_profile.FundingProfileResolver {
|
|
if core == nil {
|
|
return nil
|
|
}
|
|
|
|
cardRoutes := make(map[string]gateway_funding_profile.CardGatewayFundingRoute, len(core.deps.cardRoutes))
|
|
for key, route := range core.deps.cardRoutes {
|
|
routeKey := strings.TrimSpace(key)
|
|
if routeKey == "" {
|
|
continue
|
|
}
|
|
cardRoutes[routeKey] = gateway_funding_profile.CardGatewayFundingRoute{
|
|
FundingAddress: strings.TrimSpace(route.FundingAddress),
|
|
FeeAddress: strings.TrimSpace(route.FeeAddress),
|
|
FeeWalletRef: strings.TrimSpace(route.FeeWalletRef),
|
|
}
|
|
}
|
|
|
|
feeLedgerAccounts := make(map[string]string, len(core.deps.feeLedgerAccounts))
|
|
for key, accountRef := range core.deps.feeLedgerAccounts {
|
|
accountKey := strings.TrimSpace(key)
|
|
account := strings.TrimSpace(accountRef)
|
|
if accountKey == "" || account == "" {
|
|
continue
|
|
}
|
|
feeLedgerAccounts[accountKey] = account
|
|
}
|
|
|
|
if len(cardRoutes) == 0 && len(feeLedgerAccounts) == 0 {
|
|
return nil
|
|
}
|
|
return gateway_funding_profile.NewStaticFundingProfileResolver(gateway_funding_profile.StaticFundingProfileResolverInput{
|
|
DefaultCardGateway: defaultCardGateway,
|
|
DefaultMode: model.FundingModeNone,
|
|
CardRoutes: cardRoutes,
|
|
FeeLedgerAccounts: feeLedgerAccounts,
|
|
})
|
|
}
|
|
|
|
type legacyQuoteComputationCore struct {
|
|
core *Service
|
|
}
|
|
|
|
func (c legacyQuoteComputationCore) BuildQuote(ctx context.Context, in quote_computation_service.BuildQuoteInput) (*quote_computation_service.ComputedQuote, time.Time, error) {
|
|
if c.core == nil {
|
|
return nil, time.Time{}, errStorageUnavailable
|
|
}
|
|
|
|
request := "eRequest{
|
|
Meta: &sharedv1.RequestMeta{
|
|
OrganizationRef: strings.TrimSpace(in.OrganizationRef),
|
|
},
|
|
IdempotencyKey: strings.TrimSpace(in.IdempotencyKey),
|
|
Intent: protoIntentFromModel(in.Intent),
|
|
}
|
|
|
|
legacyQuote, expiresAt, err := c.core.buildPaymentQuote(ctx, strings.TrimSpace(in.OrganizationRef), request)
|
|
if err != nil {
|
|
return nil, time.Time{}, err
|
|
}
|
|
return mapLegacyQuote(in, legacyQuote), expiresAt, nil
|
|
}
|
|
|
|
func mapLegacyQuote(in quote_computation_service.BuildQuoteInput, src *sharedv1.PaymentQuote) *quote_computation_service.ComputedQuote {
|
|
if src == nil {
|
|
return "e_computation_service.ComputedQuote{}
|
|
}
|
|
resolvedSettlementMode := settlementModeToProto(in.Intent.SettlementMode)
|
|
if resolvedSettlementMode == paymentv1.SettlementMode_SETTLEMENT_UNSPECIFIED {
|
|
resolvedSettlementMode = paymentv1.SettlementMode_SETTLEMENT_FIX_SOURCE
|
|
}
|
|
resolvedFeeTreatment := feeTreatmentToProto(in.Intent.FeeTreatment)
|
|
if resolvedFeeTreatment == quotationv2.FeeTreatment_FEE_TREATMENT_UNSPECIFIED {
|
|
resolvedFeeTreatment = payecon.DefaultFeeTreatment()
|
|
}
|
|
return "e_computation_service.ComputedQuote{
|
|
DebitAmount: cloneProtoMoney(src.GetDebitSettlementAmount()),
|
|
CreditAmount: cloneProtoMoney(src.GetExpectedSettlementAmount()),
|
|
TotalCost: cloneProtoMoney(src.GetDebitAmount()),
|
|
FeeLines: cloneFeeLines(src.GetFeeLines()),
|
|
FeeRules: cloneFeeRules(src.GetFeeRules()),
|
|
FXQuote: cloneFXQuote(src.GetFxQuote()),
|
|
Route: cloneRouteSpecification(in.Route),
|
|
ExecutionConditions: cloneExecutionConditions(in.ExecutionConditions),
|
|
ResolvedSettlementMode: resolvedSettlementMode,
|
|
ResolvedFeeTreatment: resolvedFeeTreatment,
|
|
}
|
|
}
|
|
|
|
func feeTreatmentToProto(value model.FeeTreatment) quotationv2.FeeTreatment {
|
|
switch value {
|
|
case model.FeeTreatmentDeductFromDestination:
|
|
return quotationv2.FeeTreatment_FEE_TREATMENT_DEDUCT_FROM_DESTINATION
|
|
case model.FeeTreatmentAddToSource:
|
|
return quotationv2.FeeTreatment_FEE_TREATMENT_ADD_TO_SOURCE
|
|
default:
|
|
return quotationv2.FeeTreatment_FEE_TREATMENT_UNSPECIFIED
|
|
}
|
|
}
|
|
|
|
func cloneRouteSpecification(src *quotationv2.RouteSpecification) *quotationv2.RouteSpecification {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
cloned, ok := proto.Clone(src).(*quotationv2.RouteSpecification)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return cloned
|
|
}
|
|
|
|
func cloneExecutionConditions(src *quotationv2.ExecutionConditions) *quotationv2.ExecutionConditions {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
cloned, ok := proto.Clone(src).(*quotationv2.ExecutionConditions)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return cloned
|
|
}
|
|
|
|
func cloneFXQuote(src *oraclev1.Quote) *oraclev1.Quote {
|
|
if src == nil {
|
|
return nil
|
|
}
|
|
cloned, ok := proto.Clone(src).(*oraclev1.Quote)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return cloned
|
|
}
|