微信相关接口
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.

336 lines
9.4 KiB

  1. package wechat
  2. import (
  3. "encoding/xml"
  4. "errors"
  5. "fmt"
  6. "log"
  7. "reflect"
  8. "strings"
  9. "git.tetele.net/tgo/network"
  10. "git.tetele.net/tgo/helper"
  11. )
  12. //请求支付
  13. type PayXml struct {
  14. XMLName xml.Name `xml:"xml"`
  15. Appid string `xml:"appid"`
  16. MchId string `xml:"mch_id"`
  17. DeviceInfo string `xml:"device_info"`
  18. NonceStr string `xml:"nonce_str"`
  19. Sign string `xml:"sign"`
  20. Body string `xml:"body"`
  21. OutTradeNo string `xml:"out_trade_no"`
  22. TotalFee int `xml:"total_fee"`
  23. SpbillCreateIp string `xml:"spbill_create_ip"`
  24. AuthCode string `xml:"auth_code"`
  25. }
  26. //支付结果
  27. type PayReturnXml struct {
  28. ReturnCode string `xml:"return_code"`
  29. ReturnMsg string `xml:"return_msg"`
  30. Appid string `xml:"appid"`
  31. MchId string `xml:"mch_id"`
  32. DeviceInfo string `xml:"device_info"`
  33. NonceStr string `xml:"nonce_str"`
  34. Sign string `xml:"sign"`
  35. ResultCode string `xml:"result_code"`
  36. ErrCode string `xml:"err_code"`
  37. ErrCodeDes string `xml:"err_code_des"`
  38. Openid string `xml:"openid"`
  39. IsSubscribe string `xml:"is_subscribe"`
  40. TradeType string `xml:"trade_type"`
  41. BankType string `xml:"bank_type"`
  42. FeeType string `xml:"fee_type"`
  43. TotalFee string `xml:"total_fee"`
  44. SettlementTotalFee string `xml:"settlement_total_fee"`
  45. CouponFee string `xml:"coupon_fee"`
  46. CashFeeType string `xml:"cash_fee_type"`
  47. CashFee string `xml:"cash_fee"`
  48. TransactionId string `xml:"transaction_id"`
  49. OutTradeNo string `xml:"out_trade_no"`
  50. Attach string `xml:"attach"`
  51. TimeEnd string `xml:"time_end"`
  52. PromotionDetail string `xml:"promotion_detail"`
  53. }
  54. //查询支付
  55. type QueryXml struct {
  56. XMLName xml.Name `xml:"xml"`
  57. Appid string `xml:"appid"`
  58. MchId string `xml:"mch_id"`
  59. OutTradeNo string `xml:"out_trade_no"`
  60. NonceStr string `xml:"nonce_str"`
  61. Sign string `xml:"sign"`
  62. }
  63. //查询结果
  64. type QueryReturnXml struct {
  65. ReturnCode string `xml:"return_code"`
  66. ReturnMsg string `xml:"return_msg"`
  67. Appid string `xml:"appid"`
  68. MchId string `xml:"mch_id"`
  69. NonceStr string `xml:"nonce_str"`
  70. Sign string `xml:"sign"`
  71. ResultCode string `xml:"result_code"`
  72. ErrCode string `xml:"err_code"`
  73. ErrCodeDes string `xml:"err_code_des"`
  74. DeviceInfo string `xml:"device_info"`
  75. Openid string `xml:"openid"`
  76. IsSubscribe string `xml:"is_subscribe"`
  77. TradeType string `xml:"trade_type"`
  78. TradeState string `xml:"trade_state"`
  79. BankType string `xml:"bank_type"`
  80. TotalFee string `xml:"total_fee"`
  81. SettlementTotalFee string `xml:"settlement_total_fee"`
  82. FeeType string `xml:"fee_type"`
  83. CashFee string `xml:"cash_fee"`
  84. CashFeeType string `xml:"cash_fee_type"`
  85. CouponFee string `xml:"coupon_fee"`
  86. CouponCount string `xml:"coupon_count"`
  87. TransactionId string `xml:"transaction_id"`
  88. OutTradeNo string `xml:"out_trade_no"`
  89. Attach string `xml:"attach"`
  90. TimeEnd string `xml:"time_end"`
  91. TradeStateDesc string `xml:"trade_state_desc"`
  92. }
  93. // 撤消
  94. type ReverseXml struct {
  95. XMLName xml.Name `xml:"xml"`
  96. Appid string `xml:"appid"`
  97. MchId string `xml:"mch_id"`
  98. OutTradeNo string `xml:"out_trade_no"`
  99. NonceStr string `xml:"nonce_str"`
  100. Sign string `xml:"sign"`
  101. }
  102. //撤消结果
  103. type ReverseReturnXml struct {
  104. ReturnCode string `xml:"return_code"`
  105. ReturnMsg string `xml:"return_msg"`
  106. Appid string `xml:"appid"`
  107. MchId string `xml:"mch_id"`
  108. NonceStr string `xml:"nonce_str"`
  109. Sign string `xml:"sign"`
  110. ResultCode string `xml:"result_code"`
  111. ErrCode string `xml:"err_code"`
  112. ErrCodeDes string `xml:"err_code_des"`
  113. Recall string `xml:"recall"`
  114. }
  115. /**
  116. * 微信二维码支付
  117. * 2022/02/07
  118. * @param device_info 设备号
  119. * @param body 商品描述
  120. * @param out_trade_no 商户订单号
  121. * @param total_fee 订单金额
  122. * @param auth_code 付款码
  123. */
  124. func MicroPay(appid, mchid, mch_key, device_info, body, out_trade_no, auth_code, total_fee string) (PayReturnXml, error) {
  125. log.Println("wechat micropay")
  126. var requestxml PayXml
  127. var retXml PayReturnXml
  128. requestxml.Appid = appid
  129. requestxml.MchId = mchid
  130. requestxml.DeviceInfo = device_info
  131. requestxml.NonceStr = helper.GetRandomString(30)
  132. requestxml.Body = body
  133. requestxml.OutTradeNo = out_trade_no
  134. requestxml.TotalFee = helper.ToInt(helper.FloatMul(total_fee, 100))
  135. requestxml.SpbillCreateIp = network.GetLocalIp()
  136. requestxml.AuthCode = auth_code
  137. signStr := helper.StringJoin("appid=", requestxml.Appid, "&auth_code=", requestxml.AuthCode,
  138. "&body=", requestxml.Body, "&device_info=", requestxml.DeviceInfo,
  139. "&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr,
  140. "&out_trade_no=", requestxml.OutTradeNo, "&spbill_create_ip=", requestxml.SpbillCreateIp,
  141. "&total_fee=", helper.ToString(requestxml.TotalFee), "&key=", mch_key)
  142. signMd5 := Md5Str(signStr)
  143. requestxml.Sign = strings.ToUpper(signMd5)
  144. reqXmlJson, err := xml.Marshal(requestxml)
  145. log.Println(requestxml, string(reqXmlJson), err)
  146. url := "https://api.mch.weixin.qq.com/pay/micropay"
  147. ret, err := network.SendXml("POST", url, requestxml)
  148. log.Println("wechat micropay pay result:", string(ret), err)
  149. if err != nil {
  150. log.Println("Err:", err)
  151. return retXml, errors.New("微信支付失败,请检查网络")
  152. }
  153. err = xml.Unmarshal(ret, &retXml)
  154. if err != nil {
  155. log.Println("xml Unmarshal Err:", retXml, err)
  156. return retXml, errors.New("未知的支付结果")
  157. }
  158. if retXml.ResultCode == "SUCCESS" {
  159. retMap := XmlStructToMap(retXml)
  160. //验证sign
  161. signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key
  162. sign := strings.ToUpper(Md5Str(signStr))
  163. if sign != retXml.Sign {
  164. log.Println("Err:签名错误", string(ret), sign)
  165. return retXml, errors.New("支付结果签名错误")
  166. }
  167. } else {
  168. return retXml, errors.New(retXml.ReturnMsg)
  169. }
  170. return retXml, nil
  171. }
  172. func MicroPayQuery(appid, mchid, out_trade_no, mch_key string) (QueryReturnXml, error) {
  173. log.Println("wechat micropay query", out_trade_no)
  174. var requestxml QueryXml
  175. var retXml QueryReturnXml
  176. requestxml.Appid = appid
  177. requestxml.MchId = mchid
  178. requestxml.NonceStr = helper.GetRandomString(30)
  179. requestxml.OutTradeNo = out_trade_no
  180. signStr := helper.StringJoin("appid=", requestxml.Appid,
  181. "&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr,
  182. "&out_trade_no=", requestxml.OutTradeNo, "&key=", mch_key)
  183. signMd5 := Md5Str(signStr)
  184. requestxml.Sign = strings.ToUpper(signMd5)
  185. url := "https://api.mch.weixin.qq.com/pay/orderquery"
  186. ret, err := network.SendXml("POST", url, requestxml)
  187. if err != nil {
  188. log.Println("Err:", err)
  189. return retXml, errors.New("微信支付失败,请检查网络")
  190. }
  191. err = xml.Unmarshal(ret, &retXml)
  192. if err != nil {
  193. log.Println("xml Unmarshal Err:", retXml, err)
  194. return retXml, errors.New("未知的支付结果")
  195. }
  196. if retXml.ReturnCode == "SUCCESS" {
  197. retMap := XmlStructToMap(retXml)
  198. //验证sign
  199. signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key
  200. sign := strings.ToUpper(Md5Str(signStr))
  201. if sign != retXml.Sign {
  202. log.Println("Err:签名错误", string(ret), sign)
  203. return retXml, errors.New("查询支付结果签名错误")
  204. }
  205. } else {
  206. return retXml, errors.New(retXml.ReturnMsg)
  207. }
  208. return retXml, nil
  209. }
  210. /**
  211. * 撤消订单
  212. * 2023/06/13
  213. */
  214. func MicroPayReverse(appid, mchid, out_trade_no, mch_key string) (ReverseReturnXml, error) {
  215. log.Println("wechat micropay reverse", out_trade_no)
  216. var requestxml ReverseXml
  217. var retXml ReverseReturnXml
  218. requestxml.Appid = appid
  219. requestxml.MchId = mchid
  220. requestxml.NonceStr = helper.GetRandomString(30)
  221. requestxml.OutTradeNo = out_trade_no
  222. signStr := helper.StringJoin("appid=", requestxml.Appid,
  223. "&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr,
  224. "&out_trade_no=", requestxml.OutTradeNo, "&key=", mch_key)
  225. signMd5 := Md5Str(signStr)
  226. requestxml.Sign = strings.ToUpper(signMd5)
  227. url := "https://api.mch.weixin.qq.com/secapi/pay/reverse"
  228. ret, err := network.SendXml("POST", url, requestxml)
  229. if err != nil {
  230. log.Println("Err:", err)
  231. return retXml, errors.New("撤消微信支付失败,请检查网络")
  232. }
  233. err = xml.Unmarshal(ret, &retXml)
  234. if err != nil {
  235. log.Println("xml Unmarshal Err:", retXml, err)
  236. return retXml, errors.New("未知的撤消结果")
  237. }
  238. if retXml.ReturnCode == "SUCCESS" {
  239. retMap := XmlStructToMap(retXml)
  240. //验证sign
  241. signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key
  242. sign := strings.ToUpper(Md5Str(signStr))
  243. if sign != retXml.Sign {
  244. log.Println("Err:签名错误", string(ret), sign)
  245. return retXml, errors.New("查询支付结果签名错误")
  246. }
  247. } else {
  248. return retXml, errors.New(retXml.ReturnMsg)
  249. }
  250. return retXml, nil
  251. }
  252. func XmlStructToMap(retXml interface{}) map[string]string {
  253. t := reflect.TypeOf(retXml)
  254. v := reflect.ValueOf(retXml)
  255. //转为map,验签
  256. retMap := make(map[string]string)
  257. var retValue string
  258. var tag string
  259. for k := 0; k < t.NumField(); k++ {
  260. if t.Field(k).Name != "Sign" {
  261. retValue = helper.ToString(v.Field(k).Interface())
  262. tag = fmt.Sprintf("%s", t.Field(k).Tag)
  263. tag = strings.ReplaceAll(tag, "xml:", "")
  264. tag = strings.ReplaceAll(tag, "\"", "")
  265. retMap[tag] = retValue
  266. }
  267. }
  268. return retMap
  269. }