wechat_servier_api.go 12 KB

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