JdSdk.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: LZP
  5. * Date: 2022/4/14
  6. * Time: 15:21
  7. */
  8. namespace App\Com;
  9. use App\Com\Redis;
  10. use App\Models\JdConfig;
  11. use EasySwoole\EasySwoole\Logger;
  12. class JdSdk
  13. {
  14. protected $host = "https://bizapi.jd.com";
  15. protected $edition = "";
  16. protected $grant_type = "access_token";
  17. protected $scope = "";
  18. protected $time_stamp = "";
  19. // protected $client_id = 'FlCa4YWCw3m7XcrD2ki0';
  20. // protected $client_secret = 'zWkZHHRc6hwJmCy0XRQq';
  21. // 企业内购
  22. // protected $username = '中航物业VOP';
  23. // protected $password = '123000';
  24. // 福利商城
  25. // protected $username = '中航物业员工福利';
  26. // protected $password = '123000';
  27. protected $client_id = '';
  28. protected $client_secret = '';
  29. protected $username = '';
  30. protected $password = '';
  31. //protected $access_token = "NIwoLn95BLs0ZdKLyawIiuFPM";
  32. protected $access_token = "";
  33. protected $timeout = 30;
  34. protected $connecttimeout = 30;
  35. protected $ssl_verifypeer = FALSE;
  36. protected $useragent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36';
  37. protected $format = 'form';// form || json
  38. protected $decode_json = true;
  39. protected $debug = false;
  40. protected $params_base = array();
  41. protected $redis = null; //redis客户端
  42. protected static $boundary = '';
  43. function __construct()
  44. {
  45. $this->time_stamp = date('Y-m-d H:i:s', time());//时间 yyyyMMddhhmmss
  46. // $this->params_base['IMEI'] = '0000';//设备串号(移动终端设备串号,默认0000)
  47. // $this->params_base['apiVersion'] = $this->api_version;//接口版本号(服务端向下版本兼容控制),默认值为1
  48. // $this->params_base['SIGN'] = md5($this->params_base['IMEI']."&".$this->client_id."&".$this->params_base['timeStamp']);
  49. // echo $this->params_base['IMEI']."&".$this->client_id."&".$this->params_base['timeStamp'];
  50. // 设定default时产生
  51. //加载redis
  52. $this->redis = Redis::getInstance()->getConnect();
  53. //从redis中拿参数..这些参数来自于数据库ims_superdesk_jd_vop_configs表..然后设置默认的时候存到了redis zjh 2018年8月17日 14:22:06
  54. $key = 'superdesk_jd_vop_' . 'web_jd_vop_configs_manage' . ':' . 17;
  55. $api_conf = $this->redis->get($key);
  56. if (!empty($api_conf)) {
  57. $api_conf = json_decode($api_conf, true);
  58. $this->client_id = $api_conf['client_id'];
  59. $this->client_secret = $api_conf['client_secret'];
  60. $this->username = $api_conf['username'];
  61. $this->password = $api_conf['password'];
  62. } else {
  63. $api_conf = JdConfig::create()->where('is_default',1)->get()->toArray();
  64. if(!empty($api_conf)){
  65. $this->client_id = $api_conf['client_id'];
  66. $this->client_secret = $api_conf['client_secret'];
  67. $this->username = $api_conf['username'];
  68. $this->password = $api_conf['password'];
  69. } else {
  70. //手动配置 201998
  71. $this->client_id = 'nuAeRH1KFlSeiIrfPYTZ';
  72. $this->client_secret = 'ioyqcauADf0v3ayE73I5';
  73. $this->username = '中航物业员工福利';
  74. $this->password = 'avic-s';
  75. }
  76. }
  77. $this->init_access_token();
  78. }
  79. function init_access_token()
  80. {
  81. /******************************************************* redis *********************************************************/
  82. $table__key = 'superdesk_jd_vop_' . 'sdk_access_token' . ':' . 17;
  83. $colunm_key = 'jd_vop_access_token';
  84. //microtime_format('Y-m-d H:i:s.x',microtime())
  85. $result = $this->redis->hget($table__key, $colunm_key);
  86. $result = json_decode($result, true);
  87. // echo json_encode($result,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
  88. if (empty($result) || empty($result['result'])) {
  89. $result = $this->access_token();
  90. $this->redis->hset($table__key, $colunm_key, $result);
  91. $result = json_decode($result, true);
  92. }
  93. if (intval(floatval($result['result']['time']) / 1000) + intval($result['result']['expires_in']) <= time()) {
  94. $result = $this->refresh_token($result['result']['refresh_token']);
  95. $this->redis->hset($table__key, $colunm_key, $result);
  96. // BUG 当要去刷时 $result 变回字符串
  97. $result = json_decode($result, true);
  98. }
  99. $this->access_token = $result['result']['access_token'];
  100. // echo $this->access_token;
  101. // echo "<br/>";
  102. // echo date('Y-m-d H:i:s', floatval($result['result']['time']) / 1000);
  103. // echo "<br/>";
  104. // echo "<br/>";
  105. /******************************************************* redis *********************************************************/
  106. }
  107. /**
  108. * 签名,生成规则如下:
  109. * 1. 按照以下顺序将字符串拼接起来
  110. * client_secret+timestamp+client_id+username+password+grant_type+scope+client_secret
  111. * 其中
  112. * client_secret的值是京东分配的
  113. * username使用原文
  114. * password需要md5加密后的
  115. * 2. 将上述拼接的字符串使用MD5加密,加密后的值再转为大写
  116. */
  117. function create_sign()
  118. {
  119. $sign_str =
  120. $this->client_secret .
  121. $this->time_stamp . $this->client_id . $this->username . md5($this->password) . $this->grant_type . $this->scope .
  122. $this->client_secret;
  123. $sign_md5 = md5($sign_str);
  124. $sign_result = strtoupper($sign_md5);
  125. return $sign_result;
  126. }
  127. /**
  128. * TODO 已测 1.3 获取Access Token
  129. * HTTPS请求方式:POST
  130. *
  131. * @return string
  132. */
  133. function access_token_url()
  134. {
  135. return $this->host . $this->edition . "/oauth2/accessToken";
  136. }
  137. function access_token()
  138. {
  139. $params = array();
  140. $params['grant_type'] = $this->grant_type;//该值固定为access_token
  141. $params['client_id'] = $this->client_id;
  142. $params['client_secret'] = $this->client_secret;
  143. $params['timestamp'] = $this->time_stamp;
  144. $params['username'] = $this->username;
  145. $params['password'] = md5($this->password);
  146. $params['scope'] = $this->scope;//非必须 申请权限。(目前推荐为空。为以后扩展用)
  147. $params['sign'] = $this->create_sign();
  148. $response = $this->post($this->access_token_url(), $params, true);
  149. // {
  150. // "success": true,
  151. // "resultMessage": "",
  152. // "resultCode": "0000",
  153. // "result": {
  154. // "uid": "6165718786",
  155. // "refresh_token_expires": 1525071742348,
  156. // "time": 1509346942348,
  157. // "expires_in": 86400,
  158. // "refresh_token": "A23k88ryXJx9QJBay7QEmdPcM0GoJk8uidR2pIjy",
  159. // "access_token": "NIwoLn95BLs0ZdKLyawIiuFPM"
  160. // }
  161. // }
  162. return $response;
  163. }
  164. /**
  165. * TODO 已测 1.4 使用Refresh Token 刷新 Access Token
  166. * HTTPS请求方式:POST
  167. *
  168. * @return string
  169. */
  170. function refresh_token_url()
  171. {
  172. return $this->host . $this->edition . "/oauth2/refreshToken";
  173. }
  174. function refresh_token($refresh_token)
  175. {
  176. $params = array();
  177. $params['refresh_token'] = $refresh_token;
  178. $params['client_id'] = $this->client_id;
  179. $params['client_secret'] = $this->client_secret;
  180. $response = $this->post($this->refresh_token_url(), $params, true);
  181. // {
  182. // "success": true,
  183. // "resultMessage": "",
  184. // "resultCode": "0000",
  185. // "result": {
  186. // "uid": "6165718786",
  187. // "refresh_token_expires": 1525077000000,
  188. // "time": 1509352200000,
  189. // "expires_in": 86400,
  190. // "refresh_token": "lhC8Btoft3GovRWad71v7b2RZ0ZQ45wQWjNmn46j",
  191. // "access_token": "yEZ3Ut9m1Np4XAGm8Qaq0f9s2"
  192. // }
  193. // }
  194. return $response;
  195. }
  196. //======================Other End========================
  197. /**
  198. * GET wrappwer for oAuthRequest.
  199. *
  200. * @return mixed
  201. */
  202. function get($url, $parameters = array())
  203. {
  204. $response = $this->oAuthRequest($url, 'GET', $parameters);
  205. if ($this->format === 'json' && $this->decode_json) {
  206. return json_decode($response, true);
  207. }
  208. return $response;
  209. }
  210. /**
  211. * POST wreapper for oAuthRequest.
  212. *
  213. * @return mixed
  214. */
  215. function post($url, $parameters = array(), $multi = false)
  216. {
  217. $response = $this->oAuthRequest($url, 'POST', $parameters, $multi);
  218. if ($this->format === 'json' && $this->decode_json) {
  219. return json_decode($response, true);
  220. }
  221. return $response;
  222. }
  223. /**
  224. * Format and sign an OAuth / API request
  225. *
  226. * @return string
  227. * @ignore
  228. */
  229. function oAuthRequest($url, $method, $params, $multi = false)
  230. {
  231. if (strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0) {
  232. $url = "{$this->host}/{$this->edition}/{$url}";
  233. } else {
  234. $url = $url . '?client_id=' . $this->client_id . '&client_secret=' . $this->client_secret;
  235. }
  236. $parameters = array_merge_recursive($this->params_base, $params);
  237. // TODO jd_vop_api_logs
  238. switch ($method) {
  239. case 'GET' :
  240. $url = $url . http_build_query($parameters);
  241. return $this->http($url, 'GET');
  242. case 'PUT' :
  243. $headers = array();
  244. if (!$multi && (is_array($parameters) || is_object($parameters))) {
  245. if ($this->format === 'json' && $this->decode_json) {
  246. $body = json_encode($parameters);
  247. if ($this->debug) {
  248. echo '============= BODY =============';
  249. echo '<br/>';
  250. echo $body;
  251. echo '<br/>';
  252. echo '============= BODY =============';
  253. echo '<br/>';
  254. }
  255. } else {
  256. $body = http_build_query($parameters);
  257. }
  258. } else {
  259. $body = self::build_http_query_multi($parameters);
  260. $headers [] = "Content-Type: multipart/form-data; boundary=" . self::$boundary;
  261. }
  262. return $this->http($url, 'PUT', $body, $headers);
  263. case 'DELETE' :
  264. if (!empty($parameters)) {
  265. $url = $url . '&' . http_build_query($parameters);
  266. }
  267. return $this->http($url, 'DELETE');
  268. default :
  269. $headers = array();
  270. if (!$multi && (is_array($parameters) || is_object($parameters))) {
  271. if ($this->format === 'json' && $this->decode_json) {
  272. $body = json_encode($parameters);
  273. if ($this->debug) {
  274. echo '============= BODY =============';
  275. echo '<br/>';
  276. echo $body;
  277. echo '<br/>';
  278. echo '============= BODY =============';
  279. echo '<br/>';
  280. }
  281. } else {
  282. $body = http_build_query($parameters);
  283. }
  284. } else {
  285. $body = self::build_http_query_multi($parameters);
  286. $headers [] = "Content-Type: multipart/form-data; boundary=" . self::$boundary;
  287. }
  288. return $this->http($url, $method, $body, $headers);
  289. }
  290. }
  291. /**
  292. * Make an HTTP request
  293. *
  294. * @return string API results
  295. * @ignore
  296. */
  297. function http($url, $method, $postfields = NULL, $headers = array())
  298. {
  299. $this->http_info = array();
  300. $ci = curl_init();
  301. /* Curl settings */
  302. curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  303. curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
  304. curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
  305. curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
  306. curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
  307. curl_setopt($ci, CURLOPT_ENCODING, "");
  308. curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
  309. // curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
  310. curl_setopt($ci, CURLOPT_HEADER, FALSE);
  311. switch ($method) {
  312. case 'GET':
  313. curl_setopt($ci, CURLOPT_HTTPGET, TRUE);
  314. if (!empty($postfields)) {
  315. curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
  316. $this->postdata = $postfields;
  317. }
  318. break;
  319. case 'POST':
  320. curl_setopt($ci, CURLOPT_POST, TRUE);
  321. if ($this->format === 'json') {
  322. $headers [] = "Content-Type: application/json;";
  323. }
  324. if (!empty($postfields)) {
  325. curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
  326. $this->postdata = $postfields;
  327. }
  328. break;
  329. case 'PUT':
  330. curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'PUT');
  331. $headers [] = "Content-Type: application/json;";
  332. if (!empty($postfields)) {
  333. curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
  334. $this->postdata = $postfields;
  335. }
  336. break;
  337. case 'DELETE':
  338. curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
  339. if (!empty($postfields)) {
  340. $url = "{$url}?{$postfields}";
  341. echo $url;
  342. echo '<br/>';
  343. } else {
  344. $url = $url;
  345. }
  346. break;
  347. }
  348. // $headers[] = "API-RemoteIP: " . $_SERVER['REMOTE_ADDR'];
  349. curl_setopt($ci, CURLOPT_URL, $url);
  350. curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
  351. curl_setopt($ci, CURLINFO_HEADER_OUT, TRUE);
  352. $response = curl_exec($ci);
  353. $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
  354. $this->http_info = array_merge($this->http_info, curl_getinfo($ci));
  355. $this->url = $url;
  356. if ($this->debug) {
  357. echo "=====post data======\r\n";
  358. echo '<br/>';
  359. var_dump($postfields);
  360. echo '<br/>';
  361. echo '=====info=====' . "\r\n";
  362. echo '<br/>';
  363. print_r(curl_getinfo($ci));
  364. echo '<br/>';
  365. echo '=====$response=====' . "\r\n";
  366. echo '<br/>';
  367. print_r($response);
  368. echo '<br/>';
  369. }
  370. $code = curl_getinfo($ci);
  371. $response = json_decode($response, true);
  372. if (!$response) {// TODO 暂时修正京东返回空的问题
  373. $response = array();
  374. }
  375. $response['code'] = $code['http_code'];
  376. $___url___ = str_replace($this->host . $this->edition, '', $url);
  377. $log_data = array();
  378. $log_data['url'] = $url;
  379. $log_data['api'] = substr($___url___, 0, strpos($___url___, '?'));
  380. $log_data['method'] = $method;
  381. $log_data['post_fields'] = $postfields;
  382. $log_data['curl_info'] = json_encode(curl_getinfo($ci), JSON_UNESCAPED_UNICODE);
  383. $log_data['success'] = isset($response['success']) ? $response['success'] : "exit0";
  384. $log_data['resultMessage'] = isset($response['resultMessage']) ? $response['resultMessage'] : "exit0";
  385. $log_data['resultCode'] = isset($response['resultCode']) ? $response['resultCode'] : "exit0";
  386. $log_data['result'] = isset($response['result']) ? json_encode($response['result'], JSON_UNESCAPED_UNICODE) : "";
  387. /************************** 2019年12月31日 09:42:42 zjh 改数据库为文件 *************************************/
  388. // $this->_logsModel->insert($log_data);// TODO 屏蔽测试
  389. //日志记录
  390. Logger::getInstance()->info('京东请求日志:' . json_encode($log_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
  391. /************************** 2019年12月31日 09:42:42 zjh 改数据库为文件 *************************************/
  392. if (!empty($response['status']) && $response['status'] == 'Unauthorized.') {
  393. /* header("Location:".site_url("Login/logout"));
  394. exit();*/
  395. $response['status'] = 'Unauthorized.';
  396. }
  397. $response = json_encode($response);
  398. curl_close($ci);
  399. return $response;
  400. }
  401. /**
  402. * 处理多媒体数据内容
  403. *
  404. * @ignore
  405. */
  406. public static function build_http_query_multi($params)
  407. {
  408. if (!$params) return '';
  409. uksort($params, 'strcmp');
  410. $pairs = array();
  411. self::$boundary = $boundary = uniqid('------------------');
  412. $MPboundary = '--' . $boundary;
  413. $endMPboundary = $MPboundary . '--';
  414. $multipartbody = '';
  415. foreach ($params as $parameter => $value) {
  416. if (in_array($parameter, array('pic', 'image', 'Filedata')) && $value[0] == '@') {
  417. $url = ltrim($value, '@');
  418. $content = file_get_contents($url);
  419. $array = explode('?', basename($url));
  420. $filename = $array[0];
  421. $multipartbody .= $MPboundary . "\r\n";
  422. $multipartbody .= 'Content-Disposition: form-data; name="' . $parameter . '"; filename="' . $filename . '"' . "\r\n";
  423. $multipartbody .= "Content-Type: image/unknown\r\n\r\n";
  424. $multipartbody .= $content . "\r\n";
  425. } else {
  426. $multipartbody .= $MPboundary . "\r\n";
  427. $multipartbody .= 'content-disposition: form-data; name="' . $parameter . "\"\r\n\r\n";
  428. $multipartbody .= $value . "\r\n";
  429. }
  430. }
  431. $multipartbody .= $endMPboundary;
  432. return $multipartbody;
  433. }
  434. /***********************************************************************************************************************/
  435. /**************** 以下处理业务逻辑 *******************/
  436. /**
  437. * TODO 已测 3.5 商品上下架状态接口
  438. * HTTPS请求方式:POST
  439. *
  440. * @return string
  441. */
  442. function api_product_sku_state_url()
  443. {
  444. return $this->host . $this->edition . "/api/product/skuState";
  445. }
  446. function api_product_sku_state($sku)
  447. {
  448. $params = array();
  449. $params['token'] = $this->access_token; //授权时获取的access token
  450. $params['sku'] = $sku;//商品编号,支持批量,以,分隔 (最高支持100个商品)1为上架,0为下架
  451. $response = $this->post($this->api_product_sku_state_url(), $params, true);
  452. // 参见:3.5 获取商品上下架状态接口.json
  453. return $response;
  454. }
  455. /**
  456. * TODO 待测 6.2 批量获取库存接口(建议订单详情页、下单使用)
  457. * HTTPS请求方式:POST
  458. *
  459. * @return string
  460. */
  461. function api_stock_get_new_stock_by_id_url()
  462. {
  463. return $this->host . $this->edition . "/api/stock/getNewStockById";
  464. }
  465. function api_stock_get_new_stock_by_id($skuNums/*商品和数量 [{skuId: 569172,num:101}]*/, $area/*格式:1_0_0 (分别代表1、2、3级地址)*/)
  466. {
  467. $params = array();
  468. $params['token'] = $this->access_token;
  469. $params['skuNums'] = $skuNums;//商品和数量 [{skuId: 569172,num:101}]
  470. $params['area'] = $area;//格式:1_0_0 (分别代表1、2、3级地址)
  471. $response = $this->post($this->api_stock_get_new_stock_by_id_url(), $params, true);
  472. //参数名称 意义 
  473. //areaId 配送地址id
  474. //desc 描述
  475. //skuId 商品编号
  476. //stockStateId 库存状态编号 33,39,40,36,34
  477. //StockStateDesc 库存状态描述
  478. //33 有货 现货-下单立即发货
  479. //39 有货 在途-正在内部配货,预计2~6天到达本仓库
  480. //40 有货 可配货-下单后从有货仓库配货
  481. //36 预订
  482. //34 无货
  483. //remainNum 剩余数量 -1未知;当库存小于5时展示真实库存数量
  484. // 参见:6.2 批量获取库存接口(建议订单详情页、下单使用).json
  485. return $response;
  486. }
  487. /**
  488. * TODO 待测 5.2 批量查询协议价价格
  489. * HTTPS请求方式:POST
  490. *
  491. * @return string
  492. */
  493. function api_price_get_price_url()
  494. {
  495. return $this->host . $this->edition . "/api/price/getPrice";
  496. }
  497. function api_price_get_price($sku)
  498. {
  499. // $start = microtime(true);
  500. $params = array();
  501. $params['token'] = $this->access_token;//授权时获取的access token
  502. $params['sku'] = $sku;//商品编号,请以,分割。例如:J_129408,J_129409(最高支持100个商品)
  503. $response = $this->post($this->api_price_get_price_url(), $params, true);
  504. // 参见:5.2 批量查询协议价价格.json
  505. // $end = microtime(true);
  506. // echo 'api_price_get_price 耗时'.round($end - $start,4).'秒';
  507. // echo '<br/>';
  508. return $response;
  509. }
  510. }