第三方api接口
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.

client.go 6.4 KiB

11 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. package jd
  2. import (
  3. "bytes"
  4. "crypto/md5"
  5. "crypto/tls"
  6. "encoding/hex"
  7. "encoding/json"
  8. "errors"
  9. "io"
  10. "io/ioutil"
  11. "net/http"
  12. "net/url"
  13. "sort"
  14. "strconv"
  15. "strings"
  16. "time"
  17. "github.com/bitly/go-simplejson"
  18. "github.com/nilorg/sdk/convert"
  19. )
  20. var (
  21. /**公用方法**/
  22. // Session 用户登录授权成功后,TOP颁发给应用的授权信息。当此API的标签上注明:“需要授权”,则此参数必传;“不需要授权”,则此参数不需要传;“可选授权”,则此参数为可选
  23. Session string
  24. // Timeout ...
  25. Timeout time.Duration
  26. // CacheExpiration 缓存过期时间
  27. CacheExpiration = time.Hour
  28. /**淘宝平台信息**/
  29. TaobaoAppKey string
  30. TaobaoAppSecret string
  31. TaobaoRouter string
  32. TaobaoVersion = "2.0"
  33. /**京东平台信息**/
  34. JDAppKey string
  35. JDAppSecret string
  36. JDRouter string
  37. JDVersion = "2.0"
  38. /**拼多多平台信息**/
  39. PDDAppKey string
  40. PDDAppSecret string
  41. PDDRouter string
  42. PDDVersion = "v1.0"
  43. /**考拉海购平台信息**/
  44. KaolaAppKey string
  45. KaolaAppSecret string
  46. KaolaRouter string
  47. KaolaVersion = "1.0"
  48. )
  49. // Arg 参数
  50. type Arg map[string]interface{}
  51. // copyArg 复制参数
  52. func copyArg(srcArgs Arg) Arg {
  53. newArgs := make(Arg)
  54. for key, value := range srcArgs {
  55. newArgs[key] = value
  56. }
  57. return newArgs
  58. }
  59. // newCacheKey 创建缓存Key
  60. func newCacheKey(p Arg) string {
  61. cpArgs := copyArg(p)
  62. delete(cpArgs, "session")
  63. delete(cpArgs, "timestamp")
  64. delete(cpArgs, "sign")
  65. keys := []string{}
  66. for k := range cpArgs {
  67. keys = append(keys, k)
  68. }
  69. // 排序asc
  70. sort.Strings(keys)
  71. // 把所有参数名和参数值串在一起
  72. cacheKeyBuf := new(bytes.Buffer)
  73. for _, k := range keys {
  74. cacheKeyBuf.WriteString(k)
  75. cacheKeyBuf.WriteString("=")
  76. cacheKeyBuf.WriteString(interfaceToString(cpArgs[k]))
  77. }
  78. h := md5.New()
  79. io.Copy(h, cacheKeyBuf)
  80. return hex.EncodeToString(h.Sum(nil))
  81. }
  82. // execute 执行API接口
  83. func execute(p Arg, router string) (bytes []byte, err error) {
  84. err = checkConfig()
  85. if err != nil {
  86. return
  87. }
  88. var req *http.Request
  89. req, err = http.NewRequest("POST", router, strings.NewReader(p.getReqData()))
  90. if err != nil {
  91. return
  92. }
  93. tr := &http.Transport{
  94. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  95. }
  96. req.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
  97. httpClient := &http.Client{Transport: tr}
  98. httpClient.Timeout = Timeout
  99. var resp *http.Response
  100. resp, err = httpClient.Do(req)
  101. if err != nil {
  102. return
  103. }
  104. if resp.StatusCode != 200 {
  105. err = errors.New("请求错误:" + strconv.Itoa(resp.StatusCode))
  106. return
  107. }
  108. defer resp.Body.Close()
  109. bytes, err = ioutil.ReadAll(resp.Body)
  110. // fmt.Printf("\n\n\nreq: %v\n\nresp: %v\n\n\n", p.getReqData(), string(bytes))
  111. return
  112. }
  113. // Execute 执行API接口
  114. func Execute(method string, p Arg) (res *simplejson.Json, err error) {
  115. p, r := setReqData(p, method)
  116. var bodyBytes []byte
  117. bodyBytes, err = execute(p, r)
  118. if err != nil {
  119. return
  120. }
  121. return bytesToResult(bodyBytes)
  122. }
  123. func bytesToResult(bytes []byte) (res *simplejson.Json, err error) {
  124. res, err = simplejson.NewJson(bytes)
  125. if err != nil {
  126. return
  127. }
  128. if responseError, ok := res.CheckGet("error_response"); ok {
  129. if subMsg, subOk := responseError.CheckGet("sub_msg"); subOk {
  130. err = errors.New(subMsg.MustString())
  131. } else if zhDesc, descOk := responseError.CheckGet("zh_desc"); descOk {
  132. err = errors.New(zhDesc.MustString())
  133. } else {
  134. err = errors.New(responseError.Get("msg").MustString())
  135. }
  136. res = nil
  137. }
  138. return
  139. }
  140. // ExecuteCache 执行API接口,缓存
  141. // 检查配置
  142. func checkConfig() error {
  143. if TaobaoAppKey == "" && JDAppKey == "" && KaolaAppKey == "" && PDDAppKey == "" {
  144. return errors.New("至少需要设置一个平台参数")
  145. }
  146. return nil
  147. }
  148. //组装参数及添加公共参数
  149. func setReqData(p Arg, method string) (Arg, string) {
  150. platform := strings.Split(method, ".")[0]
  151. router := ""
  152. hh, _ := time.ParseDuration("8h")
  153. loc := time.Now().UTC().Add(hh)
  154. if platform == "taobao" {
  155. //淘宝
  156. p["timestamp"] = strconv.FormatInt(loc.Unix(), 10)
  157. p["partner_id"] = "Blant"
  158. p["app_key"] = TaobaoAppKey
  159. p["v"] = TaobaoVersion
  160. if Session != "" {
  161. p["session"] = Session
  162. }
  163. p["method"] = method
  164. p["format"] = "json"
  165. p["sign_method"] = "md5"
  166. // 设置签名
  167. p["sign"] = getSign(p, TaobaoAppSecret)
  168. router = TaobaoRouter
  169. } else if platform == "jd" {
  170. //京东
  171. param := p
  172. p = Arg{}
  173. p["param_json"] = param
  174. p["app_key"] = JDAppKey
  175. p["v"] = JDVersion
  176. p["timestamp"] = loc.Format("2006-01-02 15:04:05")
  177. p["method"] = method
  178. p["format"] = "json"
  179. p["sign_method"] = "md5"
  180. // 设置签名
  181. p["sign"] = getSign(p, JDAppSecret)
  182. router = JDRouter
  183. } else if platform == "pdd" {
  184. //拼多多
  185. p["type"] = method
  186. p["data_type"] = "json"
  187. p["version"] = PDDVersion
  188. p["client_id"] = PDDAppKey
  189. p["timestamp"] = strconv.FormatInt(loc.Unix(), 10)
  190. // 设置签名
  191. p["sign"] = getSign(p, PDDAppSecret)
  192. router = PDDRouter
  193. } else if platform == "kaola" {
  194. //考拉海购
  195. p["method"] = method
  196. p["v"] = KaolaVersion
  197. p["signMethod"] = "md5"
  198. p["unionId"] = KaolaAppKey
  199. p["timestamp"] = loc.Format("2006-01-02 15:04:05")
  200. // 设置签名
  201. p["sign"] = getSign(p, KaolaAppSecret)
  202. router = KaolaRouter
  203. } else if platform == "suning" {
  204. // TODO 苏宁
  205. } else if platform == "vip" {
  206. // TODO 唯品会
  207. }
  208. return p, router
  209. }
  210. // 获取请求数据
  211. func (p Arg) getReqData() string {
  212. // 公共参数
  213. args := url.Values{}
  214. // 请求参数
  215. for key, val := range p {
  216. args.Set(key, interfaceToString(val))
  217. }
  218. return args.Encode()
  219. }
  220. // 获取签名
  221. func getSign(p Arg, secret string) string {
  222. // 获取Key
  223. var keys []string
  224. for k := range p {
  225. keys = append(keys, k)
  226. }
  227. // 排序asc
  228. sort.Strings(keys)
  229. // 把所有参数名和参数值串在一起
  230. query := bytes.NewBufferString(secret)
  231. for _, k := range keys {
  232. query.WriteString(k)
  233. query.WriteString(interfaceToString(p[k]))
  234. }
  235. query.WriteString(secret)
  236. // 使用MD5加密
  237. h := md5.New()
  238. _, _ = io.Copy(h, query)
  239. // 把二进制转化为大写的十六进制
  240. return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
  241. }
  242. func interfaceToString(src interface{}) string {
  243. if src == nil {
  244. panic(ErrTypeIsNil)
  245. }
  246. switch src.(type) {
  247. case string:
  248. return src.(string)
  249. case int, int8, int32, int64:
  250. case uint8, uint16, uint32, uint64:
  251. case float32, float64:
  252. return convert.ToString(src)
  253. }
  254. data, err := json.Marshal(src)
  255. if err != nil {
  256. panic(err)
  257. }
  258. return string(data)
  259. }