wechat_servier_api.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. //==================================
  2. // * Name:Jerry
  3. // * DateTime:2019/5/6 13:16
  4. // * Desc:
  5. //==================================
  6. package gopay
  7. import (
  8. "bytes"
  9. "crypto/aes"
  10. "crypto/cipher"
  11. "crypto/hmac"
  12. "crypto/md5"
  13. "crypto/sha256"
  14. "crypto/tls"
  15. "encoding/base64"
  16. "encoding/hex"
  17. "encoding/json"
  18. "encoding/xml"
  19. "errors"
  20. "github.com/parnurzeal/gorequest"
  21. "net/http"
  22. "reflect"
  23. "strings"
  24. )
  25. func HttpAgent() (agent *gorequest.SuperAgent) {
  26. agent = gorequest.New()
  27. agent.TLSClientConfig(&tls.Config{InsecureSkipVerify: true})
  28. return
  29. }
  30. //解析支付完成后的回调信息
  31. func ParseNotifyResult(req *http.Request) (notifyRsp *WeChatNotifyRequest, err error) {
  32. notifyRsp = new(WeChatNotifyRequest)
  33. defer req.Body.Close()
  34. err = xml.NewDecoder(req.Body).Decode(notifyRsp)
  35. if err != nil {
  36. return nil, err
  37. }
  38. return
  39. }
  40. type WeChatNotifyResponse struct {
  41. ReturnCode string `xml:"return_code"`
  42. ReturnMsg string `xml:"return_msg"`
  43. }
  44. //返回数据给微信
  45. func (this *WeChatNotifyResponse) ToXmlString() (xmlStr string) {
  46. buffer := new(bytes.Buffer)
  47. buffer.WriteString("<xml><return_code><![CDATA[")
  48. buffer.WriteString(this.ReturnCode)
  49. buffer.WriteString("]]></return_code>")
  50. buffer.WriteString("<return_msg><![CDATA[")
  51. buffer.WriteString(this.ReturnMsg)
  52. buffer.WriteString("]]></return_msg></xml>")
  53. xmlStr = buffer.String()
  54. return
  55. }
  56. //支付通知的签名验证和参数签名后的Sign
  57. // apiKey:API秘钥值
  58. // signType:签名类型 MD5 或 HMAC-SHA256(默认请填写 MD5)
  59. // notifyRsp:利用 gopay.ParseNotifyResult() 得到的结构体
  60. // 返回参数ok:是否验证通过
  61. // 返回参数sign:根据参数计算的sign值,非微信返回参数中的Sign
  62. func VerifyPayResultSign(apiKey string, signType string, notifyRsp *WeChatNotifyRequest) (ok bool, sign string) {
  63. body := make(BodyMap)
  64. body.Set("return_code", notifyRsp.ReturnCode)
  65. body.Set("return_msg", notifyRsp.ReturnMsg)
  66. body.Set("appid", notifyRsp.Appid)
  67. body.Set("mch_id", notifyRsp.MchId)
  68. body.Set("device_info", notifyRsp.DeviceInfo)
  69. body.Set("nonce_str", notifyRsp.NonceStr)
  70. body.Set("sign_type", notifyRsp.SignType)
  71. body.Set("result_code", notifyRsp.ResultCode)
  72. body.Set("err_code", notifyRsp.ErrCode)
  73. body.Set("err_code_des", notifyRsp.ErrCodeDes)
  74. body.Set("openid", notifyRsp.Openid)
  75. body.Set("is_subscribe", notifyRsp.IsSubscribe)
  76. body.Set("trade_type", notifyRsp.TradeType)
  77. body.Set("bank_type", notifyRsp.BankType)
  78. body.Set("total_fee", notifyRsp.TotalFee)
  79. body.Set("settlement_total_fee", notifyRsp.SettlementTotalFee)
  80. body.Set("fee_type", notifyRsp.FeeType)
  81. body.Set("cash_fee", notifyRsp.CashFee)
  82. body.Set("cash_fee_type", notifyRsp.CashFeeType)
  83. body.Set("coupon_fee", notifyRsp.CouponFee)
  84. body.Set("coupon_count", notifyRsp.CouponCount)
  85. body.Set("coupon_type_0", notifyRsp.CouponType0)
  86. body.Set("coupon_id_0", notifyRsp.CouponId0)
  87. body.Set("coupon_fee_$n", notifyRsp.CouponFee0)
  88. body.Set("transaction_id", notifyRsp.TransactionId)
  89. body.Set("out_trade_no", notifyRsp.OutTradeNo)
  90. body.Set("attach", notifyRsp.Attach)
  91. body.Set("time_end", notifyRsp.TimeEnd)
  92. newBody := make(BodyMap)
  93. for k, v := range body {
  94. vStr := convert2String(v)
  95. if vStr != "" && vStr != "0" {
  96. newBody.Set(k, v)
  97. }
  98. }
  99. signStr := sortSignParams(apiKey, newBody)
  100. var hashSign []byte
  101. if signType == SignType_MD5 {
  102. hash := md5.New()
  103. hash.Write([]byte(signStr))
  104. hashSign = hash.Sum(nil)
  105. } else {
  106. hash := hmac.New(sha256.New, []byte(apiKey))
  107. hash.Write([]byte(signStr))
  108. hashSign = hash.Sum(nil)
  109. }
  110. sign = strings.ToUpper(hex.EncodeToString(hashSign))
  111. ok = sign == notifyRsp.Sign
  112. return
  113. }
  114. //JSAPI支付,统一下单获取支付参数后,再次计算出小程序用的paySign
  115. func GetMiniPaySign(appId, nonceStr, prepayId, signType, timeStamp, apiKey string) (paySign string) {
  116. buffer := new(bytes.Buffer)
  117. buffer.WriteString("appId=")
  118. buffer.WriteString(appId)
  119. buffer.WriteString("&nonceStr=")
  120. buffer.WriteString(nonceStr)
  121. buffer.WriteString("&package=")
  122. buffer.WriteString(prepayId)
  123. buffer.WriteString("&signType=")
  124. buffer.WriteString(signType)
  125. buffer.WriteString("&timeStamp=")
  126. buffer.WriteString(timeStamp)
  127. buffer.WriteString("&key=")
  128. buffer.WriteString(apiKey)
  129. signStr := buffer.String()
  130. var hashSign []byte
  131. if signType == SignType_MD5 {
  132. hash := md5.New()
  133. hash.Write([]byte(signStr))
  134. hashSign = hash.Sum(nil)
  135. } else {
  136. hash := hmac.New(sha256.New, []byte(apiKey))
  137. hash.Write([]byte(signStr))
  138. hashSign = hash.Sum(nil)
  139. }
  140. paySign = strings.ToUpper(hex.EncodeToString(hashSign))
  141. return
  142. }
  143. //JSAPI支付,统一下单获取支付参数后,再次计算出微信内H5支付需要用的paySign
  144. func GetH5PaySign(appId, nonceStr, prepayId, signType, timeStamp, apiKey string) (paySign string) {
  145. buffer := new(bytes.Buffer)
  146. buffer.WriteString("appId=")
  147. buffer.WriteString(appId)
  148. buffer.WriteString("&nonceStr=")
  149. buffer.WriteString(nonceStr)
  150. buffer.WriteString("&package=")
  151. buffer.WriteString(prepayId)
  152. buffer.WriteString("&signType=")
  153. buffer.WriteString(signType)
  154. buffer.WriteString("&timeStamp=")
  155. buffer.WriteString(timeStamp)
  156. buffer.WriteString("&key=")
  157. buffer.WriteString(apiKey)
  158. signStr := buffer.String()
  159. var hashSign []byte
  160. if signType == SignType_MD5 {
  161. hash := md5.New()
  162. hash.Write([]byte(signStr))
  163. hashSign = hash.Sum(nil)
  164. } else {
  165. hash := hmac.New(sha256.New, []byte(apiKey))
  166. hash.Write([]byte(signStr))
  167. hashSign = hash.Sum(nil)
  168. }
  169. paySign = strings.ToUpper(hex.EncodeToString(hashSign))
  170. return
  171. }
  172. //APP支付,统一下单获取支付参数后,再次计算APP支付所需要的的sign
  173. // signType:此处签名方式,务必与统一下单时用的签名方式一致
  174. func GetAppPaySign(appid, partnerid, noncestr, prepayid, signType, timestamp, apiKey string) (paySign string) {
  175. buffer := new(bytes.Buffer)
  176. buffer.WriteString("appid=")
  177. buffer.WriteString(appid)
  178. buffer.WriteString("&nonceStr=")
  179. buffer.WriteString(noncestr)
  180. buffer.WriteString("&package=Sign=WXPay")
  181. buffer.WriteString("&partnerid=")
  182. buffer.WriteString(partnerid)
  183. buffer.WriteString("&prepayid=")
  184. buffer.WriteString(prepayid)
  185. buffer.WriteString("&timeStamp=")
  186. buffer.WriteString(timestamp)
  187. buffer.WriteString("&key=")
  188. buffer.WriteString(apiKey)
  189. signStr := buffer.String()
  190. var hashSign []byte
  191. if signType == SignType_MD5 {
  192. hash := md5.New()
  193. hash.Write([]byte(signStr))
  194. hashSign = hash.Sum(nil)
  195. } else {
  196. hash := hmac.New(sha256.New, []byte(apiKey))
  197. hash.Write([]byte(signStr))
  198. hashSign = hash.Sum(nil)
  199. }
  200. paySign = strings.ToUpper(hex.EncodeToString(hashSign))
  201. return
  202. }
  203. //解密开放数据
  204. // encryptedData:包括敏感数据在内的完整用户信息的加密数据
  205. // iv:加密算法的初始向量
  206. // sessionKey:会话密钥
  207. // beanPtr:需要解析到的结构体指针
  208. func DecryptOpenDataToStruct(encryptedData, iv, sessionKey string, beanPtr interface{}) (err error) {
  209. //验证参数类型
  210. beanValue := reflect.ValueOf(beanPtr)
  211. if beanValue.Kind() != reflect.Ptr {
  212. return errors.New("传入beanPtr类型必须是以指针形式")
  213. }
  214. //验证interface{}类型
  215. if beanValue.Elem().Kind() != reflect.Struct {
  216. return errors.New("传入interface{}必须是结构体")
  217. }
  218. aesKey, _ := base64.StdEncoding.DecodeString(sessionKey)
  219. ivKey, _ := base64.StdEncoding.DecodeString(iv)
  220. cipherText, _ := base64.StdEncoding.DecodeString(encryptedData)
  221. if len(cipherText)%len(aesKey) != 0 {
  222. return errors.New("encryptedData is error")
  223. }
  224. //fmt.Println("cipherText:", cipherText)
  225. block, err := aes.NewCipher(aesKey)
  226. if err != nil {
  227. return err
  228. }
  229. //解密
  230. blockMode := cipher.NewCBCDecrypter(block, ivKey)
  231. plainText := make([]byte, len(cipherText))
  232. blockMode.CryptBlocks(plainText, cipherText)
  233. //fmt.Println("plainText1:", plainText)
  234. plainText = PKCS7UnPadding(plainText)
  235. //fmt.Println("plainText:", plainText)
  236. //解析
  237. err = json.Unmarshal(plainText, beanPtr)
  238. if err != nil {
  239. return err
  240. }
  241. return nil
  242. }
  243. //获取微信用户的OpenId、SessionKey、UnionId
  244. // appId:APPID
  245. // appSecret:AppSecret
  246. // wxCode:小程序调用wx.login 获取的code
  247. func Code2Session(appId, appSecret, wxCode string) (sessionRsp *Code2SessionRsp, err error) {
  248. sessionRsp = new(Code2SessionRsp)
  249. url := "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + wxCode + "&grant_type=authorization_code"
  250. agent := HttpAgent()
  251. _, _, errs := agent.Get(url).EndStruct(sessionRsp)
  252. if len(errs) > 0 {
  253. return nil, errs[0]
  254. } else {
  255. return sessionRsp, nil
  256. }
  257. }
  258. //获取小程序全局唯一后台接口调用凭据(AccessToken:157字符)
  259. // appId:APPID
  260. // appSecret:AppSecret
  261. func GetAccessToken(appId, appSecret string) (accessToken *AccessToken, err error) {
  262. accessToken = new(AccessToken)
  263. url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret
  264. agent := HttpAgent()
  265. _, _, errs := agent.Get(url).EndStruct(accessToken)
  266. if len(errs) > 0 {
  267. return nil, errs[0]
  268. } else {
  269. return accessToken, nil
  270. }
  271. }
  272. //用户支付完成后,获取该用户的 UnionId,无需用户授权。
  273. // accessToken:接口调用凭据
  274. // openId:用户的OpenID
  275. // transactionId:微信支付订单号
  276. func GetPaidUnionId(accessToken, openId, transactionId string) (unionId *PaidUnionId, err error) {
  277. unionId = new(PaidUnionId)
  278. url := "https://api.weixin.qq.com/wxa/getpaidunionid?access_token=" + accessToken + "&openid=" + openId + "&transaction_id=" + transactionId
  279. agent := HttpAgent()
  280. _, _, errs := agent.Get(url).EndStruct(unionId)
  281. if len(errs) > 0 {
  282. return nil, errs[0]
  283. } else {
  284. return unionId, nil
  285. }
  286. }
  287. //获取用户基本信息(UnionID机制)
  288. // accessToken:接口调用凭据
  289. // openId:用户的OpenID
  290. // lang:默认为 zh_CN ,可选填 zh_CN 简体,zh_TW 繁体,en 英语
  291. func GetWeChatUserInfo(accessToken, openId string, lang ...string) (userInfo *WeChatUserInfo, err error) {
  292. userInfo = new(WeChatUserInfo)
  293. var url string
  294. if len(lang) > 0 {
  295. url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openId + "&lang=" + lang[0]
  296. } else {
  297. url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN"
  298. }
  299. agent := HttpAgent()
  300. _, _, errs := agent.Get(url).EndStruct(userInfo)
  301. if len(errs) > 0 {
  302. return nil, errs[0]
  303. } else {
  304. return userInfo, nil
  305. }
  306. }