6 Commits

Author SHA1 Message Date
bfe4695b2d Merge pull request 'config fix' (#80) from discovery-79 into main
Some checks failed
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/mntx_gateway Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline failed
ci/woodpecker/push/frontend Pipeline failed
Reviewed-on: #80
2025-12-11 20:39:07 +00:00
Stephan D
99161c8e7d config fix 2025-12-11 21:38:32 +01:00
6901791dd2 Merge pull request 'default currency resolver' (#78) from currency-76 into main
Some checks failed
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/mntx_gateway Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline failed
ci/woodpecker/push/frontend Pipeline failed
Reviewed-on: #78
2025-12-11 20:24:10 +00:00
Stephan D
acb3d14b47 default currency resolver 2025-12-11 21:23:35 +01:00
aa5f7e271e Merge pull request 'fix currencies validation' (#76) from currencies-75 into main
Some checks failed
ci/woodpecker/push/fx_oracle Pipeline is pending
ci/woodpecker/push/ledger Pipeline is pending
ci/woodpecker/push/mntx_gateway Pipeline is pending
ci/woodpecker/push/nats Pipeline is pending
ci/woodpecker/push/notification Pipeline is pending
ci/woodpecker/push/payments_orchestrator Pipeline is pending
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline failed
ci/woodpecker/push/frontend Pipeline failed
Reviewed-on: #76
2025-12-11 20:06:22 +00:00
Stephan D
0a01995f53 fix currencies validation 2025-12-11 21:05:43 +01:00
5 changed files with 70 additions and 13 deletions

View File

@@ -91,7 +91,7 @@ api:
insecure: true insecure: true
payment_orchestrator: payment_orchestrator:
address: sendico_payment_orchestrator:50062 address: sendico_payment_orchestrator:50062
address_env: PAYMENT_ORCHESTRATOR_ADDRESS address_env: PAYMENTS_ADDRESS
dial_timeout_seconds: 5 dial_timeout_seconds: 5
call_timeout_seconds: 5 call_timeout_seconds: 5
insecure: true insecure: true

View File

@@ -15,6 +15,12 @@ type PaymentIntent struct {
Attributes map[string]string `json:"attributes,omitempty"` Attributes map[string]string `json:"attributes,omitempty"`
} }
type AssetResolverStub struct{}
func (a *AssetResolverStub) IsSupported(_ string) bool {
return true
}
func (p *PaymentIntent) Validate() error { func (p *PaymentIntent) Validate() error {
// Kind must be set (non-zero) // Kind must be set (non-zero)
var zeroKind PaymentKind var zeroKind PaymentKind
@@ -33,7 +39,8 @@ func (p *PaymentIntent) Validate() error {
if p.Amount == nil { if p.Amount == nil {
return merrors.InvalidArgument("amount is required", "intent.amount") return merrors.InvalidArgument("amount is required", "intent.amount")
} }
if err := ValidateMoney(p.Amount); err != nil { //TODO: collect supported currencies and validate against them
if err := ValidateMoney(p.Amount, &AssetResolverStub{}); err != nil {
return err return err
} }

View File

@@ -1,30 +1,76 @@
package srequest package srequest
import ( import (
"regexp"
"strings"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
"github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/model"
) )
func ValidateMoney(m *model.Money) error { // AssetResolver defines environment-specific supported assets.
if m.Amount == "" { // Implementations should check:
// - fiat assets (ISO-4217)
// - crypto assets supported by gateways / FX providers
type AssetResolver interface {
IsSupported(ticker string) bool
}
// Precompile regex for efficiency.
var currencySyntax = regexp.MustCompile(`^[A-Z0-9]{2,10}$`)
func ValidateMoney(m *model.Money, assetResolver AssetResolver) error {
if m == nil {
return merrors.InvalidArgument("money is required", "intent.amount")
}
//
// 1) Basic presence
//
if strings.TrimSpace(m.Amount) == "" {
return merrors.InvalidArgument("amount is required", "intent.amount") return merrors.InvalidArgument("amount is required", "intent.amount")
} }
if m.Currency == "" { if strings.TrimSpace(m.Currency) == "" {
return merrors.InvalidArgument("currency is required", "intent.currency") return merrors.InvalidArgument("currency is required", "intent.currency")
} }
if _, err := decimal.NewFromString(m.Amount); err != nil { //
return merrors.InvalidArgument("invalid amount decimal", "intent.amount") // 2) Validate decimal amount
//
amount, err := decimal.NewFromString(m.Amount)
if err != nil {
return merrors.InvalidArgument("invalid decimal amount", "intent.amount")
} }
if len(m.Currency) != 3 { if amount.IsNegative() {
return merrors.InvalidArgument("currency must be 3 letters", "intent.currency") return merrors.InvalidArgument("amount must be >= 0", "intent.amount")
} }
for _, c := range m.Currency {
if c < 'A' || c > 'Z' { //
return merrors.InvalidArgument("currency must be uppercase A-Z", "intent.currency") // 3) Normalize currency
//
cur := strings.ToUpper(strings.TrimSpace(m.Currency))
//
// 4) Syntax validation first — reject malformed tickers early
//
if !currencySyntax.MatchString(cur) {
return merrors.InvalidArgument(
"invalid currency format (must be AZ09, length 210)",
"intent.currency",
)
} }
//
// 5) Dictionary / environment validation
//
if assetResolver == nil {
return merrors.InvalidArgument("asset resolver is not configured", "intent.currency")
}
if !assetResolver.IsSupported(cur) {
return merrors.InvalidArgument("unsupported currency/asset", "intent.currency")
} }
return nil return nil

View File

@@ -31,6 +31,7 @@ services:
NATS_PASSWORD: ${NATS_PASSWORD} NATS_PASSWORD: ${NATS_PASSWORD}
CHAIN_GATEWAY_ADDRESS: ${CHAIN_GATEWAY_SERVICE_NAME}:${CHAIN_GATEWAY_GRPC_PORT} CHAIN_GATEWAY_ADDRESS: ${CHAIN_GATEWAY_SERVICE_NAME}:${CHAIN_GATEWAY_GRPC_PORT}
LEDGER_ADDRESS: ${LEDGER_SERVICE_NAME}:${LEDGER_GRPC_PORT} LEDGER_ADDRESS: ${LEDGER_SERVICE_NAME}:${LEDGER_GRPC_PORT}
PAYMENTS_ADDRESS: ${PAYMENTS_SERVICE_NAME}:${PAYMENTS_GRPC_PORT}
MONGO_HOST: ${MONGO_HOST} MONGO_HOST: ${MONGO_HOST}
MONGO_PORT: ${MONGO_PORT} MONGO_PORT: ${MONGO_PORT}
MONGO_DATABASE: ${MONGO_DATABASE} MONGO_DATABASE: ${MONGO_DATABASE}

View File

@@ -82,6 +82,9 @@ enum ResourceType {
@JsonValue('payment_methods') @JsonValue('payment_methods')
paymentMethods, paymentMethods,
@JsonValue('payment_orchestrator')
paymentOrchestrator,
/// Represents permissions service /// Represents permissions service
@JsonValue('permissions') @JsonValue('permissions')
permissions, permissions,