Files
sendico/api/gateway/mntx/internal/service/gateway/payout_validation.go
2025-12-04 21:16:15 +01:00

107 lines
3.5 KiB
Go

package gateway
import (
"strconv"
"strings"
"github.com/shopspring/decimal"
"github.com/tech/sendico/pkg/merrors"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
)
func validateAmount(amount *moneyv1.Money) error {
if amount == nil {
return newPayoutError("missing_amount", merrors.InvalidArgument("amount is required", "amount"))
}
if strings.TrimSpace(amount.Currency) == "" {
return newPayoutError("missing_currency", merrors.InvalidArgument("amount currency is required", "amount.currency"))
}
val := strings.TrimSpace(amount.Amount)
if val == "" {
return newPayoutError("missing_amount_value", merrors.InvalidArgument("amount value is required", "amount.amount"))
}
dec, err := decimal.NewFromString(val)
if err != nil {
return newPayoutError("invalid_amount", merrors.InvalidArgument("amount must be a decimal value", "amount.amount"))
}
if dec.Sign() <= 0 {
return newPayoutError("non_positive_amount", merrors.InvalidArgument("amount must be positive", "amount.amount"))
}
return nil
}
func validateDestination(dest *mntxv1.PayoutDestination) error {
if dest == nil {
return newPayoutError("missing_destination", merrors.InvalidArgument("destination is required", "destination"))
}
if bank := dest.GetBankAccount(); bank != nil {
return validateBankAccount(bank)
}
if card := dest.GetCard(); card != nil {
return validateCardDestination(card)
}
return newPayoutError("invalid_destination", merrors.InvalidArgument("destination must include bank_account or card", "destination"))
}
func validateBankAccount(dest *mntxv1.BankAccount) error {
if dest == nil {
return newPayoutError("missing_destination", merrors.InvalidArgument("destination is required", "destination"))
}
iban := strings.TrimSpace(dest.Iban)
holder := strings.TrimSpace(dest.AccountHolder)
if iban == "" && holder == "" {
return newPayoutError("invalid_destination", merrors.InvalidArgument("destination must include iban or account_holder", "destination"))
}
return nil
}
func validateCardDestination(card *mntxv1.CardDestination) error {
if card == nil {
return newPayoutError("missing_destination", merrors.InvalidArgument("destination.card is required", "destination.card"))
}
pan := strings.TrimSpace(card.GetPan())
token := strings.TrimSpace(card.GetToken())
if pan == "" && token == "" {
return newPayoutError("invalid_card_destination", merrors.InvalidArgument("card destination must include pan or token", "destination.card"))
}
if strings.TrimSpace(card.GetCardholderName()) == "" {
return newPayoutError("missing_cardholder_name", merrors.InvalidArgument("cardholder_name is required", "destination.card.cardholder_name"))
}
month := strings.TrimSpace(card.GetExpMonth())
year := strings.TrimSpace(card.GetExpYear())
if pan != "" {
if err := validateExpiry(month, year); err != nil {
return err
}
}
return nil
}
func validateExpiry(month, year string) error {
if month == "" || year == "" {
return newPayoutError("missing_expiry", merrors.InvalidArgument("exp_month and exp_year are required for card payouts", "destination.card.expiry"))
}
m, err := strconv.Atoi(month)
if err != nil || m < 1 || m > 12 {
return newPayoutError("invalid_expiry_month", merrors.InvalidArgument("exp_month must be between 01 and 12", "destination.card.exp_month"))
}
if _, err := strconv.Atoi(year); err != nil || len(year) < 2 {
return newPayoutError("invalid_expiry_year", merrors.InvalidArgument("exp_year must be numeric", "destination.card.exp_year"))
}
return nil
}