pay.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. package dao
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/md5"
  6. "encoding/hex"
  7. "encoding/json"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "net/url"
  12. "sort"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "go-common/app/service/main/vip/model"
  17. "go-common/library/ecode"
  18. "go-common/library/log"
  19. "github.com/pkg/errors"
  20. )
  21. const (
  22. //pay url
  23. _addPayOrder = "/api/add.pay.order"
  24. _paySDK = "/api/pay.sdk"
  25. _quickPayToken = "/api/quick.pay.token"
  26. _quickPayDo = "/api/quick.pay.do"
  27. _payQrcode = "/api/pay.qrcode"
  28. _payCashier = "/api/pay.cashier"
  29. _payIapAccess = "/api/add.iap.access"
  30. _payBanks = "/api/pay.banks"
  31. _payWallet = "/api/v2/user/account"
  32. _payRescission = "/payplatform/pay/rescission"
  33. _payClose = "/payplatform/pay/cancel"
  34. _createPayQrCode = "/payplatform/qrcode/createPayQrCode"
  35. _payRemark = "购买大会员服务%d个月"
  36. _merchantID = 17
  37. _productMonthID = 60
  38. _productQuarterID = 61
  39. _productYearID = 62
  40. _quarterMonths = 3
  41. _yearMonths = 12
  42. _iapQuantity = "1"
  43. _minRead = 1024 * 64
  44. _retry = 3
  45. _defversion = "1.0"
  46. _defsigntype = "MD5"
  47. )
  48. //BasicResp pay response.
  49. type BasicResp struct {
  50. Code int `json:"code"`
  51. Message string `json:"messge"`
  52. Data *interface{} `json:"data"`
  53. }
  54. // merchantProduct get Product id by months
  55. func (d *Dao) productID(months int16) (id int8) {
  56. id = _productMonthID
  57. if months >= _quarterMonths && months < _yearMonths {
  58. id = _productQuarterID
  59. } else if months >= _yearMonths {
  60. id = _productYearID
  61. }
  62. return
  63. }
  64. //PayWallet .
  65. func (d *Dao) PayWallet(c context.Context, mid int64, ip string, data *model.PayAccountResp) (err error) {
  66. val := url.Values{}
  67. val.Add("mid", fmt.Sprintf("%d", mid))
  68. return d.dopay(c, _payWallet, ip, val, data, d.client.Post)
  69. }
  70. //PayBanks .
  71. func (d *Dao) PayBanks(c context.Context, ip string, data []*model.PayBankResp) (err error) {
  72. val := url.Values{}
  73. return d.dopay(c, _payBanks, ip, val, data, d.client.Get)
  74. }
  75. //AddPayOrder add pay order.
  76. func (d *Dao) AddPayOrder(c context.Context, ip string, o *model.PayOrder, data *model.AddPayOrderResp) (err error) {
  77. val := url.Values{}
  78. val.Add("mid", fmt.Sprintf("%d", o.Mid))
  79. val.Add("to_mid", fmt.Sprintf("%d", o.ToMid))
  80. val.Add("money", fmt.Sprintf("%f", o.Money))
  81. val.Add("remark", fmt.Sprintf(_payRemark, o.BuyMonths))
  82. val.Add("subject", fmt.Sprintf(_payRemark, o.BuyMonths))
  83. val.Add("out_trade_no", o.OrderNo)
  84. val.Add("notify_url", d.c.Property.NotifyURL)
  85. val.Add("merchant_id", fmt.Sprintf("%d", _merchantID))
  86. val.Add("merchant_product_id", fmt.Sprintf("%d", d.productID(o.BuyMonths)))
  87. var platform string
  88. switch o.Platform {
  89. case model.DeviceIOS:
  90. platform = "1"
  91. case model.DeviceANDROID:
  92. platform = "2"
  93. default:
  94. platform = "3"
  95. }
  96. val.Add("platform_type", platform)
  97. return d.dopay(c, _addPayOrder, ip, val, data, d.client.Post)
  98. }
  99. // PaySDK moblie pay sdk.
  100. func (d *Dao) PaySDK(c context.Context, ip string, o *model.PayOrder, data *model.APIPayOrderResp, payCode string) (err error) {
  101. val := url.Values{}
  102. val.Add("money", fmt.Sprintf("%f", o.Money))
  103. val.Add("pay_order_no", o.OrderNo)
  104. val.Add("pay_type", payCode)
  105. return d.dopay(c, _paySDK, ip, val, data, d.client.Post)
  106. }
  107. // PayQrcode pay qrcode.
  108. func (d *Dao) PayQrcode(c context.Context, ip string, o *model.PayOrder, data *model.APIPayOrderResp, payCode string) (err error) {
  109. val := url.Values{}
  110. val.Add("money", fmt.Sprintf("%f", o.Money))
  111. val.Add("pay_order_no", o.OrderNo)
  112. val.Add("pay_type", payCode)
  113. return d.dopay(c, _payQrcode, ip, val, data, d.client.Post)
  114. }
  115. // QuickPayToken quick pay token.
  116. func (d *Dao) QuickPayToken(c context.Context, ip string, accessKey string, cookie []*http.Cookie, data *model.QucikPayResp) (err error) {
  117. params := make(map[string]string)
  118. params["access_key"] = accessKey
  119. //return d.dopay(c, _quickPayToken, ip, val, cookie, data, d.client.Post)
  120. return d.doPaySend(c, d.c.Property.PayURL, _quickPayToken, ip, cookie, nil, http.MethodPost, params, data, d.PaySign)
  121. }
  122. // DoQuickPay do quick pay.
  123. func (d *Dao) DoQuickPay(c context.Context, ip string, token string, thirdTradeNo string, data *model.PayRetResp) (err error) {
  124. val := url.Values{}
  125. val.Add("token", token)
  126. val.Add("pay_order_no", thirdTradeNo)
  127. return d.dopay(c, _quickPayDo, ip, val, data, d.client.Post)
  128. }
  129. // PayCashier pay cashier.
  130. func (d *Dao) PayCashier(c context.Context, ip string, o *model.PayOrder, data *model.APIPayOrderResp, payCode string, bankCode string) (err error) {
  131. val := url.Values{}
  132. val.Add("pay_order_no", o.ThirdTradeNo)
  133. val.Add("money", fmt.Sprintf("%f", o.Money))
  134. val.Add("pay_type", payCode)
  135. val.Add("bank_code", bankCode)
  136. return d.dopay(c, _payCashier, ip, val, data, d.client.Post)
  137. }
  138. // PayIapAccess pay iap access.
  139. func (d *Dao) PayIapAccess(c context.Context, ip string, o *model.PayOrder, data *model.APIPayOrderResp, productID string) (err error) {
  140. val := url.Values{}
  141. val.Add("mid", fmt.Sprintf("%d", o.Mid))
  142. val.Add("product_id", productID)
  143. val.Add("quantity", _iapQuantity)
  144. val.Add("money", fmt.Sprintf("%f", o.Money))
  145. val.Add("remark", fmt.Sprintf(_payRemark, o.BuyMonths))
  146. val.Add("pay_order_no", o.ThirdTradeNo)
  147. val.Add("merchant_id", fmt.Sprintf("%d", _merchantID))
  148. val.Add("merchant_product_id", fmt.Sprintf("%d", d.productID(o.BuyMonths)))
  149. return d.dopay(c, _payIapAccess, ip, val, data, d.client.Post)
  150. }
  151. //PayClose pay close.
  152. func (d *Dao) PayClose(c context.Context, orderNO string, ip string) (data *model.APIPayCancelResp, err error) {
  153. params := make(map[string]string)
  154. params["customerId"] = strconv.FormatInt(d.c.PayConf.CustomerID, 10)
  155. params["orderId"] = orderNO
  156. params["timestamp"] = fmt.Sprintf("%d", time.Now().Unix()*1000)
  157. params["traceId"] = model.UUID4()
  158. params["version"] = _defversion
  159. params["signType"] = _defsigntype
  160. sign := d.PaySign(params, d.c.PayConf.Token)
  161. params["sign"] = sign
  162. resp := new(
  163. struct {
  164. Code int64 `json:"errno"`
  165. Data *model.APIPayCancelResp `json:"data"`
  166. })
  167. header := make(map[string]string)
  168. header["Content-Type"] = "application/json"
  169. marshal, _ := json.Marshal(params)
  170. if err = d.doSend(c, d.payCloseURL, "127.0.0.1", header, marshal, resp); err != nil {
  171. err = errors.Wrapf(err, "Call pay service(%s)", d.payCloseURL+"?"+string(marshal))
  172. return
  173. }
  174. if resp.Code != 0 {
  175. err = fmt.Errorf("Call pay service(%s) error, response code is not 0, resp:%v", d.payCloseURL+"?"+string(marshal), resp)
  176. return
  177. }
  178. data = resp.Data
  179. log.Info("Call pay service(%s) successful, resp(%v)", d.payCloseURL+"?"+string(marshal), data)
  180. return
  181. }
  182. //PayQrCode pay qr code.
  183. func (d *Dao) PayQrCode(c context.Context, mid int64, orderID string, req map[string]interface{}) (data *model.PayQrCode, err error) {
  184. payParam, _ := json.Marshal(req)
  185. params := make(map[string]string)
  186. params["customerId"] = strconv.FormatInt(d.c.PayConf.CustomerID, 10)
  187. params["uid"] = fmt.Sprintf("%d", mid)
  188. params["orderId"] = orderID
  189. params["payParam"] = string(payParam)
  190. params["timestamp"] = fmt.Sprintf("%d", time.Now().Unix()*1000)
  191. params["traceId"] = model.UUID4()
  192. params["version"] = d.c.PayConf.Version
  193. params["signType"] = d.c.PayConf.SignType
  194. sign := d.PaySign(params, d.c.PayConf.Token)
  195. params["sign"] = sign
  196. resp := new(
  197. struct {
  198. Code int64 `json:"errno"`
  199. Data *model.PayQrCode `json:"data"`
  200. })
  201. url := d.c.Property.PayCoURL + _createPayQrCode
  202. header := make(map[string]string)
  203. header["Content-Type"] = "application/json"
  204. marshal, _ := json.Marshal(params)
  205. if err = d.doSend(c, url, "127.0.0.1", header, marshal, resp); err != nil {
  206. err = errors.Wrapf(err, "Call pay service(%s)", url+"?"+string(marshal))
  207. return
  208. }
  209. if resp.Code != 0 {
  210. err = fmt.Errorf("Call pay service(%s) error, response code is not 0, resp:%v", url+"?"+string(marshal), resp)
  211. return
  212. }
  213. data = resp.Data
  214. log.Info("Call pay service(%s) successful, resp(%v)", url+"?"+string(marshal), data)
  215. return
  216. }
  217. func (d *Dao) doSend(c context.Context, url, IP string, header map[string]string, marshal []byte, data interface{}) (err error) {
  218. var (
  219. req *http.Request
  220. client = new(http.Client)
  221. resp *http.Response
  222. bs []byte
  223. )
  224. if req, err = http.NewRequest(http.MethodPost, url, strings.NewReader(string(marshal))); err != nil {
  225. err = errors.WithStack(err)
  226. return
  227. }
  228. for k, v := range header {
  229. req.Header.Add(k, v)
  230. }
  231. if resp, err = client.Do(req); err != nil {
  232. err = errors.Wrapf(err, "Call pay service(%s)", url+"?"+string(marshal))
  233. return
  234. }
  235. defer resp.Body.Close()
  236. defer func() {
  237. log.Info("call url:%v params:(%v) result:(%+v) header:(%+v)", url, string(marshal), data, header)
  238. }()
  239. if resp.StatusCode >= http.StatusBadRequest {
  240. err = errors.Errorf("incorrect http status:%d host:%s, url:%s", resp.StatusCode, req.URL.Host, req.URL.String())
  241. return
  242. }
  243. if bs, err = readAll(resp.Body, _minRead); err != nil {
  244. err = errors.Wrapf(err, "host:%s, url:%s", req.URL.Host, req.URL.String())
  245. return
  246. }
  247. if err = json.Unmarshal(bs, data); err != nil {
  248. err = errors.WithStack(err)
  249. return
  250. }
  251. return
  252. }
  253. func (d *Dao) dopay(c context.Context,
  254. urlPath string, ip string, params url.Values, data interface{},
  255. fn func(c context.Context, uri string, ip string, params url.Values, r interface{}) error,
  256. ) (err error) {
  257. var (
  258. resp = &BasicResp{}
  259. urlAddr string
  260. )
  261. urlAddr = d.c.Property.PayURL + urlPath
  262. err = fn(c, urlAddr, ip, params, resp)
  263. if err != nil {
  264. err = errors.Wrapf(err, "Call pay service(%s)", urlAddr+"?"+params.Encode())
  265. return
  266. }
  267. if resp.Code != 0 {
  268. err = fmt.Errorf("Call pay service(%s) error, response code is not 0, resp:%v,%v", urlAddr+"?"+params.Encode(), resp, data)
  269. err = errors.WithStack(err)
  270. return
  271. }
  272. data = resp.Data
  273. log.Info("Call pay service(%s) successful, resp: %v", urlAddr+"?"+params.Encode(), resp)
  274. return
  275. }
  276. // PayRecission call pay refund api.
  277. func (d *Dao) PayRecission(c context.Context, params map[string]interface{}, clietIP string) (err error) {
  278. rel := new(struct {
  279. Code int64 `json:"errno"`
  280. Data int64 `json:"msg"`
  281. })
  282. paramsObj := make(map[string]string)
  283. for k, v := range params {
  284. paramsObj[k] = fmt.Sprintf("%v", v)
  285. }
  286. header := make(map[string]string)
  287. header["Content-Type"] = "application/json"
  288. defer func() {
  289. log.Info("url:%v params:%+v return:%+v error(%+v)", d.c.Property.PayURL+_payRescission, paramsObj, rel, err)
  290. }()
  291. success := false
  292. for i := 0; i < _retry; i++ {
  293. if err = d.doPaySend(c, d.c.Property.PayURL, _payRescission, clietIP, nil, header, http.MethodPost, paramsObj, rel, d.PaySignNotDel); err != nil {
  294. log.Error(" dopaysend(url:%v,params:%+v) return(%+v) ", d.c.Property.PayURL+_payRescission, paramsObj, rel)
  295. continue
  296. }
  297. if rel.Code == int64(ecode.OK.Code()) {
  298. success = true
  299. break
  300. }
  301. }
  302. if !success {
  303. err = ecode.VipRescissionErr
  304. }
  305. return
  306. }
  307. // PaySign pay sign.
  308. func (d *Dao) PaySign(params map[string]string, token string) (sign string) {
  309. delete(params, "payChannelId")
  310. delete(params, "payChannel")
  311. delete(params, "accessKey")
  312. delete(params, "sdkVersion")
  313. delete(params, "openId")
  314. delete(params, "sign")
  315. delete(params, "device")
  316. tmp := d.sortParamsKey(params)
  317. var b bytes.Buffer
  318. b.WriteString(tmp)
  319. b.WriteString(fmt.Sprintf("&token=%s", token))
  320. log.Info("pay sign params:(%s) \n", b.String())
  321. mh := md5.Sum(b.Bytes())
  322. // query
  323. sign = hex.EncodeToString(mh[:])
  324. log.Info("pay sign (%v)", sign)
  325. return
  326. }
  327. //PaySignNotDel pay sign not del.
  328. func (d *Dao) PaySignNotDel(params map[string]string, token string) (sign string) {
  329. tmp := d.sortParamsKey(params)
  330. var b bytes.Buffer
  331. b.WriteString(tmp)
  332. b.WriteString(fmt.Sprintf("&token=%s", token))
  333. log.Info("pay sign params:(%s) \n", b.String())
  334. mh := md5.Sum(b.Bytes())
  335. // query
  336. sign = hex.EncodeToString(mh[:])
  337. log.Info("pay sign (%v)", sign)
  338. return
  339. }
  340. func (d *Dao) sortParamsKey(v map[string]string) string {
  341. if v == nil {
  342. return ""
  343. }
  344. var buf bytes.Buffer
  345. keys := make([]string, 0, len(v))
  346. for k := range v {
  347. keys = append(keys, k)
  348. }
  349. sort.Strings(keys)
  350. for _, k := range keys {
  351. vs := v[k]
  352. prefix := k + "="
  353. if buf.Len() > 0 {
  354. buf.WriteByte('&')
  355. }
  356. buf.WriteString(prefix)
  357. buf.WriteString(vs)
  358. }
  359. return buf.String()
  360. }
  361. func (d *Dao) doPaySend(c context.Context, basePath, path, IP string, cookie []*http.Cookie, header map[string]string, method string, params map[string]string, data interface{}, signFn func(params map[string]string, token string) string) (err error) {
  362. var (
  363. req *http.Request
  364. client = new(http.Client)
  365. resp *http.Response
  366. bs []byte
  367. )
  368. url := basePath + path
  369. sign := signFn(params, d.c.PayConf.Token)
  370. params["sign"] = sign
  371. marshal, _ := json.Marshal(params)
  372. if req, err = http.NewRequest(method, url, strings.NewReader(string(marshal))); err != nil {
  373. err = errors.WithStack(err)
  374. return
  375. }
  376. for _, v := range cookie {
  377. req.AddCookie(v)
  378. }
  379. for k, v := range header {
  380. req.Header.Add(k, v)
  381. }
  382. if resp, err = client.Do(req); err != nil {
  383. log.Error("call url:%v params:(%+v)", basePath+path, params)
  384. err = errors.WithStack(err)
  385. return
  386. }
  387. defer resp.Body.Close()
  388. if resp.StatusCode >= http.StatusBadRequest {
  389. err = errors.Errorf("incorrect http status:%d host:%s, url:%s", resp.StatusCode, req.URL.Host, req.URL.String())
  390. return
  391. }
  392. if bs, err = readAll(resp.Body, _minRead); err != nil {
  393. err = errors.Wrapf(err, "host:%s, url:%s", req.URL.Host, req.URL.String())
  394. return
  395. }
  396. if err = json.Unmarshal(bs, data); err != nil {
  397. err = errors.WithStack(err)
  398. return
  399. }
  400. log.Info("call url:%v params:%+v result:%+v", url, params, data)
  401. return
  402. }
  403. func readAll(r io.Reader, capacity int64) (b []byte, err error) {
  404. buf := bytes.NewBuffer(make([]byte, 0, capacity))
  405. // If the buffer overflows, we will get bytes.ErrTooLarge.
  406. // Return that as an error. Any other panic remains.
  407. defer func() {
  408. e := recover()
  409. if e == nil {
  410. return
  411. }
  412. if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
  413. err = panicErr
  414. } else {
  415. panic(e)
  416. }
  417. }()
  418. _, err = buf.ReadFrom(r)
  419. return buf.Bytes(), err
  420. }