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

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