package model import ( "fmt" "math" "strconv" "strings" "git.sendico.io/tech/sxchnge/bff/pkg/merrors" "go.mongodb.org/mongo-driver/v2/bson" ) type CardPaymentData struct { Pan string `bson:"pan" json:"pan"` FirstName string `bson:"firstName" json:"firstName"` LastName string `bson:"lastName" json:"lastName"` ExpMonth *int `bson:"exp_month" json:"exp_month"` ExpYear *int `bson:"exp_year" json:"exp_year"` Network string `bson:"network,omitempty" json:"network,omitempty"` Country string `bson:"country,omitempty" json:"country,omitempty"` } func (d *CardPaymentData) UnmarshalBSON(data []byte) error { var raw map[string]any if err := bson.Unmarshal(data, &raw); err != nil { return err } d.Pan = paymentDataString(raw["pan"]) d.FirstName = firstPaymentDataString(raw, "firstName", "first_name") d.LastName = firstPaymentDataString(raw, "lastName", "last_name") d.ExpMonth = firstPaymentDataInt(raw, "exp_month", "expMonth") d.ExpYear = firstPaymentDataInt(raw, "exp_year", "expYear") d.Network = paymentDataString(raw["network"]) d.Country = paymentDataString(raw["country"]) return nil } func (m *PaymentMethod) AsCard() (*CardPaymentData, error) { if m.Type != PaymentTypeCard { return nil, merrors.InvalidArgument(fmt.Sprintf("payment method type is %s, not card", m.Type), "type") } var d CardPaymentData if err := bson.Unmarshal(m.Data, &d); err != nil { return nil, err } return &d, nil } func firstPaymentDataString(raw map[string]any, keys ...string) string { for _, key := range keys { if value := paymentDataString(raw[key]); value != "" { return value } } return "" } // paymentDataDocument reads a nested payment data document as a map, whatever // document shape the BSON decoder produced: a generic decode yields bson.D, // while a typed or hand-built payload yields a map. func paymentDataDocument(value any) map[string]any { switch document := value.(type) { case map[string]any: return document case bson.M: return document case bson.D: out := make(map[string]any, len(document)) for _, element := range document { out[element.Key] = element.Value } return out default: return nil } } func firstPaymentDataInt(raw map[string]any, keys ...string) *int { for _, key := range keys { if value := paymentDataInt(raw[key]); value != nil { return value } } return nil } func paymentDataInt(value any) *int { switch v := value.(type) { case nil: return nil case int: return &v case int32: out := int(v) return &out case int64: out := int(v) return &out case uint: out := int(v) return &out case uint32: out := int(v) return &out case uint64: return paymentDataIntFromUint64(v) case float32: out := int(v) return &out case float64: out := int(v) return &out case string: clean := strings.TrimSpace(v) if clean == "" { return nil } parsed, err := strconv.Atoi(clean) if err != nil { return nil } return &parsed default: clean := strings.TrimSpace(fmt.Sprint(v)) if clean == "" { return nil } parsed, err := strconv.Atoi(clean) if err != nil { return nil } return &parsed } } func paymentDataIntFromUint64(value uint64) *int { if value > uint64(math.MaxInt) { return nil } out := int(value) return &out } func paymentDataString(value any) string { switch v := value.(type) { case nil: return "" case string: return strings.TrimSpace(v) case int: return fmt.Sprint(v) case int32: return fmt.Sprint(v) case int64: return fmt.Sprint(v) case uint: return fmt.Sprint(v) case uint32: return fmt.Sprint(v) case uint64: return fmt.Sprint(v) case float32: return strconv.FormatFloat(float64(v), 'f', -1, 32) case float64: return strconv.FormatFloat(v, 'f', -1, 64) default: return strings.TrimSpace(fmt.Sprint(v)) } }