Lakala.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. <?php
  2. namespace common\components\lakala;
  3. use common\components\noticeUtil;
  4. use common\components\stringUtil;
  5. class Lakala
  6. {
  7. private $appid;
  8. private $serial_no;
  9. private $merchant_no;
  10. private $term_no;
  11. private $merchantPrivateKeyPath;
  12. private $lklCertificatePath;
  13. private $schema = 'LKLAPI-SHA256withRSA';
  14. private $version = '3.0';
  15. private $preUrl = '';
  16. public function accountBalanceQuery()
  17. {
  18. $arr = [
  19. 'merchantNo' => $this->merchant_no,
  20. 'orgNo' => 970936,
  21. ];
  22. $rand = time() . '_' . stringUtil::charsShuffle(22);
  23. $baseRequestVO = [
  24. 'reqData' => $arr,
  25. 'timestamp' => time(),
  26. 'reqId' => $rand,
  27. 'ver' => '1.0.0',
  28. ];
  29. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  30. $authorization = $this->getAuthorization($body);
  31. $url = 'https://s2.lakala.com/api/v2/laep/industry/ewalletBalanceQuery';
  32. return $this->post($url, $body, $authorization);
  33. }
  34. //申请主动结算 ssh
  35. public function applyInviteClear($params)
  36. {
  37. $subMerchantId = $params['subMerchantId'];
  38. $tradeMode = $params['tradeMode'];
  39. $arr = [
  40. 'tradeMode' => $tradeMode,
  41. 'merchantNo' => $this->merchant_no,
  42. 'subMerchantId' => $subMerchantId,
  43. 'orderNo' => $orderNo,
  44. //机构代号
  45. 'orgCode' => '970936',
  46. 'version' => 'v1.0.0',
  47. ];
  48. $rand = time() . '_' . stringUtil::charsShuffle(22);
  49. $baseRequestVO = [
  50. 'reqData' => $arr,
  51. 'timestamp' => time(),
  52. 'reqId' => $rand,
  53. 'ver' => '1.0.0',
  54. ];
  55. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  56. $authorization = $this->getAuthorization($body);
  57. $url = 'https://s2.lakala.com/api/v2/mms/sme/mrchAuthStateQuery';
  58. return $this->post($url, $body, $authorization);
  59. }
  60. public function __construct($params)
  61. {
  62. $this->appid = $params['appid'] ?? '';
  63. $this->serial_no = $params['serial_no'] ?? '';
  64. $this->merchant_no = $params['merchant_no'] ?? '';
  65. $this->term_no = $params['term_no'] ?? '';
  66. $this->merchantPrivateKeyPath = $params['merchantPrivateKeyPath'];
  67. $this->lklCertificatePath = $params['lklCertificatePath'];
  68. if (getenv('YII_ENV') == 'production') {
  69. $this->preUrl = 'https://s2.lakala.com/api/v3/labs/trans/';
  70. } else {
  71. //$this->preUrl = 'https://test.wsmsd.cn/sit/api/v3/labs/trans/';
  72. $this->preUrl = 'https://s2.lakala.com/api/v3/labs/trans/';
  73. }
  74. }
  75. //退款查询
  76. public function refundQuery($params)
  77. {
  78. $orderSn = $params['orderSn'];
  79. $arr = [
  80. 'merchant_no' => $this->merchant_no,
  81. 'term_no' => $this->term_no,
  82. //'out_trade_no' => $orderSn,
  83. 'trade_no' => $orderSn,
  84. ];
  85. $baseRequestVO = [
  86. 'req_data' => $arr,
  87. 'req_time' => date("YmdHis"),
  88. 'version' => $this->version,
  89. ];
  90. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  91. $authorization = $this->getAuthorization($body);
  92. $url = 'https://s2.lakala.com/api/v3/labs/query/tradequery';
  93. return $this->post($url, $body, $authorization);
  94. }
  95. public function openStateQuery($params)
  96. {
  97. $subMerchantId = $params['subMerchantId'];
  98. $tradeMode = $params['tradeMode'];
  99. $arr = [
  100. 'tradeMode' => $tradeMode,
  101. 'merchantNo' => $this->merchant_no,
  102. 'subMerchantId' => $subMerchantId,
  103. ];
  104. $baseRequestVO = [
  105. 'reqData' => $arr,
  106. 'timestamp' => time(),
  107. 'ver' => '1.0.0',
  108. ];
  109. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  110. $authorization = $this->getAuthorization($body);
  111. $url = 'https://s2.lakala.com/api/v2/mms/sme/mrchAuthStateQuery';
  112. return $this->post($url, $body, $authorization);
  113. }
  114. //收银台支付,APP支付和H5支付时使用 ssh 2023112
  115. public function cashierPay($params)
  116. {
  117. $orderSn = $params['orderSn'];
  118. $amount = $params['amount'];
  119. $payWay = $params['payWay'] ?? 1;
  120. $payMode = $payWay==1?'ALIPAY':'WECHAT';
  121. $notifyUrl = $params['notifyUrl'];
  122. $subject = $params['subject'];
  123. $capitalType = $params['capitalType'];
  124. $time = time() + 108000;
  125. $orderEfficientTime = date("YmdHis", $time);
  126. $bus = json_encode([['busi_type' => 'SCPAY', 'params' => ['pay_mode' => $payMode]]]);
  127. $arr = [
  128. 'merchant_no' => $this->merchant_no,
  129. 'term_no' => $this->term_no,
  130. 'out_order_no' => $orderSn,
  131. 'total_amount' => bcmul($amount, 100),
  132. 'order_info' => $subject,
  133. 'support_refund' => 1,
  134. 'notify_url' => $notifyUrl,
  135. 'order_efficient_time' => $orderEfficientTime,
  136. 'busi_type_param' => $bus,
  137. 'counter_param' => json_encode(['pay_mode' => $payMode]),
  138. 'counter_remark' => '{"capitalType":' . $capitalType . '}'
  139. ];
  140. $baseRequestVO = [
  141. 'req_data' => $arr,
  142. 'req_time' => date("YmdHis"),
  143. 'version' => $this->version,
  144. ];
  145. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  146. $authorization = $this->getAuthorization($body);
  147. $url = 'https://s2.lakala.com/api/v3/ccss/counter/order/create';
  148. return $this->post($url, $body, $authorization);
  149. }
  150. //收银机订单查询 ssh 20231026
  151. public function cashierQuery($params)
  152. {
  153. $out_order_no = $params['out_order_no'];
  154. $arr = [
  155. 'merchant_no' => $this->merchant_no,
  156. 'out_order_no' => $out_order_no,
  157. ];
  158. $baseRequestVO = [
  159. 'req_data' => $arr,
  160. 'req_time' => date("YmdHis"),
  161. 'version' => $this->version,
  162. ];
  163. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  164. $authorization = $this->getAuthorization($body);
  165. $url = 'https://s2.lakala.com/api/v3/ccss/counter/order/query';
  166. return $this->post($url, $body, $authorization);
  167. }
  168. public function query($params, $lklSn = 0)
  169. {
  170. $orderSn = $params['orderSn'] ?? '';
  171. $tradeNo = $params['tradeNo'] ?? '';
  172. $ip = $this->getClientIp();
  173. $location_info = ['request_ip' => $ip];
  174. $arr = [
  175. 'merchant_no' => $this->merchant_no,
  176. 'term_no' => $this->term_no,
  177. 'location_info' => $location_info,
  178. ];
  179. if ($lklSn == 0) {
  180. $arr['out_trade_no'] = $orderSn;
  181. } else {
  182. $arr['trade_no'] = $tradeNo;
  183. }
  184. $baseRequestVO = [
  185. 'req_data' => $arr,
  186. 'req_time' => date("YmdHis"),
  187. 'version' => $this->version,
  188. ];
  189. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  190. $authorization = $this->getAuthorization($body);
  191. $url = 'https://s2.lakala.com/api/v3/labs/query/tradequery';
  192. return $this->post($url, $body, $authorization);
  193. }
  194. //被扫付款
  195. public function scanPay($params)
  196. {
  197. $orderSn = $params['orderSn'];
  198. $amount = $params['amount'];
  199. $notifyUrl = $params['notifyUrl'];
  200. $subject = $params['subject'];
  201. $authCode = $params['authCode'];
  202. $capitalType = $params['capitalType'];
  203. $ip = $this->getClientIp();
  204. $location_info = ['request_ip' => $ip];
  205. $arr = [
  206. 'merchant_no' => $this->merchant_no,
  207. 'term_no' => $this->term_no,
  208. 'out_trade_no' => $orderSn,
  209. 'total_amount' => bcmul($amount, 100),
  210. 'auth_code' => $authCode,
  211. 'location_info' => $location_info,
  212. 'subject' => $subject,
  213. 'notify_url' => $notifyUrl,
  214. 'remark' => '{"capitalType":' . $capitalType . '}',
  215. ];
  216. $baseRequestVO = [
  217. 'req_data' => $arr,
  218. 'req_time' => date("YmdHis"),
  219. 'version' => $this->version,
  220. ];
  221. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  222. $authorization = $this->getAuthorization($body);
  223. $url = 'https://s2.lakala.com/api/v3/labs/trans/micropay';
  224. return $this->post($url, $body, $authorization);
  225. }
  226. //关掉 ssh 20231009
  227. public function close($params)
  228. {
  229. $orderSn = $params['orderSn'];
  230. $ip = $this->getClientIp();
  231. $location_info = ['request_ip' => $ip];
  232. $arr = [
  233. 'merchant_no' => $this->merchant_no,
  234. 'term_no' => $this->term_no,
  235. 'location_info' => $location_info,
  236. 'origin_out_trade_no' => $orderSn,
  237. ];
  238. $baseRequestVO = [
  239. 'req_data' => $arr,
  240. 'req_time' => date("YmdHis"),
  241. 'version' => $this->version,
  242. ];
  243. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  244. $authorization = $this->getAuthorization($body);
  245. $lklUrl = 'https://s2.lakala.com/api/v3/labs/relation/close';
  246. return $this->post($lklUrl, $body, $authorization);
  247. }
  248. public function cashClose($params)
  249. {
  250. $orderSn = $params['orderSn'];
  251. $ip = $this->getClientIp();
  252. $location_info = ['request_ip' => $ip];
  253. $arr = [
  254. 'merchant_no' => $this->merchant_no,
  255. 'term_no' => $this->term_no,
  256. 'location_info' => $location_info,
  257. 'out_order_no' => $orderSn,
  258. ];
  259. $baseRequestVO = [
  260. 'req_data' => $arr,
  261. 'req_time' => date("YmdHis"),
  262. 'version' => '1.0',
  263. ];
  264. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  265. $authorization = $this->getAuthorization($body);
  266. $lklUrl = 'https://s2.lakala.com/api/v3/ccss/counter/order/close';
  267. return $this->post($lklUrl, $body, $authorization);
  268. }
  269. //退款 ssh 20231009
  270. public function refund($params)
  271. {
  272. $orderSn = $params['orderSn'] ?? '';
  273. $refundSn = $params['refundSn'];
  274. $refundReason = $params['refundReason'];
  275. $refundAmount = $params['refundAmount'];
  276. //这个参数必传,不然聚合收银合支付宝付款的退款不会成功
  277. $thirdNo = $params['thirdNo'] ?? '';
  278. $ip = $this->getClientIp();
  279. $location_info = ['request_ip' => $ip];
  280. $arr = [
  281. 'merchant_no' => $this->merchant_no,
  282. 'term_no' => $this->term_no,
  283. 'out_trade_no' => $refundSn,
  284. 'refund_amount' => $refundAmount,
  285. 'location_info' => $location_info,
  286. 'refund_reason' => $refundReason,
  287. 'origin_out_trade_no' => $orderSn,
  288. 'origin_trade_no' => $thirdNo,
  289. ];
  290. //原拉卡拉交易流水号,收银台接口下单的,发起退款时必传
  291. if (isset($params['originTradeNo'])) {
  292. $arr['origin_trade_no'] = $params['originTradeNo'];
  293. }
  294. if (isset($params['originLogNo'])) {
  295. $arr['origin_log_no'] = $params['originLogNo'];
  296. }
  297. $baseRequestVO = [
  298. 'req_data' => $arr,
  299. 'req_time' => date("YmdHis"),
  300. 'version' => $this->version,
  301. ];
  302. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  303. $authorization = $this->getAuthorization($body);
  304. $lklUrl = 'https://s2.lakala.com/api/v3/labs/relation/refund';
  305. return $this->post($lklUrl, $body, $authorization);
  306. }
  307. //主扫交易,微信
  308. public function driveWxPay($params)
  309. {
  310. $orderSn = $params['orderSn'];
  311. $amount = $params['amount'];
  312. $notifyUrl = $params['notifyUrl'];
  313. $subject = $params['subject'];
  314. $capitalType = $params['capitalType'];
  315. $wxAppId = $params['wxAppId'] ?? '';
  316. $openId = $params['openId'] ?? '';
  317. $ip = $this->getClientIp();
  318. $location_info = ['request_ip' => $ip];
  319. $arr = [
  320. 'merchant_no' => $this->merchant_no,
  321. 'term_no' => $this->term_no,
  322. 'out_trade_no' => $orderSn,
  323. 'account_type' => 'WECHAT',
  324. 'trans_type' => 71,
  325. 'total_amount' => bcmul($amount, 100),
  326. 'location_info' => $location_info,
  327. 'subject' => $subject,
  328. 'notify_url' => $notifyUrl,
  329. 'remark' => '{"capitalType":' . $capitalType . '}',
  330. 'acc_busi_fields' => [
  331. 'sub_appid' => $wxAppId,
  332. 'user_id' => $openId,
  333. 'timeout_express' => 30,
  334. ],
  335. ];
  336. $baseRequestVO = [
  337. 'req_data' => $arr,
  338. 'req_time' => date("YmdHis"),
  339. 'version' => $this->version,
  340. ];
  341. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  342. $authorization = $this->getAuthorization($body);
  343. return $this->post($this->preUrl . 'preorder', $body, $authorization);
  344. }
  345. public function driveNativeWxPay($params)
  346. {
  347. $orderSn = $params['orderSn'];
  348. $amount = $params['amount'];
  349. $notifyUrl = $params['notifyUrl'];
  350. $subject = $params['subject'];
  351. $capitalType = $params['capitalType'];
  352. $wxAppId = $params['wxAppId'] ?? '';
  353. $ip = $this->getClientIp();
  354. $location_info = ['request_ip' => $ip];
  355. $arr = [
  356. 'merchant_no' => $this->merchant_no,
  357. 'term_no' => $this->term_no,
  358. 'out_trade_no' => $orderSn,
  359. 'account_type' => 'WECHAT',
  360. 'trans_type' => 71,
  361. 'total_amount' => bcmul($amount, 100),
  362. 'location_info' => $location_info,
  363. 'subject' => $subject,
  364. 'notify_url' => $notifyUrl,
  365. 'remark' => '{"capitalType":' . $capitalType . '}',
  366. 'acc_busi_fields' => [
  367. 'timeout_express' => 30,
  368. 'sub_appid' => $wxAppId,
  369. ],
  370. ];
  371. $baseRequestVO = [
  372. 'req_data' => $arr,
  373. 'req_time' => date("YmdHis"),
  374. 'version' => $this->version,
  375. ];
  376. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  377. $authorization = $this->getAuthorization($body);
  378. return $this->post($this->preUrl . 'preorder', $body, $authorization);
  379. }
  380. //主扫交易,支付宝
  381. public function driveAliPay($params)
  382. {
  383. $orderSn = $params['orderSn'];
  384. $amount = $params['amount'];
  385. $notifyUrl = $params['notifyUrl'];
  386. $subject = $params['subject'];
  387. $capitalType = $params['capitalType'];
  388. $ip = $this->getClientIp();
  389. $location_info = ['request_ip' => $ip];
  390. $arr = [
  391. 'merchant_no' => $this->merchant_no,
  392. 'term_no' => $this->term_no,
  393. 'out_trade_no' => $orderSn,
  394. 'account_type' => 'ALIPAY',
  395. 'trans_type' => '41',
  396. 'total_amount' => bcmul($amount, 100),
  397. 'location_info' => $location_info,
  398. 'subject' => $subject,
  399. 'notify_url' => $notifyUrl,
  400. 'remark' => '{"capitalType":' . $capitalType . '}',
  401. ];
  402. $baseRequestVO = [
  403. 'req_data' => $arr,
  404. 'req_time' => date("YmdHis"),
  405. 'version' => $this->version,
  406. ];
  407. $body = json_encode($baseRequestVO, JSON_UNESCAPED_UNICODE);
  408. $authorization = $this->getAuthorization($body);
  409. return $this->post($this->preUrl . 'preorder', $body, $authorization);
  410. }
  411. //验签
  412. public function signatureVerification($authorization, $body)
  413. {
  414. $authorization = str_replace($this->schema . " ", "", $authorization);
  415. $authorization = str_replace(",", "&", $authorization);
  416. $authorization = str_replace("\"", "", $authorization);
  417. $authorization = $this->convertUrlQuery($authorization);
  418. $authorization['signature'] = base64_decode($authorization['signature']);
  419. $message = $authorization['timestamp'] . "\n" . $authorization['nonce_str'] . "\n" . $body . "\n";
  420. $key = openssl_get_publickey(file_get_contents($this->lklCertificatePath));
  421. $flag = openssl_verify($message, $authorization['signature'], $key, OPENSSL_ALGO_SHA256);
  422. openssl_free_key($key);
  423. if ($flag) {
  424. return true;
  425. }
  426. return false;
  427. }
  428. //签名
  429. public function getAuthorization($body)
  430. {
  431. $nonceStr = $this->randString(12);
  432. $timestamp = time();
  433. $message = $this->appid . "\n" . $this->serial_no . "\n" . $timestamp . "\n" . $nonceStr . "\n" . $body . "\n";
  434. $key = openssl_get_privatekey(file_get_contents($this->merchantPrivateKeyPath));
  435. openssl_sign($message, $signature, $key, OPENSSL_ALGO_SHA256);
  436. openssl_free_key($key);
  437. return $this->schema . " appid=\"" . $this->appid . "\"," . "serial_no=\"" . $this->serial_no . "\"," . "timestamp=\"" . $timestamp . "\"," . "nonce_str=\"" . $nonceStr . "\"," . "signature=\"" . base64_encode($signature) . "\"";
  438. }
  439. //获取随机数
  440. public function randString($length)
  441. {
  442. $characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  443. $randomString = "";
  444. for ($i = 0; $i < $length; $i++) {
  445. $randomString .= $characters[rand(0, strlen($characters) - 1)];
  446. }
  447. return $randomString;
  448. }
  449. //请求
  450. public function post($url, $data, $authorization)
  451. {
  452. $headers = [
  453. "Authorization: " . $authorization,
  454. "Accept: application/json",
  455. "Content-Type:application/json",
  456. ];
  457. \Yii::info("请求参数:" . $data);
  458. $ch = curl_init();
  459. curl_setopt($ch, CURLOPT_POST, 1);
  460. curl_setopt($ch, CURLOPT_URL, $url);
  461. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
  462. curl_setopt($ch, CURLOPT_TIMEOUT, 10);
  463. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  464. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);//设置HTTP头
  465. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  466. curl_setopt($ch, CURLOPT_POST, 1);
  467. $res = curl_exec($ch);
  468. curl_close($ch);
  469. return json_decode($res, 1);
  470. }
  471. //签名参数转数组
  472. private function convertUrlQuery($query)
  473. {
  474. $queryParts = explode('&', $query);
  475. $params = array();
  476. foreach ($queryParts as $param) {
  477. $item = explode('=', $param);
  478. $params[$item[0]] = $item[1];
  479. }
  480. if ($params['signature']) {
  481. $params['signature'] = substr($query, strrpos($query, 'signature=') + 10);
  482. }
  483. return $params;
  484. }
  485. public function getClientIp()
  486. {
  487. if (isset($_SERVER['REMOTE_ADDR'])) {
  488. $cip = $_SERVER['REMOTE_ADDR'];
  489. } elseif (getenv("REMOTE_ADDR")) {
  490. $cip = getenv("REMOTE_ADDR");
  491. } elseif (getenv("HTTP_CLIENT_IP")) {
  492. $cip = getenv("HTTP_CLIENT_IP");
  493. } else {
  494. if (getenv('YII_ENV') == 'production') {
  495. $cip = "114.55.169.20";
  496. } else {
  497. $cip = "118.178.193.23";
  498. }
  499. }
  500. return $cip;
  501. }
  502. }