WithdrawLogic.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeshop100%开源免费商用商城系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | 商业版本务必购买商业授权,以免引起法律纠纷
  8. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  9. // | gitee下载:https://gitee.com/likeshop_gitee
  10. // | github下载:https://github.com/likeshop-github
  11. // | 访问官网:https://www.likeshop.cn
  12. // | 访问社区:https://home.likeshop.cn
  13. // | 访问手册:http://doc.likeshop.cn
  14. // | 微信公众号:likeshop技术社区
  15. // | likeshop团队 版权所有 拥有最终解释权
  16. // +----------------------------------------------------------------------
  17. // | author: likeshopTeam
  18. // +----------------------------------------------------------------------
  19. namespace app\adminapi\logic\withdraw;
  20. use app\common\enum\AccountLogEnum;
  21. use app\common\enum\NoticeEnum;
  22. use app\common\enum\WithdrawEnum;
  23. use app\common\logic\AccountLogLogic;
  24. use app\common\logic\BaseLogic;
  25. use app\common\model\User;
  26. use app\common\model\UserAuth;
  27. use app\common\model\WithdrawApply;
  28. use app\common\service\ConfigService;
  29. use app\common\service\FileService;
  30. use app\common\service\WeChatConfigService;
  31. use EasyWeChat\Factory;
  32. use think\facade\Db;
  33. use think\facade\Log;
  34. /**
  35. * 提现逻辑层
  36. * Class WithdrawLogic
  37. * @package app\adminapi\logic\withdraw
  38. */
  39. class WithdrawLogic extends BaseLogic
  40. {
  41. /**
  42. * @notes 查看提现详情
  43. * @param $params
  44. * @return mixed
  45. * @author Tab
  46. * @date 2021/8/6 20:48
  47. */
  48. public static function detail($params)
  49. {
  50. $field = 'wa.id,wa.money,wa.handling_fee,wa.left_money,wa.type,wa.type as type_desc,wa.create_time,wa.status,wa.status as status_desc,wa.transfer_voucher,wa.transfer_time,wa.transfer_remark,wa.account,wa.real_name,wa.bank,wa.subbank,wa.money_qr_code';
  51. $field .= ',u.sn,u.nickname,u.mobile';
  52. $withdrawApply = WithdrawApply::field($field)
  53. ->alias('wa')
  54. ->leftJoin('user u', 'u.id = wa.user_id')
  55. ->findOrEmpty($params['id'])
  56. ->toArray();
  57. return $withdrawApply;
  58. }
  59. /**
  60. * @notes 审核拒绝
  61. * @param $params
  62. * @return bool
  63. * @author Tab
  64. * @date 2021/8/9 9:58
  65. */
  66. public static function refuse($params)
  67. {
  68. Db::startTrans();
  69. try {
  70. // 修改提现申请单状态
  71. $withdrawApply = WithdrawApply::findOrEmpty($params['id']);
  72. if($withdrawApply->status != WithdrawEnum::STATUS_WAIT) {
  73. throw new \think\Exception('不是待提现状态,不允许审核');
  74. }
  75. $withdrawApply->status = WithdrawEnum::STATUS_FAIL;
  76. $withdrawApply->audit_remark = $params['audit_remark'] ?? '';
  77. $withdrawApply->save();
  78. // 回退提现金额
  79. self::fallbackMoney($withdrawApply);
  80. // 增加账户流水变动记录
  81. AccountLogLogic::add($withdrawApply['user_id'], AccountLogEnum::BW_INC_REFUSE_WITHDRAWAL, AccountLogEnum::INC, $withdrawApply['money'], $withdrawApply['sn'], '拒绝提现回退金额');
  82. Db::commit();
  83. return true;
  84. } catch(\Exception $e) {
  85. Db::rollback();
  86. self::setError($e->getMessage());
  87. return false;
  88. }
  89. }
  90. /**
  91. * @notes 回退提现金额
  92. * @param $withdrawApply
  93. * @author Tab
  94. * @date 2021/8/9 9:50
  95. */
  96. public static function fallbackMoney($withdrawApply)
  97. {
  98. $user = User::findOrEmpty($withdrawApply->user_id);
  99. $user->user_earnings = $user->user_earnings + $withdrawApply->money;
  100. $user->save();
  101. }
  102. /**
  103. * @notes 审核通过
  104. * @param $params
  105. * @return bool
  106. * @author Tab
  107. * @date 2021/8/9 10:49
  108. */
  109. public static function pass($params)
  110. {
  111. Db::startTrans();
  112. try {
  113. $withdrawApply = WithdrawApply::findOrEmpty($params['id']);
  114. if($withdrawApply->status != WithdrawEnum::STATUS_WAIT) {
  115. throw new \think\Exception('不是待提现状态,不允许审核');
  116. }
  117. switch($withdrawApply->type) {
  118. // 提现至余额
  119. case WithdrawEnum::TYPE_BALANCE:
  120. self::balance($withdrawApply, $params);
  121. break;
  122. // 提现至微信零钱
  123. case WithdrawEnum::TYPE_WECHAT_CHANGE:
  124. self::wechatChange($withdrawApply, $params);
  125. break;
  126. // 提现至银行卡
  127. case WithdrawEnum::TYPE_BANK:
  128. // 提现至微信收款码
  129. case WithdrawEnum::TYPE_WECHAT_CODE:
  130. // 提现至支付宝收款码
  131. case WithdrawEnum::TYPE_ALI_CODE:
  132. self::common($withdrawApply, $params);
  133. break;
  134. }
  135. Db::commit();
  136. return true;
  137. } catch(\Exception $e) {
  138. Db::rollback();
  139. Log::write($e->__toString(), 'withdraw_pass_error');
  140. self::setError($e->getMessage());
  141. return false;
  142. }
  143. }
  144. /**
  145. * @notes 提现至余额
  146. * @param $withdrawApply
  147. * @author Tab
  148. * @date 2021/8/9 10:48
  149. */
  150. public static function balance($withdrawApply, $params)
  151. {
  152. // 增加用户余额
  153. $user = User::findOrEmpty($withdrawApply->user_id);
  154. $user->user_money = $user->user_money + $withdrawApply->left_money;
  155. $user->save();
  156. // 记录账户流水
  157. AccountLogLogic::add($withdrawApply->user_id, AccountLogEnum::BNW_INC_WITHDRAW, AccountLogEnum::INC, $withdrawApply->left_money, $withdrawApply->sn, '提现至余额');
  158. // 更新提现状态
  159. $withdrawApply->status = WithdrawEnum::STATUS_SUCCESS;
  160. $withdrawApply->audit_remark = $params['audit_remark'] ?? '';
  161. $withdrawApply->save();
  162. }
  163. /**
  164. * @notes 提现至银行卡/微信收款码/支付宝收款
  165. * @param $withdrawApply
  166. * @param $params
  167. * @author Tab
  168. * @date 2021/8/9 11:01
  169. */
  170. public static function common($withdrawApply, $params)
  171. {
  172. $withdrawApply->status = WithdrawEnum::STATUS_ING;
  173. $withdrawApply->audit_remark = $params['audit_remark'] ?? '';
  174. $withdrawApply->save();
  175. }
  176. /**
  177. * @notes 提现至微信零钱
  178. * @param $withdrawApply
  179. * @param $params
  180. * @return bool
  181. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  182. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  183. * @throws \GuzzleHttp\Exception\GuzzleException
  184. * @throws \think\Exception
  185. * @author Tab
  186. * @date 2021/8/9 12:03
  187. */
  188. public static function wechatChange($withdrawApply, $params)
  189. {
  190. // 校验条件
  191. self::checkCondition($withdrawApply);
  192. //微信零钱接口:1-企业付款到零钱;2-商家转账到零钱;
  193. $transfer_way = ConfigService::get('config', 'transfer_way',1);
  194. //企业付款到零钱
  195. if ($transfer_way == WithdrawEnum::ENTERPRISE) {
  196. // 用户授权信息
  197. $userAuth = UserAuth::where('user_id', $withdrawApply->user_id)
  198. ->where('terminal', $withdrawApply->terminal)
  199. ->order('terminal', 'asc')
  200. ->findOrEmpty();
  201. if($userAuth->isEmpty()) {
  202. throw new \think\Exception('获取不到用户的openid');
  203. }
  204. // 获取app
  205. $config = WeChatConfigService::getWechatConfigByTerminal($userAuth->terminal);
  206. if ($config['interface_version'] == 'v3') {
  207. throw new \Exception('微信支付v3不支持企业付款到零钱');
  208. }
  209. $app = Factory::payment($config);
  210. // 发起企业付款
  211. $result = $app->transfer->toBalance([
  212. // 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号)
  213. 'partner_trade_no' => $withdrawApply->sn,
  214. 'openid' => $userAuth->openid,
  215. // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名
  216. 'check_name' => 'NO_CHECK',
  217. // 如果 check_name 设置为FORCE_CHECK,则必填用户真实姓名
  218. 're_user_name' => '',
  219. // 企业付款金额,单位为分 100分=1元
  220. 'amount' => intval(bcmul(100, $withdrawApply->left_money)),
  221. // 企业付款操作说明信息。必填
  222. 'desc' => '提现至微信零钱'
  223. ]);
  224. // 过滤敏感字段
  225. $fiterField = ['appid','mch_appid', 'mchid', 'mch_id', 'openid'];
  226. $filterResult = array_filter($result, function($key) use ($fiterField) {
  227. return !in_array($key, $fiterField);
  228. }, ARRAY_FILTER_USE_KEY);
  229. // 更新提现申请单为提现中状态
  230. $withdrawApply->status = WithdrawEnum::STATUS_ING;
  231. $withdrawApply->audit_remark = $params['audit_remark'] ?? '';
  232. $withdrawApply->pay_desc = json_encode($filterResult);
  233. $withdrawApply->save();
  234. // 通信标识 return_code && 业务结果 result_code
  235. if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
  236. // 企业付款成功, 更新提现申请单状态为提现成功并记录支付单号及支付时间
  237. $withdrawApply->status = WithdrawEnum::STATUS_SUCCESS;
  238. $withdrawApply->payment_no = $result['payment_no'];
  239. $withdrawApply->payment_time = strtotime($result['payment_time']);
  240. $withdrawApply->save();
  241. return true;
  242. }
  243. if($result['return_code'] == 'FAIL') {
  244. // 企业付款失败,更新提现申请单为提现失败
  245. $withdrawApply->status = WithdrawEnum::STATUS_FAIL;
  246. $withdrawApply->save();
  247. // 回退提现金额
  248. self::fallbackMoney($withdrawApply);
  249. // 记录账户流水
  250. AccountLogLogic::add($withdrawApply->user_id, AccountLogEnum::BW_INC_PAYMENT_FAIL, AccountLogEnum::INC, $withdrawApply->money, $withdrawApply->sn, '付款失败回退金额');
  251. return false;
  252. }
  253. }
  254. //商家转账到零钱
  255. if ($transfer_way == WithdrawEnum::MERCHANT) {
  256. WechatMerchantTransferLogic::transfer($withdrawApply);
  257. // 消息通知 - 通知买家
  258. event('Notice', [
  259. 'scene_id' => NoticeEnum::RECEIPT_PAYMENT_NOTICE,
  260. 'params' => [
  261. 'user_id' => $withdrawApply['user_id'],
  262. 'withdraw_id' => $withdrawApply['id']
  263. ]
  264. ]);
  265. }
  266. }
  267. /**
  268. * @notes 校验条件(提现至微信零钱)
  269. * @param $withdrawApply
  270. * @throws \think\Exception
  271. * @author Tab
  272. * @date 2021/8/9 11:30
  273. */
  274. public static function checkCondition($withdrawApply)
  275. {
  276. if($withdrawApply->left_money < 1) {
  277. throw new \think\Exception('扣除手续费后提现金额不能小于1元');
  278. }
  279. $count = WithdrawApply::whereDay('update_time')->where([
  280. ['user_id', '=', $withdrawApply->user_id],
  281. ['type', '=', WithdrawEnum::TYPE_WECHAT_CHANGE],
  282. ['status', 'in', [WithdrawEnum::STATUS_ING,WithdrawEnum::STATUS_SUCCESS,WithdrawEnum::STATUS_FAIL]],
  283. ])->count();
  284. if($count >= 10) {
  285. throw new \think\Exception('同一天向同一个用户最多付款10次');
  286. }
  287. }
  288. /**
  289. * @notes 查询结果(提现至微信零钱)
  290. * @param $params
  291. * @return bool
  292. * @throws \GuzzleHttp\Exception\GuzzleException
  293. * @author Tab
  294. * @date 2021/8/9 15:06
  295. */
  296. public static function search($params)
  297. {
  298. Db::startTrans();
  299. try {
  300. $withdrawApply = WithdrawApply::findOrEmpty($params['id']);
  301. if($withdrawApply->status != WithdrawEnum::STATUS_ING) {
  302. throw new \think\Exception('非提现中状态无法查询结果');
  303. }
  304. if($withdrawApply->type != WithdrawEnum::TYPE_WECHAT_CHANGE) {
  305. throw new \think\Exception('非微信零钱提现方式无法查询结果');
  306. }
  307. //微信零钱接口:1-企业付款到零钱;2-商家转账到零钱;
  308. $transfer_way = ConfigService::get('config', 'transfer_way',1);
  309. //企业付款到零钱
  310. if ($transfer_way == WithdrawEnum::ENTERPRISE) {
  311. // 用户授权信息
  312. $userAuth = UserAuth::where('user_id', $withdrawApply->user_id)->order('terminal', 'asc')->findOrEmpty();
  313. if($userAuth->isEmpty()) {
  314. throw new \think\Exception('获取不到用户的openid');
  315. }
  316. // 获取app
  317. $config = WeChatConfigService::getWechatConfigByTerminal($userAuth->terminal);
  318. $app = Factory::payment($config);
  319. $result = $app->transfer->queryBalanceOrder($withdrawApply->sn);
  320. // 过滤敏感字段
  321. $fiterField = ['appid','mch_appid', 'mchid', 'mch_id', 'openid'];
  322. $filterResult = array_filter($result, function($key) use ($fiterField) {
  323. return !in_array($key, $fiterField);
  324. }, ARRAY_FILTER_USE_KEY );
  325. // 记录查询结果
  326. $withdrawApply->pay_search_result = json_encode($filterResult);
  327. $withdrawApply->save();
  328. // 查询失败
  329. if($result['return_code'] == 'FAIL') {
  330. throw new \think\Exception($result['return_msg']);
  331. }
  332. // 提示信息
  333. $tips = $result['err_code_des'] ?? '';
  334. // 查询结果:转账成功
  335. if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' && $result['status'] == 'SUCCESS') {
  336. // 更新提现申请单状态
  337. $withdrawApply->status = WithdrawEnum::STATUS_SUCCESS;
  338. $withdrawApply->payment_no = $result['detail_id'];
  339. $withdrawApply->payment_time = strtotime($result['payment_time']);
  340. $withdrawApply->save();
  341. $tips = '提现成功';
  342. }
  343. // 查询结果:转账失败
  344. if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' && $result['status'] == 'FAILED') {
  345. // 更新提现申请单状态
  346. $withdrawApply->status = WithdrawEnum::STATUS_FAIL;
  347. $withdrawApply->save();
  348. // 回退提现金额
  349. self::fallbackMoney($withdrawApply);
  350. // 记录账户流水变动
  351. AccountLogLogic::add($withdrawApply->user_id, AccountLogEnum::BW_INC_PAYMENT_FAIL, AccountLogEnum::INC, $withdrawApply->money, $withdrawApply->sn, '付款失败回退金额');
  352. $tips = '提现失败';
  353. }
  354. // 查询结果:处理中
  355. if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' && $result['status'] == 'PROCESSING') {
  356. $tips = '提现处理中';
  357. }
  358. }
  359. //商家转账到零钱
  360. if ($transfer_way == WithdrawEnum::MERCHANT) {
  361. $result = WechatMerchantTransferLogic::details($withdrawApply);
  362. $tips = '正在处理中';
  363. if(isset($result['state'])) {
  364. if ($result['state'] == 'SUCCESS') {
  365. //提现成功,更新提现申请单
  366. WithdrawApply::update([
  367. 'status' => WithdrawEnum::STATUS_SUCCESS,
  368. 'pay_search_result' => json_encode($result, JSON_UNESCAPED_UNICODE),
  369. 'payment_no' => $result['detail_id'],
  370. 'payment_time' => strtotime($result['update_time']),
  371. ],['id'=>$withdrawApply['id']]);
  372. $tips = '提现成功';
  373. }
  374. if ($result['state'] == 'FAIL') {
  375. //提现失败,更新提现申请单
  376. WithdrawApply::update([
  377. 'status' => WithdrawEnum::STATUS_FAIL,
  378. 'pay_search_result' => json_encode($result, JSON_UNESCAPED_UNICODE),
  379. ],['id'=>$withdrawApply['id']]);
  380. // 回退提现金额
  381. WithdrawLogic::fallbackMoney($withdrawApply);
  382. // 记录账户流水
  383. AccountLogLogic::add($withdrawApply->user_id, AccountLogEnum::BW_INC_PAYMENT_FAIL, AccountLogEnum::INC, $withdrawApply->money, $withdrawApply->sn, '付款失败回退金额');
  384. $tips = '提现失败';
  385. }
  386. if ($result['state'] == 'PROCESSING') {
  387. $tips = '正在处理中';
  388. }
  389. if ($result['state'] == 'WAIT_USER_CONFIRM') {
  390. $tips = '待收款用户确认';
  391. }
  392. }else {
  393. // 查询失败
  394. throw new \think\Exception($result['message'] ?? '商家转账到零钱查询失败');
  395. }
  396. }
  397. // 提交事务
  398. Db::commit();
  399. // 返回提示消息
  400. return $tips;
  401. } catch(\Exception $e) {
  402. Db::rollback();
  403. self::setError($e->getMessage());
  404. return false;
  405. }
  406. }
  407. /**
  408. * @notes 转账成功
  409. * @param $params
  410. * @return bool
  411. * @author Tab
  412. * @date 2021/8/9 15:56
  413. */
  414. public static function transferSuccess($params)
  415. {
  416. try {
  417. if(!isset($params['transfer_voucher']) || empty($params['transfer_voucher'])) {
  418. throw new \think\Exception('请上传转账凭证');
  419. }
  420. if(!isset($params['transfer_remark']) || empty($params['transfer_remark'])) {
  421. throw new \think\Exception('请填写转账说明');
  422. }
  423. $withdrawApply = WithdrawApply::findOrEmpty($params['id']);
  424. if ($withdrawApply->isEmpty()) {
  425. throw new \think\Exception('提现申请不存在');
  426. }
  427. if($withdrawApply['status'] != WithdrawEnum::STATUS_ING) {
  428. throw new \think\Exception('非提现中状态无法转账');
  429. }
  430. if($withdrawApply['type'] != WithdrawEnum::TYPE_BANK && $withdrawApply['type'] != WithdrawEnum::TYPE_WECHAT_CODE && $withdrawApply['type'] != WithdrawEnum::TYPE_ALI_CODE) {
  431. throw new \think\Exception('该提现无需转账');
  432. }
  433. $params['transfer_voucher'] = FileService::setFileUrl($params['transfer_voucher']);
  434. $withdrawApply->status = WithdrawEnum::STATUS_SUCCESS;
  435. $withdrawApply->transfer_voucher = $params['transfer_voucher'];
  436. $withdrawApply->transfer_remark = $params['transfer_remark'];
  437. $withdrawApply->transfer_time = time();
  438. $withdrawApply->save();
  439. return true;
  440. } catch(\Exception $e) {
  441. self::setError($e->getMessage());
  442. return false;
  443. }
  444. }
  445. /**
  446. * @notes 转账失败
  447. * @param $params
  448. * @return bool
  449. * @author Tab
  450. * @date 2021/8/9 16:13
  451. */
  452. public static function transferFail($params)
  453. {
  454. Db::startTrans();
  455. try {
  456. $withdrawApply = WithdrawApply::findOrEmpty($params['id']);
  457. if ($withdrawApply->isEmpty()) {
  458. throw new \think\Exception('提现申请不存在');
  459. }
  460. if($withdrawApply['status'] != WithdrawEnum::STATUS_ING) {
  461. throw new \think\Exception('非提现中状态无法转账');
  462. }
  463. if($withdrawApply['type'] != WithdrawEnum::TYPE_BANK && $withdrawApply['type'] != WithdrawEnum::TYPE_WECHAT_CODE && $withdrawApply['type'] != WithdrawEnum::TYPE_ALI_CODE) {
  464. throw new \think\Exception('该提现无需转账');
  465. }
  466. // 更新状态
  467. $withdrawApply->status = WithdrawEnum::STATUS_FAIL;
  468. $withdrawApply->transfer_remark = $params['transfer_remark'];
  469. $withdrawApply->transfer_voucher = $params['transfer_voucher'];
  470. $withdrawApply->save();
  471. // 回退提现金额
  472. self::fallbackMoney($withdrawApply);
  473. // 记录账户流水
  474. AccountLogLogic::add($withdrawApply->user_id, AccountLogEnum::BW_INC_TRANSFER_FAIL, AccountLogEnum::INC, $withdrawApply->money, $withdrawApply->sn, '转账失败回退金额');
  475. Db::commit();
  476. return true;
  477. } catch(\Exception $e) {
  478. Db::rollback();
  479. self::setError($e->getMessage());
  480. return false;
  481. }
  482. }
  483. }