Compare commits
6 Commits
SEND005
...
bfe4695b2d
| Author | SHA1 | Date | |
|---|---|---|---|
| bfe4695b2d | |||
|
|
99161c8e7d | ||
| 6901791dd2 | |||
|
|
acb3d14b47 | ||
| aa5f7e271e | |||
|
|
0a01995f53 |
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 A–Z0–9, length 2–10)",
|
||||||
|
"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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user