互动
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

327 lines
9.8 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. package pay_service
  2. import (
  3. "fmt"
  4. "github.com/chanxuehong/wechat/mch/mmpaymkttransfers"
  5. "github.com/chanxuehong/wechat/mch/mmpaymkttransfers/promotion"
  6. "go.uber.org/zap"
  7. "hudongzhuanjia/logger"
  8. "hudongzhuanjia/models"
  9. "hudongzhuanjia/utils"
  10. "math"
  11. "strconv"
  12. "time"
  13. )
  14. func init() {
  15. //go loopTransfer()
  16. }
  17. var transferDelayQueue = make(chan *transferDelayQueueParam, math.MaxInt8)
  18. type transferDelayQueueParam struct {
  19. first bool // 首次跳过
  20. Retries int `json:"retries"` // 尝试次数
  21. Delay int `json:"delay"` // 延迟时间, 单位second
  22. Amount int `json:"amount"` // 转账金额
  23. Desc string `json:"desc"` // 转账描述
  24. OpenId string `json:"open_id"` // 被转账人
  25. PartnerTradeNo string `json:"partner_trade_no"` // 转账账单单号
  26. }
  27. func loopTransfer() {
  28. //初始化
  29. transfers, err := models.GetUserTransferByStatus(0, 1, 3)
  30. if err != nil {
  31. panic(err)
  32. }
  33. transferDelayQueue = make(chan *transferDelayQueueParam, math.MaxInt8)
  34. for _, transfer := range transfers {
  35. transferDelayQueue <- &transferDelayQueueParam{
  36. first: true,
  37. Retries: 5,
  38. Delay: 2 * 60,
  39. Amount: transfer.PaymentAmount,
  40. Desc: transfer.Desc,
  41. OpenId: transfer.OpenId,
  42. PartnerTradeNo: transfer.PartnerTradeNo,
  43. }
  44. }
  45. defer func() {
  46. if errRec := recover(); errRec != nil {
  47. logger.Error("转账轮询 loop transfer panic", zap.Any("错误原因", errRec))
  48. }
  49. loopTransfer()
  50. }()
  51. for {
  52. select {
  53. case param, ok := <-transferDelayQueue:
  54. if !ok {
  55. panic("转账延迟通道异常关闭")
  56. }
  57. // 尝试次数
  58. if param.Retries <= 0 {
  59. logger.Info("微信转账尝试3次失败", zap.String("转账账单单号", param.PartnerTradeNo))
  60. userTransfer := new(models.UserTransfer)
  61. userTransfer.Status = 4
  62. _, err = userTransfer.UpdateByPartnerTradeNo(param.PartnerTradeNo)
  63. if err != nil {
  64. logger.Info("微信转账更新状态失败", zap.String("失败原因", err.Error()),
  65. zap.String("转账账单单号", param.PartnerTradeNo))
  66. }
  67. continue
  68. } else {
  69. param.Retries--
  70. }
  71. if !param.first {
  72. time.Sleep(time.Duration(param.Delay) * time.Second)
  73. }
  74. param.first = false
  75. res, err := TransferInfo(param.PartnerTradeNo)
  76. if err != nil {
  77. logger.Error("微信转账查询出现的错误", zap.String("错误原因", err.Error()),
  78. zap.String("转账账单", param.PartnerTradeNo))
  79. transferDelayQueue <- param
  80. continue
  81. }
  82. if res.Status == CODE_SUCCESS {
  83. continue
  84. } else if res.Status == CODE_TRANSFER_PROCESSING {
  85. transferDelayQueue <- param
  86. continue
  87. } else {
  88. //失败 --> 重新转账
  89. _, err = Transfer(param.Desc, param.OpenId, param.PartnerTradeNo, param.Amount)
  90. if err != nil {
  91. logger.Error("微信转账出现的错误", zap.String("错误原因", err.Error()),
  92. zap.String("转账账单", param.PartnerTradeNo))
  93. }
  94. transferDelayQueue <- param // 重新确认
  95. continue
  96. }
  97. }
  98. }
  99. }
  100. // 企业向微信用户个人付款(不支持沙箱环境)
  101. type TransferResponse struct {
  102. DeviceInfo string `xml:"device_info,omitempty" json:"device_info,omitempty"`
  103. NonceStr string `xml:"nonce_str,omitempty" json:"nonce_str,omitempty"`
  104. PartnerTradeNo string `xml:"partner_trade_no,omitempty" json:"partner_trade_no,omitempty"`
  105. PaymentNo string `xml:"payment_no,omitempty" json:"payment_no,omitempty"`
  106. PaymentTime string `xml:"payment_time,omitempty" json:"payment_time,omitempty"`
  107. }
  108. func PutTransferDelayQueue(desc, openId, partnerTradeNo string, amount, retries, delay int) {
  109. if retries <= 0 {
  110. retries = 3
  111. }
  112. if delay == 0 {
  113. delay = 30
  114. }
  115. transferDelayQueue <- &transferDelayQueueParam{
  116. first: true,
  117. Retries: retries,
  118. Delay: delay,
  119. Amount: amount,
  120. Desc: desc,
  121. OpenId: openId,
  122. PartnerTradeNo: partnerTradeNo,
  123. }
  124. }
  125. func Transfer(desc, openId, partnerTradeNo string, amount int) (*TransferResponse, error) {
  126. nonceStr := utils.RandomStr(32)
  127. if partnerTradeNo == "" { // 需要提前存入
  128. partnerTradeNo = utils.RandomStr(32)
  129. }
  130. // 初始化参数结构体
  131. body := make(map[string]string)
  132. body["nonce_str"] = nonceStr
  133. body["partner_trade_no"] = partnerTradeNo
  134. body["openid"] = openId
  135. body["check_name"] = "NO_CHECK" // NO_CHECK:不校验真实姓名 , FORCE_CHECK:强校验真实姓名
  136. body["amount"] = fmt.Sprintf("%d", amount) // 企业付款金额,单位为分
  137. body["desc"] = desc // 企业付款备注,必填。注意:备注中的敏感词会被转成字符*
  138. body["mchid"] = Mchid
  139. body["mch_appid"] = Appid
  140. client, err := Client()
  141. m, err := promotion.Transfers(client, body)
  142. // 记录错误信息
  143. if err != nil {
  144. transfer := new(models.UserTransfer)
  145. transfer.UpdateErrMsg(partnerTradeNo, err.Error())
  146. return nil, err
  147. }
  148. res := &TransferResponse{
  149. DeviceInfo: m["device_info"],
  150. NonceStr: m["nonce_str"],
  151. PartnerTradeNo: m["partner_trade_no"],
  152. PaymentNo: m["payment_no"],
  153. PaymentTime: m["payment_time"],
  154. }
  155. // 获取某些信息
  156. transfer := new(models.UserTransfer)
  157. exist, err := transfer.GetByPartnerTradeNo(partnerTradeNo)
  158. if err != nil {
  159. return nil, err
  160. }
  161. transfer.Desc = desc
  162. transfer.OpenId = openId
  163. transfer.PartnerTradeNo = partnerTradeNo
  164. transfer.PaymentAmount = amount
  165. transfer.PaymentNo = res.PaymentNo
  166. transfer.DeviceInfo = res.DeviceInfo
  167. transfer.PaymentTime = res.PaymentTime
  168. if !exist {
  169. _, err = transfer.Add()
  170. if err != nil {
  171. return nil, err
  172. }
  173. } else {
  174. _, err = transfer.UpdateByPartnerTradeNo(partnerTradeNo)
  175. if err != nil {
  176. return nil, err
  177. }
  178. }
  179. return res, nil
  180. }
  181. type TransferInfoResponse struct {
  182. ErrCode string `json:"err_code"`
  183. Status string `json:"status"`
  184. Reason string `json:"reason"`
  185. Openid string `json:"openid"`
  186. PaymentAmount int `json:"payment_amount"`
  187. PaymentTime string `json:"payment_time"`
  188. TransferTime string `json:"transfer_time"`
  189. Desc string `json:"desc"`
  190. }
  191. func TransferInfo(partnerTradeNo string) (*TransferInfoResponse, error) {
  192. client, err := Client()
  193. if err != nil {
  194. return nil, err
  195. }
  196. nonceStr := utils.RandomStr(32)
  197. var body = make(map[string]string, 0)
  198. body["nonce_str"] = nonceStr
  199. body["partner_trade_no"] = partnerTradeNo
  200. m, err := mmpaymkttransfers.GetTransferInfo(client, body)
  201. if err != nil {
  202. transfer := new(models.UserTransfer)
  203. transfer.UpdateErrMsg(partnerTradeNo, err.Error())
  204. return nil, err
  205. }
  206. amount, _ := strconv.Atoi(m["payment_amount"])
  207. res := &TransferInfoResponse{
  208. Status: m["status"],
  209. Reason: m["reason"],
  210. Openid: m["openid"],
  211. PaymentAmount: amount,
  212. PaymentTime: m["payment_time"],
  213. TransferTime: m["transfer_time"],
  214. Desc: m["desc"],
  215. ErrCode: m["err_code"],
  216. }
  217. transfer := new(models.UserTransfer)
  218. if res.Status == CODE_SUCCESS {
  219. transfer.Status = 2
  220. } else if res.Status == CODE_TRANSFER_PROCESSING {
  221. transfer.Status = 1
  222. } else if res.Status == CODE_FAIL {
  223. transfer.Status = 3
  224. }
  225. transfer.Reason = res.Reason
  226. transfer.TransferTime = res.TransferTime
  227. _, err = transfer.UpdateByPartnerTradeNo(partnerTradeNo)
  228. if err != nil {
  229. return nil, err
  230. }
  231. return res, nil
  232. }
  233. type RedPackResult struct {
  234. MchBillno string `xml:"mch_billno,omitempty" json:"mch_billno,omitempty"`
  235. ReOpenid string `xml:"re_openid,omitempty" json:"re_openid,omitempty"`
  236. TotalAmount int `xml:"total_amount,omitempty" json:"total_amount,omitempty"`
  237. SendListid string `xml:"send_listid,omitempty" json:"send_listid,omitempty"`
  238. }
  239. // 发送红包
  240. func SendRedPack(sendName, openId, wishing, ip, actName, remark string, totalAmount, totalNum, scene int) (*RedPackResult, error) {
  241. client, err := Client()
  242. if err != nil {
  243. logger.Error("client init error: %v", err)
  244. return nil, err
  245. }
  246. mchBillNo := utils.RandomStr(28)
  247. nonceStr := utils.RandomStr(32)
  248. body := make(map[string]string, 0)
  249. body["wxappid"] = client.AppId()
  250. body["mch_id"] = client.MchId()
  251. body["nonce_str"] = nonceStr
  252. body["mch_billno"] = mchBillNo
  253. body["send_name"] = sendName
  254. body["re_openid"] = openId
  255. body["total_amount"] = fmt.Sprintf("%d", totalAmount)
  256. body["total_num"] = fmt.Sprintf("%d", totalNum)
  257. body["wishing"] = wishing
  258. body["client_ip"] = ip
  259. body["act_name"] = actName
  260. body["remark"] = remark
  261. body["scene_id"] = fmt.Sprintf("PRODUCT_%d", scene)
  262. m, err := mmpaymkttransfers.SendRedPack(client, body)
  263. if err != nil {
  264. return nil, err
  265. }
  266. res := &RedPackResult{
  267. MchBillno: m["mch_billno"],
  268. ReOpenid: m["re_openid"],
  269. TotalAmount: totalAmount,
  270. SendListid: m["send_listid"],
  271. }
  272. return res, nil
  273. }
  274. type QueryRedPackResult struct {
  275. CommonReturn
  276. CommonResult
  277. MchBillno string `xml:"mch_billno,omitempty" json:"mch_billno,omitempty"`
  278. Mchid string `xml:"mchid,omitempty" json:"mchid,omitempty"`
  279. DetailId string `xml:"detail_id,omitempty" json:"detail_id,omitempty"`
  280. Status string `xml:"status,omitempty" json:"status,omitempty"`
  281. SendType string `xml:"send_type,omitempty" json:"send_type,omitempty"`
  282. HbType string `xml:"hb_type,omitempty" json:"hb_type,omitempty"`
  283. TotalNum int `xml:"total_num,omitempty" json:"total_num,omitempty"`
  284. TotalAmount int `xml:"total_amount,omitempty" json:"total_amount,omitempty"`
  285. Reason string `xml:"reason,omitempty" json:"reason,omitempty"`
  286. SendTime string `xml:"send_time,omitempty" json:"send_time,omitempty"`
  287. RefundTime string `xml:"refund_time,omitempty" json:"refund_time,omitempty"`
  288. RefundAmount int `xml:"refund_amount,omitempty" json:"refund_amount,omitempty"`
  289. Wishing string `xml:"wishing,omitempty" json:"wishing,omitempty"`
  290. Remark string `xml:"remark,omitempty" json:"remark,omitempty"`
  291. ActName string `xml:"act_name,omitempty" json:"act_name,omitempty"`
  292. }