diff --git a/pay/pay.go b/pay/pay.go index c1f42f6e3..95416c2c1 100644 --- a/pay/pay.go +++ b/pay/pay.go @@ -4,6 +4,7 @@ import ( "github.com/silenceper/wechat/v2/pay/config" "github.com/silenceper/wechat/v2/pay/notify" "github.com/silenceper/wechat/v2/pay/order" + "github.com/silenceper/wechat/v2/pay/redpacket" "github.com/silenceper/wechat/v2/pay/refund" "github.com/silenceper/wechat/v2/pay/transfer" ) @@ -37,3 +38,8 @@ func (pay *Pay) GetRefund() *refund.Refund { func (pay *Pay) GetTransfer() *transfer.Transfer { return transfer.NewTransfer(pay.cfg) } + +// GetRedpacket 红包 +func (pay *Pay) GetRedpacket() *redpacket.Redpacket { + return redpacket.NewRedpacket(pay.cfg) +} diff --git a/pay/redpacket/redpacket.go b/pay/redpacket/redpacket.go new file mode 100644 index 000000000..ff1a397b2 --- /dev/null +++ b/pay/redpacket/redpacket.go @@ -0,0 +1,131 @@ +package redpacket + +import ( + "encoding/xml" + "fmt" + "strconv" + + "github.com/silenceper/wechat/v2/pay/config" + "github.com/silenceper/wechat/v2/util" +) + +// redpacketGateway 发放红包接口 +// https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 +var redpacketGateway = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack" + +// Redpacket struct extends context +type Redpacket struct { + *config.Config +} + +// NewRedpacket return an instance of Redpacket package +func NewRedpacket(cfg *config.Config) *Redpacket { + return &Redpacket{cfg} +} + +// Params 调用参数 +type Params struct { + MchBillno string // 商户订单号 + SendName string // 商户名称 + ReOpenID string + TotalAmount int + TotalNum int + Wishing string + ClientIP string + ActName string + Remark string + + RootCa string // ca证书 +} + +// request 接口请求参数 +type request struct { + NonceStr string `xml:"nonce_str"` + Sign string `xml:"sign"` + MchID string `xml:"mch_id"` + MchBillno string `xml:"mch_billno"` + Wxappid string `xml:"wxappid"` + SendName string `xml:"send_name"` + ReOpenID string `xml:"re_openid"` + TotalAmount int `xml:"total_amount"` + TotalNum int `xml:"total_num"` + Wishing string `xml:"wishing"` + ClientIP string `xml:"client_ip"` + ActName string `xml:"act_name"` + Remark string `xml:"remark"` +} + +// Response 接口返回 +type Response struct { + ReturnCode string `xml:"return_code"` + ReturnMsg string `xml:"return_msg"` + ResultCode string `xml:"result_code,omitempty"` + ErrCode string `xml:"err_code,omitempty"` + ErrCodeDes string `xml:"err_code_des,omitempty"` + MchBillno string `xml:"mch_billno,omitempty"` + MchID string `xml:"mch_id,omitempty"` + Wxappid string `xml:"wxappid"` + ReOpenID string `xml:"re_openid"` + TotalAmount int `xml:"total_amount"` + SendListid string `xml:"send_listid"` +} + +// SendRedpacket 发放红包 +func (redpacket *Redpacket) SendRedpacket(p *Params) (rsp *Response, err error) { + nonceStr := util.RandomStr(32) + param := make(map[string]string) + + param["nonce_str"] = nonceStr + param["mch_id"] = redpacket.MchID + param["wxappid"] = redpacket.AppID + param["mch_billno"] = p.MchBillno + param["send_name"] = p.SendName + param["re_openid"] = p.ReOpenID + param["total_amount"] = strconv.Itoa(p.TotalAmount) + param["total_num"] = strconv.Itoa(p.TotalNum) + param["wishing"] = p.Wishing + param["client_ip"] = p.ClientIP + param["act_name"] = p.ActName + param["remark"] = p.Remark + //param["scene_id"] = "PRODUCT_2" + + sign, err := util.ParamSign(param, redpacket.Key) + if err != nil { + return + } + + req := request{ + NonceStr: nonceStr, + Sign: sign, + MchID: redpacket.MchID, + Wxappid: redpacket.AppID, + MchBillno: p.MchBillno, + SendName: p.SendName, + ReOpenID: p.ReOpenID, + TotalAmount: p.TotalAmount, + TotalNum: p.TotalNum, + Wishing: p.Wishing, + ClientIP: p.ClientIP, + ActName: p.ActName, + Remark: p.Remark, + } + + rawRet, err := util.PostXMLWithTLS(redpacketGateway, req, p.RootCa, redpacket.MchID) + if err != nil { + return + } + err = xml.Unmarshal(rawRet, &rsp) + if err != nil { + return + } + if rsp.ReturnCode == "SUCCESS" { + if rsp.ResultCode == "SUCCESS" { + err = nil + return + } + err = fmt.Errorf("send redpacket error, errcode=%s,errmsg=%s", rsp.ErrCode, rsp.ErrCodeDes) + return + } + err = fmt.Errorf("[msg : xmlUnmarshalError] [rawReturn : %s] [sign : %s]", string(rawRet), sign) + return +}