pay.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package shanghu
  2. import (
  3. "duoduo/apis/common"
  4. "duoduo/apis/shanghu/models"
  5. "duoduo/models/shanghu"
  6. "duoduo/tools"
  7. "duoduo/tools/app"
  8. "encoding/xml"
  9. "errors"
  10. "fmt"
  11. "github.com/gin-gonic/gin"
  12. "github.com/go-pay/gopay"
  13. "github.com/go-pay/gopay/wechat"
  14. "github.com/shopspring/decimal"
  15. "net/http"
  16. "strconv"
  17. "time"
  18. )
  19. type wechatCallbackResp struct {
  20. XMLName xml.Name `xml:"xml"`
  21. ReturnCode Cdata `xml:"return_code"`
  22. ReturnMsg Cdata `xml:"return_msg"`
  23. }
  24. type Cdata struct {
  25. Value string `xml:",cdata"`
  26. }
  27. var (
  28. successResp = &wechatCallbackResp{ReturnCode: Cdata{Value: "SUCCESS"}, ReturnMsg: Cdata{Value: "OK"}}
  29. failResp = &wechatCallbackResp{ReturnCode: Cdata{Value: "FAIL"}, ReturnMsg: Cdata{Value: "数据处理异常"}}
  30. )
  31. func UnifiedOrder(c *gin.Context) {
  32. var inData models.UnifiedOrderRequest
  33. var sqlData shanghu.ClientPayTrans
  34. var outData models.UnifiedOrderReply
  35. err := c.ShouldBindJSON(&inData)
  36. if err != nil {
  37. app.Error(c, 400, err, err.Error())
  38. return
  39. }
  40. //校验 防止同一笔记录存在
  41. sqlData.RequestID = inData.RequestId
  42. if sqlData.GetRequestNum() > 0 {
  43. app.Error(c, 400, errors.New("交易已存在"), "交易已存在")
  44. return
  45. }
  46. //校验金额
  47. // 检查微信相关参数
  48. if !inData.Amount.GreaterThan(decimal.NewFromInt(0)) {
  49. app.Error(c, 400, errors.New("amount:金额必须大于0"), "amount:金额必须大于0")
  50. return
  51. }
  52. if inData.Amount.Round(2).String() != inData.Amount.String() {
  53. app.Error(c, 400, errors.New("total_fee:金额最多只能保留两位小数"), "total_fee:金额最多只能保留两位小数")
  54. return
  55. }
  56. //创建支付记录
  57. sqlData.ClientOpenID = inData.ClientOpenId
  58. sqlData.RequestID = inData.RequestId
  59. sqlData.CreatedAt = time.Now()
  60. sqlData.UpdatedAt = time.Now()
  61. sqlData.Amount = inData.Amount
  62. sqlData.OutTradeNo = strconv.FormatInt(inData.MerchantCardId, 10) + strconv.FormatInt(time.Now().UnixNano(), 10)
  63. sqlData.Status = 1 //未支付
  64. sqlData.MerchantCardID = inData.MerchantCardId
  65. sqlData.InvitationCode = inData.InvitationCode
  66. _, err = sqlData.Create()
  67. if err != nil {
  68. app.Error(c, 400, err, "创建支付失败")
  69. return
  70. }
  71. fmt.Println(sqlData.OutTradeNo)
  72. bm := make(gopay.BodyMap)
  73. bm.Set("nonce_str", common.GetRandomString(32))
  74. bm.Set("body", "商户卡")
  75. bm.Set("out_trade_no", sqlData.OutTradeNo)
  76. bm.Set("total_fee", inData.Amount.Mul(decimal.NewFromInt(100)).IntPart())
  77. bm.Set("spbill_create_ip", "127.0.0.1")
  78. bm.Set("notify_url", "https://shisanmiao.com/v1/client/pay/callback")
  79. bm.Set("device_info", "WEB")
  80. bm.Set("trade_type", "JSAPI")
  81. bm.Set("sign_type", wechat.SignType_MD5)
  82. bm.Set("openid", inData.ClientOpenId)
  83. client := NewWechatService()
  84. //请求支付下单,成功后得到结果
  85. wxResp, err := client.UnifiedOrder(c, bm)
  86. if err != nil {
  87. app.Error(c, 400, err, "下单失败")
  88. return
  89. }
  90. if wxResp.ReturnCode != "SUCCESS" {
  91. app.Error(c, 400, errors.New(wxResp.ReturnMsg), "下单失败")
  92. return
  93. }
  94. if wxResp.ResultCode != "SUCCESS" {
  95. app.Error(c, 400, errors.New(wxResp.ErrCode+"--"+wxResp.ErrCodeDes), "下单失败")
  96. return
  97. }
  98. timestamp := strconv.FormatInt(time.Now().Unix(), 10)
  99. pac := "prepay_id=" + wxResp.PrepayId
  100. paySign := wechat.GetMiniPaySign("wx25357518f710b8ce", wxResp.NonceStr, pac, wechat.SignType_MD5, timestamp, "1RKRJBVH4vaRrF0XPW9GX2M3ZSImukIz")
  101. outData.Timestamp = timestamp
  102. outData.NonceStr = wxResp.NonceStr
  103. outData.Package = pac
  104. outData.PaySign = paySign
  105. outData.SignType = wechat.SignType_MD5
  106. //merchant, count, err := sqlData.GetOpenIdList(pageSize, pageIndex)
  107. //if err != nil {
  108. // app.Error(c, 500, err, err.Error())
  109. // return
  110. //}
  111. app.OK(c, outData, app.Success)
  112. }
  113. func PayCallBack(c *gin.Context) {
  114. var payLog shanghu.PayCallbackLog
  115. var payTrans shanghu.ClientPayTrans
  116. wxNotify, err := wechat.ParseNotifyToBodyMap(c.Request)
  117. if err != nil {
  118. c.XML(http.StatusOK, failResp)
  119. return
  120. }
  121. //通知回调log
  122. payLog.CallBackLog = wxNotify.JsonBody()
  123. payLog.ThirdTradeNo = wxNotify.Get("transaction_id")
  124. payLog.OutTradeNo = wxNotify.Get("out_trade_no")
  125. payLog.CreatedAt = time.Now()
  126. payLog.UpdatedAt = time.Now()
  127. _, err = payLog.Create()
  128. if err != nil {
  129. c.XML(http.StatusOK, failResp)
  130. return
  131. }
  132. if wxNotify.Get("return_code") != "SUCCESS" || wxNotify.Get("result_code") != "SUCCESS" {
  133. payLog.ErrLog = "微信返回错误:" + wxNotify.Get("return_code") + "--" + wxNotify.Get("result_code")
  134. payLog.UpdateMerchant()
  135. c.XML(http.StatusOK, failResp)
  136. return
  137. }
  138. //校验金额
  139. payTrans.OutTradeNo = wxNotify.Get("out_trade_no")
  140. payTransInfo, err := payTrans.GetPayTransByTradeNo()
  141. if err != nil {
  142. payLog.ErrLog = "查询交易信息错误:" + " err=" + err.Error()
  143. payLog.UpdateMerchant()
  144. c.XML(http.StatusOK, failResp)
  145. return
  146. }
  147. // 判断金额与支付流水是否一致
  148. totalFee, err := decimal.NewFromString(wxNotify.Get("total_fee"))
  149. if err != nil {
  150. payLog.ErrLog = "解析总金额报错:" + "err=" + err.Error()
  151. payLog.UpdateMerchant()
  152. c.XML(http.StatusOK, failResp)
  153. return
  154. }
  155. if !totalFee.Equal(payTransInfo.Amount.Mul(decimal.NewFromInt(100))) {
  156. payLog.ErrLog = "验证金额报错:total_fee=" + wxNotify.Get("total_fee") + " amount=" + payTransInfo.Amount.String()
  157. payLog.UpdateMerchant()
  158. c.XML(http.StatusOK, failResp)
  159. return
  160. }
  161. // 解析支付时间
  162. timeEnd, err := time.ParseInLocation("20060102150405", wxNotify.Get("time_end"), tools.TimeLocation)
  163. if err != nil {
  164. payLog.ErrLog = "付款时间解析出错:err=" + err.Error()
  165. payLog.UpdateMerchant()
  166. c.XML(http.StatusOK, failResp)
  167. return
  168. }
  169. payTrans.ThirdTradeNo = wxNotify.Get("transaction_id")
  170. payTrans.Status = 2 //支付成功
  171. payTrans.PayTime = timeEnd
  172. payTrans.AccountStatus = 1 //未分账
  173. err = payTrans.UpdatePayTransByTradeNo()
  174. if err != nil {
  175. payLog.ErrLog = "更新支付状态失败:err=" + err.Error()
  176. payLog.UpdateMerchant()
  177. c.XML(http.StatusOK, failResp)
  178. return
  179. }
  180. c.XML(http.StatusOK, successResp)
  181. }
  182. func NewWechatService() *wechat.Client {
  183. client := wechat.NewClient("wx25357518f710b8ce", "1501641641", "1RKRJBVH4vaRrF0XPW9GX2M3ZSImukIz", true)
  184. //设置国家
  185. client.SetCountry(wechat.China)
  186. return client
  187. }