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
}