ps:在读代码前一定要先阅读微信公众号发红包官方文档:

https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3


以下是代码实现,

package server

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"crypto/x509"
	"net/http"
	"bytes"
	"strings"
	"sort"
	"reflect"
	"time"
	"math/rand"
	"strconv"
	"encoding/xml"
	"github.com/alecthomas/log4go"
	"crypto/md5"
	"encoding/hex"
)

var _tlsConfig *tls.Config
var (
	weixinPayKey="19200bbb0b4c09247ec02edce69f6ddd"   //微信密钥
	weixinMchId = "10000098"  //微信商户号
	wechatUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack"  //请求发红包的地址
	formatDate = "20060102"
	xmlStr = "xml"
	RedPacketRequestStr = "RedPacketRequest"
	strWeixinAppId =            "wx8888888888888888"         //公众账号ID
	strWeixinClientCertPemPath = "/home/apiclient_cert.pem"  //客户端证书存放绝对路径
	strWeixinClientKeyPemPath = "/home/apiclient_key.pem"    //客户端私匙存放绝对路径
	strWeixinRootCaPath = "/home/rootca.pem"                 //服务端证书存放绝对路径
)

type WeixinSendRedPacket struct {

}


//发红包的请求实体
type RedPacketRequest struct {
	ActName  	string      `xml:"act_name"`	  //必填,活动名称
	ClientIp 	string 		`xml:"client_ip"`	  //必填,调用接口的机器ip地址
	MchBillno 	string  	`xml:"mch_billno"`   //必填,商户订单号
	MchId    	string		`xml:"mch_id"`       //必填,微信支付分配的商户号
	NonceStr  	string  	`xml:"nonce_str"`    //必填,随机字符串,不超过32位
	ReOpenid  	string   	`xml:"re_openid"`	  //必填,接收红包者用户,用户在wxappid下的openid
	Remark		string      `xml:"remark"`		  //必填,备注信息
	SendName  	string		`xml:"send_name"`    //必填,红包发送者名称
	TotalAmount int		  	`xml:"total_amount"` //必填,付款金额,单位为分
	TotalNum    int 		`xml:"total_num"`    //必填,红包发放人数
	Wishing     string 		`xml:"wishing"`      //必填,红包祝福语
	Wxappid  	string		`xml:"wxappid"`      //必填,微信公众号id
	Sign      	string  	`xml:"sign"`		  //必填,签名
	//SceneId		string		`xml:"scene_id"`	  //非必填,红包使用场景
	//RiskInfo 	string		`xml:"risk_info"`    //非必填,用户操作的时间戳
	//ConsumeMchId string		`xml:"consume_mch_id"` //非必填,资金授权商户号
}



//发微信红包
func (*WeixinSendRedPacket)SendRedPack(redPacketEntity *RedPacketRequest) (*http.Response, error) {
	nonce_str,_ := getUUID()   //随机字符串

	//订单号,随机生成
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	mch_billno:=weixinMchId + time.Now().Format(formatDate) + strconv.FormatInt(time.Now().Unix(), 10)[4:]+strconv.Itoa(r.Intn(8999) + 1000)

	redPacketEntity.NonceStr=nonce_str
	redPacketEntity.MchBillno=mch_billno
	redPacketEntity.MchId=weixinMchId
	redPacketEntity.Wxappid = strWeixinAppId
	// 生成签名
	sign := strings.ToUpper(signature(*redPacketEntity)) //签名
	redPacketEntity.Sign=sign

	data, err := xml.MarshalIndent(redPacketEntity, "", "   ")

	if err!=nil {
		log4go.Error(err)
		return nil,err
	}
	sendData:=strings.Replace(string(data), RedPacketRequestStr , xmlStr ,-1)

	fmt.Println(sendData)
	//POST数据
	return securePost(wechatUrl, []byte(sendData))
}

//加载微信发红包需要的证书
func getTLSConfig() (*tls.Config, error) {
	if _tlsConfig != nil {
		return _tlsConfig, nil
	}

	// load cert
	cert, err := tls.LoadX509KeyPair(strWeixinClientCertPemPath, strWeixinClientKeyPemPath)
	if err != nil {
		fmt.Println("load wechat keys fail", err)
		return nil, err
	}

	// load root ca
	caData, err := ioutil.ReadFile(strWeixinRootCaPath)
	if err != nil {
		fmt.Println("read wechat ca fail", err)
		return nil, err
	}
	pool := x509.NewCertPool()
	pool.AppendCertsFromPEM(caData)

	_tlsConfig = &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      pool,
	}
	return _tlsConfig, nil
}

//http发送请求
func securePost(url string, xmlContent []byte) (*http.Response, error) {
	tlsConfig, err := getTLSConfig()
	if err != nil {
		return nil, err
	}

	tr := &http.Transport{TLSClientConfig: tlsConfig}
	client := &http.Client{Transport: tr}

	return client.Post(
		url,
		"text/xml",
		bytes.NewBuffer(xmlContent))
}


//签名算法
func signature(sendParamEntity interface{}) string {
	str:=getFieldString(sendParamEntity)
	if str!=""{
		str=fmt.Sprintf("%s&%s=%s",str,"key",weixinPayKey)
		fmt.Println(str)
		md5Ctx2 := md5.New()
		md5Ctx2.Write([]byte(str))
		str = hex.EncodeToString(md5Ctx2.Sum(nil))

		return str
	}else{
		return ""
	}
}

//获取结构体字段及值的拼接值
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
		}

		fieldValue:=v.FieldByName(fieldName).Interface()

		if fieldValue!=""  {
			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,"&")
}

//获取uuid
func getUUID() (string, error) {
	uuid := make([]byte, 16)
	n, err := rand.Read(uuid)
	if n != len(uuid) || err != nil {
		return "", err
	}
	uuid[8] = 0x80 // variant bits see page 5
	uuid[4] = 0x40 // version 4 Pseudo Random, see page 7

	return hex.EncodeToString(uuid), nil
}