Merge pull request 'got rid of fees dependency in ledger' (#575) from ledger-565 into main
All checks were successful
ci/woodpecker/push/billing_documents Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/discovery Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/gateway_mntx Pipeline was successful
ci/woodpecker/push/gateway_chain Pipeline was successful
ci/woodpecker/push/gateway_tgsettle Pipeline was successful
ci/woodpecker/push/gateway_tron Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_methods Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/payments_quotation Pipeline was successful
All checks were successful
ci/woodpecker/push/billing_documents Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/discovery Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/gateway_mntx Pipeline was successful
ci/woodpecker/push/gateway_chain Pipeline was successful
ci/woodpecker/push/gateway_tgsettle Pipeline was successful
ci/woodpecker/push/gateway_tron Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_methods Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/payments_quotation Pipeline was successful
Reviewed-on: #575
This commit was merged in pull request #575.
This commit is contained in:
@@ -860,7 +860,23 @@ func normalizeLedgerTxRails(tx rail.LedgerTx) rail.LedgerTx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func normalizeRail(value string) string {
|
func normalizeRail(value string) string {
|
||||||
return discovery.NormalizeRail(value)
|
clean := discovery.NormalizeRail(value)
|
||||||
|
switch clean {
|
||||||
|
case "RAIL_CRYPTO":
|
||||||
|
return discovery.RailCrypto
|
||||||
|
case "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT":
|
||||||
|
return discovery.RailProviderSettlement
|
||||||
|
case "RAIL_LEDGER":
|
||||||
|
return discovery.RailLedger
|
||||||
|
case "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT":
|
||||||
|
return discovery.RailCardPayout
|
||||||
|
case "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP":
|
||||||
|
return discovery.RailFiatOnRamp
|
||||||
|
case "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP":
|
||||||
|
return discovery.RailFiatOffRamp
|
||||||
|
default:
|
||||||
|
return clean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneMoney(input *moneyv1.Money) *moneyv1.Money {
|
func cloneMoney(input *moneyv1.Money) *moneyv1.Money {
|
||||||
|
|||||||
@@ -40,7 +40,3 @@ messaging:
|
|||||||
batch_size: 100
|
batch_size: 100
|
||||||
poll_interval_seconds: 1
|
poll_interval_seconds: 1
|
||||||
max_attempts: 5
|
max_attempts: 5
|
||||||
|
|
||||||
fees:
|
|
||||||
address: "dev-billing-fees:50060"
|
|
||||||
timeout_seconds: 3
|
|
||||||
|
|||||||
@@ -40,7 +40,3 @@ messaging:
|
|||||||
batch_size: 100
|
batch_size: 100
|
||||||
poll_interval_seconds: 1
|
poll_interval_seconds: 1
|
||||||
max_attempts: 5
|
max_attempts: 5
|
||||||
|
|
||||||
fees:
|
|
||||||
address: "sendico_billing_fees:50060"
|
|
||||||
timeout_seconds: 3
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package serverimp
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tech/sendico/ledger/internal/service/ledger"
|
"github.com/tech/sendico/ledger/internal/service/ledger"
|
||||||
@@ -13,11 +12,8 @@ import (
|
|||||||
"github.com/tech/sendico/pkg/db"
|
"github.com/tech/sendico/pkg/db"
|
||||||
msg "github.com/tech/sendico/pkg/messaging"
|
msg "github.com/tech/sendico/pkg/messaging"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
|
||||||
"github.com/tech/sendico/pkg/server/grpcapp"
|
"github.com/tech/sendico/pkg/server/grpcapp"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,29 +22,13 @@ type Imp struct {
|
|||||||
file string
|
file string
|
||||||
debug bool
|
debug bool
|
||||||
|
|
||||||
config *config
|
config *config
|
||||||
app *grpcapp.App[storage.Repository]
|
app *grpcapp.App[storage.Repository]
|
||||||
service *ledger.Service
|
service *ledger.Service
|
||||||
feesConn *grpc.ClientConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
*grpcapp.Config `yaml:",inline"`
|
*grpcapp.Config `yaml:",inline"`
|
||||||
Fees FeesClientConfig `yaml:"fees"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FeesClientConfig struct {
|
|
||||||
Address string `yaml:"address"`
|
|
||||||
TimeoutSeconds int `yaml:"timeout_seconds"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultFeesTimeout = 3 * time.Second
|
|
||||||
|
|
||||||
func (c FeesClientConfig) timeout() time.Duration {
|
|
||||||
if c.TimeoutSeconds <= 0 {
|
|
||||||
return defaultFeesTimeout
|
|
||||||
}
|
|
||||||
return time.Duration(c.TimeoutSeconds) * time.Second
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Create(logger mlogger.Logger, file string, debug bool) (*Imp, error) {
|
func Create(logger mlogger.Logger, file string, debug bool) (*Imp, error) {
|
||||||
@@ -64,9 +44,6 @@ func (i *Imp) Shutdown() {
|
|||||||
if i.service != nil {
|
if i.service != nil {
|
||||||
i.service.Shutdown()
|
i.service.Shutdown()
|
||||||
}
|
}
|
||||||
if i.feesConn != nil {
|
|
||||||
_ = i.feesConn.Close()
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,10 +59,6 @@ func (i *Imp) Shutdown() {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
i.app.Shutdown(ctx)
|
i.app.Shutdown(ctx)
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
if i.feesConn != nil {
|
|
||||||
_ = i.feesConn.Close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Imp) Start() error {
|
func (i *Imp) Start() error {
|
||||||
@@ -99,22 +72,6 @@ func (i *Imp) Start() error {
|
|||||||
return mongostorage.New(logger, conn)
|
return mongostorage.New(logger, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
var feesClient feesv1.FeeEngineClient
|
|
||||||
feesTimeout := cfg.Fees.timeout()
|
|
||||||
if addr := strings.TrimSpace(cfg.Fees.Address); addr != "" {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), feesTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
conn, err := grpc.DialContext(ctx, addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
||||||
if err != nil {
|
|
||||||
i.logger.Warn("Failed to connect to fees service", zap.String("address", addr), zap.Error(err))
|
|
||||||
} else {
|
|
||||||
i.logger.Info("Connected to fees service", zap.String("address", addr))
|
|
||||||
i.feesConn = conn
|
|
||||||
feesClient = feesv1.NewFeeEngineClient(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceFactory := func(logger mlogger.Logger, repo storage.Repository, producer msg.Producer) (grpcapp.Service, error) {
|
serviceFactory := func(logger mlogger.Logger, repo storage.Repository, producer msg.Producer) (grpcapp.Service, error) {
|
||||||
invokeURI := ""
|
invokeURI := ""
|
||||||
if cfg.GRPC != nil {
|
if cfg.GRPC != nil {
|
||||||
@@ -124,7 +81,7 @@ func (i *Imp) Start() error {
|
|||||||
if cfg.Messaging != nil {
|
if cfg.Messaging != nil {
|
||||||
msgSettings = cfg.Messaging.Settings
|
msgSettings = cfg.Messaging.Settings
|
||||||
}
|
}
|
||||||
svc, err := ledger.NewService(logger, repo, producer, msgSettings, feesClient, feesTimeout, invokeURI)
|
svc, err := ledger.NewService(logger, repo, producer, msgSettings, invokeURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,13 +97,6 @@ func (s *Service) postCreditResponder(_ context.Context, req *ledgerv1.PostCredi
|
|||||||
entryTotal := creditAmount
|
entryTotal := creditAmount
|
||||||
|
|
||||||
charges := req.Charges
|
charges := req.Charges
|
||||||
if len(charges) == 0 {
|
|
||||||
if computed, err := s.quoteFeesForCredit(ctx, req); err != nil {
|
|
||||||
logger.Warn("Failed to quote fees", zap.Error(err))
|
|
||||||
} else if len(computed) > 0 {
|
|
||||||
charges = computed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := validatePostingLines(charges); err != nil {
|
if err := validatePostingLines(charges); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,13 +94,6 @@ func (s *Service) postDebitResponder(_ context.Context, req *ledgerv1.PostDebitR
|
|||||||
entryTotal := debitAmount.Neg()
|
entryTotal := debitAmount.Neg()
|
||||||
|
|
||||||
charges := req.Charges
|
charges := req.Charges
|
||||||
if len(charges) == 0 {
|
|
||||||
if computed, err := s.quoteFeesForDebit(ctx, req); err != nil {
|
|
||||||
logger.Warn("Failed to quote fees", zap.Error(err))
|
|
||||||
} else if len(computed) > 0 {
|
|
||||||
charges = computed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := validatePostingLines(charges); err != nil {
|
if err := validatePostingLines(charges); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,13 +111,6 @@ func (s *Service) postExternalCreditResponder(_ context.Context, req *ledgerv1.P
|
|||||||
entryTotal := creditAmount
|
entryTotal := creditAmount
|
||||||
|
|
||||||
charges := req.Charges
|
charges := req.Charges
|
||||||
if len(charges) == 0 {
|
|
||||||
if computed, err := s.quoteFeesForCredit(ctx, req); err != nil {
|
|
||||||
logger.Warn("Failed to quote fees", zap.Error(err))
|
|
||||||
} else if len(computed) > 0 {
|
|
||||||
charges = computed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := validatePostingLines(charges); err != nil {
|
if err := validatePostingLines(charges); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -344,13 +337,6 @@ func (s *Service) postExternalDebitResponder(_ context.Context, req *ledgerv1.Po
|
|||||||
entryTotal := debitAmount.Neg()
|
entryTotal := debitAmount.Neg()
|
||||||
|
|
||||||
charges := req.Charges
|
charges := req.Charges
|
||||||
if len(charges) == 0 {
|
|
||||||
if computed, err := s.quoteFeesForDebit(ctx, req); err != nil {
|
|
||||||
logger.Warn("Failed to quote fees", zap.Error(err))
|
|
||||||
} else if len(computed) > 0 {
|
|
||||||
charges = computed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := validatePostingLines(charges); err != nil {
|
if err := validatePostingLines(charges); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,26 +2,17 @@ package ledger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
|
||||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
|
|
||||||
accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1"
|
|
||||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
|
||||||
tracev1 "github.com/tech/sendico/pkg/proto/common/trace/v1"
|
|
||||||
|
|
||||||
"github.com/tech/sendico/ledger/internal/appversion"
|
"github.com/tech/sendico/ledger/internal/appversion"
|
||||||
"github.com/tech/sendico/ledger/storage"
|
"github.com/tech/sendico/ledger/storage"
|
||||||
"github.com/tech/sendico/pkg/api/routers"
|
"github.com/tech/sendico/pkg/api/routers"
|
||||||
"github.com/tech/sendico/pkg/discovery"
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
|
||||||
pmessaging "github.com/tech/sendico/pkg/messaging"
|
pmessaging "github.com/tech/sendico/pkg/messaging"
|
||||||
pmessagingreliable "github.com/tech/sendico/pkg/messaging/reliable"
|
pmessagingreliable "github.com/tech/sendico/pkg/messaging/reliable"
|
||||||
"github.com/tech/sendico/pkg/mlogger"
|
"github.com/tech/sendico/pkg/mlogger"
|
||||||
@@ -46,7 +37,6 @@ type Service struct {
|
|||||||
storage storage.Repository
|
storage storage.Repository
|
||||||
producer pmessaging.Producer
|
producer pmessaging.Producer
|
||||||
msgCfg pmodel.SettingsT
|
msgCfg pmodel.SettingsT
|
||||||
fees feesDependency
|
|
||||||
announcer *discovery.Announcer
|
announcer *discovery.Announcer
|
||||||
invokeURI string
|
invokeURI string
|
||||||
|
|
||||||
@@ -63,16 +53,7 @@ type Service struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type feesDependency struct {
|
func NewService(logger mlogger.Logger, repo storage.Repository, prod pmessaging.Producer, msgCfg pmodel.SettingsT, invokeURI string) (*Service, error) {
|
||||||
client feesv1.FeeEngineClient
|
|
||||||
timeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f feesDependency) available() bool {
|
|
||||||
return f.client != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewService(logger mlogger.Logger, repo storage.Repository, prod pmessaging.Producer, msgCfg pmodel.SettingsT, feesClient feesv1.FeeEngineClient, feesTimeout time.Duration, invokeURI string) (*Service, error) {
|
|
||||||
// Initialize Prometheus metrics
|
// Initialize Prometheus metrics
|
||||||
initMetrics()
|
initMetrics()
|
||||||
|
|
||||||
@@ -82,10 +63,6 @@ func NewService(logger mlogger.Logger, repo storage.Repository, prod pmessaging.
|
|||||||
producer: prod,
|
producer: prod,
|
||||||
msgCfg: msgCfg,
|
msgCfg: msgCfg,
|
||||||
invokeURI: strings.TrimSpace(invokeURI),
|
invokeURI: strings.TrimSpace(invokeURI),
|
||||||
fees: feesDependency{
|
|
||||||
client: feesClient,
|
|
||||||
timeout: feesTimeout,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := service.startOutboxReliableProducer(); err != nil {
|
if err := service.startOutboxReliableProducer(); err != nil {
|
||||||
@@ -493,190 +470,3 @@ func (s *Service) GetStatement(ctx context.Context, req *ledgerv1.GetStatementRe
|
|||||||
responder := s.getStatementResponder(ctx, req)
|
responder := s.getStatementResponder(ctx, req)
|
||||||
return responder(ctx)
|
return responder(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) quoteFeesForCredit(ctx context.Context, req *ledgerv1.PostCreditRequest) ([]*ledgerv1.PostingLine, error) {
|
|
||||||
if !s.fees.available() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
attrs := map[string]string{}
|
|
||||||
if strings.TrimSpace(req.GetDescription()) != "" {
|
|
||||||
attrs["description"] = req.GetDescription()
|
|
||||||
}
|
|
||||||
return s.quoteFees(ctx, feesv1.Trigger_TRIGGER_CAPTURE, req.GetOrganizationRef(), req.GetIdempotencyKey(), req.GetLedgerAccountRef(), "ledger.post_credit", req.GetIdempotencyKey(), req.GetEventTime(), req.Money, attrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) quoteFeesForDebit(ctx context.Context, req *ledgerv1.PostDebitRequest) ([]*ledgerv1.PostingLine, error) {
|
|
||||||
if !s.fees.available() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
attrs := map[string]string{}
|
|
||||||
if strings.TrimSpace(req.GetDescription()) != "" {
|
|
||||||
attrs["description"] = req.GetDescription()
|
|
||||||
}
|
|
||||||
return s.quoteFees(ctx, feesv1.Trigger_TRIGGER_REFUND, req.GetOrganizationRef(), req.GetIdempotencyKey(), req.GetLedgerAccountRef(), "ledger.post_debit", req.GetIdempotencyKey(), req.GetEventTime(), req.Money, attrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) quoteFees(ctx context.Context, trigger feesv1.Trigger, organizationRef, idempotencyKey, ledgerAccountRef, originType, originRef string, eventTime *timestamppb.Timestamp, baseAmount *moneyv1.Money, attributes map[string]string) ([]*ledgerv1.PostingLine, error) {
|
|
||||||
if !s.fees.available() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(organizationRef) == "" {
|
|
||||||
return nil, merrors.InvalidArgument("organization reference is required to quote fees")
|
|
||||||
}
|
|
||||||
if baseAmount == nil {
|
|
||||||
return nil, merrors.InvalidArgument("base amount is required to quote fees")
|
|
||||||
}
|
|
||||||
|
|
||||||
amountCopy := &moneyv1.Money{Amount: baseAmount.GetAmount(), Currency: baseAmount.GetCurrency()}
|
|
||||||
bookedAt := eventTime
|
|
||||||
if bookedAt == nil {
|
|
||||||
bookedAt = timestamppb.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := &tracev1.TraceContext{
|
|
||||||
RequestRef: idempotencyKey,
|
|
||||||
IdempotencyKey: idempotencyKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &feesv1.QuoteFeesRequest{
|
|
||||||
Meta: &feesv1.RequestMeta{
|
|
||||||
OrganizationRef: organizationRef,
|
|
||||||
Trace: trace,
|
|
||||||
},
|
|
||||||
Intent: &feesv1.Intent{
|
|
||||||
Trigger: trigger,
|
|
||||||
BaseAmount: amountCopy,
|
|
||||||
BookedAt: bookedAt,
|
|
||||||
OriginType: originType,
|
|
||||||
OriginRef: originRef,
|
|
||||||
Attributes: map[string]string{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
setFeeAttributeIfMissing(req.Intent.Attributes, "product", "ledger")
|
|
||||||
setFeeAttributeIfMissing(req.Intent.Attributes, "operation", ledgerOperation(originType, trigger))
|
|
||||||
setFeeAttributeIfMissing(req.Intent.Attributes, "currency", strings.TrimSpace(baseAmount.GetCurrency()))
|
|
||||||
|
|
||||||
if ledgerAccountRef != "" {
|
|
||||||
req.Intent.Attributes["ledger_account_ref"] = ledgerAccountRef
|
|
||||||
}
|
|
||||||
for k, v := range attributes {
|
|
||||||
if strings.TrimSpace(k) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
req.Intent.Attributes[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
callCtx := ctx
|
|
||||||
if s.fees.timeout > 0 {
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
callCtx, cancel = context.WithTimeout(ctx, s.fees.timeout)
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := s.fees.client.QuoteFees(callCtx, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lines, err := convertFeeDerivedLines(resp.GetLines())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return lines, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertFeeDerivedLines(lines []*feesv1.DerivedPostingLine) ([]*ledgerv1.PostingLine, error) {
|
|
||||||
result := make([]*ledgerv1.PostingLine, 0, len(lines))
|
|
||||||
for idx, line := range lines {
|
|
||||||
if line == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if line.GetMoney() == nil {
|
|
||||||
return nil, merrors.Internal(fmt.Sprintf("fee line %d missing money", idx))
|
|
||||||
}
|
|
||||||
dec, err := decimal.NewFromString(line.GetMoney().GetAmount())
|
|
||||||
if err != nil {
|
|
||||||
return nil, merrors.InternalWrap(err, fmt.Sprintf("fee line %d invalid amount", idx))
|
|
||||||
}
|
|
||||||
dec = ensureAmountForSide(dec, line.GetSide())
|
|
||||||
posting := &ledgerv1.PostingLine{
|
|
||||||
LedgerAccountRef: line.GetLedgerAccountRef(),
|
|
||||||
Money: &moneyv1.Money{
|
|
||||||
Amount: dec.String(),
|
|
||||||
Currency: line.GetMoney().GetCurrency(),
|
|
||||||
},
|
|
||||||
LineType: mapFeeLineType(line.GetLineType()),
|
|
||||||
}
|
|
||||||
result = append(result, posting)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureAmountForSide(amount decimal.Decimal, side accountingv1.EntrySide) decimal.Decimal {
|
|
||||||
switch side {
|
|
||||||
case accountingv1.EntrySide_ENTRY_SIDE_DEBIT:
|
|
||||||
if amount.Sign() > 0 {
|
|
||||||
return amount.Neg()
|
|
||||||
}
|
|
||||||
case accountingv1.EntrySide_ENTRY_SIDE_CREDIT:
|
|
||||||
if amount.Sign() < 0 {
|
|
||||||
return amount.Neg()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return amount
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFeeAttributeIfMissing(attrs map[string]string, key, value string) {
|
|
||||||
if attrs == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(key) == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value = strings.TrimSpace(value)
|
|
||||||
if value == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, exists := attrs[key]; exists {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
attrs[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func ledgerOperation(originType string, trigger feesv1.Trigger) string {
|
|
||||||
originType = strings.TrimSpace(originType)
|
|
||||||
if originType != "" {
|
|
||||||
parts := strings.SplitN(originType, ".", 2)
|
|
||||||
if len(parts) == 2 && strings.TrimSpace(parts[1]) != "" {
|
|
||||||
return strings.TrimSpace(parts[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch trigger {
|
|
||||||
case feesv1.Trigger_TRIGGER_CAPTURE:
|
|
||||||
return "credit"
|
|
||||||
case feesv1.Trigger_TRIGGER_REFUND:
|
|
||||||
return "debit"
|
|
||||||
case feesv1.Trigger_TRIGGER_PAYOUT:
|
|
||||||
return "payout"
|
|
||||||
case feesv1.Trigger_TRIGGER_DISPUTE:
|
|
||||||
return "dispute"
|
|
||||||
case feesv1.Trigger_TRIGGER_FX_CONVERSION:
|
|
||||||
return "fx_conversion"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapFeeLineType(lineType accountingv1.PostingLineType) ledgerv1.LineType {
|
|
||||||
switch lineType {
|
|
||||||
case accountingv1.PostingLineType_POSTING_LINE_FEE:
|
|
||||||
return ledgerv1.LineType_LINE_FEE
|
|
||||||
case accountingv1.PostingLineType_POSTING_LINE_SPREAD:
|
|
||||||
return ledgerv1.LineType_LINE_SPREAD
|
|
||||||
case accountingv1.PostingLineType_POSTING_LINE_REVERSAL:
|
|
||||||
return ledgerv1.LineType_LINE_REVERSAL
|
|
||||||
default:
|
|
||||||
return ledgerv1.LineType_LINE_FEE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func ParseAsset(assetString, network, tokenSymbol, contractAddress string) (*cha
|
|||||||
return &chainv1.Asset{
|
return &chainv1.Asset{
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
TokenSymbol: strings.ToUpper(token),
|
TokenSymbol: strings.ToUpper(token),
|
||||||
ContractAddress: strings.ToLower(contract),
|
ContractAddress: contract,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ func TestParseAsset(t *testing.T) {
|
|||||||
require.Equal(t, "USDT", asset.GetTokenSymbol())
|
require.Equal(t, "USDT", asset.GetTokenSymbol())
|
||||||
require.Equal(t, "", asset.GetContractAddress())
|
require.Equal(t, "", asset.GetContractAddress())
|
||||||
|
|
||||||
|
contract := "TR7NhQjeKQxGTCi8q8ZY4pL8otSzgjLj6T"
|
||||||
|
asset, err = ParseAsset("USDT-TRC20", "TRON_MAINNET", "", contract)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, contract, asset.GetContractAddress())
|
||||||
|
|
||||||
asset, err = ParseAsset("USDT-TRC20", "CHAIN_NETWORK_TRON_MAINNET", "", "")
|
asset, err = ParseAsset("USDT-TRC20", "CHAIN_NETWORK_TRON_MAINNET", "", "")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, chainv1.ChainNetwork_CHAIN_NETWORK_TRON_MAINNET, asset.GetChain())
|
require.Equal(t, chainv1.ChainNetwork_CHAIN_NETWORK_TRON_MAINNET, asset.GetChain())
|
||||||
|
|||||||
@@ -364,8 +364,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260223185530-2f722ef697dc h1:ULD+ToGXUIU6Pkzr1ARxdyvwfHbelw+agoFDRbLg4TU=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260223185530-2f722ef697dc/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
ledgerclient "github.com/tech/sendico/ledger/client"
|
|
||||||
trongatewayclient "github.com/tech/sendico/gateway/tron/client"
|
trongatewayclient "github.com/tech/sendico/gateway/tron/client"
|
||||||
|
ledgerclient "github.com/tech/sendico/ledger/client"
|
||||||
api "github.com/tech/sendico/pkg/api/http"
|
api "github.com/tech/sendico/pkg/api/http"
|
||||||
"github.com/tech/sendico/pkg/auth"
|
"github.com/tech/sendico/pkg/auth"
|
||||||
"github.com/tech/sendico/pkg/db/account"
|
"github.com/tech/sendico/pkg/db/account"
|
||||||
@@ -256,7 +256,7 @@ func buildGatewayAsset(cfg eapi.ChainGatewayAssetConfig) (*chainv1.Asset, error)
|
|||||||
return &chainv1.Asset{
|
return &chainv1.Asset{
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
TokenSymbol: strings.ToUpper(tokenSymbol),
|
TokenSymbol: strings.ToUpper(tokenSymbol),
|
||||||
ContractAddress: strings.ToLower(strings.TrimSpace(cfg.ContractAddress)),
|
ContractAddress: strings.TrimSpace(cfg.ContractAddress),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
api/server/internal/server/accountapiimp/service_test.go
Normal file
19
api/server/internal/server/accountapiimp/service_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package accountapiimp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
eapi "github.com/tech/sendico/server/interface/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildGatewayAsset_PreservesContractAddressCase(t *testing.T) {
|
||||||
|
asset, err := buildGatewayAsset(eapi.ChainGatewayAssetConfig{
|
||||||
|
Chain: "TRON_MAINNET",
|
||||||
|
TokenSymbol: "usdt",
|
||||||
|
ContractAddress: "TR7NhQjeKQxGTCi8q8ZY4pL8otSzgjLj6T",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "USDT", asset.GetTokenSymbol())
|
||||||
|
require.Equal(t, "TR7NhQjeKQxGTCi8q8ZY4pL8otSzgjLj6T", asset.GetContractAddress())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user