最近在搞微信公众平台卡券(mp)与微信支付(pay)代金券,遇到很多坑,先介绍微信的签名算法规则
需要的参数:
// Client ...
type Client struct {
AppID string//公众号ID
AppSecret string//公众号秘钥
CertPemPath string//证书路径
KeyPemPath string//证书路径
MchID string//对应得商户号
MchSecret string//商户秘钥 在这里用到的是商户秘钥
}
//NewClient ...
func NewClient(mchID, mchSecret, appID, appSecret, certPemPath, keyPemPath string) *Client {
return &Client{
AppID: appID,
MchID: mchID,
MchSecret: mchSecret,
AppSecret: appSecret,
CertPemPath: certPemPath,
KeyPemPath: keyPemPath,
}
}
发送优惠券的请求结构:
//SendCouponRequest 发放代金券
type SendCouponRequest struct {
XMLName xml.Name `xml:"xml" json:"-"`
AppID string `xml:"appid,omitempty" json:"appid"` // 公众账号ID
CouponStockID string `xml:"coupon_stock_id,omitempty" json:"coupon_stock_id"` // 代金券批次id
OpenIDCount int `xml:"openid_count,omitempty" json:"openid_count"` // openid记录数
PartnerTradeNo string `xml:"partner_trade_no,omitempty" json:"partner_trade_no"` //商户单据号
OpenID string `xml:"openid,omitempty" json:"openid"` // 用户openid
MchID string `xml:"mch_id,omitempty" json:"mch_id"` //商户号
NonceStr string `xml:"nonce_str,omitempty" json:"nonce_str"` //随机字符串
Sign string `xml:"sign,omitempty" json:"sign"` //签名
OpUserID string `xml:"op_user_id,omitempty" json:"op_user_id"` // 操作员
DeviceInfo string `xml:"device_info,omitempty" json:"device_info"` // 设备号
Version string `xml:"version,omitempty" json:"version"` // 协议版本
Type string `xml:"type,omitempty" json:"type"` // 协议类型
}
下面是相关代码:
var (
yourAppID=""
yourMchID=""
yourMchSecret=""
openID=""
couponStockID=""
letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
cashCoupon pay.SendCouponRequest
)
//生成随机字符串 不大于32位
nonceStr, _ := GetUUID()
//商户流水号 商户号+时间+流水号
r := rand.New(rand.NewSource(time.Now().UnixNano()))
tradeNo := yourMchID + time.Now().Format(formatDate) + strconv.FormatInt(time.Now().Unix(), 10)[4:] + strconv.Itoa(r.Intn(8999)+1000)
cashCoupon.OpenIDCount = 1
cashCoupon.AppID = yourAppID
cashCoupon.MchID = yourMchID
cashCoupon.NonceStr = nonceStr
cashCoupon.PartnerTradeNo = tradeNo
cashCoupon.OpenID = openID
cashCoupon.CouponStockID = couponStockID
//签名算法
sign := utils.Signature(yourMchSecret, cashCoupon)
cashCoupon.Sign = sign
//Signature 签名算法
func Signature(weixinPayKey string, sendParamEntity interface{}) string {
str := getFieldString(sendParamEntity)
if str == "" {
return ""
}
stringA := fmt.Sprintf("%s&%s=%s", str, "key", weixinPayKey)
md5Ctx := md5.New()
md5Ctx.Write([]byte(stringA))
sign := hex.EncodeToString(md5Ctx.Sum(nil))
return strings.ToUpper(sign)
}
//GetFieldString 获取结构体字段及值的拼接值
func getFieldString(sendParamEntity interface{}) string {
m := reflect.TypeOf(sendParamEntity)
v := reflect.ValueOf(sendParamEntity)
var tagName string
numField := m.NumField()
w := make([]string, numField)
numFieldCount := 0
for i := 0; i < numField; i++ {
fieldName := m.Field(i).Name
tags := strings.Split(string(m.Field(i).Tag), "\"")
if len(tags) > 1 {
tagName = tags[1]
} else {
tagName = m.Field(i).Name
}
if tagName == "xml" {
continue
}
fieldValue := v.FieldByName(fieldName).Interface()
if fieldValue != "" {
if strings.Contains(tagName, "omitempty") {
tagName = strings.Split(tagName, ",")[0]
}
s := fmt.Sprintf("%s=%v", tagName, fieldValue)
w[numFieldCount] = s
numFieldCount++
}
}
if numFieldCount == 0 {
return ""
}
w = w[:numFieldCount]
sort.Strings(w)
return strings.Join(w, "&")
}
//GetUUID 获取随机字符串
func GetUUID() (string, error) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
b := make([]rune, 32)
for i := range b {
b[i] = letters[r.Intn(len(letters))]
}
return string(b), nil
}
生成签名后可以到: 微信公众平台支付接口调试工具 来检测签名的正确与否