neq quotation definition + priced_at field
This commit is contained in:
@@ -15,7 +15,7 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
|
||||
@@ -260,8 +260,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -10,7 +10,7 @@ require (
|
||||
github.com/tech/sendico/fx/oracle v0.0.0
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
|
||||
@@ -210,8 +210,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -44,6 +44,6 @@ require (
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/grpc v1.79.0 // indirect
|
||||
google.golang.org/grpc v1.79.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
@@ -210,8 +210,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -48,6 +48,6 @@ require (
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/grpc v1.79.0 // indirect
|
||||
google.golang.org/grpc v1.79.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
@@ -210,8 +210,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -68,6 +68,7 @@ type Quote struct {
|
||||
BaseAmount *moneyv1.Money
|
||||
QuoteAmount *moneyv1.Money
|
||||
ExpiresAt time.Time
|
||||
PricedAt time.Time
|
||||
Provider string
|
||||
RateRef string
|
||||
Firm bool
|
||||
@@ -237,6 +238,10 @@ func fromProtoQuote(quote *oraclev1.Quote) *Quote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
pricedAt := time.Time{}
|
||||
if ts := quote.GetPricedAt(); ts != nil {
|
||||
pricedAt = ts.AsTime()
|
||||
}
|
||||
return &Quote{
|
||||
QuoteRef: quote.GetQuoteRef(),
|
||||
Pair: quote.Pair,
|
||||
@@ -245,6 +250,7 @@ func fromProtoQuote(quote *oraclev1.Quote) *Quote {
|
||||
BaseAmount: quote.BaseAmount,
|
||||
QuoteAmount: quote.QuoteAmount,
|
||||
ExpiresAt: time.UnixMilli(quote.GetExpiresAtUnixMs()),
|
||||
PricedAt: pricedAt,
|
||||
Provider: quote.GetProvider(),
|
||||
RateRef: quote.GetRateRef(),
|
||||
Firm: quote.GetFirm(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type stubOracle struct {
|
||||
@@ -75,6 +76,7 @@ func TestLatestRate(t *testing.T) {
|
||||
|
||||
func TestGetQuote(t *testing.T) {
|
||||
expiresAt := time.Date(2024, 2, 2, 12, 0, 0, 0, time.UTC)
|
||||
pricedAt := time.Date(2024, 2, 2, 11, 59, 0, 0, time.UTC)
|
||||
stub := &stubOracle{
|
||||
quoteResp: &oraclev1.GetQuoteResponse{
|
||||
Quote: &oraclev1.Quote{
|
||||
@@ -85,6 +87,7 @@ func TestGetQuote(t *testing.T) {
|
||||
BaseAmount: &moneyv1.Money{Amount: "100.00", Currency: "GBP"},
|
||||
QuoteAmount: &moneyv1.Money{Amount: "125.00", Currency: "USD"},
|
||||
ExpiresAtUnixMs: expiresAt.UnixMilli(),
|
||||
PricedAt: timestamppb.New(pricedAt),
|
||||
Provider: "Test",
|
||||
RateRef: "test-ref",
|
||||
Firm: true,
|
||||
@@ -113,4 +116,7 @@ func TestGetQuote(t *testing.T) {
|
||||
if resp.QuoteRef != "quote-123" || resp.Price != "1.2500" || !resp.ExpiresAt.Equal(expiresAt) {
|
||||
t.Fatalf("unexpected quote response: %+v", resp)
|
||||
}
|
||||
if !resp.PricedAt.Equal(pricedAt) {
|
||||
t.Fatalf("expected priced_at %s, got %s", pricedAt, resp.PricedAt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -210,8 +210,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -130,6 +130,10 @@ func (qc *quoteComputation) buildModelQuote(firm bool, expiryMillis int64, req *
|
||||
if qc.baseRounded == nil || qc.quoteRounded == nil || qc.priceRounded == nil {
|
||||
return nil, merrors.Internal("oracle: computation not executed")
|
||||
}
|
||||
pricedAtUnixMs := qc.rate.AsOfUnixMs
|
||||
if pricedAtUnixMs <= 0 {
|
||||
pricedAtUnixMs = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
quote := &model.Quote{
|
||||
QuoteRef: uuid.NewString(),
|
||||
@@ -147,6 +151,7 @@ func (qc *quoteComputation) buildModelQuote(firm bool, expiryMillis int64, req *
|
||||
Amount: formatRat(qc.quoteRounded, qc.quoteScale),
|
||||
},
|
||||
AmountType: qc.amountType,
|
||||
PricedAtUnixMs: pricedAtUnixMs,
|
||||
RateRef: qc.rate.RateRef,
|
||||
Provider: qc.provider,
|
||||
PreferredProvider: req.GetPreferredProvider(),
|
||||
|
||||
@@ -111,6 +111,7 @@ func (currencyStoreStub) List(ctx context.Context, codes ...string) ([]*model.Cu
|
||||
func (currencyStoreStub) Upsert(ctx context.Context, currency *model.Currency) error { return nil }
|
||||
|
||||
func TestServiceGetQuoteFirm(t *testing.T) {
|
||||
pricedAt := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
repo := &repositoryStub{}
|
||||
repo.pairs = &pairStoreStub{
|
||||
getFn: func(ctx context.Context, pair model.CurrencyPair) (*model.Pair, error) {
|
||||
@@ -129,7 +130,7 @@ func TestServiceGetQuoteFirm(t *testing.T) {
|
||||
Ask: "1.10",
|
||||
Bid: "1.08",
|
||||
RateRef: "rate#1",
|
||||
AsOfUnixMs: time.Now().UnixMilli(),
|
||||
AsOfUnixMs: pricedAt.UnixMilli(),
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
@@ -169,9 +170,15 @@ func TestServiceGetQuoteFirm(t *testing.T) {
|
||||
if resp.GetQuote().GetQuoteAmount().GetAmount() != "110.00" {
|
||||
t.Fatalf("unexpected quote amount: %s", resp.GetQuote().GetQuoteAmount().GetAmount())
|
||||
}
|
||||
if got := resp.GetQuote().GetPricedAt(); got == nil || !got.AsTime().Equal(pricedAt) {
|
||||
t.Fatalf("expected priced_at %s, got %v", pricedAt, got)
|
||||
}
|
||||
if savedQuote.QuoteRef == "" {
|
||||
t.Fatalf("expected quote persisted")
|
||||
}
|
||||
if savedQuote.PricedAtUnixMs != pricedAt.UnixMilli() {
|
||||
t.Fatalf("expected stored pricedAtUnixMs %d, got %d", pricedAt.UnixMilli(), savedQuote.PricedAtUnixMs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceGetQuoteRateNotFound(t *testing.T) {
|
||||
|
||||
@@ -2,12 +2,14 @@ package oracle
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/fx/storage/model"
|
||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||
fxv1 "github.com/tech/sendico/pkg/proto/common/fx/v1"
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func buildResponseMeta(meta *oraclev1.RequestMeta) *oraclev1.ResponseMeta {
|
||||
@@ -36,6 +38,7 @@ func quoteModelToProto(q *model.Quote) *oraclev1.Quote {
|
||||
BaseAmount: moneyModelToProto(&q.BaseAmount),
|
||||
QuoteAmount: moneyModelToProto(&q.QuoteAmount),
|
||||
ExpiresAtUnixMs: q.ExpiresAtUnixMs,
|
||||
PricedAt: timestampFromUnixMillis(q.PricedAtUnixMs, q.CreatedAt),
|
||||
Provider: q.Provider,
|
||||
RateRef: q.RateRef,
|
||||
Firm: q.Firm,
|
||||
@@ -117,3 +120,13 @@ func decimalStringToProto(value string) *moneyv1.Decimal {
|
||||
}
|
||||
return &moneyv1.Decimal{Value: value}
|
||||
}
|
||||
|
||||
func timestampFromUnixMillis(ms int64, fallback time.Time) *timestamppb.Timestamp {
|
||||
if ms > 0 {
|
||||
return timestamppb.New(time.UnixMilli(ms).UTC())
|
||||
}
|
||||
if !fallback.IsZero() {
|
||||
return timestamppb.New(fallback.UTC())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ type Quote struct {
|
||||
QuoteAmount paymenttypes.Money `bson:"quoteAmount" json:"quoteAmount"`
|
||||
AmountType QuoteAmountType `bson:"amountType" json:"amountType"`
|
||||
ExpiresAtUnixMs int64 `bson:"expiresAtUnixMs" json:"expiresAtUnixMs"`
|
||||
PricedAtUnixMs int64 `bson:"pricedAtUnixMs,omitempty" json:"pricedAtUnixMs,omitempty"`
|
||||
ExpiresAt *time.Time `bson:"expiresAt,omitempty" json:"expiresAt,omitempty"`
|
||||
RateRef string `bson:"rateRef" json:"rateRef"`
|
||||
Provider string `bson:"provider" json:"provider"`
|
||||
|
||||
@@ -95,6 +95,9 @@ func (q *quotesStore) Issue(ctx context.Context, quote *model.Quote) error {
|
||||
expiry := time.UnixMilli(quote.ExpiresAtUnixMs)
|
||||
quote.ExpiresAt = &expiry
|
||||
}
|
||||
if quote.PricedAtUnixMs <= 0 {
|
||||
quote.PricedAtUnixMs = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
quote.Status = model.QuoteStatusIssued
|
||||
quote.ConsumedByLedgerTxnRef = ""
|
||||
|
||||
@@ -32,6 +32,9 @@ func TestQuotesStoreIssue(t *testing.T) {
|
||||
if inserted == nil || inserted.Status != model.QuoteStatusIssued {
|
||||
t.Fatalf("expected issued quote to be inserted")
|
||||
}
|
||||
if inserted.PricedAtUnixMs <= 0 {
|
||||
t.Fatalf("expected pricedAtUnixMs to be populated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuotesStoreIssueSetsExpiryDate(t *testing.T) {
|
||||
|
||||
@@ -15,14 +15,14 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354 // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||
|
||||
@@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354 h1:BgaMXBpcqcW74afzqI3iKo07K3tC+VuyWU3/FIvLlNI=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3 h1:QD30TjDPWtvXb5PBZGZ6Wdvaq7HQixIBtZ/yuseNXc8=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
||||
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -362,8 +362,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -11,7 +11,7 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -212,8 +212,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -210,8 +210,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -17,14 +17,14 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354 // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||
|
||||
@@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354 h1:BgaMXBpcqcW74afzqI3iKo07K3tC+VuyWU3/FIvLlNI=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260212005555-3a7e5700f354/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3 h1:QD30TjDPWtvXb5PBZGZ6Wdvaq7HQixIBtZ/yuseNXc8=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260213131322-086e44a26cf3/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
||||
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -383,8 +383,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -11,7 +11,7 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -212,8 +212,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -51,6 +51,6 @@ require (
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/grpc v1.79.0 // indirect
|
||||
google.golang.org/grpc v1.79.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
@@ -227,8 +227,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -11,7 +11,8 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -48,5 +49,4 @@ require (
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
@@ -210,8 +210,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -24,7 +24,7 @@ func (s *Service) CreatePaymentMethod(ctx context.Context, req *methodsv1.Create
|
||||
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
pm, err := decodePaymentMethod(req.GetPaymentMethodJson())
|
||||
pm, err := decodePaymentMethodPayload(req.GetPaymentMethod(), "payment_method")
|
||||
if err != nil {
|
||||
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
@@ -32,10 +32,10 @@ func (s *Service) CreatePaymentMethod(ctx context.Context, req *methodsv1.Create
|
||||
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
payload, err := encodePaymentMethod(pm)
|
||||
record, err := encodePaymentMethodRecord(pm)
|
||||
if err != nil {
|
||||
return autoError[methodsv1.CreatePaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
return &methodsv1.CreatePaymentMethodResponse{PaymentMethodJson: payload}, nil
|
||||
return &methodsv1.CreatePaymentMethodResponse{PaymentMethodRecord: record}, nil
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@ func (s *Service) GetPaymentMethod(ctx context.Context, req *methodsv1.GetPaymen
|
||||
return autoError[methodsv1.GetPaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
payload, err := encodePaymentMethod(pm)
|
||||
record, err := encodePaymentMethodRecord(pm)
|
||||
if err != nil {
|
||||
return autoError[methodsv1.GetPaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
return &methodsv1.GetPaymentMethodResponse{PaymentMethodJson: payload}, nil
|
||||
return &methodsv1.GetPaymentMethodResponse{PaymentMethodRecord: record}, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
||||
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
|
||||
)
|
||||
|
||||
@@ -33,16 +34,16 @@ func (s *Service) ListPaymentMethods(ctx context.Context, req *methodsv1.ListPay
|
||||
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
result := make([][]byte, 0, len(items))
|
||||
result := make([]*endpointv1.PaymentMethodRecord, 0, len(items))
|
||||
for i := range items {
|
||||
payload, err := encodePaymentMethod(&items[i])
|
||||
record, err := encodePaymentMethodRecord(&items[i])
|
||||
if err != nil {
|
||||
return autoError[methodsv1.ListPaymentMethodsResponse](ctx, s.logger, err)
|
||||
}
|
||||
result = append(result, payload)
|
||||
result = append(result, record)
|
||||
}
|
||||
|
||||
return &methodsv1.ListPaymentMethodsResponse{
|
||||
PaymentMethodsJson: result,
|
||||
PaymentMethods: result,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ func (s *Service) UpdatePaymentMethod(ctx context.Context, req *methodsv1.Update
|
||||
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
pm, err := decodePaymentMethod(req.GetPaymentMethodJson())
|
||||
pm, err := decodePaymentMethodRecord(req.GetPaymentMethodRecord())
|
||||
if err != nil {
|
||||
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
@@ -28,10 +28,10 @@ func (s *Service) UpdatePaymentMethod(ctx context.Context, req *methodsv1.Update
|
||||
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
payload, err := encodePaymentMethod(pm)
|
||||
record, err := encodePaymentMethodRecord(pm)
|
||||
if err != nil {
|
||||
return autoError[methodsv1.UpdatePaymentMethodResponse](ctx, s.logger, err)
|
||||
}
|
||||
|
||||
return &methodsv1.UpdatePaymentMethodResponse{PaymentMethodJson: payload}, nil
|
||||
return &methodsv1.UpdatePaymentMethodResponse{PaymentMethodRecord: record}, nil
|
||||
}
|
||||
|
||||
@@ -2,17 +2,24 @@ package methods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/routers/gsresponse"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
archivablev1 "github.com/tech/sendico/pkg/proto/common/archivable/v1"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
oboundv1 "github.com/tech/sendico/pkg/proto/common/organization_bound/v1"
|
||||
paginationv2 "github.com/tech/sendico/pkg/proto/common/pagination/v2"
|
||||
pboundv1 "github.com/tech/sendico/pkg/proto/common/permission_bound/v1"
|
||||
storablev1 "github.com/tech/sendico/pkg/proto/common/storable/v1"
|
||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func autoError[T any](ctx context.Context, logger mlogger.Logger, err error) (*T, error) {
|
||||
@@ -31,26 +38,228 @@ func parseObjectID(value, field string) (bson.ObjectID, error) {
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func decodePaymentMethod(data []byte) (*model.PaymentMethod, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, merrors.InvalidArgument("payment_method_json is required", "payment_method_json")
|
||||
func decodePaymentMethodRecord(record *endpointv1.PaymentMethodRecord) (*model.PaymentMethod, error) {
|
||||
if record == nil {
|
||||
return nil, merrors.InvalidArgument("payment_method_record is required", "payment_method_record")
|
||||
}
|
||||
res := &model.PaymentMethod{}
|
||||
if err := json.Unmarshal(data, res); err != nil {
|
||||
return nil, merrors.InvalidArgumentWrap(err, "failed to decode payment method", "payment_method_json")
|
||||
res, err := decodePaymentMethodPayload(record.GetPaymentMethod(), "payment_method_record.payment_method")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := applyPermissionBoundRecord(res, record.GetPermissionBound()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func encodePaymentMethod(pm *model.PaymentMethod) ([]byte, error) {
|
||||
func decodePaymentMethodPayload(method *endpointv1.PaymentMethod, field string) (*model.PaymentMethod, error) {
|
||||
if method == nil {
|
||||
return nil, merrors.InvalidArgument(field+" is required", field)
|
||||
}
|
||||
|
||||
recipientRef, err := parseObjectID(method.GetRecipientRef(), field+".recipient_ref")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pt, err := paymentTypeFromProto(method.GetType(), field+".type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PaymentMethod{
|
||||
Describable: describableFromProto(method.GetDescribable()),
|
||||
RecipientRef: recipientRef,
|
||||
Type: pt,
|
||||
Data: cloneBytes(method.GetData()),
|
||||
IsMain: method.GetIsMain(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodePaymentMethodRecord(pm *model.PaymentMethod) (*endpointv1.PaymentMethodRecord, error) {
|
||||
if pm == nil {
|
||||
return nil, merrors.InvalidArgument("payment method is required")
|
||||
}
|
||||
payload, err := json.Marshal(pm)
|
||||
pt, err := paymentTypeToProto(pm.Type)
|
||||
if err != nil {
|
||||
return nil, merrors.InternalWrap(err, "failed to encode payment method")
|
||||
return nil, err
|
||||
}
|
||||
return payload, nil
|
||||
|
||||
return &endpointv1.PaymentMethodRecord{
|
||||
PermissionBound: permissionBoundFromModel(pm),
|
||||
PaymentMethod: &endpointv1.PaymentMethod{
|
||||
Describable: describableToProto(pm.Describable),
|
||||
RecipientRef: toObjectHex(pm.RecipientRef),
|
||||
Type: pt,
|
||||
Data: cloneBytes(pm.Data),
|
||||
IsMain: pm.IsMain,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func paymentTypeFromProto(value endpointv1.PaymentMethodType, field string) (model.PaymentType, error) {
|
||||
switch value {
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN:
|
||||
return model.PaymentTypeIban, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD:
|
||||
return model.PaymentTypeCard, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN:
|
||||
return model.PaymentTypeCardToken, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT:
|
||||
return model.PaymentTypeBankAccount, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET:
|
||||
return model.PaymentTypeWallet, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS:
|
||||
return model.PaymentTypeCryptoAddress, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER:
|
||||
return model.PaymentTypeLedger, nil
|
||||
default:
|
||||
return model.PaymentTypeIban, merrors.InvalidArgument(fmt.Sprintf("%s has unsupported value: %s", field, value.String()), field)
|
||||
}
|
||||
}
|
||||
|
||||
func paymentTypeToProto(value model.PaymentType) (endpointv1.PaymentMethodType, error) {
|
||||
switch value {
|
||||
case model.PaymentTypeIban:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN, nil
|
||||
case model.PaymentTypeCard:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD, nil
|
||||
case model.PaymentTypeCardToken:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN, nil
|
||||
case model.PaymentTypeBankAccount:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT, nil
|
||||
case model.PaymentTypeWallet:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET, nil
|
||||
case model.PaymentTypeCryptoAddress:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS, nil
|
||||
case model.PaymentTypeLedger:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER, nil
|
||||
default:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_UNSPECIFIED, merrors.InvalidArgument(fmt.Sprintf("unsupported payment method type: %s", value.String()), "type")
|
||||
}
|
||||
}
|
||||
|
||||
func describableFromProto(src *describablev1.Describable) model.Describable {
|
||||
if src == nil {
|
||||
return model.Describable{}
|
||||
}
|
||||
res := model.Describable{Name: src.GetName()}
|
||||
if src.Description != nil {
|
||||
v := src.GetDescription()
|
||||
res.Description = &v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func describableToProto(src model.Describable) *describablev1.Describable {
|
||||
if strings.TrimSpace(src.Name) == "" && src.Description == nil {
|
||||
return nil
|
||||
}
|
||||
res := &describablev1.Describable{
|
||||
Name: src.Name,
|
||||
}
|
||||
if src.Description != nil {
|
||||
v := *src.Description
|
||||
res.Description = &v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func cloneBytes(src []byte) []byte {
|
||||
if len(src) == 0 {
|
||||
return nil
|
||||
}
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
func applyPermissionBoundRecord(pm *model.PaymentMethod, src *pboundv1.PermissionBound) error {
|
||||
if pm == nil || src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if storable := src.GetStorable(); storable != nil {
|
||||
if methodRef, err := parseOptionalObjectID(storable.GetId(), "payment_method_record.permission_bound.storable.id"); err != nil {
|
||||
return err
|
||||
} else if methodRef != bson.NilObjectID {
|
||||
pm.ID = methodRef
|
||||
}
|
||||
pm.CreatedAt = fromProtoTime(storable.GetCreatedAt())
|
||||
pm.UpdatedAt = fromProtoTime(storable.GetUpdatedAt())
|
||||
}
|
||||
|
||||
if archivable := src.GetArchivable(); archivable != nil {
|
||||
pm.Archived = archivable.GetIsArchived()
|
||||
}
|
||||
|
||||
if orgBound := src.GetOrganizationBound(); orgBound != nil {
|
||||
if orgRef, err := parseOptionalObjectID(orgBound.GetOrganizationRef(), "payment_method_record.permission_bound.organization_bound.organization_ref"); err != nil {
|
||||
return err
|
||||
} else if orgRef != bson.NilObjectID {
|
||||
pm.SetOrganizationRef(orgRef)
|
||||
}
|
||||
}
|
||||
|
||||
if permissionRef, err := parseOptionalObjectID(src.GetPermissionRef(), "payment_method_record.permission_bound.permission_ref"); err != nil {
|
||||
return err
|
||||
} else if permissionRef != bson.NilObjectID {
|
||||
pm.SetPermissionRef(permissionRef)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func permissionBoundFromModel(pm *model.PaymentMethod) *pboundv1.PermissionBound {
|
||||
if pm == nil {
|
||||
return nil
|
||||
}
|
||||
return &pboundv1.PermissionBound{
|
||||
Storable: &storablev1.Storable{
|
||||
Id: toObjectHex(pm.ID),
|
||||
CreatedAt: toProtoTime(pm.CreatedAt),
|
||||
UpdatedAt: toProtoTime(pm.UpdatedAt),
|
||||
},
|
||||
Archivable: &archivablev1.Archivable{
|
||||
IsArchived: pm.Archived,
|
||||
},
|
||||
OrganizationBound: &oboundv1.OrganizationBound{
|
||||
OrganizationRef: toObjectHex(pm.GetOrganizationRef()),
|
||||
},
|
||||
PermissionRef: toObjectHex(pm.GetPermissionRef()),
|
||||
}
|
||||
}
|
||||
|
||||
func parseOptionalObjectID(value, field string) (bson.ObjectID, error) {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return bson.NilObjectID, nil
|
||||
}
|
||||
ref, err := bson.ObjectIDFromHex(trimmed)
|
||||
if err != nil {
|
||||
return bson.NilObjectID, merrors.InvalidArgument(fmt.Sprintf("%s must be a valid object id", field), field)
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func toObjectHex(value bson.ObjectID) string {
|
||||
if value == bson.NilObjectID {
|
||||
return ""
|
||||
}
|
||||
return value.Hex()
|
||||
}
|
||||
|
||||
func toProtoTime(value time.Time) *timestamppb.Timestamp {
|
||||
if value.IsZero() {
|
||||
return nil
|
||||
}
|
||||
return timestamppb.New(value)
|
||||
}
|
||||
|
||||
func fromProtoTime(value *timestamppb.Timestamp) time.Time {
|
||||
if value == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return value.AsTime()
|
||||
}
|
||||
|
||||
func toModelCursor(cursor *paginationv2.ViewCursor) *model.ViewCursor {
|
||||
|
||||
@@ -28,7 +28,7 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -213,8 +213,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -685,6 +685,10 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
pricedAtUnixMs := int64(0)
|
||||
if ts := quote.GetPricedAt(); ts != nil {
|
||||
pricedAtUnixMs = ts.AsTime().UnixMilli()
|
||||
}
|
||||
return &paymenttypes.FXQuote{
|
||||
QuoteRef: strings.TrimSpace(quote.GetQuoteRef()),
|
||||
Pair: pairFromProto(quote.GetPair()),
|
||||
@@ -693,6 +697,7 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
|
||||
BaseAmount: moneyFromProto(quote.GetBaseAmount()),
|
||||
QuoteAmount: moneyFromProto(quote.GetQuoteAmount()),
|
||||
ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(),
|
||||
PricedAtUnixMs: pricedAtUnixMs,
|
||||
Provider: strings.TrimSpace(quote.GetProvider()),
|
||||
RateRef: strings.TrimSpace(quote.GetRateRef()),
|
||||
Firm: quote.GetFirm(),
|
||||
@@ -703,6 +708,10 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
var pricedAt *timestamppb.Timestamp
|
||||
if quote.PricedAtUnixMs > 0 {
|
||||
pricedAt = timestamppb.New(time.UnixMilli(quote.PricedAtUnixMs).UTC())
|
||||
}
|
||||
return &oraclev1.Quote{
|
||||
QuoteRef: strings.TrimSpace(quote.QuoteRef),
|
||||
Pair: pairToProto(quote.Pair),
|
||||
@@ -711,6 +720,7 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
|
||||
BaseAmount: protoMoney(quote.BaseAmount),
|
||||
QuoteAmount: protoMoney(quote.QuoteAmount),
|
||||
ExpiresAtUnixMs: quote.ExpiresAtUnixMs,
|
||||
PricedAt: pricedAt,
|
||||
Provider: strings.TrimSpace(quote.Provider),
|
||||
RateRef: strings.TrimSpace(quote.RateRef),
|
||||
Firm: quote.Firm,
|
||||
|
||||
@@ -2,6 +2,7 @@ package orchestrator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TestMoneyConversionRoundTrip(t *testing.T) {
|
||||
@@ -69,6 +71,7 @@ func TestFeeLineConversionRoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFXQuoteConversionRoundTrip(t *testing.T) {
|
||||
pricedAt := int64(1700000000000)
|
||||
proto := &oraclev1.Quote{
|
||||
QuoteRef: "q1",
|
||||
Pair: &fxv1.CurrencyPair{Base: "USD", Quote: "EUR"},
|
||||
@@ -77,6 +80,7 @@ func TestFXQuoteConversionRoundTrip(t *testing.T) {
|
||||
BaseAmount: &moneyv1.Money{Currency: "USD", Amount: "100"},
|
||||
QuoteAmount: &moneyv1.Money{Currency: "EUR", Amount: "90"},
|
||||
ExpiresAtUnixMs: 1700000000000,
|
||||
PricedAt: timestamppb.New(time.UnixMilli(pricedAt).UTC()),
|
||||
Provider: "provider",
|
||||
RateRef: "rate",
|
||||
Firm: true,
|
||||
@@ -88,6 +92,9 @@ func TestFXQuoteConversionRoundTrip(t *testing.T) {
|
||||
if model.Side != paymenttypes.FXSideSellBaseBuyQuote || model.Price.GetValue() != "0.9" {
|
||||
t.Fatalf("fxQuoteFromProto enums mismatch: %#v", model)
|
||||
}
|
||||
if model.PricedAtUnixMs != pricedAt {
|
||||
t.Fatalf("fxQuoteFromProto priced_at mismatch: %#v", model)
|
||||
}
|
||||
back := fxQuoteToProto(model)
|
||||
if back == nil || back.GetQuoteRef() != "q1" || back.GetPair().GetBase() != "USD" || back.GetPair().GetQuote() != "EUR" {
|
||||
t.Fatalf("fxQuoteToProto mismatch: %#v", back)
|
||||
@@ -95,6 +102,9 @@ func TestFXQuoteConversionRoundTrip(t *testing.T) {
|
||||
if back.GetSide() != fxv1.Side_SELL_BASE_BUY_QUOTE || back.GetPrice().GetValue() != "0.9" {
|
||||
t.Fatalf("fxQuoteToProto enums mismatch: %#v", back)
|
||||
}
|
||||
if got := back.GetPricedAt(); got == nil || got.AsTime().UnixMilli() != pricedAt {
|
||||
t.Fatalf("fxQuoteToProto priced_at mismatch: %#v", back)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssetConversionRoundTrip(t *testing.T) {
|
||||
|
||||
@@ -69,6 +69,7 @@ func cloneStoredFXQuote(src *paymenttypes.FXQuote) *paymenttypes.FXQuote {
|
||||
QuoteRef: strings.TrimSpace(src.QuoteRef),
|
||||
Side: src.Side,
|
||||
ExpiresAtUnixMs: src.ExpiresAtUnixMs,
|
||||
PricedAtUnixMs: src.PricedAtUnixMs,
|
||||
Provider: strings.TrimSpace(src.Provider),
|
||||
RateRef: strings.TrimSpace(src.RateRef),
|
||||
Firm: src.Firm,
|
||||
|
||||
@@ -2,6 +2,7 @@ package plan_builder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/tech/sendico/payments/storage/model"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type moneyGetter interface {
|
||||
@@ -226,6 +228,10 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
pricedAtUnixMs := int64(0)
|
||||
if ts := quote.GetPricedAt(); ts != nil {
|
||||
pricedAtUnixMs = ts.AsTime().UnixMilli()
|
||||
}
|
||||
return &paymenttypes.FXQuote{
|
||||
QuoteRef: strings.TrimSpace(quote.GetQuoteRef()),
|
||||
Pair: pairFromProto(quote.GetPair()),
|
||||
@@ -234,6 +240,7 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
|
||||
BaseAmount: moneyFromProto(quote.GetBaseAmount()),
|
||||
QuoteAmount: moneyFromProto(quote.GetQuoteAmount()),
|
||||
ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(),
|
||||
PricedAtUnixMs: pricedAtUnixMs,
|
||||
Provider: strings.TrimSpace(quote.GetProvider()),
|
||||
RateRef: strings.TrimSpace(quote.GetRateRef()),
|
||||
Firm: quote.GetFirm(),
|
||||
@@ -244,6 +251,10 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
var pricedAt *timestamppb.Timestamp
|
||||
if quote.PricedAtUnixMs > 0 {
|
||||
pricedAt = timestamppb.New(time.UnixMilli(quote.PricedAtUnixMs).UTC())
|
||||
}
|
||||
return &oraclev1.Quote{
|
||||
QuoteRef: strings.TrimSpace(quote.QuoteRef),
|
||||
Pair: pairToProto(quote.Pair),
|
||||
@@ -252,6 +263,7 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
|
||||
BaseAmount: protoMoney(quote.BaseAmount),
|
||||
QuoteAmount: protoMoney(quote.QuoteAmount),
|
||||
ExpiresAtUnixMs: quote.ExpiresAtUnixMs,
|
||||
PricedAt: pricedAt,
|
||||
Provider: strings.TrimSpace(quote.Provider),
|
||||
RateRef: strings.TrimSpace(quote.RateRef),
|
||||
Firm: quote.Firm,
|
||||
|
||||
@@ -26,7 +26,7 @@ require (
|
||||
github.com/tech/sendico/pkg v0.1.0
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -213,8 +213,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -2,6 +2,7 @@ package plan
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/tech/sendico/payments/storage/model"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type moneyGetter interface {
|
||||
@@ -158,6 +160,10 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
pricedAtUnixMs := int64(0)
|
||||
if ts := quote.GetPricedAt(); ts != nil {
|
||||
pricedAtUnixMs = ts.AsTime().UnixMilli()
|
||||
}
|
||||
return &paymenttypes.FXQuote{
|
||||
QuoteRef: strings.TrimSpace(quote.GetQuoteRef()),
|
||||
Pair: pairFromProto(quote.GetPair()),
|
||||
@@ -166,6 +172,7 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
|
||||
BaseAmount: moneyFromProto(quote.GetBaseAmount()),
|
||||
QuoteAmount: moneyFromProto(quote.GetQuoteAmount()),
|
||||
ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(),
|
||||
PricedAtUnixMs: pricedAtUnixMs,
|
||||
Provider: strings.TrimSpace(quote.GetProvider()),
|
||||
RateRef: strings.TrimSpace(quote.GetRateRef()),
|
||||
Firm: quote.GetFirm(),
|
||||
@@ -176,6 +183,10 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
var pricedAt *timestamppb.Timestamp
|
||||
if quote.PricedAtUnixMs > 0 {
|
||||
pricedAt = timestamppb.New(time.UnixMilli(quote.PricedAtUnixMs).UTC())
|
||||
}
|
||||
return &oraclev1.Quote{
|
||||
QuoteRef: strings.TrimSpace(quote.QuoteRef),
|
||||
Pair: pairToProto(quote.Pair),
|
||||
@@ -184,6 +195,7 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
|
||||
BaseAmount: protoMoney(quote.BaseAmount),
|
||||
QuoteAmount: protoMoney(quote.QuoteAmount),
|
||||
ExpiresAtUnixMs: quote.ExpiresAtUnixMs,
|
||||
PricedAt: pricedAt,
|
||||
Provider: strings.TrimSpace(quote.Provider),
|
||||
RateRef: strings.TrimSpace(quote.RateRef),
|
||||
Firm: quote.Firm,
|
||||
|
||||
@@ -2,6 +2,7 @@ package quotation
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/payments/storage/model"
|
||||
chainasset "github.com/tech/sendico/pkg/chain"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func intentFromProto(src *sharedv1.PaymentIntent) model.PaymentIntent {
|
||||
@@ -423,6 +425,10 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
pricedAtUnixMs := int64(0)
|
||||
if ts := quote.GetPricedAt(); ts != nil {
|
||||
pricedAtUnixMs = ts.AsTime().UnixMilli()
|
||||
}
|
||||
return &paymenttypes.FXQuote{
|
||||
QuoteRef: strings.TrimSpace(quote.GetQuoteRef()),
|
||||
Pair: pairFromProto(quote.GetPair()),
|
||||
@@ -431,6 +437,7 @@ func fxQuoteFromProto(quote *oraclev1.Quote) *paymenttypes.FXQuote {
|
||||
BaseAmount: moneyFromProto(quote.GetBaseAmount()),
|
||||
QuoteAmount: moneyFromProto(quote.GetQuoteAmount()),
|
||||
ExpiresAtUnixMs: quote.GetExpiresAtUnixMs(),
|
||||
PricedAtUnixMs: pricedAtUnixMs,
|
||||
Provider: strings.TrimSpace(quote.GetProvider()),
|
||||
RateRef: strings.TrimSpace(quote.GetRateRef()),
|
||||
Firm: quote.GetFirm(),
|
||||
@@ -441,6 +448,10 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
|
||||
if quote == nil {
|
||||
return nil
|
||||
}
|
||||
var pricedAt *timestamppb.Timestamp
|
||||
if quote.PricedAtUnixMs > 0 {
|
||||
pricedAt = timestamppb.New(time.UnixMilli(quote.PricedAtUnixMs).UTC())
|
||||
}
|
||||
return &oraclev1.Quote{
|
||||
QuoteRef: strings.TrimSpace(quote.QuoteRef),
|
||||
Pair: pairToProto(quote.Pair),
|
||||
@@ -449,6 +460,7 @@ func fxQuoteToProto(quote *paymenttypes.FXQuote) *oraclev1.Quote {
|
||||
BaseAmount: protoMoney(quote.BaseAmount),
|
||||
QuoteAmount: protoMoney(quote.QuoteAmount),
|
||||
ExpiresAtUnixMs: quote.ExpiresAtUnixMs,
|
||||
PricedAt: pricedAt,
|
||||
Provider: strings.TrimSpace(quote.Provider),
|
||||
RateRef: strings.TrimSpace(quote.RateRef),
|
||||
Firm: quote.Firm,
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
oraclev1 "github.com/tech/sendico/pkg/proto/oracle/v1"
|
||||
sharedv1 "github.com/tech/sendico/pkg/proto/payments/shared/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
|
||||
accountingv1 "github.com/tech/sendico/pkg/proto/common/accounting/v1"
|
||||
@@ -293,6 +294,10 @@ func quoteToProto(src *oracleclient.Quote) *oraclev1.Quote {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
var pricedAt *timestamppb.Timestamp
|
||||
if !src.PricedAt.IsZero() {
|
||||
pricedAt = timestamppb.New(src.PricedAt.UTC())
|
||||
}
|
||||
return &oraclev1.Quote{
|
||||
QuoteRef: src.QuoteRef,
|
||||
Pair: src.Pair,
|
||||
@@ -301,6 +306,7 @@ func quoteToProto(src *oracleclient.Quote) *oraclev1.Quote {
|
||||
BaseAmount: cloneProtoMoney(src.BaseAmount),
|
||||
QuoteAmount: cloneProtoMoney(src.QuoteAmount),
|
||||
ExpiresAtUnixMs: src.ExpiresAt.UnixMilli(),
|
||||
PricedAt: pricedAt,
|
||||
Provider: src.Provider,
|
||||
RateRef: src.RateRef,
|
||||
Firm: src.Firm,
|
||||
|
||||
@@ -119,6 +119,7 @@ func cloneStoredFXQuote(src *paymenttypes.FXQuote) *paymenttypes.FXQuote {
|
||||
QuoteRef: strings.TrimSpace(src.QuoteRef),
|
||||
Side: src.Side,
|
||||
ExpiresAtUnixMs: src.ExpiresAtUnixMs,
|
||||
PricedAtUnixMs: src.PricedAtUnixMs,
|
||||
Provider: strings.TrimSpace(src.Provider),
|
||||
RateRef: strings.TrimSpace(src.RateRef),
|
||||
Firm: src.Firm,
|
||||
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
golang.org/x/crypto v0.48.0
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
|
||||
@@ -273,8 +273,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -38,6 +38,7 @@ type FXQuote struct {
|
||||
BaseAmount *Money `bson:"baseAmount,omitempty" json:"baseAmount,omitempty"`
|
||||
QuoteAmount *Money `bson:"quoteAmount,omitempty" json:"quoteAmount,omitempty"`
|
||||
ExpiresAtUnixMs int64 `bson:"expiresAtUnixMs,omitempty" json:"expiresAtUnixMs,omitempty"`
|
||||
PricedAtUnixMs int64 `bson:"pricedAtUnixMs,omitempty" json:"pricedAtUnixMs,omitempty"`
|
||||
Provider string `bson:"provider,omitempty" json:"provider,omitempty"`
|
||||
RateRef string `bson:"rateRef,omitempty" json:"rateRef,omitempty"`
|
||||
Firm bool `bson:"firm,omitempty" json:"firm,omitempty"`
|
||||
@@ -85,6 +86,13 @@ func (q *FXQuote) GetExpiresAtUnixMs() int64 {
|
||||
return q.ExpiresAtUnixMs
|
||||
}
|
||||
|
||||
func (q *FXQuote) GetPricedAtUnixMs() int64 {
|
||||
if q == nil {
|
||||
return 0
|
||||
}
|
||||
return q.PricedAtUnixMs
|
||||
}
|
||||
|
||||
func (q *FXQuote) GetProvider() string {
|
||||
if q == nil {
|
||||
return ""
|
||||
|
||||
@@ -5,10 +5,10 @@ package fees.v1;
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/billing/fees/v1;feesv1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "common/money/v1/money.proto";
|
||||
import "common/fx/v1/fx.proto";
|
||||
import "common/accounting/v1/posting.proto";
|
||||
import "common/trace/v1/trace.proto";
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
import "api/proto/common/fx/v1/fx.proto";
|
||||
import "api/proto/common/accounting/v1/posting.proto";
|
||||
import "api/proto/common/trace/v1/trace.proto";
|
||||
|
||||
// --------------------
|
||||
// Core business enums
|
||||
@@ -74,10 +74,10 @@ message FXUsed {
|
||||
|
||||
// A derived posting line ready for the ledger to post as-is.
|
||||
message DerivedPostingLine {
|
||||
string ledger_account_ref = 1; // resolved account
|
||||
common.money.v1.Money money = 2; // amount/currency
|
||||
common.accounting.v1.PostingLineType line_type = 3; // FEE/TAX/SPREAD/REVERSAL
|
||||
common.accounting.v1.EntrySide side = 4; // DEBIT/CREDIT
|
||||
string ledger_account_ref = 1; // resolved account
|
||||
common.money.v1.Money money = 2; // amount/currency
|
||||
common.accounting.v1.PostingLineType line_type = 3; // FEE/TAX/SPREAD/REVERSAL
|
||||
common.accounting.v1.EntrySide side = 4; // DEBIT/CREDIT
|
||||
map<string,string> meta = 5; // fee_rule_id, rule_version, tax_code, tax_rate, fx_rate_used, etc.
|
||||
}
|
||||
|
||||
@@ -87,8 +87,8 @@ message AppliedRule {
|
||||
string rule_version = 2;
|
||||
string formula = 3; // e.g., "2.90% + 0.30 (min 0.50)"
|
||||
common.money.v1.RoundingMode rounding = 4;
|
||||
string tax_code = 5; // if applicable
|
||||
string tax_rate = 6; // decimal string
|
||||
string tax_code = 5; // if applicable
|
||||
string tax_rate = 6; // decimal string
|
||||
map<string,string> parameters = 7; // thresholds, tiers, etc.
|
||||
}
|
||||
|
||||
|
||||
11
api/proto/common/archivable/v1/archivable.proto
Normal file
11
api/proto/common/archivable/v1/archivable.proto
Normal file
@@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.archivable.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/archivable/v1;archivablev1";
|
||||
|
||||
|
||||
|
||||
message Archivable {
|
||||
bool is_archived = 1;
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.gateway.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/gateway/v1;gatewayv1";
|
||||
|
||||
import "common/money/v1/money.proto";
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
import "api/proto/payments/endpoint/v1/endpoint.proto";
|
||||
|
||||
|
||||
enum Operation {
|
||||
@@ -18,17 +21,6 @@ enum Operation {
|
||||
OPERATION_CREATE_ACCOUNT = 9;
|
||||
}
|
||||
|
||||
enum PaymentMethodType {
|
||||
PM_UNSPECIFIED = 0;
|
||||
PM_CARD = 1;
|
||||
PM_SEPA = 2;
|
||||
PM_ACH = 3;
|
||||
PM_PIX = 4;
|
||||
PM_WALLET = 5;
|
||||
PM_CRYPTO = 6;
|
||||
PM_LOCAL_BANK = 7; // generic local rails, refine later if needed
|
||||
}
|
||||
|
||||
// Rail identifiers for orchestration. Extend with new rails as needed.
|
||||
enum Rail {
|
||||
RAIL_UNSPECIFIED = 0;
|
||||
@@ -70,7 +62,7 @@ message OperationCapabilities {
|
||||
|
||||
// Per-method matrix entry
|
||||
message MethodCapability {
|
||||
PaymentMethodType method = 1;
|
||||
payments.endpoint.v1.PaymentMethod method = 1;
|
||||
|
||||
// ISO 4217 currency codes, e.g. "EUR", "USD"
|
||||
repeated string currencies = 2;
|
||||
@@ -187,4 +179,4 @@ message OperationExecutionStatus {
|
||||
common.money.v1.Money executed_money = 3;
|
||||
OperationResult status = 4;
|
||||
OperationError error = 5;
|
||||
}
|
||||
}
|
||||
|
||||
10
api/proto/common/organization_bound/v1/obound.proto
Normal file
10
api/proto/common/organization_bound/v1/obound.proto
Normal file
@@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.obound.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/organization_bound/v1;oboundv1";
|
||||
|
||||
|
||||
message OrganizationBound {
|
||||
string organization_ref = 1;
|
||||
}
|
||||
73
api/proto/common/payment/v1/card.proto
Normal file
73
api/proto/common/payment/v1/card.proto
Normal file
@@ -0,0 +1,73 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/payment/v1;paymentv1";
|
||||
|
||||
import "google/protobuf/wrappers.proto";
|
||||
|
||||
|
||||
// -------------------------
|
||||
// Card network (payment system)
|
||||
// -------------------------
|
||||
enum CardNetwork {
|
||||
CARD_NETWORK_UNSPECIFIED = 0;
|
||||
CARD_NETWORK_VISA = 1;
|
||||
CARD_NETWORK_MASTERCARD = 2;
|
||||
CARD_NETWORK_MIR = 3;
|
||||
CARD_NETWORK_AMEX = 4;
|
||||
CARD_NETWORK_UNIONPAY = 5;
|
||||
CARD_NETWORK_JCB = 6;
|
||||
CARD_NETWORK_DISCOVER = 7;
|
||||
}
|
||||
|
||||
enum CardFundingType {
|
||||
CARD_FUNDING_UNSPECIFIED = 0;
|
||||
CARD_FUNDING_DEBIT = 1;
|
||||
CARD_FUNDING_CREDIT = 2;
|
||||
CARD_FUNDING_PREPAID = 3;
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// PCI scope: raw card details
|
||||
// -------------------------
|
||||
|
||||
message RawCardData {
|
||||
string pan = 1;
|
||||
uint32 exp_month = 2; // 1–12
|
||||
uint32 exp_year = 3; // YYYY
|
||||
string cvv = 4; // optional; often omitted for payouts
|
||||
}
|
||||
|
||||
|
||||
// -------------------------
|
||||
// Safe metadata (display / routing hints)
|
||||
// -------------------------
|
||||
message CardMetadata {
|
||||
string masked_pan = 1; // e.g. 411111******1111
|
||||
CardNetwork network = 2; // Visa/Mastercard/Mir/...
|
||||
CardFundingType funding = 3; // debit/credit/prepaid (if known)
|
||||
string issuing_country = 4; // ISO 3166-1 alpha-2 (if known)
|
||||
string issuer_name = 5; // display only (if known)
|
||||
}
|
||||
|
||||
|
||||
// -------------------------
|
||||
// Card details
|
||||
// Either inline credentials OR reference to stored payment method
|
||||
// -------------------------
|
||||
message CardDetails {
|
||||
string id = 1;
|
||||
|
||||
oneof source {
|
||||
RawCardData raw = 2;
|
||||
string payment_method_id = 3;
|
||||
}
|
||||
|
||||
string cardholder_name = 4;
|
||||
string cardholder_surname = 5;
|
||||
|
||||
string billing_country = 6; // ISO 3166-1 alpha-2, if you need it per operation
|
||||
}
|
||||
|
||||
|
||||
11
api/proto/common/payment/v1/custom.proto
Normal file
11
api/proto/common/payment/v1/custom.proto
Normal file
@@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/payment/v1;paymentv1";
|
||||
|
||||
|
||||
message CustomPaymentDetails {
|
||||
string id = 1;
|
||||
bytes payment_method_json = 2;
|
||||
}
|
||||
16
api/proto/common/payment/v1/external_chain.proto
Normal file
16
api/proto/common/payment/v1/external_chain.proto
Normal file
@@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/payment/v1;paymentv1";
|
||||
|
||||
import "api/proto/gateway/chain/v1/chain.proto";
|
||||
|
||||
|
||||
message ExternalChainDetails {
|
||||
string id = 1;
|
||||
chain.gateway.v1.Asset asset = 2;
|
||||
string address = 3;
|
||||
string memo = 4;
|
||||
}
|
||||
|
||||
14
api/proto/common/payment/v1/ledger.proto
Normal file
14
api/proto/common/payment/v1/ledger.proto
Normal file
@@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/payment/v1;paymentv1";
|
||||
|
||||
|
||||
message LedgerDetails {
|
||||
string id = 1;
|
||||
oneof source {
|
||||
string ledger_account_ref = 2;
|
||||
string account_code = 3;
|
||||
}
|
||||
}
|
||||
11
api/proto/common/payment/v1/managed_wallet.proto
Normal file
11
api/proto/common/payment/v1/managed_wallet.proto
Normal file
@@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/payment/v1;paymentv1";
|
||||
|
||||
|
||||
message ManagedWalletDetails {
|
||||
string id = 1;
|
||||
string managed_wallet_ref = 2;
|
||||
}
|
||||
16
api/proto/common/payment/v1/rba.proto
Normal file
16
api/proto/common/payment/v1/rba.proto
Normal file
@@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/payment/v1;paymentv1";
|
||||
|
||||
// -------------------------
|
||||
// Russian bank account details
|
||||
// -------------------------
|
||||
|
||||
message RussianBankDetails {
|
||||
string id = 1;
|
||||
string account_number = 2; // 20 digits
|
||||
string bik = 3; // 9 digits
|
||||
string account_holder_name = 4;
|
||||
}
|
||||
17
api/proto/common/payment/v1/sepa.proto
Normal file
17
api/proto/common/payment/v1/sepa.proto
Normal file
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/payment/v1;paymentv1";
|
||||
|
||||
// -------------------------
|
||||
// SEPA bank account details
|
||||
// -------------------------
|
||||
|
||||
message SepaBankDetails {
|
||||
string id = 1;
|
||||
string iban = 2; // IBAN
|
||||
string bic = 3; // optional (BIC/SWIFT)
|
||||
string account_holder_name = 4;
|
||||
}
|
||||
|
||||
13
api/proto/common/payment/v1/settlement.proto
Normal file
13
api/proto/common/payment/v1/settlement.proto
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package common.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/payment/v1;paymentv1";
|
||||
|
||||
// SettlementMode defines how to treat fees/FX variance for payouts.
|
||||
enum SettlementMode {
|
||||
SETTLEMENT_UNSPECIFIED = 0;
|
||||
SETTLEMENT_FIX_SOURCE = 1; // customer pays fees; sent amount fixed
|
||||
SETTLEMENT_FIX_RECEIVED = 2; // receiver gets fixed amount; source flexes
|
||||
}
|
||||
17
api/proto/common/permission_bound/v1/pbound.proto
Normal file
17
api/proto/common/permission_bound/v1/pbound.proto
Normal file
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.pbound.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/permission_bound/v1;pboundv1";
|
||||
|
||||
import "api/proto/common/storable/v1/storable.proto";
|
||||
import "api/proto/common/archivable/v1/archivable.proto";
|
||||
import "api/proto/common/organization_bound/v1/obound.proto";
|
||||
|
||||
|
||||
message PermissionBound {
|
||||
common.storable.v1.Storable storable = 1;
|
||||
common.archivable.v1.Archivable archivable = 2;
|
||||
common.obound.v1.OrganizationBound organization_bound = 3;
|
||||
string permission_ref = 4;
|
||||
}
|
||||
14
api/proto/common/storable/v1/storable.proto
Normal file
14
api/proto/common/storable/v1/storable.proto
Normal file
@@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package common.storable.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/common/storable/v1;storablev1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
|
||||
message Storable {
|
||||
string id = 1;
|
||||
google.protobuf.Timestamp created_at = 10;
|
||||
google.protobuf.Timestamp updated_at = 11;
|
||||
}
|
||||
@@ -7,10 +7,10 @@ option go_package = "github.com/tech/sendico/pkg/proto/connector/v1;connectorv1"
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/wrappers.proto";
|
||||
import "common/account_role/v1/account_role.proto";
|
||||
import "common/describable/v1/describable.proto";
|
||||
import "common/money/v1/money.proto";
|
||||
import "common/pagination/v1/cursor.proto";
|
||||
import "api/proto/common/account_role/v1/account_role.proto";
|
||||
import "api/proto/common/describable/v1/describable.proto";
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
import "api/proto/common/pagination/v1/cursor.proto";
|
||||
|
||||
// ConnectorService exposes capability-driven account and operation primitives.
|
||||
service ConnectorService {
|
||||
|
||||
@@ -6,9 +6,9 @@ option go_package = "github.com/tech/sendico/pkg/proto/gateway/chain/v1;chainv1"
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/wrappers.proto";
|
||||
import "common/money/v1/money.proto";
|
||||
import "common/pagination/v1/cursor.proto";
|
||||
import "common/describable/v1/describable.proto";
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
import "api/proto/common/pagination/v1/cursor.proto";
|
||||
import "api/proto/common/describable/v1/describable.proto";
|
||||
|
||||
// Supported blockchain networks for the managed wallets.
|
||||
enum ChainNetwork {
|
||||
|
||||
@@ -5,7 +5,7 @@ package mntx.gateway.v1;
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/gateway/mntx/v1;mntxv1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "common/gateway/v1/gateway.proto";
|
||||
import "api/proto/common/gateway/v1/gateway.proto";
|
||||
|
||||
// Lifecycle status of a payout handled by Monetix.
|
||||
enum PayoutStatus {
|
||||
|
||||
@@ -6,8 +6,8 @@ option go_package = "github.com/tech/sendico/pkg/proto/ledger/v1;ledgerv1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/wrappers.proto";
|
||||
import "common/describable/v1/describable.proto";
|
||||
import "common/money/v1/money.proto";
|
||||
import "api/proto/common/describable/v1/describable.proto";
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
|
||||
// ===== Enums =====
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ package oracle.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/oracle/v1;oraclev1";
|
||||
|
||||
import "common/money/v1/money.proto";
|
||||
import "common/fx/v1/fx.proto";
|
||||
import "common/trace/v1/trace.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
import "api/proto/common/fx/v1/fx.proto";
|
||||
import "api/proto/common/trace/v1/trace.proto";
|
||||
|
||||
|
||||
message RateSnapshot {
|
||||
common.fx.v1.CurrencyPair pair = 1;
|
||||
@@ -46,6 +48,7 @@ message Quote {
|
||||
string provider = 8;
|
||||
string rate_ref = 9;
|
||||
bool firm = 10;
|
||||
google.protobuf.Timestamp priced_at = 11;
|
||||
}
|
||||
|
||||
message GetQuoteRequest {
|
||||
|
||||
40
api/proto/payments/endpoint/v1/endpoint.proto
Normal file
40
api/proto/payments/endpoint/v1/endpoint.proto
Normal file
@@ -0,0 +1,40 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package payments.endpoint.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/endpoint/v1;endpointv1";
|
||||
|
||||
import "api/proto/common/describable/v1/describable.proto";
|
||||
import "api/proto/common/permission_bound/v1/pbound.proto";
|
||||
|
||||
enum PaymentMethodType {
|
||||
PAYMENT_METHOD_TYPE_UNSPECIFIED = 0;
|
||||
PAYMENT_METHOD_TYPE_IBAN = 1;
|
||||
PAYMENT_METHOD_TYPE_CARD = 2;
|
||||
PAYMENT_METHOD_TYPE_CARD_TOKEN = 3;
|
||||
PAYMENT_METHOD_TYPE_BANK_ACCOUNT = 4;
|
||||
PAYMENT_METHOD_TYPE_WALLET = 5;
|
||||
PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS = 6;
|
||||
PAYMENT_METHOD_TYPE_LEDGER = 7;
|
||||
}
|
||||
|
||||
message PaymentMethod {
|
||||
common.describable.v1.Describable describable = 1;
|
||||
string recipient_ref = 2;
|
||||
PaymentMethodType type = 3;
|
||||
bytes data = 4;
|
||||
bool is_main = 5;
|
||||
}
|
||||
|
||||
message PaymentEndpoint {
|
||||
oneof source {
|
||||
string payment_method_ref = 1;
|
||||
PaymentMethod payment_method = 2;
|
||||
string payee_ref = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message PaymentMethodRecord {
|
||||
common.pbound.v1.PermissionBound permission_bound = 1;
|
||||
PaymentMethod payment_method = 2;
|
||||
}
|
||||
@@ -4,18 +4,17 @@ package payments.methods.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/methods/v1;methodsv1";
|
||||
|
||||
import "google/protobuf/wrappers.proto";
|
||||
|
||||
import "common/pagination/v2/cursor.proto";
|
||||
import "api/proto/common/pagination/v2/cursor.proto";
|
||||
import "api/proto/payments/endpoint/v1/endpoint.proto";
|
||||
|
||||
message CreatePaymentMethodRequest {
|
||||
string account_ref = 1;
|
||||
string organization_ref = 2;
|
||||
bytes payment_method_json = 3;
|
||||
payments.endpoint.v1.PaymentMethod payment_method = 3;
|
||||
}
|
||||
|
||||
message CreatePaymentMethodResponse {
|
||||
bytes payment_method_json = 1;
|
||||
payments.endpoint.v1.PaymentMethodRecord payment_method_record = 1;
|
||||
}
|
||||
|
||||
message GetPaymentMethodRequest {
|
||||
@@ -24,16 +23,16 @@ message GetPaymentMethodRequest {
|
||||
}
|
||||
|
||||
message GetPaymentMethodResponse {
|
||||
bytes payment_method_json = 1;
|
||||
payments.endpoint.v1.PaymentMethodRecord payment_method_record = 1;
|
||||
}
|
||||
|
||||
message UpdatePaymentMethodRequest {
|
||||
string account_ref = 1;
|
||||
bytes payment_method_json = 2;
|
||||
payments.endpoint.v1.PaymentMethodRecord payment_method_record = 2;
|
||||
}
|
||||
|
||||
message UpdatePaymentMethodResponse {
|
||||
bytes payment_method_json = 1;
|
||||
payments.endpoint.v1.PaymentMethodRecord payment_method_record = 1;
|
||||
}
|
||||
|
||||
message DeletePaymentMethodRequest {
|
||||
@@ -62,14 +61,21 @@ message ListPaymentMethodsRequest {
|
||||
}
|
||||
|
||||
message ListPaymentMethodsResponse {
|
||||
repeated bytes payment_methods_json = 1;
|
||||
repeated payments.endpoint.v1.PaymentMethodRecord payment_methods = 1;
|
||||
}
|
||||
|
||||
// PaymentMethodsService provides operations for managing payment methods.
|
||||
service PaymentMethodsService {
|
||||
// CreatePaymentMethod creates a new payment method.
|
||||
rpc CreatePaymentMethod(CreatePaymentMethodRequest) returns (CreatePaymentMethodResponse);
|
||||
// GetPaymentMethod retrieves a payment method by reference.
|
||||
rpc GetPaymentMethod(GetPaymentMethodRequest) returns (GetPaymentMethodResponse);
|
||||
// UpdatePaymentMethod updates an existing payment method.
|
||||
rpc UpdatePaymentMethod(UpdatePaymentMethodRequest) returns (UpdatePaymentMethodResponse);
|
||||
// Delete exising payment method
|
||||
rpc DeletePaymentMethod(DeletePaymentMethodRequest) returns (DeletePaymentMethodResponse);
|
||||
// SetPaymentMethodArchived sets the archived status of a payment method.
|
||||
rpc SetPaymentMethodArchived(SetPaymentMethodArchivedRequest) returns (SetPaymentMethodArchivedResponse);
|
||||
// ListPaymentMethods retrieves a list of payment methods.
|
||||
rpc ListPaymentMethods(ListPaymentMethodsRequest) returns (ListPaymentMethodsResponse);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ package payments.orchestration.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/orchestration/v1;orchestrationv1";
|
||||
|
||||
import "common/pagination/v1/cursor.proto";
|
||||
import "billing/fees/v1/fees.proto";
|
||||
import "gateway/chain/v1/chain.proto";
|
||||
import "gateway/mntx/v1/mntx.proto";
|
||||
import "payments/shared/v1/shared.proto";
|
||||
import "api/proto/common/pagination/v1/cursor.proto";
|
||||
import "api/proto/billing/fees/v1/fees.proto";
|
||||
import "api/proto/gateway/chain/v1/chain.proto";
|
||||
import "api/proto/gateway/mntx/v1/mntx.proto";
|
||||
import "api/proto/payments/shared/v1/shared.proto";
|
||||
|
||||
message InitiatePaymentsRequest {
|
||||
payments.shared.v1.RequestMeta meta = 1;
|
||||
|
||||
20
api/proto/payments/payment/v1/payment.proto
Normal file
20
api/proto/payments/payment/v1/payment.proto
Normal file
@@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package payments.payment.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/payment/v1;paymentv1";
|
||||
|
||||
import "api/proto/payments/transfer/v1/transfer.proto";
|
||||
|
||||
|
||||
// -------------------------
|
||||
// External payment semantics
|
||||
// -------------------------
|
||||
message PaymentIntent {
|
||||
payments.transfer.v1.TransferIntent transfer = 1;
|
||||
|
||||
string payer_ref = 2;
|
||||
string payee_ref = 3;
|
||||
|
||||
string purpose = 4;
|
||||
}
|
||||
@@ -4,7 +4,8 @@ package payments.quotation.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/quotation/v1;quotationv1";
|
||||
|
||||
import "payments/shared/v1/shared.proto";
|
||||
import "api/proto/payments/shared/v1/shared.proto";
|
||||
|
||||
|
||||
message QuotePaymentRequest {
|
||||
payments.shared.v1.RequestMeta meta = 1;
|
||||
@@ -35,6 +36,8 @@ message QuotePaymentsResponse {
|
||||
}
|
||||
|
||||
service QuotationService {
|
||||
// QuotePayment returns a quote for a single payment request.
|
||||
rpc QuotePayment(QuotePaymentRequest) returns (QuotePaymentResponse);
|
||||
// QuotePayments returns quotes for multiple payment requests.
|
||||
rpc QuotePayments(QuotePaymentsRequest) returns (QuotePaymentsResponse);
|
||||
}
|
||||
|
||||
59
api/proto/payments/quotation/v2/interface.proto
Normal file
59
api/proto/payments/quotation/v2/interface.proto
Normal file
@@ -0,0 +1,59 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package payments.quotation.v2;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/quotation/v2;quotationv2";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "api/proto/common/storable/v1/storable.proto";
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
import "api/proto/common/fx/v1/fx.proto";
|
||||
import "api/proto/billing/fees/v1/fees.proto";
|
||||
import "api/proto/oracle/v1/oracle.proto";
|
||||
|
||||
enum QuoteKind {
|
||||
QUOTE_KIND_UNSPECIFIED = 0;
|
||||
|
||||
QUOTE_KIND_EXECUTABLE = 1; // can be executed now (subject to execution-time checks)
|
||||
QUOTE_KIND_INDICATIVE = 2; // informational only
|
||||
}
|
||||
|
||||
enum QuoteState {
|
||||
QUOTE_STATE_UNSPECIFIED = 0;
|
||||
|
||||
QUOTE_STATE_ACTIVE = 1;
|
||||
QUOTE_STATE_EXPIRED = 2;
|
||||
}
|
||||
|
||||
enum QuoteBlockReason {
|
||||
QUOTE_BLOCK_REASON_UNSPECIFIED = 0;
|
||||
|
||||
QUOTE_BLOCK_REASON_ROUTE_UNAVAILABLE = 1;
|
||||
QUOTE_BLOCK_REASON_LIMIT_BLOCKED = 2;
|
||||
QUOTE_BLOCK_REASON_RISK_BLOCKED = 3;
|
||||
QUOTE_BLOCK_REASON_INSUFFICIENT_LIQUIDITY = 4;
|
||||
QUOTE_BLOCK_REASON_PRICE_STALE = 5;
|
||||
QUOTE_BLOCK_REASON_AMOUNT_TOO_SMALL = 6;
|
||||
QUOTE_BLOCK_REASON_AMOUNT_TOO_LARGE = 7;
|
||||
}
|
||||
|
||||
|
||||
message PaymentQuote {
|
||||
common.storable.v1.Storable storable = 1;
|
||||
QuoteKind kind = 2;
|
||||
QuoteState state = 3;
|
||||
optional QuoteBlockReason block_reason = 4;
|
||||
|
||||
common.money.v1.Money debit_amount = 5;
|
||||
common.money.v1.Money credit_amount = 6;
|
||||
|
||||
repeated fees.v1.DerivedPostingLine fee_lines = 7;
|
||||
repeated fees.v1.AppliedRule fee_rules = 8;
|
||||
|
||||
oracle.v1.Quote fx_quote = 9;
|
||||
|
||||
string quote_ref = 10;
|
||||
|
||||
google.protobuf.Timestamp expires_at = 11;
|
||||
google.protobuf.Timestamp priced_at = 12;
|
||||
}
|
||||
37
api/proto/payments/quotation/v2/quotation.proto
Normal file
37
api/proto/payments/quotation/v2/quotation.proto
Normal file
@@ -0,0 +1,37 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package payments.quotation.v2;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/quotation/v2;quotationv2";
|
||||
|
||||
import "api/proto/payments/shared/v1/shared.proto";
|
||||
import "api/proto/payments/transfer/v1/transfer.proto";
|
||||
import "api/proto/payments/quotation/v2/interface.proto";
|
||||
|
||||
|
||||
message QuotePaymentRequest {
|
||||
payments.shared.v1.RequestMeta meta = 1;
|
||||
string idempotency_key = 2;
|
||||
payments.transfer.v1.TransferIntent intent = 3;
|
||||
bool preview_only = 4;
|
||||
string initiator_ref = 5;
|
||||
}
|
||||
|
||||
message QuotePaymentResponse {
|
||||
payments.quotation.v2.PaymentQuote quote = 1;
|
||||
string idempotency_key = 2;
|
||||
}
|
||||
|
||||
message QuotePaymentsRequest {
|
||||
payments.shared.v1.RequestMeta meta = 1;
|
||||
string idempotency_key = 2;
|
||||
repeated payments.transfer.v1.TransferIntent intents = 3;
|
||||
bool preview_only = 4;
|
||||
string initiator_ref = 5;
|
||||
}
|
||||
|
||||
message QuotePaymentsResponse {
|
||||
string quote_ref = 1;
|
||||
repeated payments.quotation.v2.PaymentQuote quotes = 3;
|
||||
string idempotency_key = 4;
|
||||
}
|
||||
@@ -5,13 +5,14 @@ package payments.shared.v1;
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/shared/v1;sharedv1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "common/money/v1/money.proto";
|
||||
import "common/fx/v1/fx.proto";
|
||||
import "common/gateway/v1/gateway.proto";
|
||||
import "common/trace/v1/trace.proto";
|
||||
import "billing/fees/v1/fees.proto";
|
||||
import "gateway/chain/v1/chain.proto";
|
||||
import "oracle/v1/oracle.proto";
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
import "api/proto/common/fx/v1/fx.proto";
|
||||
import "api/proto/common/gateway/v1/gateway.proto";
|
||||
import "api/proto/common/payment/v1/settlement.proto";
|
||||
import "api/proto/common/trace/v1/trace.proto";
|
||||
import "api/proto/billing/fees/v1/fees.proto";
|
||||
import "api/proto/gateway/chain/v1/chain.proto";
|
||||
import "api/proto/oracle/v1/oracle.proto";
|
||||
|
||||
enum PaymentKind {
|
||||
PAYMENT_KIND_UNSPECIFIED = 0;
|
||||
@@ -20,13 +21,6 @@ enum PaymentKind {
|
||||
PAYMENT_KIND_FX_CONVERSION = 3;
|
||||
}
|
||||
|
||||
// SettlementMode defines how to treat fees/FX variance for payouts.
|
||||
enum SettlementMode {
|
||||
SETTLEMENT_UNSPECIFIED = 0;
|
||||
SETTLEMENT_FIX_SOURCE = 1; // customer pays fees; sent amount fixed
|
||||
SETTLEMENT_FIX_RECEIVED = 2; // receiver gets fixed amount; source flexes
|
||||
}
|
||||
|
||||
enum PaymentState {
|
||||
PAYMENT_STATE_UNSPECIFIED = 0;
|
||||
PAYMENT_STATE_ACCEPTED = 1;
|
||||
@@ -111,7 +105,7 @@ message PaymentIntent {
|
||||
FXIntent fx = 6;
|
||||
fees.v1.PolicyOverrides fee_policy = 7;
|
||||
map<string, string> attributes = 8;
|
||||
SettlementMode settlement_mode = 9;
|
||||
common.payment.v1.SettlementMode settlement_mode = 9;
|
||||
Customer customer = 10;
|
||||
string settlement_currency = 11;
|
||||
string ref = 12;
|
||||
|
||||
21
api/proto/payments/transfer/v1/transfer.proto
Normal file
21
api/proto/payments/transfer/v1/transfer.proto
Normal file
@@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package payments.transfer.v1;
|
||||
|
||||
option go_package = "github.com/tech/sendico/pkg/proto/payments/transfer/v1;transferv1";
|
||||
|
||||
import "api/proto/common/money/v1/money.proto";
|
||||
import "api/proto/common/storable/v1/storable.proto";
|
||||
import "api/proto/payments/endpoint/v1/endpoint.proto";
|
||||
|
||||
|
||||
// -------------------------
|
||||
// Base value movement
|
||||
// -------------------------
|
||||
message TransferIntent {
|
||||
payments.endpoint.v1.PaymentEndpoint source = 1;
|
||||
payments.endpoint.v1.PaymentEndpoint destination = 2;
|
||||
common.money.v1.Money amount = 3;
|
||||
|
||||
string comment = 4;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ require (
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
go.uber.org/zap v1.27.1
|
||||
golang.org/x/net v0.50.0
|
||||
google.golang.org/grpc v1.79.0
|
||||
google.golang.org/grpc v1.79.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
moul.io/chizap v1.0.3
|
||||
|
||||
@@ -365,8 +365,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.0 h1:6/+EFlxsMyoSbHbBoEDx94n/Ycx/bi0IhJ5Qh7b7LaA=
|
||||
google.golang.org/grpc v1.79.0/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
|
||||
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -32,6 +32,7 @@ type FxQuote struct {
|
||||
BaseAmount *paymenttypes.Money `json:"baseAmount,omitempty"`
|
||||
QuoteAmount *paymenttypes.Money `json:"quoteAmount,omitempty"`
|
||||
ExpiresAtUnixMs int64 `json:"expiresAtUnixMs,omitempty"`
|
||||
PricedAtUnixMs int64 `json:"pricedAtUnixMs,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
RateRef string `json:"rateRef,omitempty"`
|
||||
Firm bool `json:"firm,omitempty"`
|
||||
@@ -163,6 +164,10 @@ func toFxQuote(q *oraclev1.Quote) *FxQuote {
|
||||
return nil
|
||||
}
|
||||
pair := q.GetPair()
|
||||
pricedAtUnixMs := int64(0)
|
||||
if ts := q.GetPricedAt(); ts != nil {
|
||||
pricedAtUnixMs = ts.AsTime().UnixMilli()
|
||||
}
|
||||
base := ""
|
||||
quote := ""
|
||||
if pair != nil {
|
||||
@@ -178,6 +183,7 @@ func toFxQuote(q *oraclev1.Quote) *FxQuote {
|
||||
BaseAmount: toMoney(q.GetBaseAmount()),
|
||||
QuoteAmount: toMoney(q.GetQuoteAmount()),
|
||||
ExpiresAtUnixMs: q.GetExpiresAtUnixMs(),
|
||||
PricedAtUnixMs: pricedAtUnixMs,
|
||||
Provider: q.GetProvider(),
|
||||
RateRef: q.GetRateRef(),
|
||||
Firm: q.GetFirm(),
|
||||
|
||||
@@ -3,6 +3,7 @@ package paymethodsimp
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -16,13 +17,21 @@ import (
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
archivablev1 "github.com/tech/sendico/pkg/proto/common/archivable/v1"
|
||||
describablev1 "github.com/tech/sendico/pkg/proto/common/describable/v1"
|
||||
oboundv1 "github.com/tech/sendico/pkg/proto/common/organization_bound/v1"
|
||||
paginationv2 "github.com/tech/sendico/pkg/proto/common/pagination/v2"
|
||||
pboundv1 "github.com/tech/sendico/pkg/proto/common/permission_bound/v1"
|
||||
storablev1 "github.com/tech/sendico/pkg/proto/common/storable/v1"
|
||||
endpointv1 "github.com/tech/sendico/pkg/proto/payments/endpoint/v1"
|
||||
methodsv1 "github.com/tech/sendico/pkg/proto/payments/methods/v1"
|
||||
eapi "github.com/tech/sendico/server/interface/api"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
)
|
||||
|
||||
@@ -98,17 +107,25 @@ func (a *PaymentMethodsAPI) create(r *http.Request, account *model.Account, toke
|
||||
if err != nil {
|
||||
return response.BadPayload(a.logger, a.Name(), err)
|
||||
}
|
||||
pm, err := decodePaymentMethodJSON(payload)
|
||||
if err != nil {
|
||||
return response.BadPayload(a.logger, a.Name(), err)
|
||||
}
|
||||
method, err := encodePaymentMethodProto(pm)
|
||||
if err != nil {
|
||||
return response.Internal(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
resp, err := a.client.CreatePaymentMethod(r.Context(), &methodsv1.CreatePaymentMethodRequest{
|
||||
AccountRef: account.ID.Hex(),
|
||||
OrganizationRef: orgRef.Hex(),
|
||||
PaymentMethodJson: payload,
|
||||
AccountRef: account.ID.Hex(),
|
||||
OrganizationRef: orgRef.Hex(),
|
||||
PaymentMethod: method,
|
||||
})
|
||||
if err != nil {
|
||||
return grpcErrorResponse(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
pm, err := decodePaymentMethod(resp.GetPaymentMethodJson())
|
||||
pm, err = decodePaymentMethodRecord(resp.GetPaymentMethodRecord())
|
||||
if err != nil {
|
||||
return response.Internal(a.logger, a.Name(), err)
|
||||
}
|
||||
@@ -140,7 +157,7 @@ func (a *PaymentMethodsAPI) list(r *http.Request, account *model.Account, token
|
||||
return grpcErrorResponse(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
items, err := decodePaymentMethods(resp.GetPaymentMethodsJson())
|
||||
items, err := decodePaymentMethods(resp.GetPaymentMethods())
|
||||
if err != nil {
|
||||
return response.Internal(a.logger, a.Name(), err)
|
||||
}
|
||||
@@ -161,7 +178,7 @@ func (a *PaymentMethodsAPI) get(r *http.Request, account *model.Account, token *
|
||||
return grpcErrorResponse(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
pm, err := decodePaymentMethod(resp.GetPaymentMethodJson())
|
||||
pm, err := decodePaymentMethodRecord(resp.GetPaymentMethodRecord())
|
||||
if err != nil {
|
||||
return response.Internal(a.logger, a.Name(), err)
|
||||
}
|
||||
@@ -173,16 +190,24 @@ func (a *PaymentMethodsAPI) update(r *http.Request, account *model.Account, toke
|
||||
if err != nil {
|
||||
return response.BadPayload(a.logger, a.Name(), err)
|
||||
}
|
||||
pm, err := decodePaymentMethodJSON(payload)
|
||||
if err != nil {
|
||||
return response.BadPayload(a.logger, a.Name(), err)
|
||||
}
|
||||
record, err := encodePaymentMethodRecord(pm)
|
||||
if err != nil {
|
||||
return response.Internal(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
resp, err := a.client.UpdatePaymentMethod(r.Context(), &methodsv1.UpdatePaymentMethodRequest{
|
||||
AccountRef: account.ID.Hex(),
|
||||
PaymentMethodJson: payload,
|
||||
AccountRef: account.ID.Hex(),
|
||||
PaymentMethodRecord: record,
|
||||
})
|
||||
if err != nil {
|
||||
return grpcErrorResponse(a.logger, a.Name(), err)
|
||||
}
|
||||
|
||||
pm, err := decodePaymentMethod(resp.GetPaymentMethodJson())
|
||||
pm, err = decodePaymentMethodRecord(resp.GetPaymentMethodRecord())
|
||||
if err != nil {
|
||||
return response.Internal(a.logger, a.Name(), err)
|
||||
}
|
||||
@@ -300,7 +325,7 @@ func toProtoCursor(cursor *model.ViewCursor) *paginationv2.ViewCursor {
|
||||
return res
|
||||
}
|
||||
|
||||
func decodePaymentMethod(payload []byte) (*model.PaymentMethod, error) {
|
||||
func decodePaymentMethodJSON(payload []byte) (*model.PaymentMethod, error) {
|
||||
var pm model.PaymentMethod
|
||||
if err := json.Unmarshal(payload, &pm); err != nil {
|
||||
return nil, err
|
||||
@@ -308,13 +333,78 @@ func decodePaymentMethod(payload []byte) (*model.PaymentMethod, error) {
|
||||
return &pm, nil
|
||||
}
|
||||
|
||||
func decodePaymentMethods(items [][]byte) ([]model.PaymentMethod, error) {
|
||||
func decodePaymentMethodRecord(record *endpointv1.PaymentMethodRecord) (*model.PaymentMethod, error) {
|
||||
if record == nil {
|
||||
return nil, merrors.InvalidArgument("payment_method_record is required")
|
||||
}
|
||||
pm, err := decodePaymentMethodProto(record.GetPaymentMethod())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := applyPermissionBound(pm, record.GetPermissionBound()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
func decodePaymentMethodProto(method *endpointv1.PaymentMethod) (*model.PaymentMethod, error) {
|
||||
if method == nil {
|
||||
return nil, merrors.InvalidArgument("payment_method is required")
|
||||
}
|
||||
|
||||
recipientRef, err := parseRequiredObjectID(method.GetRecipientRef(), "payment_method.recipient_ref")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pt, err := paymentTypeFromProto(method.GetType(), "payment_method.type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.PaymentMethod{
|
||||
Describable: describableFromProto(method.GetDescribable()),
|
||||
RecipientRef: recipientRef,
|
||||
Type: pt,
|
||||
Data: cloneBytes(method.GetData()),
|
||||
IsMain: method.GetIsMain(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodePaymentMethodProto(pm *model.PaymentMethod) (*endpointv1.PaymentMethod, error) {
|
||||
if pm == nil {
|
||||
return nil, merrors.InvalidArgument("payment method is required")
|
||||
}
|
||||
pt, err := paymentTypeToProto(pm.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &endpointv1.PaymentMethod{
|
||||
Describable: describableToProto(pm.Describable),
|
||||
RecipientRef: toObjectHex(pm.RecipientRef),
|
||||
Type: pt,
|
||||
Data: cloneBytes(pm.Data),
|
||||
IsMain: pm.IsMain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func encodePaymentMethodRecord(pm *model.PaymentMethod) (*endpointv1.PaymentMethodRecord, error) {
|
||||
method, err := encodePaymentMethodProto(pm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &endpointv1.PaymentMethodRecord{
|
||||
PermissionBound: permissionBoundFromModel(pm),
|
||||
PaymentMethod: method,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodePaymentMethods(items []*endpointv1.PaymentMethodRecord) ([]model.PaymentMethod, error) {
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
res := make([]model.PaymentMethod, 0, len(items))
|
||||
for i := range items {
|
||||
pm, err := decodePaymentMethod(items[i])
|
||||
pm, err := decodePaymentMethodRecord(items[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -323,6 +413,183 @@ func decodePaymentMethods(items [][]byte) ([]model.PaymentMethod, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func paymentTypeFromProto(value endpointv1.PaymentMethodType, field string) (model.PaymentType, error) {
|
||||
switch value {
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN:
|
||||
return model.PaymentTypeIban, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD:
|
||||
return model.PaymentTypeCard, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN:
|
||||
return model.PaymentTypeCardToken, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT:
|
||||
return model.PaymentTypeBankAccount, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET:
|
||||
return model.PaymentTypeWallet, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS:
|
||||
return model.PaymentTypeCryptoAddress, nil
|
||||
case endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER:
|
||||
return model.PaymentTypeLedger, nil
|
||||
default:
|
||||
return model.PaymentTypeIban, merrors.InvalidArgument(fmt.Sprintf("%s has unsupported value: %s", field, value.String()), field)
|
||||
}
|
||||
}
|
||||
|
||||
func paymentTypeToProto(value model.PaymentType) (endpointv1.PaymentMethodType, error) {
|
||||
switch value {
|
||||
case model.PaymentTypeIban:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_IBAN, nil
|
||||
case model.PaymentTypeCard:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD, nil
|
||||
case model.PaymentTypeCardToken:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CARD_TOKEN, nil
|
||||
case model.PaymentTypeBankAccount:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_BANK_ACCOUNT, nil
|
||||
case model.PaymentTypeWallet:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_WALLET, nil
|
||||
case model.PaymentTypeCryptoAddress:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_CRYPTO_ADDRESS, nil
|
||||
case model.PaymentTypeLedger:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_LEDGER, nil
|
||||
default:
|
||||
return endpointv1.PaymentMethodType_PAYMENT_METHOD_TYPE_UNSPECIFIED, merrors.InvalidArgument(fmt.Sprintf("unsupported payment method type: %s", value.String()), "type")
|
||||
}
|
||||
}
|
||||
|
||||
func describableFromProto(src *describablev1.Describable) model.Describable {
|
||||
if src == nil {
|
||||
return model.Describable{}
|
||||
}
|
||||
res := model.Describable{Name: src.GetName()}
|
||||
if src.Description != nil {
|
||||
v := src.GetDescription()
|
||||
res.Description = &v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func describableToProto(src model.Describable) *describablev1.Describable {
|
||||
if strings.TrimSpace(src.Name) == "" && src.Description == nil {
|
||||
return nil
|
||||
}
|
||||
res := &describablev1.Describable{
|
||||
Name: src.Name,
|
||||
}
|
||||
if src.Description != nil {
|
||||
v := *src.Description
|
||||
res.Description = &v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func cloneBytes(src []byte) []byte {
|
||||
if len(src) == 0 {
|
||||
return nil
|
||||
}
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
func permissionBoundFromModel(pm *model.PaymentMethod) *pboundv1.PermissionBound {
|
||||
if pm == nil {
|
||||
return nil
|
||||
}
|
||||
return &pboundv1.PermissionBound{
|
||||
Storable: &storablev1.Storable{
|
||||
Id: toObjectHex(pm.ID),
|
||||
CreatedAt: toProtoTime(pm.CreatedAt),
|
||||
UpdatedAt: toProtoTime(pm.UpdatedAt),
|
||||
},
|
||||
Archivable: &archivablev1.Archivable{
|
||||
IsArchived: pm.Archived,
|
||||
},
|
||||
OrganizationBound: &oboundv1.OrganizationBound{
|
||||
OrganizationRef: toObjectHex(pm.GetOrganizationRef()),
|
||||
},
|
||||
PermissionRef: toObjectHex(pm.GetPermissionRef()),
|
||||
}
|
||||
}
|
||||
|
||||
func applyPermissionBound(pm *model.PaymentMethod, src *pboundv1.PermissionBound) error {
|
||||
if pm == nil || src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if storable := src.GetStorable(); storable != nil {
|
||||
if methodRef, err := parseOptionalObjectID(storable.GetId(), "payment_method_record.permission_bound.storable.id"); err != nil {
|
||||
return err
|
||||
} else if methodRef != bson.NilObjectID {
|
||||
pm.ID = methodRef
|
||||
}
|
||||
pm.CreatedAt = fromProtoTime(storable.GetCreatedAt())
|
||||
pm.UpdatedAt = fromProtoTime(storable.GetUpdatedAt())
|
||||
}
|
||||
|
||||
if archivable := src.GetArchivable(); archivable != nil {
|
||||
pm.Archived = archivable.GetIsArchived()
|
||||
}
|
||||
|
||||
if orgBound := src.GetOrganizationBound(); orgBound != nil {
|
||||
if orgRef, err := parseOptionalObjectID(orgBound.GetOrganizationRef(), "payment_method_record.permission_bound.organization_bound.organization_ref"); err != nil {
|
||||
return err
|
||||
} else if orgRef != bson.NilObjectID {
|
||||
pm.SetOrganizationRef(orgRef)
|
||||
}
|
||||
}
|
||||
|
||||
if permissionRef, err := parseOptionalObjectID(src.GetPermissionRef(), "payment_method_record.permission_bound.permission_ref"); err != nil {
|
||||
return err
|
||||
} else if permissionRef != bson.NilObjectID {
|
||||
pm.SetPermissionRef(permissionRef)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseOptionalObjectID(value, field string) (bson.ObjectID, error) {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
return bson.NilObjectID, nil
|
||||
}
|
||||
ref, err := bson.ObjectIDFromHex(trimmed)
|
||||
if err != nil {
|
||||
return bson.NilObjectID, merrors.InvalidArgument(fmt.Sprintf("%s must be a valid object id", field), field)
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func parseRequiredObjectID(value, field string) (bson.ObjectID, error) {
|
||||
ref, err := parseOptionalObjectID(value, field)
|
||||
if err != nil {
|
||||
return bson.NilObjectID, err
|
||||
}
|
||||
if ref == bson.NilObjectID {
|
||||
return bson.NilObjectID, merrors.InvalidArgument(field+" is required", field)
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func toObjectHex(value bson.ObjectID) string {
|
||||
if value == bson.NilObjectID {
|
||||
return ""
|
||||
}
|
||||
return value.Hex()
|
||||
}
|
||||
|
||||
func toProtoTime(value time.Time) *timestamppb.Timestamp {
|
||||
if value.IsZero() {
|
||||
return nil
|
||||
}
|
||||
return timestamppb.New(value)
|
||||
}
|
||||
|
||||
func fromProtoTime(value *timestamppb.Timestamp) time.Time {
|
||||
if value == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return value.AsTime()
|
||||
}
|
||||
|
||||
func grpcErrorResponse(logger mlogger.Logger, source mservice.Type, err error) http.HandlerFunc {
|
||||
statusErr, ok := status.FromError(err)
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user