OrderService.php 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121
  1. <?php
  2. namespace addons\AvoidTax\common\service;
  3. use addons\AvoidTax\common\enums\OrderTypeEnum;
  4. use addons\AvoidTax\common\enums\StatusEnum;
  5. use addons\AvoidTax\common\models\LySilentSign;
  6. use addons\AvoidTax\common\models\TaxAccount;
  7. use addons\AvoidTax\common\models\TaxAccountBook;
  8. use addons\AvoidTax\common\models\TaxConfig;
  9. use addons\AvoidTax\common\models\TaxOrder;
  10. use addons\AvoidTax\common\models\TaxOrderBook;
  11. use addons\AvoidTax\common\models\TaxOrderLog;
  12. use addons\AvoidTax\common\models\TaxUserAuth;
  13. use addons\AvoidTax\common\models\UserWithdrawLog;
  14. use common\enums\RabbitMqEnum;
  15. use common\helpers\FormatHelper;
  16. use common\helpers\LockHelper;
  17. use Exception;
  18. use Yii;
  19. use yii\helpers\Json;
  20. /**
  21. * OrderService class
  22. * @package addons\AvoidTax\common\service
  23. */
  24. class OrderService extends BaseService
  25. {
  26. /**
  27. * 签约用户信息
  28. * @deprecated
  29. * @var array
  30. */
  31. protected $sign_users;
  32. /**
  33. * 当前订单签约用户信息
  34. *
  35. * @var TaxUserAuth|LySilentSign|null
  36. */
  37. protected $signUser;
  38. /**
  39. * 手续费订单
  40. *
  41. * @var TaxOrder
  42. */
  43. protected $feeOrder;
  44. /**
  45. * 用户订单
  46. *
  47. * @var TaxOrder
  48. */
  49. protected $userOrder;
  50. /**
  51. * 获取订单列表
  52. *
  53. * @param array $params
  54. * @return array
  55. */
  56. public function getOrderList($params)
  57. {
  58. $params = array_filter($params, function ($val) {
  59. return ($val === '' || $val === null) ? false : true;
  60. });
  61. $limit = $this->getLimit($params);
  62. $page = $this->getPage($params);
  63. $data = TaxOrder::getPageList($page, $limit, function($query) use ($params) {
  64. $query->with(['account', 'withdraw' => function ($q) {
  65. return $q->select('id, order_no');
  66. }])
  67. ->where(['mall_id' => $this->mall_id, 'sub_type' => TaxOrder::SUB_TYPE_USER]);
  68. // 标题查询
  69. !empty($params['title']) && $query->andWhere(['like', 'title', $params['title']]);
  70. // 批次号查询
  71. !empty($params['batch_num']) && $query->andWhere(['like', 'batch_num', $params['batch_num']]);
  72. !empty($params['order_sn']) && $query->andWhere(['like', 'order_sn', $params['order_sn']]);
  73. !empty($params['status']) && $query->andWhere(['=', 'status', $params['status']]);
  74. !empty($params['mobile']) && $query->andWhere(['=', 'mobile', $params['mobile']]);
  75. !empty($params['name']) && $query->andWhere(['=', 'name', $params['name']]);
  76. if (!empty($params['date'])) {
  77. $query->andWhere(['>=', 'created_at', strtotime($params['date'][0])]);
  78. $query->andWhere(['<=', 'created_at', strtotime($params['date'][1])]);
  79. }
  80. }, 'id desc');
  81. foreach ($data['list'] as $k => $v) {
  82. $data['list'][$k]['status_text'] = StatusEnum::getMap()[$v['status']];
  83. $data['list'][$k]['type_text'] = OrderTypeEnum::getMap()[$v['type']];
  84. }
  85. // 统计
  86. $statistic = TaxOrder::statistic($this->mall_id, $params['date'] ?? []);
  87. $data['statistic'] = $statistic;
  88. // 导出列表
  89. $data['export_list'] = TaxOrder::fieldsList();
  90. return $data;
  91. }
  92. /**
  93. * 获取提现方法
  94. *
  95. * @param array $params
  96. * @return array
  97. */
  98. public static function getWithdrawWay($params)
  99. {
  100. $tax_config = (new TaxConfigService(['mall_id' => intval($params['mall_id'])]))->getTaxConfig();
  101. return ['alipay_tax' => ['name' => "支付宝合规提现", 'taxFee' => $tax_config['taxFee'] ?? 0]];
  102. }
  103. /**
  104. * 批量提现
  105. *
  106. * @param array $params
  107. * [
  108. * mall_id => int,
  109. * ids => array,
  110. * ]
  111. * @return void
  112. */
  113. public static function batchWithdraw(array $params)
  114. {
  115. $ids = $params['ids'];
  116. $mall_id = $params['mall_id'];
  117. $redis = Yii::$app->redis;
  118. // 默认记帐本
  119. $account = TaxAccount::getDefaultAccount($mall_id);
  120. if (empty($account)) {
  121. return [false, '支付宝合规未设置默认记帐本'];
  122. }
  123. // 是否配置税务方
  124. $service = new TaxConfigService(['mall_id' => $mall_id]);
  125. $config = $service->getTaxConfig();
  126. if (empty($config)) {
  127. return [false, '税务方未配置'];
  128. }
  129. // 支付宝
  130. if (!isset($config['taxAccountName'])) {
  131. return [false, '未设置税务方支付宝名称'];
  132. }
  133. if (!isset($config['taxAccount'])) {
  134. return [false, '未设置税务方支付宝账号'];
  135. }
  136. if (!isset($config['taxFee'])) {
  137. return [false, '未设置税务方手续费'];
  138. }
  139. foreach ($ids as $id) {
  140. $redis_key = sprintf('tax_redis_lock_%s', $id);
  141. if ($redis->get($redis_key)) {
  142. return [false, sprintf('记录%s执行中, 请勿重复操作', $id)];
  143. }
  144. $redis->setex($redis_key, 10, 1);
  145. $withdraw_log = UserWithdrawLog::getTaxLogByAgree($mall_id, $id);
  146. if (empty($withdraw_log)) {
  147. return [false, 'ID:' . $id . ' 提现记录不存在'];
  148. }
  149. // 账户信息
  150. $extra = Json::decode($withdraw_log->extra);
  151. if (empty($extra)) {
  152. return [false, '会员ID:' . $withdraw_log['user_id'] . ' 支付宝信息有误'];
  153. }
  154. }
  155. // 添加异步队列
  156. foreach ($ids as $id) {
  157. $data = compact('mall_id', 'id');
  158. Yii::$app->services->rabbitMq->delay(
  159. 3,
  160. $data,
  161. self::class,
  162. 'handleWithdraw'
  163. );
  164. }
  165. return [true, ''];
  166. }
  167. /**
  168. * 处理提现
  169. * 异步处理提现业务
  170. * 1. 创建[提现订单]
  171. * 2. 生成税务方[手续费订单]
  172. * 3. 向税务方提交[提现订单],返回成功既可发起打款
  173. * 5. 调用支付宝 往记账本充值(包含手续费) 先判断资金是否足够,不够才充值
  174. * 6. 调用支付宝 从记账本出款到用户
  175. * 7. 调用支付宝 从记账本出款到税务方(手续费)
  176. * 8. 将结果回传给税务方
  177. *
  178. * @param array $data
  179. * [
  180. * id => int, 提现记录ID
  181. * mall_id => int, 商城ID
  182. * ]
  183. * @return void
  184. */
  185. public function handleWithdraw(array $data)
  186. {
  187. $key = 'TaxHandleWithdraw';
  188. $identification = uniqid();
  189. LockHelper::lock($key, $identification);
  190. $id = $data['id'];
  191. $this->mall_id = $data['mall_id'];
  192. $alipay_service = $this->getAlipayService();
  193. Yii::$app->db->close();
  194. try {
  195. $withdraw_log = UserWithdrawLog::getTaxLogByAgreeOrWait($this->mall_id, $id);
  196. if (empty($withdraw_log)) {
  197. throw new Exception(sprintf('提现记录不存在: %s', $id));
  198. }
  199. // 低于2元不能提现(手续费低于0.1元不能转账)
  200. if ($withdraw_log->num < 2) {
  201. // 提现记录失败
  202. UserWithdrawLog::remit_fail($withdraw_log, '合规提现不能低于2元');
  203. LockHelper::uLock($key, $identification);
  204. return true;
  205. }
  206. // 创建提现订单、生成税务方手续费订单
  207. $order = $this->createOrder($withdraw_log);
  208. if ($order === false) {
  209. // 提现记录失败
  210. UserWithdrawLog::remit_fail($withdraw_log, '创建订单失败:' . $this->getError());
  211. LockHelper::uLock($key, $identification);
  212. return true;
  213. }
  214. // 如果是自用版本 检测支付宝余额
  215. if ($this->getConfigService()->isPersonalType()) {
  216. if ($this->checkAccountAmount($order) === false) {
  217. TaxOrderLog::addLog($order->batch_num, $this->getError());
  218. $this->changeOrderFail($this->getError());
  219. UserWithdrawLog::remit_fail($withdraw_log, $this->getError());
  220. LockHelper::uLock($key, $identification);
  221. return true;
  222. }
  223. }
  224. // 如果是服务商版本 检测用工方余额
  225. if ($this->getConfigService()->isServiceType()) {
  226. if ($this->checkBookAmount($order) === false) {
  227. TaxOrderLog::addLog($order->batch_num, $this->getError());
  228. $this->changeOrderFail($this->getError());
  229. UserWithdrawLog::remit_fail($withdraw_log, $this->getError());
  230. LockHelper::uLock($key, $identification);
  231. return true;
  232. }
  233. }
  234. // 风险检测
  235. if ($this->riskCheck($order) === false) {
  236. TaxOrderLog::addLog($order->batch_num, "向税务方提交风控交易失败:" . $this->getError());
  237. $this->changeOrderFail($this->getError());
  238. UserWithdrawLog::remit_fail($withdraw_log, '风险检测未通过' . $this->getError());
  239. LockHelper::uLock($key, $identification);
  240. return true;
  241. }
  242. // 提交订单
  243. if ($this->subOrder($order) === false) {
  244. TaxOrderLog::addLog($order->batch_num, "向税务方提交订单失败:" . $this->getError());
  245. // TaxOrder::transError($order->batch_num, null, $this->getError());
  246. $this->changeOrderFail($this->getError());
  247. // 提现记录失败
  248. UserWithdrawLog::remit_fail($withdraw_log, '提交订单失败:' . $this->getError());
  249. LockHelper::uLock($key, $identification);
  250. return true;
  251. }
  252. // 充值(企业支付宝 到 记账本)
  253. // $response = $alipay_service->bookQuery($order->book->account_book_id, $order->book->agreement_no); // 查询的是税务方的记账本余额
  254. // $balance = $response->available_amount ?? 0;
  255. // 余额 大于 打款金额 ,不用充值。否则需要充值
  256. if (
  257. // 不再判断余额 默认直接充值到记账本
  258. true
  259. ) {
  260. // 自用版
  261. if ($this->getConfigService()->isPersonalType()) {
  262. // 企业支付宝 打款到 记账本
  263. $res = $this->transBook($order);
  264. if ($res === false) {
  265. TaxOrderLog::addLog($order->batch_num, "企业支付宝转账到记账本失败:" . $this->getError());
  266. // TaxOrder::transError($order->batch_num, null, $this->getError());
  267. $this->changeOrderFail($this->getError());
  268. // 提现记录失败
  269. UserWithdrawLog::remit_fail($withdraw_log, $this->getError());
  270. // 发送订单失败请求
  271. $this->orderFail();
  272. LockHelper::uLock($key, $identification);
  273. return true;
  274. }
  275. }
  276. // 服务商版
  277. if ($this->getConfigService()->isServiceType()) {
  278. // 从用工方记账本转账到税务方记账本
  279. $res = $this->transBookFromBook($order);
  280. if ($res === false) {
  281. TaxOrderLog::addLog($order->batch_num, "记账本转账到税务记账本失败:" . $this->getError());
  282. // TaxOrder::transError($order->batch_num, null, $this->getError());
  283. $this->changeOrderFail($this->getError());
  284. // 提现记录失败
  285. UserWithdrawLog::remit_fail($withdraw_log, $this->getError());
  286. // 发送订单失败请求
  287. $this->orderFail();
  288. LockHelper::uLock($key, $identification);
  289. return true;
  290. }
  291. }
  292. }
  293. // 记账本出款到用户账户
  294. $res = $this->byAlipayBookToTransUser($order);
  295. if (!$res) {
  296. // 打款失败 资金转回支付宝账户
  297. $service = new AccountService(['mall_id' => $this->mall_id]);
  298. if ($this->getConfigService()->isPersonalType()) {
  299. // 个人版使用退款
  300. $res = $service->refund($order->account_id);
  301. }
  302. if ($this->getConfigService()->isServiceType()) {
  303. // 服务商使用退回
  304. $res = $service->anarrhea($order->account_id, $order->amount);
  305. }
  306. if ($res === false) {
  307. Yii::error('退款失败:' . $service->getError());
  308. }
  309. // 可能支付宝账户有误
  310. TaxOrderLog::addLog($order->batch_num, "记账本出款到用户账户失败:" . $this->getError());
  311. // TaxOrder::transError($order->batch_num, null, $this->getError());
  312. $this->changeOrderFail($this->getError());
  313. UserWithdrawLog::remit_fail($withdraw_log, $this->getError());
  314. // 发送订单失败请求
  315. $this->orderFail();
  316. LockHelper::uLock($key, $identification);
  317. return true;
  318. }
  319. // 打款成功
  320. UserWithdrawLog::remit_success($withdraw_log);
  321. $order->taxOrderSuccess();
  322. // 自用版手续费打款
  323. if ($this->getConfigService()->isPersonalType()) {
  324. $this->transToTax($order->batch_num);
  325. }
  326. // 服务商版手续费打款
  327. if ($this->getConfigService()->isServiceType()) {
  328. $this->bookTransToTax($order->batch_num);
  329. }
  330. LockHelper::uLock($key, $identification);
  331. return true;
  332. } catch (Exception $e) {
  333. $this->error($e->getMessage());
  334. $exception = FormatHelper::exception($e, '支付宝合规队列提现');
  335. Yii::error($exception);
  336. LockHelper::uLock($key, $identification);
  337. return true;
  338. }
  339. LockHelper::uLock($key, $identification);
  340. return true;
  341. }
  342. /**
  343. * 创建订单
  344. *
  345. * @param UserWithdrawLog $withdraw_log
  346. * @return TaxOrder|false
  347. */
  348. protected function createOrder($withdraw_log)
  349. {
  350. // 批次号
  351. $batch_num = TaxOrder::getBatchNum();
  352. // 默认记帐本
  353. $book = TaxAccount::getDefaultAccount($this->mall_id);
  354. if (empty($book)) {
  355. return $this->error('未设置默认记帐本');
  356. }
  357. // 设置签约用户信息
  358. if ($this->setSignUser($withdraw_log) === false) {
  359. return $this->error('用户未签约');
  360. }
  361. $fee = self::makeFee($withdraw_log->actual_num, $this->getConfigService()->getTaxFee());
  362. if ($fee <= 0) $fee = 0.1;
  363. if (($this->userOrder = $model = TaxOrder::addUserOrder($batch_num, $book->id, $fee, $withdraw_log)) == false) {
  364. return $this->error('创建用户支付宝订单失败');
  365. }
  366. if (($this->feeOrder = TaxOrder::addTaxOrder($batch_num, $book->id, $fee, $withdraw_log, $this->config_service)) == false) {
  367. return $this->error('创建手续费订单失败');
  368. }
  369. return $model;
  370. }
  371. /**
  372. * 风险检测
  373. *
  374. * @param TaxOrder $order 必须是用户打款的订单
  375. * @return boolean
  376. */
  377. protected function riskCheck(TaxOrder $order)
  378. {
  379. if ($this->getConfigService()->isXLApiType()) {
  380. if (empty($this->signUser)) {
  381. return $this->error('用户未签约');
  382. }
  383. if ($this->getXlService()->getRiskControl($order, $this->signUser) === false) {
  384. return $this->error('打款批次风控接口检测失败' . $this->getXlService()->getError());
  385. }
  386. }
  387. if ($this->getConfigService()->isLYApiType()) {
  388. if (empty($this->signUser)) {
  389. return $this->error('用户未签约');
  390. }
  391. if ($this->getLyService()->riskCheck($order, $this->signUser) === false) {
  392. return $this->error('打款批次风控接口检测失败' . $this->getLyService()->getError());
  393. }
  394. }
  395. return true;
  396. }
  397. /**
  398. * 通过提现信息设置用户签约信息
  399. *
  400. * @param UserWithdrawLog $log
  401. * @return boolean
  402. */
  403. public function setSignUser(UserWithdrawLog $log)
  404. {
  405. $user_id = $log->user_id;
  406. list($name, $mobile, $idcard) = $log->getExtra();
  407. // 如果打款账号是邮箱
  408. if ($this->getConfigService()->isLYApiType() && strpos($mobile, "@")) {
  409. $mobile = LySilentSign::find()
  410. ->where(['user_id' => $user_id, 'account' => $mobile])
  411. ->select('phone')
  412. ->scalar();
  413. }
  414. if ($this->getConfigService()->isXLApiType()) {
  415. $sign = $this->signUser
  416. = TaxUserAuth::getSignByNMI($user_id, $name, $mobile, $idcard);
  417. }
  418. if ($this->getConfigService()->isLYApiType()) {
  419. $sign = $this->signUser
  420. = LySilentSign::getSignByNMI($user_id, $name, $mobile, $idcard);
  421. }
  422. return !empty($sign);
  423. }
  424. /**
  425. * 获取用户签约信息 废弃
  426. *
  427. * @deprecated
  428. * @param integer $user_id
  429. * @return TaxUserAuth|LySilentSign|null
  430. */
  431. protected function getSignUser(int $user_id)
  432. {
  433. $type = $this->getConfigService()->getApiType();
  434. if (!empty($this->sign_users[$type][$user_id])) {
  435. return $this->sign_users[$type][$user_id];
  436. }
  437. if ($this->getConfigService()->isXLApiType()) {
  438. return $this->sign_users[TaxConfig::XIONLONG][$user_id]
  439. = TaxUserAuth::getAccountUser($this->mall_id, $user_id, TaxUserAuth::STATUS_OK);
  440. }
  441. if ($this->getConfigService()->isLYApiType()) {
  442. return $this->sign_users[TaxConfig::LUYONG][$user_id]
  443. = LySilentSign::getSignUser($user_id);
  444. }
  445. return null;
  446. }
  447. /**
  448. * 提交订单
  449. *
  450. * @param TaxOrder $order
  451. * @return boolean
  452. */
  453. protected function subOrder(TaxOrder $order)
  454. {
  455. if ($this->getConfigService()->isXLApiType()) {
  456. if ($this->getXlService()->submitOrder(
  457. $order,
  458. $this->feeOrder,
  459. $this->signUser
  460. ) === false) {
  461. return $this->error($this->getXlService()->getError());
  462. }
  463. }
  464. if ($this->getConfigService()->isLYApiType()) {
  465. if ($this->getLyService()->asyncTransferNotify(
  466. $order,
  467. $this->feeOrder,
  468. $this->signUser
  469. ) == false) {
  470. return $this->error('支付时异步请求接口请求失败');
  471. }
  472. }
  473. return true;
  474. }
  475. /**
  476. * 检测支付宝余额是否充足
  477. *
  478. * @param TaxOrder $order
  479. * @return boolean
  480. */
  481. protected function checkAccountAmount(TaxOrder $order)
  482. {
  483. $alipay_user_id = $this->getConfigService()->getAlipayUserId();
  484. if (empty($alipay_user_id)) {
  485. return $this->error('未设置支付宝账号ID');
  486. }
  487. $account = $this->getAlipayService()->accountQuery($alipay_user_id);
  488. if ($account === false) {
  489. return $this->error('支付宝账号ID设置错误');
  490. }
  491. // 余额不足
  492. if ($account->available_amount < bcadd($order->amount, $order->pay_fee, 2)) {
  493. return $this->error('支付宝账户余额不足, 请充值');
  494. }
  495. return true;
  496. }
  497. /**
  498. * 检测记账本余额是否充足
  499. *
  500. * @param TaxOrder $order
  501. * @return boolean
  502. */
  503. protected function checkBookAmount(TaxOrder $order)
  504. {
  505. $employ = TaxAccountBook::getEmploy($this->mall_id);
  506. $response = $this->getAlipayService()->bookQuery($employ->account_book_id, $employ->agreement_no); // 查询的是税务方的记账本余额
  507. $balance = $response->available_amount ?? 0;
  508. // 余额不足
  509. if ($balance < bcadd($order->amount, $order->pay_fee, 2)) {
  510. return $this->error('记账本余额不足, 请充值');
  511. }
  512. return true;
  513. }
  514. public static function makeFee($money, $rate = TaxOrder::TAX_ALI_ACCOUNT_RATE)
  515. {
  516. $rate = bcdiv((string)$rate, 100, 4);
  517. $fee = bcmul((string)$money, $rate, 2);
  518. return $fee <= 0 ? '0.1' : $fee;
  519. }
  520. /**
  521. * 将企业支付的资金转入记账本
  522. *
  523. * @param TaxOrder $order
  524. * @return boolean
  525. */
  526. protected function transBook(TaxOrder $order)
  527. {
  528. // 仅出款不转账到记帐本
  529. if ($order->account_type == TaxOrder::ORDER_TYPE_MANUAL) {
  530. return true;
  531. }
  532. $service = $this->getAlipayService();
  533. $book_batch_num = $order->batch_num;
  534. // 获取转账的总金额,该金额包含 给用户的和给税务方手续费的
  535. $amount = TaxOrder::find()->where(['order_sn' => $order->order_sn])->sum('amount');
  536. // 记账本资金专款拨入(商户给人力资源记账本转账)
  537. $response = $service->transUniTransferBook(
  538. $book_batch_num,
  539. $order->book->account_book_id,
  540. $order->title,
  541. $order->book->agreement_no,
  542. $amount,
  543. $order->remarks ? $order->remarks : $order->title
  544. );
  545. if ($response === false) {
  546. return $this->error($service->getError());
  547. }
  548. // 记录转账数据
  549. $res = TaxOrderBook::addOrder([
  550. 'mall_id' => $this->mall_id,
  551. 'out_biz_no' => $book_batch_num,
  552. 'order_id' => $order->id,
  553. 'identity' => $order->book->account_book_id,
  554. 'agreement_no' => $order->book->agreement_no,
  555. 'amount' => $order->amount,
  556. ]);
  557. if ($res === false) {
  558. Yii::error('支付宝合规 - 添加转账记录失败' . __LINE__);
  559. // throw new Exception('添加转账记录失败');
  560. }
  561. return true;
  562. }
  563. /**
  564. * 从用工方记账本转账到税务方记账本
  565. *
  566. * @param TaxOrder $order
  567. * @return boolean
  568. */
  569. protected function transBookFromBook(TaxOrder $order)
  570. {
  571. // 仅出款不转账到记帐本
  572. if ($order->account_type == TaxOrder::ORDER_TYPE_MANUAL) {
  573. return true;
  574. }
  575. $service = $this->getAlipayService();
  576. $book_batch_num = $order->batch_num;
  577. // 打款金额
  578. $amount = $order->amount;
  579. // 获取双方记账本
  580. $payer = TaxAccountBook::getEmploy($this->mall_id);
  581. $payee = TaxAccountBook::getTaxation($this->mall_id);
  582. if ($this->getConfigService()->getPaymentFlowStatus()) {
  583. // 流水方
  584. $payment = TaxAccountBook::getPaymentFlow($this->mall_id);
  585. // 打款到流水记账本
  586. $response = $service->transBookFromBook(
  587. $book_batch_num . '_1',
  588. $amount,
  589. '转账到记账本',
  590. $payment->account_book_id,
  591. $payment->agreement_no,
  592. $payer->account_book_id,
  593. $payer->agreement_no,
  594. );
  595. if ($response === false) {
  596. return $this->error($service->error);
  597. }
  598. // 记录转账数据
  599. $res = TaxOrderBook::addOrder([
  600. 'mall_id' => $this->mall_id,
  601. 'out_biz_no' => $book_batch_num . '_1',
  602. 'order_id' => $order->id,
  603. 'identity' => $payer->account_book_id,
  604. 'agreement_no' => $payer->agreement_no,
  605. 'amount' => $order->amount,
  606. ]);
  607. if ($res === false) Yii::error('支付宝合规 - 添加转账记录失败' . __LINE__);
  608. // 打款到税务方记账本
  609. $response = $service->transBookFromBook(
  610. $book_batch_num . '_2',
  611. $amount,
  612. '转账到记账本',
  613. $payee->account_book_id,
  614. $payee->agreement_no,
  615. $payment->account_book_id,
  616. $payment->agreement_no,
  617. );
  618. if ($response === false) {
  619. return $this->error($service->error);
  620. }
  621. // 记录转账数据
  622. $res = TaxOrderBook::addOrder([
  623. 'mall_id' => $this->mall_id,
  624. 'out_biz_no' => $book_batch_num . '_2',
  625. 'order_id' => $order->id,
  626. 'identity' => $payment->account_book_id,
  627. 'agreement_no' => $payment->agreement_no,
  628. 'amount' => $order->amount,
  629. ]);
  630. if ($res === false) Yii::error('支付宝合规 - 添加转账记录失败' . __LINE__);
  631. } else {
  632. // 记账本之间相互调拨
  633. $response = $service->transBookFromBook(
  634. $book_batch_num,
  635. $amount,
  636. '转账到记账本',
  637. $payee->account_book_id,
  638. $payee->agreement_no,
  639. $payer->account_book_id,
  640. $payer->agreement_no,
  641. );
  642. if ($response === false) {
  643. return $this->error($service->error);
  644. }
  645. // 记录转账数据
  646. $res = TaxOrderBook::addOrder([
  647. 'mall_id' => $this->mall_id,
  648. 'out_biz_no' => $book_batch_num,
  649. 'order_id' => $order->id,
  650. 'identity' => $payer->account_book_id,
  651. 'agreement_no' => $payer->agreement_no,
  652. 'amount' => $order->amount,
  653. ]);
  654. if ($res === false) {
  655. Yii::error('支付宝合规 - 添加转账记录失败' . __LINE__);
  656. }
  657. }
  658. return true;
  659. }
  660. /**
  661. * 从记账本将资金转到用户
  662. *
  663. * @param TaxOrder $order
  664. * @return boolean
  665. */
  666. protected function byAlipayBookToTransUser(TaxOrder $order)
  667. {
  668. $service = new AlipayService($this->mall_id);
  669. // 代发到户
  670. $response = $service->bookTransUniTransferUser(
  671. $order->order_sn,
  672. $order->amount,
  673. '服务费',
  674. [
  675. // 用户数据
  676. 'name' => $order->name,
  677. 'identity' => $order->mobile,
  678. ],
  679. [
  680. // 记账本数据
  681. 'account_book_id' => $order->book->account_book_id,
  682. 'agreement_no' => $order->book->agreement_no
  683. ],
  684. '服务费'
  685. );
  686. if ($response === false) {
  687. // TaxOrder::transError(null, $order->order_sn, $service->error);
  688. return $this->error($service->error);
  689. }
  690. $order->addAlipayResponse($response);
  691. return true;
  692. }
  693. /**
  694. * 异步处理
  695. * 用户打款成功之后,需要给税务方打手续费
  696. *
  697. * @param string $batch_num
  698. * @return boolean
  699. */
  700. public function transToTax(string $batch_num)
  701. {
  702. // $tax_order = TaxOrder::findOne(['batch_num' => $batch_num, 'sub_type' => TaxOrder::SUB_TYPE_TAX]);
  703. $tax_order = $this->feeOrder;
  704. if (empty($tax_order)) return $this->error('手续费订单不存在');
  705. // 直接从支付宝打款给记账方
  706. $res = $this->byAlipayToTransTax($tax_order);
  707. if (!$res) {
  708. // Yii::error("给税务方转手续费失败2:" . $tax_order->batch_num);
  709. TaxOrderLog::addLog($tax_order->order_sn, "给税务方转手续费失败:" . $this->error);
  710. return false;
  711. }
  712. $tax_order->taxOrderSuccess();
  713. // 给税务方发送支付成功通知
  714. if ($this->orderSuccess($tax_order) === false) {
  715. TaxOrderLog::addLog($tax_order->order_sn, "向税务方发送订单完成失败:" . $this->getError());
  716. return false;
  717. }
  718. return true;
  719. }
  720. /**
  721. * 用户打款成功之后,需要给税务方打手续费
  722. *
  723. * @param TaxOrder $tax_order
  724. * @return boolean
  725. */
  726. public function transToTaxByOrder(TaxOrder $tax_order)
  727. {
  728. // 直接从支付宝打款给记账方
  729. $res = $this->byAlipayToTransTax($tax_order);
  730. if (!$res) {
  731. // Yii::error("给税务方转手续费失败2:" . $tax_order->batch_num);
  732. TaxOrderLog::addLog($tax_order->order_sn, "给税务方转手续费失败:" . $this->error);
  733. return false;
  734. }
  735. $tax_order->taxOrderSuccess();
  736. // 给税务方发送支付成功通知
  737. if ($this->orderSuccess($tax_order) === false) {
  738. TaxOrderLog::addLog($tax_order->order_sn, "向税务方发送订单完成失败:" . $this->getError());
  739. return false;
  740. }
  741. return true;
  742. }
  743. /**
  744. * 手续费打款
  745. *
  746. * @param string $batch_num
  747. * @return boolean
  748. */
  749. public function bookTransToTax(string $batch_num)
  750. {
  751. // $tax_order = TaxOrder::findOne(['batch_num' => $batch_num, 'sub_type' => TaxOrder::SUB_TYPE_TAX]);
  752. $tax_order = $this->feeOrder;
  753. if (empty($tax_order)) return;
  754. // 创建订单的时候已经从支付宝转入记账本了,就不用重新转,直接出款就行了
  755. $res = $this->byBookToTransTax($tax_order);
  756. if (!$res) {
  757. // Yii::error("给税务方转手续费失败2:" . $tax_order->batch_num);
  758. TaxOrderLog::addLog($tax_order->order_sn, "给税务方转手续费失败:" . $this->getError());
  759. return false;
  760. }
  761. $tax_order->taxOrderSuccess();
  762. // 给税务方发送支付成功通知
  763. if ($this->orderSuccess($tax_order) === false) {
  764. TaxOrderLog::addLog($tax_order->order_sn, "向税务方发送订单完成失败:" . $this->getError());
  765. return false;
  766. }
  767. return true;
  768. }
  769. /**
  770. * 手续费打款
  771. *
  772. * @param TaxOrder $order
  773. * @return boolean
  774. */
  775. public function bookTransToTaxByOrder(TaxOrder $tax_order)
  776. {
  777. // 创建订单的时候已经从支付宝转入记账本了,就不用重新转,直接出款就行了
  778. $res = $this->byBookToTransTax($tax_order);
  779. if (!$res) {
  780. // Yii::error("给税务方转手续费失败2:" . $tax_order->batch_num);
  781. TaxOrderLog::addLog($tax_order->order_sn, "给税务方转手续费失败:" . $this->getError());
  782. return false;
  783. }
  784. $tax_order->taxOrderSuccess();
  785. // 给税务方发送支付成功通知
  786. if ($this->orderSuccess($tax_order) === false) {
  787. TaxOrderLog::addLog($tax_order->order_sn, "向税务方发送订单完成失败:" . $this->getError());
  788. return false;
  789. }
  790. return true;
  791. }
  792. /**
  793. * 订单完成 通知税务方
  794. *
  795. * @param TaxOrder $tax_order
  796. * @return boolean
  797. */
  798. public function orderSuccess(TaxOrder $tax_order)
  799. {
  800. if ($this->getConfigService()->isXLApiType()) {
  801. if ($this->getXlService()->pushPayFinish(
  802. $this->userOrder,
  803. $this->feeOrder,
  804. $this->signUser
  805. ) === false) {
  806. return $this->error($this->getXlService()->getError());
  807. }
  808. }
  809. if ($this->getConfigService()->isLYApiType()) {
  810. // 发送订单成功通知
  811. if ($this->getLyService()->asyncTransferResultNotifyOk($this->userOrder) === false) {
  812. return $this->error('支付时异步请求接口请求失败');
  813. }
  814. }
  815. return true;
  816. }
  817. /**
  818. * 订单失败 通知税务方
  819. *
  820. * @return boolean
  821. */
  822. public function orderFail()
  823. {
  824. if ($this->getConfigService()->isXLApiType()) {
  825. // 目前没有处理方式
  826. return true;
  827. }
  828. if ($this->getConfigService()->isLYApiType()) {
  829. // 发送订单成功通知
  830. if ($this->getLyService()->asyncTransferResultNotifyNo($this->userOrder) === false) {
  831. return $this->error('支付时异步请求接口请求失败');
  832. }
  833. }
  834. return true;
  835. }
  836. /**
  837. * 手续费转账给税务公司
  838. *
  839. * @param TaxOrder $order
  840. * @return boolean
  841. */
  842. protected function byAlipayToTransTax(TaxOrder $order)
  843. {
  844. $service = $this->getAlipayService();
  845. $config = $this->getConfigService()->getConfig();
  846. $tax_config = $this->getConfigService()->getTaxConfig();
  847. if ($config['settle'] == 1) {
  848. $product_code = 'TRANS_ACCOUNT_NO_PWD';
  849. // 支付宝
  850. $payee = [
  851. 'identity_type' => 'ALIPAY_LOGON_ID',
  852. // 用户id
  853. 'identity' => $tax_config['taxAccount'],
  854. 'name' => $tax_config['taxAccountName'],
  855. ];
  856. }
  857. if ($config['settle'] == 2) {
  858. $product_code = 'TRANS_BANKCARD_NO_PWD';
  859. // 银行卡
  860. $payee = [
  861. 'identity_type' => 'BANKCARD_ACCOUNT',
  862. // 用户id
  863. 'identity' => $tax_config['bankCard'],
  864. 'name' => $tax_config['bankName'],
  865. 'bankcard_ext_info' => $tax_config['bank'],
  866. 'account_type' => '2',
  867. ];
  868. }
  869. // 代发到户
  870. $response = $service->transUniTransferUser(
  871. $order->order_sn,
  872. $order->amount,
  873. '服务费',
  874. $payee,
  875. $product_code
  876. );
  877. if ($response === false) {
  878. // TaxOrder::transError(null, $order->order_sn, $service->error);
  879. $order->transTaxFeeError($service->getError());
  880. return $this->error($service->getError());
  881. }
  882. $order->addAlipayResponse($response);
  883. return true;
  884. }
  885. /**
  886. * 手续费转账给税务公司(记账本打款)
  887. *
  888. * @param TaxOrder $order
  889. * @return boolean
  890. */
  891. protected function byBookToTransTax(TaxOrder $order)
  892. {
  893. $service = $this->getAlipayService();
  894. $config = $this->getConfigService()->getConfig();
  895. $tax_config = $this->getConfigService()->getTaxConfig();
  896. if ($config['settle'] == 1) {
  897. $product_code = 'TRANS_ACCOUNT_NO_PWD';
  898. // 支付宝
  899. $payee = [
  900. 'identity_type' => 'ALIPAY_LOGON_ID',
  901. // 用户id
  902. 'identity' => $tax_config['taxAccount'],
  903. 'name' => $tax_config['taxAccountName'],
  904. ];
  905. }
  906. if ($config['settle'] == 2) { // 目前没有2
  907. $product_code = 'TRANS_BANKCARD_NO_PWD';
  908. // 银行卡
  909. $payee = [
  910. 'identity_type' => 'BANKCARD_ACCOUNT',
  911. // 用户id
  912. 'identity' => $tax_config['bankCard'],
  913. 'name' => $tax_config['bankName'],
  914. 'bankcard_ext_info' => $tax_config['bank'],
  915. 'account_type' => '2',
  916. ];
  917. }
  918. // 用工方记账本打款
  919. $payer = TaxAccountBook::getEmploy($this->mall_id);
  920. // 代发到户
  921. $response = $service->bookTransUniTransferUser(
  922. $order->order_sn,
  923. $order->amount,
  924. '服务费',
  925. $payee,
  926. $payer
  927. );
  928. if ($response === false) {
  929. // TaxOrder::transError(null, $order->order_sn, $service->error);
  930. $order->transTaxFeeError($service->error);
  931. return $this->error($service->error);
  932. }
  933. $order->addAlipayResponse($response);
  934. return true;
  935. }
  936. /**
  937. * 返回提现内容
  938. *
  939. * @param array $params
  940. * @return array
  941. */
  942. public static function withdrawChannel($params)
  943. {
  944. $mall_id = $params['mall_id'];
  945. $config = TaxConfig::read($mall_id);
  946. $is_install = 0;
  947. !empty($config) && $is_install = 1;
  948. return [
  949. 'avoidtax' => ['is_install' => $is_install]
  950. ];
  951. }
  952. /**
  953. * 获取一笔手续费详情
  954. *
  955. * @param string $num
  956. * @return void
  957. */
  958. public function getFeeDeatil(string $num)
  959. {
  960. return TaxOrder::getFeeDeatilByNum($num);
  961. }
  962. /**
  963. * 订单打款失败修改
  964. *
  965. * @return void
  966. */
  967. public function changeOrderFail(string $msg)
  968. {
  969. $this->feeOrder->taxOrderFail($msg);
  970. $this->userOrder->taxOrderFail($msg);
  971. }
  972. /**
  973. * @param TaxOrder $order
  974. * @return void
  975. */
  976. public function setUserOrder(TaxOrder $order)
  977. {
  978. $this->userOrder = $order;
  979. }
  980. /**
  981. * @param TaxOrder $order
  982. * @return void
  983. */
  984. public function setFeeOrder(TaxOrder $order)
  985. {
  986. $this->feeOrder = $order;
  987. }
  988. }