wxpay.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. package platformPay
  2. import (
  3. "crypto/hmac"
  4. "crypto/sha256"
  5. "encoding/hex"
  6. "fmt"
  7. "github.com/sirupsen/logrus"
  8. "leafstalk/log"
  9. "os"
  10. "sort"
  11. "strconv"
  12. "strings"
  13. "time"
  14. )
  15. var (
  16. //appId string
  17. //appSecret string
  18. //
  19. //offerId string
  20. //midasSecret string
  21. //
  22. wxAccessToken string
  23. wxAccessTokenExpires = time.Now().Unix()
  24. getBalanceUri = "/cgi-bin/midas/getbalance"
  25. payUri = "/cgi-bin/midas/pay"
  26. cancelPayUri = "/cgi-bin/midas/cancelpay"
  27. presentUri = "/cgi-bin/midas/present"
  28. )
  29. func CreateOutTradeNo(id int64) string {
  30. n := time.Now().Unix()
  31. return fmt.Sprintf("%v%vS%vU%d", time.Now().Format("20060102150405"), 999, id, n%65536)
  32. }
  33. func hmacSha256(msg string, secret string) string {
  34. k := []byte(secret)
  35. h := hmac.New(sha256.New, k)
  36. h.Write([]byte(msg))
  37. sha := hex.EncodeToString(h.Sum(nil))
  38. return sha
  39. //base64.StdEncoding.EncodeToString([]byte(sha))
  40. }
  41. func MakeGetBalanceSig(md map[string]interface{}, uri string) string {
  42. var str string
  43. var ms []string
  44. for k, v := range md {
  45. switch v := v.(type) {
  46. case string:
  47. str = k + "=" + v
  48. ms = append(ms, str)
  49. case int:
  50. str = k + "=" + strconv.FormatInt(int64(v), 10)
  51. ms = append(ms, str)
  52. case int64:
  53. str = k + "=" + strconv.FormatInt(v, 10)
  54. ms = append(ms, str)
  55. }
  56. }
  57. sort.Strings(ms)
  58. str = "org_loc=" + uri
  59. ms = append(ms, str)
  60. str = "method=POST"
  61. ms = append(ms, str)
  62. str = "secret=" + os.Getenv("WXPAY_MIDASSECRET")
  63. ms = append(ms, str)
  64. var nstr string = strings.Join(ms, "&")
  65. r := hmacSha256(nstr, os.Getenv("WXPAY_MIDASSECRET"))
  66. return r
  67. }
  68. //SIG
  69. //requestData := make(map[string]interface{})
  70. // requestData["openid"] = "odkx20ENSNa2w5y3g_qOkOvBNM1g"
  71. // requestData["appid"] = "wx1234567"
  72. // requestData["offer_id"] = "12345678"
  73. // requestData["ts"] = int64(1507530737)
  74. // requestData["zone_id"] = "1"
  75. // requestData["pf"] = "android"
  76. // midasSecret = "zNLgAGgqsEWJOg1nFVaO5r7fAlIQxr1u"
  77. //result: "1ad64e8dcb2ec1dc486b7fdf01f4a15159fc623dc3422470e51cf6870734726b"
  78. func GetAccessToken(platform int) string {
  79. if platform == int(WxPlat) {
  80. return wxAccessToken
  81. } else {
  82. return dyAccessToken
  83. }
  84. }
  85. type ResponseGetBalance struct {
  86. ErrCode int `json:"errcode"`
  87. ErrMsg string `json:"errmsg"`
  88. Balance int `json:"balance"` //游戏币个数(包含赠送)
  89. GenBalance int `json:"gen_balance"` //赠送游戏币数量(赠送游戏币数量)
  90. FirstSave int `json:"first_save"` //是否满足历史首次充值
  91. SaveAmt int `json:"save_amt"` //累计充值金额的游戏币数量
  92. SaveSum int `json:"save_sum"` //历史总游戏币金额
  93. CostSum int `json:"cost_sum"` //历史总消费游戏币金额
  94. PresentSum int `json:"present_sum"` //历史累计收到赠送金额
  95. }
  96. // 查询余额
  97. func WxQueryBalance(openid string) (*ResponseGetBalance, error) {
  98. req := NewHttpRequest()
  99. requestData := make(map[string]interface{})
  100. requestData["openid"] = openid //"oNEBv5CU3aOz7FcWp5ZDlhCNc3-U"
  101. requestData["appid"] = os.Getenv("WXPAY_APPID") //"wx831355257c862ac2"
  102. requestData["offer_id"] = os.Getenv("WXPAY_OFFERID") //"1450030892"
  103. requestData["ts"] = time.Now().Unix()
  104. requestData["zone_id"] = "1"
  105. requestData["pf"] = "android"
  106. requestData["sig"] = MakeGetBalanceSig(requestData, getBalanceUri)
  107. requestData["access_token "] = wxAccessToken
  108. url := "https://api.weixin.qq.com" + getBalanceUri + "?access_token=" + wxAccessToken
  109. logrus.Warnf("url:%+v, requestData%+v", url, requestData)
  110. resp, err := req.JSON(requestData).Post(url)
  111. if err != nil {
  112. //log.Warnln(err)
  113. return nil, err
  114. }
  115. defer func() {
  116. if resp.GetBody() != nil {
  117. resp.GetBody().Close()
  118. }
  119. }()
  120. if resp.GetStatusCode() == 200 {
  121. res := new(ResponseGetBalance)
  122. err := resp.UnmarshalBody(res)
  123. if err != nil {
  124. return nil, err
  125. }
  126. //log.Warnf("%#v", res)
  127. return res, nil
  128. }
  129. logrus.Warnf("resp.GetStatusCode():%v", resp.GetStatusCode())
  130. return nil, nil
  131. }
  132. func WxQueryBalanceAndCheck(openid string) (*ResponseGetBalance, error) {
  133. rgb, err := WxQueryBalance(openid)
  134. if err != nil {
  135. return rgb, err
  136. }
  137. if rgb.ErrCode == 40001 || rgb.ErrCode == 41001 || rgb.ErrCode == 42001 {
  138. token, expired, err := QueryAccessToken(int(WxPlat), wxAccessTokenExpires)
  139. if err == nil {
  140. wxAccessToken = token
  141. wxAccessTokenExpires = expired
  142. return WxQueryBalance(openid)
  143. }
  144. }
  145. return rgb, err
  146. }
  147. type ResponsePay struct {
  148. ErrCode int `json:"errcode"`
  149. ErrMsg string `json:"errmsg"`
  150. BillNo string `json:"bill_no"`
  151. Balance int `json:"balance"` //预扣后的余额
  152. UsedGenAmt int `json:"used_gen_amt"` //本次扣的赠送币的金额
  153. }
  154. // 支付
  155. func WxPay(openid string, billNo string, amt int) (*ResponsePay, error) {
  156. req := NewHttpRequest()
  157. requestData := make(map[string]interface{})
  158. requestData["openid"] = openid //"oNEBv5CU3aOz7FcWp5ZDlhCNc3-U"
  159. requestData["appid"] = os.Getenv("WXPAY_APPID") //"wx831355257c862ac2"
  160. requestData["offer_id"] = os.Getenv("WXPAY_OFFERID") //"1450030892"
  161. requestData["ts"] = time.Now().Unix()
  162. requestData["zone_id"] = "1"
  163. requestData["pf"] = "android"
  164. requestData["amt"] = amt //123
  165. requestData["bill_no"] = billNo
  166. //requestData["pay_item"] = "钻石"
  167. requestData["sig"] = MakeGetBalanceSig(requestData, payUri)
  168. requestData["access_token "] = wxAccessToken
  169. url := "https://api.weixin.qq.com" + payUri + "?access_token=" + wxAccessToken
  170. //url := "https://api.weixin.qq.com/cgi-bin/midas/sandbox/pay?access_token=" + accessToken
  171. resp, err := req.JSON(requestData).Post(url)
  172. if err != nil {
  173. //log.Warnln(err)
  174. return nil, err
  175. }
  176. defer func() {
  177. if resp.GetBody() != nil {
  178. resp.GetBody().Close()
  179. }
  180. }()
  181. if resp.GetStatusCode() == 200 {
  182. res := new(ResponsePay)
  183. err := resp.UnmarshalBody(res)
  184. if err != nil {
  185. return nil, err
  186. }
  187. //log.Warnf("%#v", res)
  188. return res, nil
  189. }
  190. return nil, nil
  191. }
  192. func WxPayAndCheck(openid string, billNo string, amt int) (*ResponsePay, error) {
  193. var rp *ResponsePay
  194. var err error
  195. for i := 0; i < 3; i++ {
  196. rp, err = WxPay(openid, billNo, amt)
  197. if err != nil {
  198. log.Warnf("PayAndCheck error: %v", err)
  199. time.Sleep(time.Millisecond * 10)
  200. continue
  201. }
  202. if rp.ErrCode == 0 {
  203. return rp, err
  204. }
  205. if rp.ErrCode == 40001 || rp.ErrCode == 41001 || rp.ErrCode == 42001 {
  206. token, expired, err := QueryAccessToken(int(WxPlat), wxAccessTokenExpires)
  207. if err != nil {
  208. return rp, err
  209. }
  210. wxAccessToken = token
  211. wxAccessTokenExpires = expired
  212. continue
  213. }
  214. if rp.ErrCode < 0 {
  215. time.Sleep(time.Millisecond * 10)
  216. continue
  217. }
  218. return rp, err
  219. }
  220. return rp, err
  221. }
  222. type ResponseCancelPay struct {
  223. ErrCode int `json:"errcode"`
  224. ErrMsg string `json:"errmsg"`
  225. BillNo string `json:"bill_no"`
  226. }
  227. // 取消支付
  228. func CancelPay(openid string, billNo string) (*ResponseCancelPay, error) {
  229. req := NewHttpRequest()
  230. requestData := make(map[string]interface{})
  231. requestData["openid"] = openid //"oNEBv5CU3aOz7FcWp5ZDlhCNc3-U"
  232. requestData["appid"] = os.Getenv("WXPAY_APPID") //"wx831355257c862ac2"
  233. requestData["offer_id"] = os.Getenv("WXPAY_OFFERID") //"1450030892"
  234. requestData["ts"] = time.Now().Unix()
  235. requestData["zone_id"] = "1"
  236. requestData["pf"] = "android"
  237. requestData["bill_no"] = billNo
  238. //requestData["pay_item"] = "钻石"
  239. requestData["sig"] = MakeGetBalanceSig(requestData, cancelPayUri)
  240. requestData["access_token "] = wxAccessToken
  241. url := "https://api.weixin.qq.com" + cancelPayUri + "?access_token=" + wxAccessToken
  242. //url := "https://api.weixin.qq.com/cgi-bin/midas/sandbox/cancelpay?access_token=" + accessToken
  243. resp, err := req.JSON(requestData).Post(url)
  244. if err != nil {
  245. //log.Warnln(err)
  246. return nil, err
  247. }
  248. defer func() {
  249. if resp.GetBody() != nil {
  250. resp.GetBody().Close()
  251. }
  252. }()
  253. if resp.GetStatusCode() == 200 {
  254. res := new(ResponseCancelPay)
  255. resp.UnmarshalBody(res)
  256. //log.Warnf("%#v", res)
  257. return res, nil
  258. }
  259. return nil, nil
  260. }
  261. type ResponsePresent struct {
  262. ErrCode int `json:"errcode"`
  263. ErrMsg string `json:"errmsg"`
  264. Balance int `json:"balance"`
  265. BillNo string `json:"bill_no"`
  266. }
  267. // 赠送
  268. func Present(openid string, billNo string, presendCount int) (*ResponsePresent, error) {
  269. req := NewHttpRequest()
  270. requestData := make(map[string]interface{})
  271. requestData["openid"] = openid //"oNEBv5CU3aOz7FcWp5ZDlhCNc3-U"
  272. requestData["appid"] = os.Getenv("WXPAY_APPID") //"wx831355257c862ac2"
  273. requestData["offer_id"] = os.Getenv("WXPAY_OFFERID") //"1450030892"
  274. requestData["ts"] = time.Now().Unix()
  275. requestData["zone_id"] = "1"
  276. requestData["pf"] = "android"
  277. requestData["bill_no"] = billNo
  278. requestData["present_counts"] = presendCount
  279. requestData["sig"] = MakeGetBalanceSig(requestData, presentUri)
  280. requestData["access_token "] = wxAccessToken
  281. url := "https://api.weixin.qq.com" + presentUri + "?access_token=" + wxAccessToken
  282. //url := "https://api.weixin.qq.com/cgi-bin/midas/sandbox/present?access_token=" + accessToken
  283. resp, err := req.JSON(requestData).Post(url)
  284. if err != nil {
  285. //log.Warnln(err)
  286. return nil, err
  287. }
  288. defer func() {
  289. if resp.GetBody() != nil {
  290. resp.GetBody().Close()
  291. }
  292. }()
  293. if resp.GetStatusCode() == 200 {
  294. res := new(ResponsePresent)
  295. resp.UnmarshalBody(res)
  296. //log.Warnf("%#v", res)
  297. return res, nil
  298. }
  299. return nil, nil
  300. }