最近在搞微信公众平台卡券(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
}

生成签名后可以到: 微信公众平台支付接口调试工具 来检测签名的正确与否