JdSdk.php 22 KB

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