分享
  1. 首页
  2. 文章

使用golang快速开发微信公众平台(六):给用户发红包(用户提现至微信钱包)

u012210379 · · 4371 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

  1. 给用户发红包
  2. 用户买了商品,剩下的钱,可以自己再提出来。

问题1我纠结了好久,并没有找到合适解决方法,因为按照这个要求,操作由后台发起,由后台执行,而这样做,微信是不允许的,这也是为什么我会写一篇关于支付宝提现的帖子,但你去看支付宝的文档,会发现过程极其繁琐,还有时间差,而且微信与支付宝打架,这个功能还得放在微信外的页面,用户体验非常不好。

经过与老板的讨价还价,终于改变为了问题2 : 将发起者改为用户,这样以上的问题,就都不是问题了。

阅读代码之前墙裂建议先看完官方文档

业务场景:
用户点击页面上的按钮,post请求发送至后台,获取用户的id和要提现的金额,经过校验向用户微信钱包打款。

func (c *WXRedPacketController) Post() {
 uid, _ := c.GetSession("uid").(string)
 packegtMoney, err := c.GetFloat("packegtMoney", 0)
 o := orm.NewOrm()
 if err == nil {
 //检查用户钱包内是否有这么多钱可供提现
 uwallet := models.UserWallet{Uid:uid}
 if _, _, err := o.ReadOrCreate(&uwallet, "uid"); err == nil {
 umoney, _ := strconv.ParseFloat(uwallet.Money, 64)
 if umoney >= packegtMoney {
 //提现至微信红包
 str_req := makeRedPacketXML(packegtMoney, uid)
 if ok, errInfo := sendRedPacketRequest(str_req, uid); ok {
 uwallet.Money = strconv.FormatFloat(math.Floor((umoney - packegtMoney) * 100) / 100, 'f', 2, 64)
 _, err := o.Update(&uwallet, "money")
 if err != nil {
 fmt.Println("用户提现更新钱包失败", err)
 logUtils.GetLog().Error("用户提现更新钱包失败", err)
 c.Data["errinfo"] = "更新钱包失败"
 } else {
 //插入 wallet-his
 _, err = o.Insert(&models.UserWalletHis{Uid:uid, MoneyChange:strconv.FormatFloat(math.Floor(packegtMoney * 100) / 100, 'f', 2, 64), BonusChange:"0", ChangeReason:models.DRAW, BankType:"", BankNum:"", ReasonOrder:"", ReasonPerson:"", CreateTime:time.Now().Format(TIMELAYOUT), })
 if err != nil {
 fmt.Printf("插入 UserWalletHis 错误 %v", err)
 logUtils.GetLog().Error("插入 UserWalletHis 错误 %v", err)
 }
 }
 c.Redirect("/wx_red_packet", 302)
 } else {
 c.Data["errinfo"] = errInfo
 }
 } else {
 c.Data["errinfo"] = "钱包余额不足"
 }
 } else {
 c.Data["errinfo"] = "钱包查询错误"
 }
 }
 c.Data["money"] = showWalletMoney(o, uid)//可提现
 c.TplName = "wx_red_packget.html"
}
//生成红包的申请xml money 提现的金额 元
func makeRedPacketXML(money float64, uid string) string {
 //拼接xml
 var redPacket models.RedPacket
 redPacket.Nonce_str = RandomStrUtil.GetRandomString(32)
 redPacket.Mch_billno = beego.AppConfig.String("shopKey") + time.Now().Format(TIMELAYOUT3) + strconv.FormatInt(time.Now().Unix(), 10)
 redPacket.Mch_id = beego.AppConfig.String("shopKey")
 redPacket.Appid = beego.AppConfig.String("APPID")
 redPacket.Send_name = "商城"
 redPacket.Re_openid = uid
 redPacket.Total_amount = int(money * 100)//分钱
 redPacket.Total_num = 1
 redPacket.Wishing = "收红包啦"
 redPacket.Client_ip = "122.122.122.122"
 redPacket.Act_name = "红包名称"
 redPacket.Remark = "红包说明"
 var n map[string]interface{}
 n = make(map[string]interface{}, 0)
 n["nonce_str"] = redPacket.Nonce_str
 n["mch_billno"] = redPacket.Mch_billno
 n["mch_id"] = redPacket.Mch_id
 n["wxappid"] = redPacket.Appid
 n["send_name"] = redPacket.Send_name
 n["re_openid"] = redPacket.Re_openid
 n["total_amount"] = redPacket.Total_amount
 n["total_num"] = redPacket.Total_num
 n["wishing"] = redPacket.Wishing
 n["client_ip"] = redPacket.Client_ip
 n["act_name"] = redPacket.Act_name
 n["remark"] = redPacket.Remark
 redPacket.Sign = wxpayCalcSign(n, PAY_API_KEY) //这个key 微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全
 bytes_req, err := xml.Marshal(redPacket)
 if err != nil {
 fmt.Println("转换为xml错误:", err)
 logUtils.GetLog().Error("转换为xml错误:", err)
 }
 str_req := strings.Replace(string(bytes_req), "RedPacket", "xml", -1)
 fmt.Println("发红包参数转换为xml--------", str_req)
 return str_req
}
func sendRedPacketRequest(str_req, uid string) (bool, string) {
 bytes_req := []byte(str_req)
 //发送unified order请求.
 req, err := http.NewRequest("POST", "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack", bytes.NewReader(bytes_req))
 if err != nil {
 fmt.Println("发送红包 Request发生错误,原因:", err)
 logUtils.GetLog().Error("发送红包 Request发生错误,原因:", err)
 return false, "未知错误"
 }
 req.Header.Set("Accept", "application/xml")
 //这里的http header的设置是必须设置的.
 req.Header.Set("Content-Type", "application/xml;charset=utf-8")
 tlsConfig, err := WxPlatUtil.GetTLSConfig()
 if err != nil {
 fmt.Println("解析证书出现错误", err)
 return false, "未知错误"
 }
 tr := &http.Transport{TLSClientConfig: tlsConfig}
 client := &http.Client{Transport: tr}
 resp, _err := client.Do(req)
 if _err != nil {
 fmt.Println("请求发送红包接口发送错误, 原因:", _err)
 logUtils.GetLog().Error("请求发送红包接口发送错误, 原因:", _err)
 return false, "未知错误"
 }
 respBytes, err := ioutil.ReadAll(resp.Body)
 if err != nil {
 fmt.Println("请求发送红包解析返回body错误", err)
 logUtils.GetLog().Error("请求发送红包解析返回body错误", err)
 return false, "未知错误"
 } else {
 fmt.Println("请求发送红包解析返回===================>", string(respBytes))
 redPacketRes := models.RedPacketRes{}
 if err := xml.Unmarshal(respBytes, &redPacketRes); err == nil {
 recodeRedPacketRes(&redPacketRes, uid)//记录存入数据库
 if redPacketRes.Return_code == "SUCCESS" {
 if redPacketRes.Result_code == "SUCCESS" {
 return true, "红包领取成功"
 } else {
 return false, redPacketRes.Err_code_des
 }
 } else {
 //签名失败
 return false, "微信签名失败"
 }
 } else {
 fmt.Println("红包领取记录解析错误", err)
 logUtils.GetLog().Error("红包领取记录解析错误", err)
 }
 }
 return false, "未知错误"
}
func wxpayCalcSign(mReq map[string]interface{}, key string) string {
 //fmt.Println("========STEP 1, 对key进行升序排序.========")
 //fmt.Println("微信支付签名计算, API KEY:", key)
 //STEP 1, 对key进行升序排序.
 sorted_keys := make([]string, 0)
 for k, _ := range mReq {
 sorted_keys = append(sorted_keys, k)
 }
 sort.Strings(sorted_keys)
 //fmt.Println("========STEP2, 对key=value的键值对用&连接起来,略过空值========")
 //STEP2, 对key=value的键值对用&连接起来,略过空值
 var signStrings string
 for _, k := range sorted_keys {
 //fmt.Printf("k=%v, v=%v\n", k, mReq[k])
 value := fmt.Sprintf("%v", mReq[k])
 if value != "" {
 signStrings = signStrings + k + "=" + value + "&"
 }
 }
 //fmt.Println("========STEP3, 在键值对的最后加上key=API_KEY========")
 //STEP3, 在键值对的最后加上key=API_KEY
 if key != "" {
 signStrings = signStrings + "key=" + key
 }
 //fmt.Println("========STEP4, 进行MD5签名并且将所有字符转为大写.========")
 //STEP4, 进行MD5签名并且将所有字符转为大写.
 md5Ctx := md5.New()
 md5Ctx.Write([]byte(signStrings))
 cipherStr := md5Ctx.Sum(nil)
 upperSign := strings.ToUpper(hex.EncodeToString(cipherStr))
 return upperSign
}
func recodeRedPacketRes(redPacketRes *models.RedPacketRes, uid string) {
 //插入数据库 红包领取记录
 o := orm.NewOrm()
 wxUser := models.WxUser{WxId:uid}
 if err := o.Read(&wxUser, "wx_id"); err == nil {
 redPacketHis := models.RedPacketResHis{RedPacketRes:*redPacketRes, Name:wxUser.Name, Phone:wxUser.Phone, CreateTime:time.Now().Format(TIMELAYOUT)}
 _, err := o.Insert(&redPacketHis)
 if err != nil {
 fmt.Println("插入红包领取记录错误", err)
 logUtils.GetLog().Error("红包领取记录", err)
 }
 } else {
 fmt.Println("查询用户信息错误", err)
 logUtils.GetLog().Error("查询用户信息错误", err)
 }
}

这就OK啦,用户的微信钱包是实时更新的。当然要是想再友好点,可以给用户发个消息,下一篇,就来写微信的客服功能。


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
4371 次点击
2 回复 | 直到 2019年07月23日 15:46:28
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏