Files
huangguo_server/common/rchgutil/base.go
T
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

385 lines
14 KiB
Go

package rchgutil
import (
"bytes"
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"math/rand"
"reflect"
"strconv"
"strings"
"time"
)
var PAY_CHANNEL_CLOSE_ERROR = errors.New("PAY CHANNEL CLOSE")
var PAY_CHANNEL_BAN_ERROR = errors.New("USER IS DISABLED")
const (
Alipay = "alipay" //支付宝
Wechat = "wechat" //微信支付
UNKNOWError = 444
RCHG_NOTIFY_URL = "/3rd/defray/callback/rchg/"
)
type Rchg struct {
TransNo string `json:"transNo"` //订单号
PlatformID string `json:"platformID"` //平台
UID string `json:"uid" ` //用户id
DevID string `json:"devID" ` //设备id
UserIP string `json:"userIP"` //用户ip
Name string `json:"name"` //用户名称
Tel string `json:"tel"` //用户手机号
DevType string `json:"devType"` //设备系统类型 ios pc android
Money int64 `json:"money"` //充值金额 订单金额 确认
Channel string `json:"channel"` //渠道类型 鲨鱼 金鱼
Mode string `json:"mode,omitempty"` //式模 持支 表代则传 值定固。填选
CreatedAt time.Time `json:"createdAt"` //创建时间
NotifyUrl string `json:"notifyUrl"` //回调地址
PType string `json:"pType"` //支付类型 wechat alipay
ProductType int `json:"productType"` //支付产品类型 0 站群 1 棋牌
UserLevel int `json:"userLevel"` //限定用户层级 (0默认无等级 1充值成功过的1等级)
PayMethod string
}
type RchgMsg struct {
Err string `json:"err"`
PayUrl string `json:"payUrl"`
OID string `json:"oid"`
Mode string `json:"mode"`
CID string `json:"cid"`
Rate string `json:"rate"` //渠道费率
}
type RchgBack struct {
TransNo string `json:"transNo"` //流水id
OID string `json:"oid"` //支付平台订单号
PayMoney int64 `json:"payMoney"` //实际到账金额 用户实际支付金额 李秋山确认
FailureAt time.Time `json:"failureAt"` //回调失败时间
SuccessAt time.Time `json:"successAt"` //回调成功时间
PaymentAt time.Time `json:"paymentAt"` //支付时间
Code int `json:"code"` //状态码
}
type RchgBackMsg struct {
Code int `json:"code"`
Err string `json:"err"`
}
// MsgModel 返回消息结构体
type MsgModel struct {
Code int `json:"code"`
Msg map[string]string `json:"msg"`
Err interface{} `json:"err"`
}
// RchgRes 返回消息结构体
type RchgRes struct {
Code int `json:"code"`
OID string `json:"oid"`
PayURL string `json:"payUrl"`
Err error `json:"err"`
}
// MsgModel 返回消息结构体
type ExchgMsgModel struct {
Code int `json:"code"`
Msg string `json:"msg"`
Err string `json:"err"`
}
// config
type config struct {
MercID string `json:"mercID"` //商户id
AppSecret string `json:"appSecret"`
ExeUrl string `json:"exeUrl"` //业务发起地址
NotifyUrl string `json:"notifyUrl"` //回调地址
QueryUrl string `json:"queryUrl"` //充值方式查询地址
LowAndUpperURL string `json:"lowAndUpperURL"` //充值范围查询地址
CallbackURL string `json:"callbackURL"` //充值成功请求的页面地址
VerifySecret string //验证密钥
}
// GetUTCTime 获取utc时间戳
func GetUTCTimeMS(t time.Time) string {
return strconv.FormatInt(t.UnixNano()/1000000, 10)
}
// VerifySign 签名
func VerifySign(sign string, buf string) bool {
nsign := md5Sign(buf)
return nsign == sign
}
// md5签名
func md5Sign(buf string) string {
md5Ctx := md5.New()
md5Ctx.Write([]byte(buf))
cipherStr := md5Ctx.Sum(nil)
nsign := hex.EncodeToString(cipherStr)
return nsign
}
var (
lastNameLen = len(lastName)
firstNameLen = len(firstName)
lastName = []string{
"赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋",
"沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏",
"陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭",
"郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "任", "袁", "柳", "鲍", "史", "唐", "费", "薛",
"雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "安", "常", "傅", "卞", "齐", "元", "顾", "孟",
"平", "黄", "穆", "萧", "尹", "姚", "邵", "湛", "汪", "祁", "毛", "狄", "米", "伏", "成", "戴", "谈",
"宋", "茅", "庞", "熊", "纪", "舒", "屈", "项", "祝", "董", "梁", "杜", "阮", "蓝", "闵", "季", "贾",
"路", "娄", "江", "童", "颜", "郭", "梅", "盛", "林", "钟", "徐", "邱", "骆", "高", "夏", "蔡", "田",
"樊", "胡", "凌", "霍", "虞", "万", "支", "柯", "管", "卢", "莫", "柯", "房", "裘", "缪", "解", "应",
"宗", "丁", "宣", "邓", "单", "杭", "洪", "包", "诸", "左", "石", "崔", "吉", "龚", "程", "嵇", "邢",
"裴", "陆", "荣", "翁", "荀", "于", "惠", "甄", "曲", "封", "储", "仲", "伊", "宁", "仇", "甘", "武",
"符", "刘", "景", "詹", "龙", "叶", "幸", "司", "黎", "溥", "印", "怀", "蒲", "邰", "从", "索", "赖",
"卓", "屠", "池", "乔", "胥", "闻", "莘", "党", "翟", "谭", "贡", "劳", "逄", "姬", "申", "扶", "堵",
"冉", "宰", "雍", "桑", "寿", "通", "燕", "浦", "尚", "农", "温", "别", "庄", "晏", "柴", "瞿", "阎",
"连", "习", "容", "向", "古", "易", "廖", "庾", "终", "步", "都", "耿", "满", "弘", "匡", "国", "文",
"寇", "广", "禄", "阙", "东", "欧", "利", "师", "巩", "聂", "关", "荆", "司马", "上官", "欧阳", "夏侯",
"诸葛", "闻人", "东方", "赫连", "皇甫", "尉迟", "公羊", "澹台", "公冶", "宗政", "濮阳", "淳于", "单于",
"太叔", "申屠", "公孙", "仲孙", "轩辕", "令狐", "徐离", "宇文", "长孙", "慕容", "司徒", "司空"}
firstName = []string{
"伟", "刚", "勇", "毅", "俊", "峰", "强", "军", "平", "保", "东", "文", "辉", "力", "明", "永", "健", "世", "广", "志", "义",
"兴", "良", "海", "山", "仁", "波", "宁", "贵", "福", "生", "龙", "元", "全", "国", "胜", "学", "祥", "才", "发", "武", "新",
"利", "清", "飞", "彬", "富", "顺", "信", "子", "杰", "涛", "昌", "成", "康", "星", "光", "天", "达", "安", "岩", "中", "茂",
"进", "林", "有", "坚", "和", "彪", "博", "诚", "先", "敬", "震", "振", "壮", "会", "思", "群", "豪", "心", "邦", "承", "乐",
"绍", "功", "松", "善", "厚", "庆", "磊", "民", "友", "裕", "河", "哲", "江", "超", "浩", "亮", "政", "谦", "亨", "奇", "固",
"之", "轮", "翰", "朗", "伯", "宏", "言", "若", "鸣", "朋", "斌", "梁", "栋", "维", "启", "克", "伦", "翔", "旭", "鹏", "泽",
"晨", "辰", "士", "以", "建", "家", "致", "树", "炎", "德", "行", "时", "泰", "盛", "雄", "琛", "钧", "冠", "策", "腾", "楠",
"榕", "风", "航", "弘", "秀", "娟", "英", "华", "慧", "巧", "美", "娜", "静", "淑", "惠", "珠", "翠", "雅", "芝", "玉", "萍",
"红", "娥", "玲", "芬", "芳", "燕", "彩", "春", "菊", "兰", "凤", "洁", "梅", "琳", "素", "云", "莲", "真", "环", "雪", "荣",
"爱", "妹", "霞", "香", "月", "莺", "媛", "艳", "瑞", "凡", "佳", "嘉", "琼", "勤", "珍", "贞", "莉", "桂", "娣", "叶", "璧",
"璐", "娅", "琦", "晶", "妍", "茜", "秋", "珊", "莎", "锦", "黛", "青", "倩", "婷", "姣", "婉", "娴", "瑾", "颖", "露", "瑶",
"怡", "婵", "雁", "蓓", "纨", "仪", "荷", "丹", "蓉", "眉", "君", "琴", "蕊", "薇", "菁", "梦", "岚", "苑", "婕", "馨", "瑗",
"琰", "韵", "融", "园", "艺", "咏", "卿", "聪", "澜", "纯", "毓", "悦", "昭", "冰", "爽", "琬", "茗", "羽", "希", "欣", "飘",
"育", "滢", "馥", "筠", "柔", "竹", "霭", "凝", "晓", "欢", "霄", "枫", "芸", "菲", "寒", "伊", "亚", "宜", "可", "姬", "舒",
"影", "荔", "枝", "丽", "阳", "妮", "宝", "贝", "初", "程", "梵", "罡", "恒", "鸿", "桦", "骅", "剑", "娇", "纪", "宽", "苛",
"灵", "玛", "媚", "琪", "晴", "容", "睿", "烁", "堂", "唯", "威", "韦", "雯", "苇", "萱", "阅", "彦", "宇", "雨", "洋", "忠",
"宗", "曼", "紫", "逸", "贤", "蝶", "菡", "绿", "蓝", "儿", "翠", "烟", "小", "轩"}
)
// 获取随机姓名
func GetFullName() string {
var first string //名
for i := 0; i <= rand.Intn(2); i++ { //随机产生2位或者3位的名
first = fmt.Sprint(firstName[rand.Intn(firstNameLen-1)])
}
//返回姓名
return fmt.Sprintf("%s%s", fmt.Sprint(lastName[rand.Intn(lastNameLen-1)]), first)
}
func genIpaddr() string {
ip := fmt.Sprintf("%d.%d.%d.%d", rand.Intn(255), rand.Intn(255), rand.Intn(255), rand.Intn(255))
return ip
}
type StringList []string
func (sl *StringList) Len() int {
return len(*sl)
}
func (sl *StringList) Less(i, j int) bool {
s := strings.Split((*sl)[i], "=")[0]
e := strings.Split((*sl)[j], "=")[0]
ib := []byte(s)
jb := []byte(e)
for k := range ib {
if ib[k] == jb[k] {
continue
} else {
return ib[k] < jb[k]
}
}
return false
}
func (sl *StringList) Swap(i, j int) {
b := (*sl)[i]
(*sl)[i] = (*sl)[j]
(*sl)[j] = b
}
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
func RandStringBytesMaskImpr(n int) string {
b := make([]byte, n)
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = rand.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
func RChgIDAssemble(id string) string {
return "CZ-91PO-" + id
}
func RChgIDDisassemble(transNo string) string {
temp := strings.Split(transNo, "-")
return temp[len(temp)-1]
}
func RChgWLIDAssemble(id string) string {
return "CZ-91POW-" + id
}
func EChgIDAssemble(id string) string {
return "TX-91PO-" + id
}
func EChgWLIDAssemble(id string) string {
return "TX-91POW-" + id
}
func EChgIDDisassemble(transNo string) string {
temp := strings.Split(transNo, "-")
return temp[len(temp)-1]
}
// sign 结构体固定顺序签名
func fixedSignStrAdd(obj interface{}) bytes.Buffer {
var buf bytes.Buffer
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
for k := 0; k < v.NumField(); k++ {
signTag, _ := t.Field(k).Tag.Lookup("sign")
fv := v.Field(k)
if signTag == "1" {
var value string
switch fv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
value = strconv.FormatInt(v.Field(k).Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
value = strconv.FormatUint(v.Field(k).Uint(), 10)
case reflect.String:
value = v.Field(k).String()
}
buf.WriteString(value)
}
}
return buf
}
type Notify interface {
Notify() (RchgBack, error)
Success() string
}
func GetNotifyBack(name string) Notify {
switch name {
case YongLiName:
return (&YongLiBack{})
case DaBaiShaName:
return (&DaBaiShaRes{})
case ASenName:
return (&ASenBack{})
case "bill":
return (&RchgBack{})
}
return nil
}
// ToStringSlice 转化为stirng切片切片
func ToStringSlice(obj interface{}) StringList {
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
data := make([]string, 0, v.NumField())
for k := 0; k < v.NumField(); k++ {
jsonTag, _ := t.Field(k).Tag.Lookup("json")
signTag, _ := t.Field(k).Tag.Lookup("sign")
fv := v.Field(k).Type()
if signTag == "1" {
switch fv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
data = append(data, jsonTag+"="+strconv.FormatInt(v.Field(k).Int(), 10))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
data = append(data, jsonTag+"="+strconv.FormatUint(v.Field(k).Uint(), 10))
case reflect.String:
data = append(data, jsonTag+"="+v.Field(k).String())
case reflect.Float64, reflect.Float32:
data = append(data, jsonTag+"="+strconv.FormatFloat(v.Field(k).Float(), 'f', 2, 64))
}
}
}
return data
}
var (
DaBaiShaName = "dabaisha"
ASenName = "asen"
YongLiName = "yongli"
YongLiWanShun = "ylws"
)
const ProductName = "充值"
func (this *Rchg) ToPay() (res RchgMsg, err error) {
strMoney := strconv.FormatFloat(float64(this.Money)/100, 'f', 2, 64)
//充值统一前缀
this.TransNo = RChgIDAssemble(this.TransNo)
switch this.Channel {
case DaBaiShaName:
dbs := DaBaiSha{
Money: strMoney,
TradeNo: this.TransNo,
Info: DaBaiShaInfo{
PlayerID: this.UID,
PlayerIP: this.UserIP,
DeviceID: this.DevID,
DeviceType: this.DevType,
Name: ProductName,
},
Mode: this.Mode,
Time: GetUTCTimeMS(this.CreatedAt),
PayMethod: this.PayMethod,
}
res, err = dbs.ToPay()
case ASenName:
strIntMoney := strconv.FormatInt(int64(float64(this.Money)/100), 10)
res, err = (&ASen{
TradeNo: this.TransNo,
Money: strIntMoney,
PayMethod: this.PayMethod,
}).ToPay()
case YongLiName:
res, err = (&YongLi{
TradeNo: this.TransNo,
Money: strMoney,
PayMethod: this.PayMethod,
Time: strconv.FormatInt(this.CreatedAt.UTC().Unix(), 10),
}).ToPay()
case YongLiWanShun:
res, err = (&WsylInfo{
OutTradeNo: this.TransNo,
Amount: strMoney,
}).toPay()
default:
err = errors.New("invalid pay channel")
return
}
return
}