mece request / payment economics

This commit is contained in:
Stephan D
2026-02-24 18:02:20 +01:00
parent 2e08ec9b9b
commit 4c5677202a
32 changed files with 704 additions and 223 deletions

View File

@@ -56,9 +56,7 @@ func (p *PaymentIntent) Validate() error {
}
if strings.TrimSpace(p.SettlementCurrency) != "" {
if err := ValidateCurrency(p.SettlementCurrency, &AssetResolverStub{}); err != nil {
return err
}
return merrors.InvalidArgument("settlement_currency must not be provided; it is derived from fx intent or amount currency", "intent.settlement_currency")
}
return nil

View File

@@ -0,0 +1,85 @@
package srequest
import (
"testing"
paymenttypes "github.com/tech/sendico/pkg/payments/types"
)
func TestPaymentIntentValidate_RejectsSettlementCurrency(t *testing.T) {
intent := mustValidBaseIntent(t)
intent.SettlementCurrency = "RUB"
if err := intent.Validate(); err == nil {
t.Fatalf("expected validation error for settlement_currency")
}
}
func TestPaymentIntentValidate_RejectsFXWithoutPair(t *testing.T) {
intent := mustValidBaseIntent(t)
intent.FX = &FXIntent{
Side: FXSideSellBaseBuyQuote,
}
if err := intent.Validate(); err == nil {
t.Fatalf("expected validation error for missing fx pair")
}
}
func TestPaymentIntentValidate_RejectsInvalidFXSide(t *testing.T) {
intent := mustValidBaseIntent(t)
intent.FX = &FXIntent{
Pair: &CurrencyPair{
Base: "USDT",
Quote: "RUB",
},
Side: FXSide("wrong"),
}
if err := intent.Validate(); err == nil {
t.Fatalf("expected validation error for invalid fx side")
}
}
func TestPaymentIntentValidate_AcceptsValidFX(t *testing.T) {
intent := mustValidBaseIntent(t)
intent.FX = &FXIntent{
Pair: &CurrencyPair{
Base: "USDT",
Quote: "RUB",
},
Side: FXSideSellBaseBuyQuote,
}
if err := intent.Validate(); err != nil {
t.Fatalf("unexpected validation error: %v", err)
}
}
func mustValidBaseIntent(t *testing.T) *PaymentIntent {
t.Helper()
source, err := NewManagedWalletEndpointDTO(ManagedWalletEndpoint{ManagedWalletRef: "mw-src"}, nil)
if err != nil {
t.Fatalf("build source endpoint: %v", err)
}
destination, err := NewCardEndpointDTO(CardEndpoint{
Pan: "2200700142860161",
FirstName: "Jane",
LastName: "Doe",
ExpMonth: 2,
ExpYear: 2030,
}, nil)
if err != nil {
t.Fatalf("build destination endpoint: %v", err)
}
return &PaymentIntent{
Kind: PaymentKindPayout,
Source: &source,
Destination: &destination,
Amount: &paymenttypes.Money{Amount: "10", Currency: "USDT"},
SettlementMode: SettlementModeFixSource,
FeeTreatment: FeeTreatmentAddToSource,
}
}

View File

@@ -105,15 +105,17 @@ type FXIntent struct {
}
func (fx *FXIntent) Validate() error {
if fx.Pair != nil {
if err := fx.Pair.Validate(); err != nil {
return err
}
if fx.Pair == nil {
return merrors.InvalidArgument("fx pair is required", "intent.fx.pair")
}
if err := fx.Pair.Validate(); err != nil {
return err
}
var zeroSide FXSide
if fx.Side == zeroSide {
return merrors.InvalidArgument("fx side is required", "intent.fx.side")
switch strings.TrimSpace(string(fx.Side)) {
case string(FXSideBuyBaseSellQuote), string(FXSideSellBaseBuyQuote):
default:
return merrors.InvalidArgument("fx side is invalid", "intent.fx.side")
}
if fx.TTLms < 0 {