NewBasePayment.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. <?php
  2. namespace Biz\Pay;
  3. use App\Api\Model\AccountDetail;
  4. use App\Commons\Model\Pay_order;
  5. use App\Commons\Model\View_payment;
  6. use Biz\Account\AccountManager;
  7. use Biz\Money\AddOrCutMoney;
  8. use Exception;
  9. use stdClass;
  10. /**
  11. * 基础支付类
  12. */
  13. abstract class NewBasePayment
  14. {
  15. /**
  16. * 网关地址
  17. * @var [type]
  18. */
  19. protected $getWay;
  20. /**
  21. * 支付名称或代三
  22. * @var [type]
  23. */
  24. protected $paymentName;
  25. /**
  26. * 支付配置文件
  27. * @var [type]
  28. */
  29. protected $paymentConfig;
  30. /**
  31. * 通知地址
  32. * @var [type]
  33. */
  34. protected $notifyUrl;
  35. /**
  36. * 跳转地址 同步
  37. * @var [type]
  38. */
  39. protected $redirectUrl;
  40. /**
  41. * 编码
  42. * @var string
  43. */
  44. protected $charset = 'utf-8';
  45. /**
  46. * 支付信息
  47. * @var
  48. */
  49. protected $payInfo;
  50. /**
  51. * 驱动数据对像
  52. * @var [type]
  53. */
  54. protected $dataAccess;
  55. /**
  56. * 私有证书路径
  57. * @var [type]
  58. */
  59. protected $privateCertPath;
  60. /**
  61. * 公有证书路径
  62. * @var [type]
  63. */
  64. protected $publicCertPath;
  65. /**
  66. * 后台配置的商户信息将在初始化时自动解析完成
  67. * @var [type]
  68. */
  69. protected $sellerInfo;
  70. /**
  71. * 异步通知成功的返回串
  72. * @var
  73. */
  74. protected $success = 'success';
  75. /**
  76. * 当前用户信息
  77. * @var mixed
  78. */
  79. protected $userInfo;
  80. /**
  81. * 设定支付接口允许的域名
  82. * @var [type]
  83. */
  84. protected $domain;
  85. /**
  86. * 返回类型
  87. * @var [type]
  88. */
  89. protected $returnType;
  90. /**
  91. * 是否手机版
  92. *
  93. * @var [type]
  94. */
  95. protected $isMobile;
  96. /**
  97. * 开启调试模式
  98. * @var integer
  99. */
  100. protected $debug = 0;
  101. /**
  102. * 提交订单信息
  103. * @var array
  104. */
  105. protected $orderInfo;
  106. /**
  107. * 订单发起URL
  108. * @var
  109. */
  110. protected $apiUrl = '';
  111. /**
  112. * 发起请求类型ENUM['POST,GET,JSON']
  113. * @var
  114. */
  115. protected $method = "POST";
  116. /**
  117. * 商户ID
  118. * @var
  119. */
  120. protected $merchant_id;
  121. /**
  122. * 商户密钥
  123. * @var
  124. */
  125. protected $merchant_secret;
  126. /**
  127. * 是否二次跳转
  128. * @var bool
  129. */
  130. public $jump = false;
  131. /**
  132. * 接收通知
  133. * @param array $data
  134. * @return void
  135. */
  136. abstract function notify(array $data);
  137. /**
  138. * 返回跳转
  139. * @param array $data
  140. * @return void [type] [description]
  141. */
  142. abstract function redirect(array $data);
  143. /**
  144. * 检查支付信息
  145. * @return void [type] 0=失败,1=成功
  146. */
  147. abstract function check();
  148. /**
  149. * 构建订单提交数组
  150. * @return array
  151. */
  152. abstract function buildOrder(): array;
  153. /**
  154. * 生成签名函数
  155. * @param array $param
  156. * @param string $merchantSecret
  157. * @return mixed
  158. */
  159. abstract function buildSign(array $param, $merchantSecret);
  160. /**
  161. * 基础配置
  162. * @return void
  163. */
  164. abstract function config();
  165. /**
  166. * 初始化
  167. * @param PaymentDataAccess $dataAccess
  168. * @throws Exception
  169. */
  170. public function __construct(PaymentDataAccess $dataAccess)
  171. {
  172. $this->userInfo = (new AccountManager())->getCurrentUser();
  173. $this->domain = $_SERVER['HTTP_HOST'];
  174. $httpSchema = $_SERVER['REQUEST_SCHEME'] ?? 'http';
  175. $prefix = $prefix2 = "{$httpSchema}://{$this->domain}";
  176. $this->paymentName = $dataAccess->paymentName;
  177. if ($dataAccess->paymentName) {
  178. if (!$this->userInfo) Render('', -4001);
  179. $this->getConfig();
  180. $dataAccess->orderSn = $this->paymentConfig['order_prefix'] . OrderID();
  181. if ($this->paymentConfig['notify_url']) {
  182. $_prefix = self::getDomain($this->paymentConfig['notify_url']);
  183. if ($_prefix) $prefix = $_prefix;
  184. }
  185. if ($this->paymentConfig['callback_url']) {
  186. $_prefix = self::getDomain($this->paymentConfig['callback_url']);
  187. if ($_prefix) $prefix2 = $_prefix;
  188. }
  189. $bla = explode('||', $this->paymentConfig['black_list']);
  190. if (in_array($this->userInfo->account, $bla)) {
  191. Render('', -5990);
  192. }
  193. }
  194. $this->notifyUrl = "{$prefix}/Payment-Pay/Notify.{$dataAccess->paymentName}";
  195. $this->redirectUrl = "{$prefix2}/Payment-Pay/Redirect.{$dataAccess->paymentName}";
  196. $this->dataAccess = $dataAccess;
  197. $this->isMobile = is_mobile();
  198. $this->config();
  199. $this->init();
  200. }
  201. /**
  202. * 设置返回类型
  203. * @param string $type
  204. */
  205. abstract function setReturnType($type = 'HTML');
  206. /**
  207. * 发起支付函数
  208. * @return mixed
  209. */
  210. abstract function goPay();
  211. /**
  212. * 返回DATAACCESS
  213. */
  214. public function DataAccess()
  215. {
  216. return $this->dataAccess;
  217. }
  218. /**
  219. * 获取配置信息
  220. * @return void [type] [description]
  221. * @throws Exception
  222. */
  223. public function getConfig()
  224. {
  225. if (empty($this->paymentName)) {
  226. throw new Exception("支付名不能为空", 1);
  227. }
  228. $this->paymentConfig = M('view_payment')->where('gateway_identifier', $this->paymentName)->find();
  229. if (!$this->paymentConfig) {
  230. throw new Exception("支付不存在", 1);
  231. }
  232. $this->paymentConfig['notify_url'] = self::getDomain($this->paymentConfig['notify_url']);
  233. $this->paymentConfig['callback_url'] = self::getDomain($this->paymentConfig['callback_url']);
  234. }
  235. /**
  236. * 系统初始化
  237. * @return void [type] [description]
  238. */
  239. public function init()
  240. {
  241. }
  242. /**
  243. * 更新用户账户余额函数
  244. * @param string $order_no
  245. * @param string $payer
  246. * @param string $channel_name
  247. * @param float $price
  248. * @param float $real_price
  249. * @param string $bill_no
  250. * @return mixed
  251. */
  252. protected function handleUserMoney(string $order_no, string $payer, string $channel_name, float $price, float $real_price = 0, $bill_no = '')
  253. {
  254. if (!$order_no) {
  255. return -5997;
  256. }
  257. $order_info = (new Pay_order())->where('pay_orders.order_no', $order_no)
  258. ->leftJoin('pay_channels', 'pay_channels.id', '=', 'pay_orders.channel_id')
  259. ->select('pay_orders.*', 'pay_channels.gift_price_rate', 'pay_channels.gift_black_list')
  260. ->first();
  261. if ($order_info->status == 1) return 2;//如已确认订单直接返回
  262. $data['status'] = 1;
  263. if (!empty($bill_no)) {
  264. $data['bill_no'] = $bill_no;
  265. }
  266. $data['notify_query'] = 'REQUEST=>' . http_build_query($_REQUEST) . '||json=>' . file_get_contents('php://input');
  267. $data['updated_at'] = date('Y-m-d H:i:s');
  268. if ($real_price) {
  269. $data['real_price'] = $real_price;
  270. }
  271. $operator = "auto";
  272. $reason = "在线充值";
  273. $detail = sprintf("用户【%s】于%s完成在线支付,订单号:%s,支付金额:%s元。", $payer, date('Y-m-d H:i:s'), $order_no, $price);
  274. $aoc = new AddOrCutMoney();
  275. _beginTransaction();
  276. $ret = lm('Pay_order', 'commons')->where('order_no', $order_no)->update($data);
  277. if ($ret !== 1) {
  278. _rollBack();
  279. return $ret;
  280. }
  281. $accountDetail = new AccountDetail();
  282. $account_identity = $order_info->payer_id;
  283. $accountDetail = $accountDetail->where('account_identity', $account_identity)->first();
  284. if (!$accountDetail) return -5991;
  285. $moneyRecharge = lm('MoneyRecharge', 'api')->where('order_id', $order_no)->first();
  286. if (!$moneyRecharge)
  287. $ret = $aoc->addMoneyRechargeRec($accountDetail, $order_info->payer, $order_no, $order_info->price, '在线充值', $operator, $reason, $reason, 1, 1);
  288. else
  289. $ret = lm('MoneyRecharge', 'api')->where('order_id', $order_no)->update(['status' => 1]);
  290. if (is_int($ret) && $ret < 1) {
  291. _rollBack();
  292. return $ret;
  293. }
  294. $ret = $aoc->addMoneyDetailRec($accountDetail, $order_info->payer, $order_no, $order_info->price, 1, 6, $operator, $detail, $reason);
  295. if (is_int($ret)) {
  296. _rollBack();
  297. return $ret;
  298. }
  299. $ret = $aoc->incAccountDetailMoney($accountDetail, $order_info->price);
  300. if ($ret !== 1) {
  301. _rollBack();
  302. return $ret;
  303. }
  304. $gift_price_rate = explode('||', $order_info->gift_price_rate);
  305. if (!empty($gift_price_rate)) {
  306. $gbla = explode('||', $order_info->gift_black_list);
  307. $num = intval($gift_price_rate['0']) ?? 0;
  308. $rate = intval($gift_price_rate['1']) ?? 0;
  309. if (!in_array($payer, $gbla) && $rate && $num <= $price) {
  310. $gift_price = $price * $rate / 100;
  311. $gift_price = floatval($gift_price);
  312. $gift_order_no = $order_no . "-G";
  313. $reason = '在线充值赠送';
  314. $detail = sprintf("用户【%s】于%s获得充值赠送,订单号:%s,赠送金额:%s元。", $payer, date('Y-m-d H:i:s'), $gift_order_no, $gift_price);
  315. $accountDetail->cash = floatval($accountDetail->cash + $order_info->price);
  316. $ret = $aoc->addMoneyRechargeRec($accountDetail, $order_info->payer, $gift_order_no, $gift_price, '在线充值赠送', $operator, $reason, $reason);//资金明细
  317. if (is_int($ret)) {
  318. _rollBack();
  319. return $ret;
  320. }
  321. $ret = $aoc->addMoneyDetailRec($accountDetail, $order_info->payer, $gift_order_no, $gift_price, 1, 13, $operator, $detail, $reason);//流水记录
  322. if (is_int($ret)) {
  323. _rollBack();
  324. return $ret;
  325. }
  326. $ret = $aoc->incAccountDetailMoney($accountDetail, $gift_price);
  327. if ($ret !== 1) {
  328. _rollBack();
  329. return $ret;
  330. }
  331. }
  332. }
  333. _commit();
  334. return 1;
  335. }
  336. /**
  337. * 模拟表单提交处理函数
  338. * @param array $order_info
  339. * @param string $action 提交方式
  340. * @return string
  341. */
  342. public function handlePost(array $order_info, $action = "POST")
  343. {
  344. if ($this->debug) {
  345. $inputType = "text";
  346. $script = '';
  347. } else {
  348. $inputType = "hidden";
  349. $script = 'setTimeout(()=>{document.getElementById("submitForm").submit();},200)';
  350. }
  351. $orderInfo = $order_info ? $order_info : $this->buildOrder();
  352. $url = $this->apiUrl;
  353. if (is_array($orderInfo) && count($orderInfo) > 0) {
  354. $forms = '';
  355. foreach ($orderInfo as $k => $v) {
  356. $forms .= "<input type={$inputType} name='{$k}' value='{$v}' />";
  357. }
  358. /* $forms = trim($forms, PHP_EOL . "\t\t\t");*/
  359. }
  360. if (empty($url)) {
  361. $url = $this->getWay;
  362. }
  363. $str = "<html>
  364. <head>
  365. <title>Payment By CreditCard online</title>
  366. <meta http-equiv='Content-Type' content='text/html; charset={$this->charset}'>
  367. </head>
  368. <body>
  369. <form action='{$url}' method='{$action}' id='submitForm' name='E_FORM'>
  370. {$forms}
  371. </form>
  372. <script type='text/javascript'>
  373. {$script}
  374. </script>
  375. </body>
  376. </html>
  377. ";
  378. //toLog($str);
  379. return $str;
  380. }
  381. /**
  382. * 提取证书中的公钥和私钥KEy,以数据形式返回
  383. * @return array [type] [description]
  384. * @throws Exception
  385. */
  386. public function certPatchKey()
  387. {
  388. $priContent = file_get_contents($this->privateCertPath);
  389. $pubContent = file_get_contents($this->publicCertPath);
  390. $priKey = openssl_get_privatekey($priContent);
  391. $pubKey = openssl_pkey_get_public($pubContent);
  392. if (empty($priKey) || empty($pubKey)) {
  393. throw new Exception("支付的私钥和公钥不能为空", 1);
  394. }
  395. return array('private' => $priKey, 'public' => $pubKey);
  396. }
  397. /**
  398. * 处理CURL提交函数
  399. * @param [type] $url [description]
  400. * @param array $data [description]
  401. * @param array $header
  402. * @param bool $ssl
  403. * @return bool|int|string [type] [description]
  404. */
  405. function goPayCurl($url, $data = array(), $header = array(), $ssl = false)
  406. {
  407. if (is_array($data)) {
  408. $data = http_build_query($data);
  409. }
  410. $ch = curl_init();
  411. curl_setopt($ch, CURLOPT_URL, $url);
  412. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  413. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $ssl);
  414. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $ssl);
  415. curl_setopt($ch, CURLOPT_POST, 1);
  416. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  417. if (is_array($header) && count($header) > 0) {
  418. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  419. curl_setopt($ch, CURLOPT_HEADER, 0);
  420. }
  421. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  422. // curl_setopt($ch, CURLOPT_HTTPHEADER, 1);
  423. $result = curl_exec($ch);
  424. // $errno = curl_errno($ch);
  425. curl_close($ch);
  426. if ($result == NULL) {
  427. return 0;
  428. }
  429. return $result;
  430. }
  431. /**
  432. * 提示信息
  433. * @param [type] $msg [description]
  434. * @param integer $code [description]
  435. * @return void [type] [description]
  436. */
  437. function showNotice($msg, $code = -1)
  438. {
  439. if (strtolower($this->returnType) == 'json') {
  440. JsonReturn('', $code, $msg);
  441. } else {
  442. exit($msg);
  443. }
  444. }
  445. /**
  446. * 成功信息
  447. * @param [type] $data [description]
  448. * @return void [type] [description]
  449. */
  450. function jsonSuccess($data)
  451. {
  452. if ($this->returnType == 'json') {
  453. JsonReturn($data, 1, 'success');
  454. }
  455. }
  456. /**
  457. * 获取ip信息
  458. */
  459. function getRealIp()
  460. {
  461. $ip = false;
  462. if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
  463. $ip = $_SERVER["HTTP_CLIENT_IP"];
  464. }
  465. if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  466. $ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
  467. if ($ip) {
  468. array_unshift($ips, $ip);
  469. $ip = FALSE;
  470. }
  471. for ($i = 0; $i < count($ips); $i++) {
  472. if (!eregi("^(10|172\.16|192\.168)\.", $ips[$i])) {
  473. $ip = $ips[$i];
  474. break;
  475. }
  476. }
  477. }
  478. return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
  479. }
  480. /**
  481. * 订单写入数据表(pay_orders)
  482. * @param array $orderInfo
  483. * @return int|void
  484. */
  485. function addOrder(array $orderInfo)
  486. {
  487. $this->log($orderInfo['gateway_name'] . '-Pay', '开始写入订单信息');
  488. if ($this->debug) return;
  489. if (empty($orderInfo['order_no']) || empty($orderInfo['payer']) || empty($orderInfo['price']) || !$orderInfo['payer_id']) {
  490. JsonReturn('', -1, '传参有误!');
  491. }
  492. $order_info = lm('Pay_order', 'Commons')->where('order_no', $orderInfo['order_no'])->find();
  493. if ($order_info) {
  494. JsonReturn('', -2, '您已成功提交该订单,请不要重复操作!');
  495. }
  496. $payer = $orderInfo['payer'];
  497. $price = $orderInfo['price'];
  498. $order_no = $orderInfo['order_no'];
  499. $operator = 'auto';
  500. $reason = '在线充值';
  501. $accountDetail = new AccountDetail();
  502. // $account_info = (new Account())->getAccountByToken();
  503. $account_identity = $this->userInfo['account_identity'];
  504. $accountDetail = $accountDetail->where('account_identity', $account_identity)->first();
  505. if (!$accountDetail) return -5991;
  506. $aoc = new AddOrCutMoney();
  507. _beginTransaction();
  508. $res = lm('Pay_order', 'Commons')->insert($orderInfo);
  509. if ($res < 1) {
  510. _rollBack();
  511. $this->log($orderInfo['gateway_name'] . '-Pay', '订单写入失败,请尝试重新提交!');
  512. JsonReturn('', -3, '订单写入失败,请尝试重新提交!');
  513. }
  514. $res = $aoc->addMoneyRechargeRec($accountDetail, $payer, $order_no, $price, '在线充值', $operator, $reason, $reason, 1, 0);
  515. if (is_int($res)) {
  516. _rollBack();
  517. $this->log($orderInfo['gateway_name'] . '-Pay', '订单REMARK写入失败,请尝试重新提交!');
  518. JsonReturn('', -4, '订单REMARK写入失败,请尝试重新提交!');
  519. }
  520. _commit();
  521. $this->log($orderInfo['gateway_name'] . '-Pay', '订单写入成功!订单号:' . $orderInfo['order_no'] . ',金额:' . $orderInfo['price']);
  522. return 1;
  523. }
  524. /**
  525. * 获取订单信息函数
  526. * @param string $order_no
  527. * @return Pay_order||null
  528. */
  529. protected
  530. function getOrder(string $order_no)
  531. {
  532. return $channel_info = lm('Pay_order', 'commons')
  533. ->leftJoin('pay_gateways', 'pay_gateways.id', '=', 'pay_orders.gateway_id')
  534. ->where('pay_orders.order_no', $order_no)
  535. ->first();
  536. }
  537. /**
  538. * 获取订单信息函数
  539. * @param string $bill_no
  540. * @return Pay_order||null
  541. */
  542. protected
  543. function getOrderByBillNo(string $bill_no)
  544. {
  545. return
  546. $channel_info = lm('Pay_order', 'commons')
  547. ->leftJoin('pay_gateways', 'pay_gateways.id', '=', 'pay_orders.gateway_id')
  548. ->where('bill_order_no', $bill_no)
  549. ->first();
  550. }
  551. /**
  552. * 生成订单表基本数据
  553. * @param array $orderInfo
  554. * @return array
  555. */
  556. function buildOrderInfo(array $orderInfo = []): array
  557. {
  558. return [
  559. 'order_no' => $this->dataAccess->orderSn,
  560. 'payer' => ($orderInfo['payer'] ?? $this->userInfo->account) ?? 'guest',
  561. 'payer_id' => ($orderInfo['payer_id'] ?? $this->userInfo->account_identity) ?? null,
  562. 'price' => $this->dataAccess->money,
  563. 'status' => 0,
  564. 'remark' => $this->dataAccess->extra ?? '',
  565. 'ip' => $this->getRealIp(),
  566. 'created_at' => date('Y-m-d H:i:s'),
  567. 'operator' => 'auto',
  568. 'gateway_id' => $this->dataAccess->gateway_id,
  569. 'channel_id' => $this->dataAccess->channel_id,
  570. ];
  571. }
  572. /**
  573. * 返回支付通道信息
  574. * @param string $gateway_code
  575. * @param string $gateway_identifier
  576. * @return View_payment|null
  577. */
  578. protected function getChannelInfo(string $gateway_code, string $gateway_identifier)
  579. {
  580. return $channel_info = lm('View_payment', 'commons')
  581. ->where('gateway_identifier', $gateway_identifier)
  582. ->where('gateway_code', $gateway_code)
  583. ->first();
  584. }
  585. /**
  586. * 同步跳转
  587. * @param array $data
  588. * @return void [type] [description]
  589. */
  590. function callback(array $data = [])
  591. {
  592. $html = <<<EOT
  593. <script>setTimeout(function() {
  594. window.history.go(-2);
  595. },3000);</script>
  596. EOT;
  597. echo '支付已受理。如有疑问请联系客服!3秒后返回支付页,请稍候...';
  598. echo $html;
  599. }
  600. /**
  601. * 验证签名函数
  602. * @param array $param
  603. * @param string $sign
  604. * @param string $order_no
  605. * @param float $price
  606. * @param float $real_price
  607. * @param bool $is_bill_no
  608. * @param string $bill_no
  609. * @return void
  610. */
  611. protected function checkSign(array $param, string $sign, string $order_no, float $price, float $real_price = 0, bool $is_bill_no = false, $bill_no = '')
  612. {
  613. if (!$price) {
  614. die(Lang('Errors', 'Api')->get('error-5993'));
  615. }
  616. $order_info = $is_bill_no ? $this->getOrderByBillNo($order_no) : $this->getOrder($order_no);
  617. if (floatval($price) != floatval($order_info->price)) {
  618. $this->log($order_info->gateway_name . '-Notify', $order_no . ':' . Lang('Errors', 'Api')->get('error-5996'));
  619. die(Lang('Errors', 'Api')->get('error-5996'));
  620. }
  621. $order_no = $order_info->order_no;
  622. $gateway_code = $order_info->gateway_code;
  623. $gateway_identifier = $order_info->gateway_identifier;
  624. // dd($gateway_code, $gateway_identifier, $order_info);
  625. $channel_info = $this->getChannelInfo($gateway_code, $gateway_identifier);
  626. if (!$channel_info || !$order_no) {
  627. $this->log($gateway_identifier . '-Notify', $order_no . ':' . Lang('Errors', 'Api')->get('error-5994'));
  628. die(Lang('Errors', 'Api')->get('error-5994'));
  629. }
  630. if (!$bill_no) $bill_no = $order_no;
  631. $merchant_secret = $channel_info->merchant_md5_secret;
  632. $this->merchant_id = $channel_info->merchant_id;
  633. $newSign = $this->buildSign($param, $merchant_secret);
  634. if ($newSign === $sign) {
  635. $this->log($gateway_identifier . '-Notify', '订单' . $order_no . '已校验成功');
  636. $ret = $this->handleUserMoney($order_no, $order_info->payer, $channel_info->channel_name, $price, $real_price, $bill_no);
  637. if ($ret === 1) {
  638. $this->log($gateway_identifier . '-Notify', '订单已被确认!订单号:' . $order_no . ',订单金额:' . $price . '元。');
  639. } elseif ($ret === 2) {
  640. //已确认过
  641. } else {
  642. //更新账户金额出现异常
  643. $this->log($gateway_identifier . '-Notify', '订单确认失败:' . Lang('Errors', 'Api')->get('error' . $ret) . '!订单号:' . $order_no . ',订单金额:' . $price . '元,状态码:' . $ret);
  644. }
  645. die($this->success);
  646. } else {
  647. $obj = new Pay_order();
  648. $order_info = $obj->where('order_no', $order_no)->first();
  649. if ($order_info && $order_info->status == 0) {
  650. $obj->update(['notify_query' => 'REQUEST=>' . http_build_query($_REQUEST) . '||json=>' . file_get_contents('php://input')])
  651. ->where(['order_no' => $order_no]);
  652. }
  653. $this->log($gateway_identifier . '-Notify', '订单信息被篡改!订单号:' . $order_no . ',订单金额:' . $price . '元,来自IP:' . $this->getRealIp());
  654. die($this->errMsg ?? '订单信息被篡改,如有疑问请联系客服!');
  655. }
  656. }
  657. /**
  658. * 订单入库,返回订单预支付信息函数
  659. * @param bool $skip_order
  660. * @param string $qrcode_url
  661. * @param string $type
  662. * @return void
  663. */
  664. protected function prePay(bool $skip_order = false, string $qrcode_url = '', $type = '')
  665. {
  666. $orderInfo = $skip_order ? new stdClass() : $this->buildOrder();
  667. $sysOrderInfo = $this->buildOrderInfo();
  668. $payInfo = new PayInfo();
  669. $payInfo->debug = $this->debug;
  670. $payInfo->orderInfo = $orderInfo;
  671. $payInfo->apiUrl = $this->apiUrl;
  672. $payInfo->jump = $skip_order ? 1 : 0;
  673. if ($qrcode_url) {
  674. $payInfo->orderInfo = $sysOrderInfo;
  675. $payInfo->orderInfo['qrcode_url'] = $qrcode_url;
  676. $payInfo->orderInfo['type'] = $type;
  677. ksort($payInfo->orderInfo);
  678. $payInfo->orderInfo['qrcode_sign'] = md5(http_build_query($payInfo->orderInfo));
  679. $httpSchema = $_SERVER['REQUEST_SCHEME'] ?? 'http';
  680. $payInfo->apiUrl = "{$httpSchema}://{$this->domain}/Payment-Pay/Qrcode";
  681. $payInfo->jump = 0;
  682. }
  683. !$this->method ?: $payInfo->method = $this->method;
  684. $payInfo->jump_url = (!$this->paymentConfig['en_jump']) ? '' : $this->paymentConfig->jump_url;
  685. $this->payInfo = $payInfo;
  686. //跳转对方网关时直接订单入库
  687. if ($this->paymentConfig['rand_price'])
  688. $this->dataAccess->money -= $this->dataAccess->rand_price;
  689. $this->log($this->paymentConfig['gateway_identifier'] . '-Pay', '订单提交数据:' . json_encode($orderInfo));
  690. $this->addOrder($sysOrderInfo);
  691. if ($this->jump) {
  692. $this->jump($payInfo);
  693. exit;
  694. }
  695. Render($this->payInfo, 1);
  696. }
  697. /**
  698. * 支付日志操作函数
  699. * @param $file_name
  700. * @param $contents
  701. * @return bool|int
  702. */
  703. protected function log($file_name, $contents)
  704. {
  705. $file_path = ROOT_PATH . '/Cache/log/' . $file_name . '.log';
  706. $contents = PHP_EOL . date('Y-m-d H:i:s') . ':' . $contents . PHP_EOL;
  707. return file_put_contents($file_path, $contents, FILE_APPEND);
  708. }
  709. /**
  710. * 格式化RSA密钥函数
  711. * @param $secretString
  712. * @param bool $is_pub_key
  713. * @return string
  714. */
  715. function formatRsaSecret($secretString, $is_pub_key = false)
  716. {
  717. $secretString = str_replace(['-----BEGIN PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----', '-----END PRIVATE KEY-----', '-----END PUBLIC KEY-----'], '', $secretString);
  718. $secretString = str_replace(['\n', '\r', '\t'], '', $secretString);
  719. $body = '';
  720. $pos = 0;
  721. $read = true;
  722. while ($read) {
  723. $sub = substr($secretString, $pos, 64);
  724. $pos += 64;
  725. $body .= $sub;
  726. $read = $sub ? true : false;
  727. }
  728. $header = $is_pub_key ? '-----BEGIN PUBLIC KEY-----' : '-----BEGIN PRIVATE KEY-----';
  729. $footer = $is_pub_key ? '-----END PUBLIC KEY-----' : '-----END PRIVATE KEY-----';
  730. return $header . PHP_EOL . $body . PHP_EOL . $footer;
  731. }
  732. /**
  733. * 默认的二次跳转类网关处理函数
  734. * @param PayInfo $pay_info
  735. */
  736. protected function jump(PayInfo $pay_info)
  737. {
  738. $order_info = $pay_info->orderInfo;
  739. echo $this->handlePost($order_info, $this->method);
  740. }
  741. /**
  742. * 返回网址域名部份函数
  743. * @param string $url
  744. * @return string
  745. */
  746. protected function getDomain(string $url)
  747. {
  748. preg_match('/http(s)?:\/\/[\d\w]+([\-]?[\w\d]+)?(\.[\d\w]+([\-]?[\w\d]+)?[:\d+]?)+\//is', $url, $found);
  749. return $found ? $found[0] : '';
  750. }
  751. }
  752. /**
  753. * 预支付订单信息类
  754. */
  755. class PayInfo
  756. {
  757. public $debug = 0;
  758. public $orderInfo;
  759. public $apiUrl;
  760. public $method = "POST";
  761. public $jump = 0;
  762. public $jump_url = "";
  763. }