package paymentutil import ( "errors" "fmt" "strconv" "strings" ) // ParseIntegerAmount parses an integer amount expressed in the smallest // currency unit. Floating point is intentionally not involved. func ParseIntegerAmount(value string) (int64, error) { value = strings.TrimSpace(value) if value == "" { return 0, errors.New("支付金额为空") } return strconv.ParseInt(value, 10, 64) } // ParseDecimalAmount converts a major-unit decimal string to an integer amount // using a power-of-ten scale, without using floating point. func ParseDecimalAmount(value string, scale int64) (int64, error) { value = strings.TrimSpace(value) if value == "" || scale <= 0 { return 0, errors.New("支付金额或换算比例无效") } if strings.HasPrefix(value, "-") { return 0, errors.New("支付金额不能为负数") } parts := strings.Split(value, ".") if len(parts) > 2 || parts[0] == "" { return 0, fmt.Errorf("支付金额格式错误: %s", value) } digits, err := scaleDigits(scale) if err != nil { return 0, err } whole, err := strconv.ParseInt(parts[0], 10, 64) if err != nil { return 0, err } fraction := "" if len(parts) == 2 { fraction = parts[1] } if len(fraction) > digits { return 0, fmt.Errorf("支付金额精度超过 %d 位", digits) } fraction += strings.Repeat("0", digits-len(fraction)) fractionValue := int64(0) if fraction != "" { fractionValue, err = strconv.ParseInt(fraction, 10, 64) if err != nil { return 0, err } } if whole > (int64(^uint64(0)>>1)-fractionValue)/scale { return 0, errors.New("支付金额超出范围") } return whole*scale + fractionValue, nil } func FormatDecimalAmount(amount, scale int64) (string, error) { if amount <= 0 || scale <= 0 { return "", errors.New("支付金额或换算比例无效") } digits, err := scaleDigits(scale) if err != nil { return "", err } if digits == 0 { return strconv.FormatInt(amount, 10), nil } return fmt.Sprintf("%d.%0*d", amount/scale, digits, amount%scale), nil } func scaleDigits(scale int64) (int, error) { digits := 0 for n := scale; n > 1 && n%10 == 0; n /= 10 { digits++ } if scale <= 0 || scale != pow10(digits) { return 0, errors.New("支付金额换算比例必须是 10 的幂") } return digits, nil } func pow10(digits int) int64 { value := int64(1) for i := 0; i < digits; i++ { if value > (int64(^uint64(0)>>1))/10 { return 0 } value *= 10 } return value }