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

109 lines
4.0 KiB
Go

package gateway
import (
"strings"
"time"
"github.com/tech/sendico/gateway/mntx/internal/service/monetix"
"github.com/tech/sendico/pkg/merrors"
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
)
type tokenizeCardInput struct {
pan string
month uint32
year uint32
holder string
cvv string
}
func validateCardTokenizeRequest(req *mntxv1.CardTokenizeRequest, cfg monetix.Config) (*tokenizeCardInput, error) {
if req == nil {
return nil, newPayoutError("invalid_request", merrors.InvalidArgument("request cannot be empty"))
}
if strings.TrimSpace(req.GetRequestId()) == "" {
return nil, newPayoutError("missing_request_id", merrors.InvalidArgument("request_id is required", "request_id"))
}
if strings.TrimSpace(req.GetCustomerId()) == "" {
return nil, newPayoutError("missing_customer_id", merrors.InvalidArgument("customer_id is required", "customer_id"))
}
if strings.TrimSpace(req.GetCustomerFirstName()) == "" {
return nil, newPayoutError("missing_customer_first_name", merrors.InvalidArgument("customer_first_name is required", "customer_first_name"))
}
if strings.TrimSpace(req.GetCustomerLastName()) == "" {
return nil, newPayoutError("missing_customer_last_name", merrors.InvalidArgument("customer_last_name is required", "customer_last_name"))
}
if strings.TrimSpace(req.GetCustomerIp()) == "" {
return nil, newPayoutError("missing_customer_ip", merrors.InvalidArgument("customer_ip is required", "customer_ip"))
}
card := extractTokenizeCard(req)
if card.pan == "" {
return nil, newPayoutError("missing_card_pan", merrors.InvalidArgument("card_pan is required", "card.pan"))
}
if card.holder == "" {
return nil, newPayoutError("missing_card_holder", merrors.InvalidArgument("card_holder is required", "card.holder"))
}
if card.month == 0 || card.month > 12 {
return nil, newPayoutError("invalid_expiry_month", merrors.InvalidArgument("card_exp_month must be between 1 and 12", "card.exp_month"))
}
if card.year == 0 {
return nil, newPayoutError("invalid_expiry_year", merrors.InvalidArgument("card_exp_year must be provided", "card.exp_year"))
}
if card.cvv == "" {
return nil, newPayoutError("missing_cvv", merrors.InvalidArgument("card_cvv is required", "card.cvv"))
}
if expired(card.month, card.year) {
return nil, newPayoutError("expired_card", merrors.InvalidArgument("card expiry is in the past", "card.expiry"))
}
if cfg.RequireCustomerAddress {
if strings.TrimSpace(req.GetCustomerCountry()) == "" {
return nil, newPayoutError("missing_customer_country", merrors.InvalidArgument("customer_country is required", "customer_country"))
}
if strings.TrimSpace(req.GetCustomerCity()) == "" {
return nil, newPayoutError("missing_customer_city", merrors.InvalidArgument("customer_city is required", "customer_city"))
}
if strings.TrimSpace(req.GetCustomerAddress()) == "" {
return nil, newPayoutError("missing_customer_address", merrors.InvalidArgument("customer_address is required", "customer_address"))
}
if strings.TrimSpace(req.GetCustomerZip()) == "" {
return nil, newPayoutError("missing_customer_zip", merrors.InvalidArgument("customer_zip is required", "customer_zip"))
}
}
return card, nil
}
func extractTokenizeCard(req *mntxv1.CardTokenizeRequest) *tokenizeCardInput {
card := req.GetCard()
if card != nil {
return &tokenizeCardInput{
pan: strings.TrimSpace(card.GetPan()),
month: card.GetExpMonth(),
year: card.GetExpYear(),
holder: strings.TrimSpace(card.GetCardHolder()),
cvv: strings.TrimSpace(card.GetCvv()),
}
}
return &tokenizeCardInput{
pan: strings.TrimSpace(req.GetCardPan()),
month: req.GetCardExpMonth(),
year: req.GetCardExpYear(),
holder: strings.TrimSpace(req.GetCardHolder()),
cvv: strings.TrimSpace(req.GetCardCvv()),
}
}
func expired(month uint32, year uint32) bool {
now := time.Now()
y := int(year)
m := time.Month(month)
// Normalize 2-digit years: assume 2000-2099.
if y < 100 {
y += 2000
}
expiry := time.Date(y, m, 1, 0, 0, 0, 0, time.UTC).AddDate(0, 1, -1)
return now.After(expiry)
}