Pay.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. <?php
  2. /**
  3. * Niushop商城系统 - 团队十年电商经验汇集巨献!
  4. * =========================================================
  5. * Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
  6. * ----------------------------------------------
  7. * 官方网址: https://www.niushop.com
  8. * =========================================================
  9. */
  10. namespace app\model\system;
  11. use app\model\member\Member;
  12. use app\model\order\OrderPay;
  13. use think\facade\Cache;
  14. use app\model\BaseModel;
  15. use app\model\order\Config as OrderConfig;
  16. use think\facade\Log;
  17. /**
  18. * 系统配置类
  19. */
  20. class Pay extends BaseModel
  21. {
  22. public $refund_pay_type = array (
  23. 'offline_refund_pay' => '线下退款',
  24. 'online_refund_pay' => '原路退款'
  25. );
  26. /********************************************************************支付**********************************************************/
  27. /**
  28. * 支付
  29. * @param $pay_type 支付方式
  30. * @param $out_trade_no 交易号
  31. * @param $app_type 请求来源类型
  32. * @param $member_id 会员id
  33. * @param null $return_url 同步回调地址
  34. * @param int $is_balance 是否使用余额
  35. * @param int $scene 场景值
  36. * @return mixed|void
  37. */
  38. public function pay($pay_type, $out_trade_no, $app_type, $member_id, $return_url = null, $is_balance = 0, $scene = 0,$storeid =0)
  39. {
  40. $data = $this->getPayInfo($out_trade_no)[ 'data' ];
  41. if (empty($data)) return $this->error('', '未获取到支付信息');
  42. if ($data[ 'pay_status' ] == 2) return $this->success([ 'pay_success' => 1 ]);
  43. $notify_url = addon_url('pay/pay/notify');
  44. if (empty($return_url)) {
  45. $return_url = addon_url('pay/pay/payreturn');
  46. }
  47. // 是否使用余额
  48. if ($is_balance) {
  49. $data[ 'member_id' ] = $member_id;
  50. $data[ 'storeid' ] = $storeid;
  51. $use_res = $this->useBalance($data)[ 'data' ];
  52. if (isset($use_res[ 'pay_success' ])) return $this->success($use_res);
  53. $data = $this->getPayInfo($out_trade_no)[ 'data' ];
  54. }
  55. $data[ 'app_type' ] = $app_type;
  56. $data[ 'notify_url' ] = $notify_url;
  57. $data[ 'return_url' ] = $return_url;
  58. $data[ 'pay_type' ] = $pay_type;
  59. $data[ 'member_id' ] = $member_id;
  60. $data[ 'scene' ] = $scene;
  61. $res = event('Pay', $data, true);
  62. if (empty($res)) return $this->error('', '没有可用的支付方式');
  63. return $res;
  64. }
  65. /**
  66. * 创建支付流水号
  67. */
  68. public function createOutTradeNo($member_id = 0)
  69. {
  70. $cache = Cache::get('pay_out_trade_no' . $member_id . time());
  71. if (empty($cache)) {
  72. Cache::set('pay_out_trade_no' . $member_id . time(), 1000);
  73. $cache = Cache::get('pay_out_trade_no' . $member_id . time());
  74. } else {
  75. $cache = $cache + 1;
  76. Cache::set('pay_out_trade_no' . $member_id . time(), $cache);
  77. }
  78. $no = time() . rand(1000, 9999) . $member_id . $cache;
  79. return $no;
  80. }
  81. /**
  82. * 添加支付信息
  83. * @param int $site_id //站点id 默认平台配置为0
  84. * @param string $out_trade_no 交易流水号
  85. * @param string $app_type 支付端口类型
  86. * @param int $pay_type 支付方式,默认为空
  87. * @param string $pay_body 支付主体
  88. * @param string $pay_detail 支付细节
  89. * @param double $pay_money 支付金额
  90. * @param string $pay_no 支付账号
  91. * @param string $notify_url 要求的异步回调网址,实际支付后会进行执行或者回调,可以是事件或者域名
  92. * @param string $return_url 同步回调网址,知己支付后会进行同步回调
  93. */
  94. public function addPay($site_id, $out_trade_no, $pay_type, $pay_body, $pay_detail, $pay_money, $pay_no, $notify_url, $return_url)
  95. {
  96. $data = array (
  97. 'site_id' => $site_id,
  98. 'out_trade_no' => $out_trade_no,
  99. 'pay_body' => $pay_body,
  100. 'pay_detail' => $pay_detail,
  101. 'pay_money' => $pay_money,
  102. 'pay_no' => $pay_no,
  103. 'event' => $notify_url,
  104. 'return_url' => $return_url,
  105. 'pay_status' => 0,
  106. 'create_time' => time(),
  107. );
  108. $result = model('pay')->add($data);
  109. if ($pay_money == 0) {
  110. $this->onlinePay($out_trade_no, $pay_type, '', '');
  111. }
  112. return $result;
  113. }
  114. /**
  115. * 在线支付
  116. * @param $out_trade_no
  117. * @param $pay_type
  118. * @param $trade_no
  119. * @param $pay_addon
  120. * @param array $log_data
  121. * @return array|mixed|void
  122. * @throws \Exception
  123. */
  124. public function onlinePay($out_trade_no, $pay_type, $trade_no, $pay_addon, $log_data = [])
  125. {
  126. $pay_info_result = $this->getPayInfo($out_trade_no);
  127. $pay_info = $pay_info_result[ 'data' ];
  128. $pay_type = empty($pay_type) ? 'ONLINE_PAY' : $pay_type;
  129. //支付状态 (未支付 未取消)
  130. if ($pay_info[ 'pay_status' ] == 0) {
  131. $data = array (
  132. 'trade_no' => $trade_no,
  133. 'pay_type' => $pay_type,
  134. 'pay_addon' => $pay_addon,
  135. 'pay_time' => time(),
  136. 'pay_status' => 2
  137. );
  138. $res = model('pay')->update($data, [ [ 'out_trade_no', '=', $out_trade_no ] ]);
  139. //成功则直接给应用异步回调地址发送
  140. $return_data = array (
  141. 'out_trade_no' => $out_trade_no,
  142. 'trade_no' => $trade_no,
  143. 'pay_type' => $pay_type,
  144. 'log_data' => $log_data
  145. );
  146. //根据事件成功后执行
  147. if (strpos($pay_info[ 'event' ], 'http://') !== 0 || strpos($pay_info[ 'event' ], 'https://') !== 0) {
  148. $result = event($pay_info[ 'event' ], $return_data, true);
  149. if (!empty($result)) {
  150. $code = $result[ 'code' ] ?? 0;
  151. if ($code < 0) {
  152. model('pay')->update([ 'pay_status' => 0, 'pay_time' => 0 ], [ [ 'out_trade_no', '=', $out_trade_no ] ]);
  153. return $result;
  154. }
  155. }
  156. } else {
  157. http($pay_info[ 'event' ], 1);
  158. }
  159. return $this->success($return_data);
  160. } else {
  161. return $this->success('', '当前单据已支付');
  162. }
  163. }
  164. /**
  165. * 删除并关闭支付
  166. * @param $out_trade_no
  167. */
  168. public function deletePay($out_trade_no)
  169. {
  170. $pay_info_result = $this->getPayInfo($out_trade_no);
  171. $pay_info = $pay_info_result[ 'data' ];
  172. if (!empty($pay_info)) {
  173. //支付状态 (未支付 未取消)
  174. if ($pay_info[ 'pay_status' ] == 0) {
  175. // if (!empty($pay_info[ 'pay_type' ])) {
  176. try {
  177. $close_result = event('PayClose', $pay_info);
  178. if (!empty($close_result[ 'code' ]) && $close_result[ 'code' ] < 0) {
  179. return $close_result;
  180. }
  181. } catch (\Exception $e) {
  182. // dd($e->getMessage().$e->getFile().$e->getLine());
  183. }
  184. // }
  185. // 冻结中的余额返还
  186. if ($pay_info[ 'member_id' ]) {
  187. if ($pay_info[ 'balance' ]) model('member')->setDec([ [ 'site_id', '=', $pay_info[ 'site_id' ] ], [ 'member_id', '=', $pay_info[ 'member_id' ] ] ], 'balance_lock', $pay_info[ 'balance' ]);
  188. if ($pay_info[ 'balance_money' ]) model('member')->setDec([ [ 'site_id', '=', $pay_info[ 'site_id' ] ], [ 'member_id', '=', $pay_info[ 'member_id' ] ] ], 'balance_money_lock', $pay_info[ 'balance_money' ]);
  189. }
  190. $res = model('pay')->delete([ [ 'out_trade_no', '=', $out_trade_no ] ]);
  191. if ($res === false) {
  192. return $this->error('', 'UNKNOW_ERROR');
  193. } else {
  194. return $this->success($res);
  195. }
  196. } else {
  197. return $this->error([], '当前支付已完成');
  198. }
  199. } else {
  200. return $this->success();
  201. }
  202. }
  203. /**
  204. * 重新生成新的pay支付记录
  205. * @param $out_trade_no 原交易流水号
  206. * @param $pay_money
  207. */
  208. public function rewritePay($out_trade_no, $pay_money)
  209. {
  210. $pay_info_result = $this->getPayInfo($out_trade_no);
  211. $pay_info = $pay_info_result[ 'data' ];
  212. //支付状态 (未支付 未取消)
  213. if ($pay_info[ 'pay_status' ] == 0) {
  214. // if (!empty($pay_info[ 'pay_type' ])) {
  215. $close_result = event('payClose', $pay_info, true);
  216. if (!empty($close_result[ 'code' ]) && $close_result[ 'code' ] < 0) {
  217. return $close_result;
  218. }
  219. // }
  220. $new_out_trade_no = $this->createOutTradeNo();
  221. $data = array (
  222. 'out_trade_no' => $new_out_trade_no,
  223. 'pay_money' => $pay_money
  224. );
  225. $res = model('pay')->update($data, [ [ 'out_trade_no', '=', $out_trade_no ] ]);
  226. if ($res === false) {
  227. return $this->error('', 'UNKNOW_ERROR');
  228. } else {
  229. return $this->success($new_out_trade_no);
  230. }
  231. } else {
  232. return $this->error([], '当前支付已完成');
  233. }
  234. }
  235. /**
  236. * 支付绑定商户信息
  237. * @param $condition
  238. * @param $data
  239. */
  240. public function bindMchPay($out_trade_no, $json_data)
  241. {
  242. $data = array (
  243. 'mch_info' => json_encode($json_data, JSON_UNESCAPED_UNICODE)
  244. );
  245. $condition = array (
  246. [ 'out_trade_no', '=', $out_trade_no ]
  247. );
  248. $res = model('pay')->update($data, $condition);
  249. return $res;
  250. }
  251. /**
  252. * 获取支付方式
  253. * @param unknown $params 'pay_scene' => ['wap', 'wechat', 'app', 'pc', 'wechat_applet']
  254. */
  255. public function getPayType($params = [])
  256. {
  257. $res = event('PayType', $params);
  258. return $this->success($res);
  259. }
  260. /**
  261. * 获取支付信息详情
  262. * @param string $out_trade_no
  263. */
  264. public function getPayInfo($out_trade_no)
  265. {
  266. $get_pay_list = model('pay')->setIsCache(0)->getInfo([ [ 'out_trade_no', '=', $out_trade_no ] ]);
  267. return $this->success($get_pay_list);
  268. }
  269. /**
  270. * 支付记录
  271. * @param array $condition
  272. * @param number $page
  273. * @param string $page_size
  274. * @param string $order
  275. * @param string $field
  276. * @return multitype:string mixed
  277. */
  278. public function getPayPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*')
  279. {
  280. $list = model('pay')->pageList($condition, $field, $order, $page, $page_size);
  281. return $this->success($list);
  282. }
  283. /**
  284. * 支付统计
  285. * @param unknown $site_id
  286. */
  287. public function getPayStatistics($condition)
  288. {
  289. $statistics_array = array (
  290. 'count' => model('pay')->getCount($condition),
  291. 'sum_money' => model('pay')->getSum($condition, 'pay_money')
  292. );
  293. return $statistics_array;
  294. }
  295. /****************************************************************退款**************************************************************/
  296. /**
  297. * 创建退款流水号
  298. */
  299. public function createRefundNo()
  300. {
  301. $cache = Cache::get('pay_refund_out_trade_no' . time());
  302. if (empty($cache)) {
  303. Cache::set('niutk' . time(), 1000);
  304. $cache = Cache::get('pay_refund_out_trade_no' . time());
  305. } else {
  306. $cache = $cache + 1;
  307. Cache::set('pay_refund_out_trade_no' . time(), $cache);
  308. }
  309. $no = date('Ymdhis', time()) . rand(1000, 9999) . $cache;
  310. return $no;
  311. }
  312. /**
  313. * 原路退款
  314. * @param $refund_no
  315. * @param $refund_fee
  316. * @param $out_trade_no
  317. * @param $refund_desc
  318. * @param $total_fee 实际支付金额
  319. * @param $refund_type 退款方式 1 原路退款 2 线下支付
  320. */
  321. public function refund($refund_no, $refund_fee, $out_trade_no, $refund_desc, $total_fee, $site_id, $refund_type, $order_goods_id = 0, $is_video_number = 0)
  322. {
  323. //是否是原理退款方式退款
  324. if ($refund_type == 1) {
  325. $pay_info_result = $this->getPayInfo($out_trade_no);
  326. $pay_info = $pay_info_result[ 'data' ];
  327. if (empty($pay_info))
  328. return $this->error('', '付款记录不存在!');
  329. $order_goods_info = model('order_goods')->getInfo([ [ 'order_goods_id', '=', $order_goods_id ] ]);
  330. $data = array (
  331. 'refund_no' => $refund_no,
  332. 'refund_fee' => $refund_fee,
  333. 'refund_desc' => $refund_desc,
  334. 'pay_info' => $pay_info,
  335. 'total_fee' => $total_fee,
  336. 'site_id' => $site_id,
  337. 'order_goods_id' => $order_goods_id,
  338. 'is_video_number' => $is_video_number,
  339. 'out_aftersale_id' => $order_goods_info[ 'out_aftersale_id' ]
  340. );
  341. //退款金额许大于0
  342. if ($refund_fee > 0 && !in_array($pay_info[ 'pay_type' ], [ 'OFFLINE_PAY', 'BALANCE', 'ONLINE_PAY' ])) {
  343. $result = event('PayRefund', $data, true);
  344. if (empty($result))
  345. return $this->error('', '找不到可用的退款方式!');
  346. if ($result[ 'code' ] < 0)
  347. return $result;
  348. }
  349. }
  350. $refund_data = array (
  351. 'refund_no' => $refund_no,
  352. 'refund_fee' => $refund_fee,
  353. 'total_money' => $total_fee,
  354. 'refund_type' => $refund_type,
  355. 'site_id' => $site_id,
  356. 'out_trade_no' => $out_trade_no,
  357. );
  358. $this->addRefundPay($refund_data);
  359. return $this->success();
  360. }
  361. /**
  362. * 添加退款记录
  363. * @param unknown $data
  364. */
  365. public function addRefundPay($data)
  366. {
  367. $data[ 'create_time' ] = time();
  368. $data[ 'refund_detail' ] = '支付交易号:' . $data[ 'out_trade_no' ] . ',退款金额:' . $data[ 'refund_fee' ] . '元';
  369. $res = model('pay_refund')->add($data);
  370. if ($res == false) {
  371. return $this->error($res);
  372. }
  373. return $this->success($res);
  374. }
  375. /**
  376. * 获取支付状态
  377. * @param unknown $out_trade_no
  378. * @return multitype:
  379. */
  380. public function getPayStatus($out_trade_no)
  381. {
  382. $info = model('pay')->getInfo([ [ 'out_trade_no', '=', $out_trade_no ] ], 'pay_status');
  383. if (empty($info)) return $this->error();
  384. return $this->success($info);
  385. }
  386. /**
  387. * 查询转账方式
  388. * @return array
  389. */
  390. public function getTransferType($site_id)
  391. {
  392. $data = array (
  393. 'bank' => '银行卡'
  394. );
  395. $temp_array = event('TransferType', [ 'site_id' => $site_id ]);
  396. if (!empty($temp_array)) {
  397. foreach ($temp_array as $k => $v) {
  398. $data[ $v[ 'type' ] ] = $v[ 'type_name' ];
  399. }
  400. }
  401. return $data;
  402. }
  403. /**
  404. * 使用余额
  405. * @param $pay_info
  406. */
  407. public function useBalance($pay_info)
  408. {
  409. // 查询是否可使用余额
  410. $balance_config = ( new OrderConfig() )->getBalanceConfig($pay_info[ 'site_id' ])[ 'data' ][ 'value' ];
  411. if (!$balance_config[ 'balance_show' ]) return $this->success();
  412. // 查询会员当前可用余额
  413. // $balance_data = ( new Member() )->getMemberUsableBalance($pay_info[ 'site_id' ], $pay_info[ 'member_id' ]);
  414. $balance_data = ( new Member() )->getMemberUsableBalancenew($pay_info[ 'site_id' ], $pay_info[ 'member_id' ],$pay_info['storeid']);
  415. if ($balance_data[ 'code' ] != 0) return $balance_data;
  416. $balance_data = $balance_data[ 'data' ];
  417. if ($balance_data[ 'usable_balance' ] <= 0) return $this->success();
  418. $data = [
  419. 'pay_money' => $pay_info[ 'pay_money' ],
  420. 'member_id' => $pay_info[ 'member_id' ]
  421. ];
  422. if ($balance_data[ 'balance' ] > 0) {
  423. $data[ 'balance' ] = bccomp($balance_data[ 'balance' ], $data[ 'pay_money' ], 2) == 1 ? $data[ 'pay_money' ] : $balance_data[ 'balance' ];
  424. $data[ 'pay_money' ] -= $data[ 'balance' ];
  425. }
  426. if ($balance_data[ 'balance_money' ] > 0 && $data[ 'pay_money' ] > 0) {
  427. $data[ 'balance_money' ] = bccomp($balance_data[ 'balance_money' ], $data[ 'pay_money' ], 2) == 1 ? $data[ 'pay_money' ] : $balance_data[ 'balance_money' ];
  428. $data[ 'pay_money' ] -= $data[ 'balance_money' ];
  429. }
  430. model('pay')->startTrans();
  431. try {
  432. model('pay')->update($data, [ [ 'out_trade_no', '=', $pay_info[ 'out_trade_no' ] ] ]);
  433. if (isset($data[ 'balance' ]) && $data[ 'balance' ] > 0) model('member')->setInc([ [ 'site_id', '=', $pay_info[ 'site_id' ] ], [ 'member_id', '=', $pay_info[ 'member_id' ] ] ], 'balance_lock', $data[ 'balance' ]);
  434. if (isset($data[ 'balance_money' ]) && $data[ 'balance_money' ] > 0) model('member')->setInc([ [ 'site_id', '=', $pay_info[ 'site_id' ] ], [ 'member_id', '=', $pay_info[ 'member_id' ] ] ], 'balance_money_lock', $data[ 'balance_money' ]);
  435. if ($data[ 'pay_money' ] == 0) {
  436. $res = $this->onlinePay($pay_info[ 'out_trade_no' ], 'BALANCE', '', '');
  437. if ($res[ 'code' ] != 0) {
  438. model('pay')->rollback();
  439. return $res;
  440. }
  441. model('pay')->commit();
  442. return $this->success([ 'pay_success' => 1 ]);
  443. }
  444. model('pay')->commit();
  445. return $this->success();
  446. } catch (\Exception $e) {
  447. model('pay')->rollback();
  448. return $this->error('', '支付冻结余额错误');
  449. }
  450. }
  451. /**
  452. * 重置支付
  453. * @param $params
  454. */
  455. public function resetPay($params)
  456. {
  457. $out_trade_no = $params[ 'out_trade_no' ];
  458. $pay_info = $this->getPayInfo($out_trade_no)[ 'data' ] ?? [];
  459. if (empty($pay_info))
  460. return $this->error();
  461. $result = event('PayReset', $pay_info, true);//各种插件自己实现
  462. if (empty($result)) {
  463. switch ( $pay_info[ 'event' ] ) {
  464. case 'OrderPayNotify':
  465. $order_pay_model = new OrderPay();
  466. $result = $order_pay_model->reset0rderTradeNo([ 'out_trade_no' => $out_trade_no ]);
  467. break;
  468. }
  469. }
  470. if ($result[ 'code' ] < 0)
  471. return $result;
  472. return $result;
  473. }
  474. /**
  475. * 支付编辑(切勿调用,收银业务专用)
  476. * @param $data
  477. * @param $condition
  478. */
  479. public function edit($data, $condition)
  480. {
  481. model('pay')->update($data, $condition);
  482. return $this->success();
  483. }
  484. }