AccountService.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. <?php
  2. namespace addons\AvoidTax\common\service;
  3. use addons\AvoidTax\common\enums\AlipayEnum;
  4. use addons\AvoidTax\common\jdk\alipay\AopCertClient;
  5. use addons\AvoidTax\common\models\TaxAccount;
  6. use addons\AvoidTax\common\models\TaxAccountBook;
  7. use addons\AvoidTax\common\models\TaxConfig;
  8. use addons\AvoidTax\common\models\TaxRefund;
  9. use common\helpers\ArrayHelper;
  10. use Exception;
  11. use Yii;
  12. use yii\data\Pagination;
  13. use yii\helpers\Json;
  14. /**
  15. * 账户相关
  16. * @package addons\AvoidTax\common\service
  17. */
  18. class AccountService extends BaseService
  19. {
  20. /**
  21. * 是否是新签约
  22. *
  23. * @var boolean
  24. */
  25. protected $isNewSign = false;
  26. /**
  27. * 过流水(九尾)记账本
  28. *
  29. * @var array
  30. */
  31. protected $payment_flow_agreement_nos = [
  32. [
  33. 'id' => 1,
  34. 'title' => '云校招',
  35. 'no' => '2024090607834',
  36. ],
  37. [
  38. 'id' => 0,
  39. 'title' => '新增主体',
  40. 'no' => '',
  41. ],
  42. ];
  43. /**
  44. * 税务方记账本
  45. *
  46. * @var array
  47. */
  48. protected $tax_agreement_nos = [
  49. 'zkby@ishouru.com' => '2023101016938', // 周口百耀信息科技有限公司
  50. 'liumeng@7yida.com' => '2023071314153', // 湖北必达供应链管理有限公司
  51. 'yuexin0804@163.com' => '2024070417787', // 淮南市玥昕信息科技有限公司
  52. 'yxzzfb@jianzhimao.com' => '2024090607834', // 云校招信息科技(广州)有限公司
  53. 'hunanhuaihua@bangdahr.com' => '2024090622739', // 瀚德
  54. 'xianglingyun006@163.com' => '2024111316708', // 安徽聘小易科技有限公司
  55. 'wangxq@xinlongmail.com' => '2024122016471', // 南漳雄锐网络科技有限公司
  56. '15967150814@163.com' => '2025012210207', // 江西仁泰信息技术有限公司
  57. 'passengerflyer@163.com' => '2025030615457', // 河南楠韶企业服务有限公司
  58. ];
  59. /**
  60. * 获取签约列表
  61. *
  62. * @param array $params
  63. * @return array
  64. */
  65. public function getAccountList($params = [])
  66. {
  67. $limit = $params['limit'] ?? 30;
  68. $page = $params['page'] ?? 1;
  69. $model = TaxAccount::find();
  70. $model->with(['book']);
  71. // 查询条件
  72. $model->andWhere(['mall_id' => $this->mall_id]);
  73. $pagination = new Pagination(['totalCount' => $model->count(), 'pageSize' => $limit]);
  74. $model->limit($pagination->limit)->offset($pagination->offset);
  75. $list = $model->orderBy('id desc')
  76. ->asArray()
  77. ->all();
  78. foreach ($list as $k => $v) {
  79. $list[$k]['status_text'] = AlipayEnum::STATUS[$v['status']] ?? '';
  80. }
  81. $data['list'] = $list;
  82. $data['page_count'] = $pagination->pageCount;
  83. $data['current_page'] = $page + 1;
  84. return $data;
  85. }
  86. /**
  87. * 是否是新签约
  88. *
  89. * @param int $book_type
  90. * @return boolean
  91. */
  92. public function getIsNewSign($book_type = TaxAccount::TAXATION)
  93. {
  94. $this->getExternalAgreementNo($book_type);
  95. return $this->isNewSign;
  96. }
  97. /**
  98. * 返回签约二维码 (新签约返回字符串跳转 使用已有签约返回true)
  99. *
  100. * @return string|TaxAccount
  101. */
  102. public function sign($book_type = TaxAccount::TAXATION)
  103. {
  104. $service = new AlipayService($this->mall_id);
  105. // 按配置获取商户签约号
  106. $external_agreement_no = $this->getExternalAgreementNo($book_type);
  107. // 生产签约链接
  108. $html = $service->agreementPageSign($external_agreement_no);
  109. if ($model = TaxAccount::addAccount([
  110. 'mall_id' => $this->mall_id,
  111. 'book_type' => $book_type,
  112. 'external_agreement_no' => $external_agreement_no,
  113. ])) {
  114. return $this->isNewSign ? $html : $model;
  115. }
  116. }
  117. /**
  118. * 签约查询 注册记帐本绑定
  119. *
  120. * @param integer $id
  121. * @return boolean
  122. */
  123. public function query($id = null, $external_agreement_no = null)
  124. {
  125. $account = TaxAccount::find()
  126. ->where(['mall_id' => $this->mall_id])
  127. ->andFilterWhere(['id' => $id])
  128. ->andFilterWhere(['external_agreement_no' => $external_agreement_no])
  129. ->one();
  130. if (empty($account)) {
  131. return $this->error('查询账户不存在');
  132. }
  133. if ($account->status == 'NORMAL') {
  134. return $this->error('该账户已经签约');
  135. }
  136. $service = new AlipayService($this->mall_id);
  137. // api查询签约情况
  138. if (($query_response = $service->agreementQuery($account->external_agreement_no)) === false) {
  139. return $this->error($service->error);
  140. }
  141. // api创建资金记账本
  142. $book_id = TaxAccountBook::getMerchantUserId();
  143. if (($book_response = $service->accountbookCreate(
  144. $book_id,
  145. $query_response->agreement_no
  146. )) === false) {
  147. return $this->error($service->error);
  148. }
  149. $db = Yii::$app->db->beginTransaction();
  150. try {
  151. // 更新数据
  152. TaxAccount::updateAll([
  153. 'agreement_no' => $query_response->agreement_no,
  154. 'alipay_logon_id' => $query_response->alipay_logon_id,
  155. 'personal_product_code' => $query_response->personal_product_code,
  156. 'valid_time' => strtotime($query_response->valid_time),
  157. 'sign_time' => strtotime($query_response->sign_time),
  158. 'status' => $query_response->status,
  159. 'sign_scene' => $query_response->sign_scene,
  160. 'third_party_type' => $query_response->third_party_type,
  161. ], ['id' => $account->id]);
  162. // 新增记帐本
  163. TaxAccountBook::addBook([
  164. 'mall_id' => $account->mall_id,
  165. 'book_type' => $account->book_type,
  166. 'account_id' => $account->id,
  167. 'agreement_no' => $query_response->agreement_no,
  168. 'merchant_user_id' => $book_id,
  169. 'account_book_id' => $book_response->account_book_id,
  170. 'ext_card_info' => Json::encode($book_response->ext_card_info),
  171. ]);
  172. $db->commit();
  173. } catch (Exception $e) {
  174. $db->rollback();
  175. return $this->error('查询出错');
  176. }
  177. return true;
  178. }
  179. /**
  180. * 记帐本查询
  181. *
  182. * @param integer $id
  183. * @return boolean|object
  184. */
  185. public function queryBook($id)
  186. {
  187. $where = ['id' => $id, 'mall_id' => $this->mall_id];
  188. $account = TaxAccount::find()->where($where)->with(['book'])->one();
  189. if (empty($account) || empty($account->book)) {
  190. return $this->error('账户不存在');
  191. }
  192. $service = new AlipayService($this->mall_id);
  193. $response = $service->bookQuery($account->book->account_book_id, $account->agreement_no);
  194. if ($response === false) {
  195. return $this->error($service->error);
  196. }
  197. return $response;
  198. }
  199. /**
  200. * 设置默认记帐本
  201. *
  202. * @param integer $id
  203. * @return boolean
  204. */
  205. public function setDefault($id, $book_type = TaxAccount::TAXATION)
  206. {
  207. $where = ['id' => $id, 'mall_id' => $this->mall_id, 'book_type' => $book_type];
  208. $account = TaxAccount::find()->where($where)->with(['book'])->one();
  209. if (empty($account) || empty($account->book)) {
  210. return $this->error('账户不存在或者未签约');
  211. }
  212. $db = Yii::$app->db->beginTransaction();
  213. try {
  214. TaxAccount::updateAll(
  215. ['is_default' => 0],
  216. ['mall_id' => $this->mall_id, 'book_type' => $book_type]
  217. );
  218. TaxAccount::updateAll(
  219. ['is_default' => 1],
  220. ['mall_id' => $this->mall_id, 'book_type' => $book_type, 'id' => $id]
  221. );
  222. TaxAccountBook::updateAll(
  223. ['is_default' => 0],
  224. ['mall_id' => $this->mall_id, 'book_type' => $book_type]
  225. );
  226. TaxAccountBook::updateAll(
  227. ['is_default' => 1],
  228. ['mall_id' => $this->mall_id, 'book_type' => $book_type, 'account_id' => $id]
  229. );
  230. $db->commit();
  231. } catch (Exception $e) {
  232. $db->rollback();
  233. return false;
  234. }
  235. return true;
  236. }
  237. /**
  238. * 记账本充值
  239. *
  240. * @param integer $id
  241. * @return void
  242. */
  243. public function recharge(int $id, float $amount)
  244. {
  245. $condition = ['id' => $id, 'book_type' => TaxAccount::EMPLOY, 'mall_id' => $this->mall_id];
  246. $account = TaxAccount::find()->where($condition)->with(['book'])->one();
  247. if (empty($account) || empty($account->book)) {
  248. return $this->error('账户不存在');
  249. }
  250. $service = new AlipayService($this->mall_id);
  251. $html = $service->recharge(
  252. date('YmdHis') . rand(100, 999),
  253. $amount,
  254. $account->book->account_book_id,
  255. $account->agreement_no
  256. );
  257. return $html;
  258. }
  259. /**
  260. * 记账本列表
  261. *
  262. * @return array
  263. */
  264. public function getBookList()
  265. {
  266. $list = TaxAccount::find()
  267. // ->joinWith(['book'])
  268. ->where(['mall_id' => $this->mall_id, 'status' => 'NORMAL'])
  269. ->select('id, alipay_logon_id, agreement_no')
  270. ->asArray()
  271. ->all();
  272. return $list;
  273. }
  274. /**
  275. * 记账本资金逆流
  276. *
  277. * @param integer $id
  278. * @return boolean
  279. */
  280. public function anarrhea(int $id, float $amount = 0)
  281. {
  282. $taxation = TaxAccount::getAccountByIdAndType($this->mall_id, $id, [TaxAccount::TAXATION, TaxAccount::PAYMENT_FLOW]);
  283. if (empty($taxation)) {
  284. return $this->error('记账本不存在');
  285. }
  286. // 用工方记账本
  287. $employ = TaxAccountBook::getEmploy($this->mall_id);
  288. if (empty($employ)) {
  289. return $this->error('用工方记账本不存在');
  290. }
  291. // 查询税务方记账本滞留金额
  292. $service = new AlipayService($this->mall_id);
  293. // 如果没有传递金额 查询滞留金额
  294. if ($amount == 0) {
  295. $response = $service->bookQuery($taxation->book->account_book_id, $taxation->agreement_no);
  296. $amount = $response->available_amount ?? 0;
  297. }
  298. if ($amount == 0) {
  299. return $this->error('账户无滞留金额, 或者查询失败');
  300. }
  301. // 如果记账本是税务方 需要先打款到过流水方
  302. if ($taxation->isTaxation()) {
  303. // 先打款到流水方
  304. $account = TaxAccount::getPaymentFlow($this->mall_id);
  305. $response = $service->transBookFromBook(
  306. date('YmdHis') . rand(100, 999),
  307. $amount,
  308. '滞留余额逆流',
  309. $account->book->account_book_id,
  310. $account->agreement_no,
  311. $taxation->book->account_book_id,
  312. $taxation->agreement_no
  313. );
  314. if ($response === false) {
  315. return $this->error(sprintf('逆流到过流水记账本错误: %s', $service->getError()));
  316. }
  317. // 打款方变成流水方
  318. $taxation = $account;
  319. }
  320. // 逆流
  321. $response = $service->transBookFromBook(
  322. date('YmdHis') . rand(100, 999),
  323. $amount,
  324. '滞留余额逆流',
  325. $employ->account_book_id,
  326. $employ->agreement_no,
  327. $taxation->book->account_book_id,
  328. $taxation->agreement_no
  329. );
  330. if ($response === false) {
  331. return $this->error(sprintf('逆流到用工方记账本错误: %s', $service->getError()));
  332. }
  333. return true;
  334. }
  335. /**
  336. * 删除未签约记账本
  337. *
  338. * @param integer $id
  339. * @return boolean
  340. */
  341. public function deleteNotSign(int $id)
  342. {
  343. $account = TaxAccount::getAccountByIdAndNotSign($this->mall_id, $id);
  344. if (empty($account)) {
  345. return $this->error('账户不存在');
  346. }
  347. return $account->delete();
  348. }
  349. /**
  350. * 记账本滞留金额退回
  351. *
  352. * @param integer $id
  353. * @return boolean
  354. */
  355. public function refund(int $id)
  356. {
  357. $taxation = TaxAccount::getAccountByIdAndType($this->mall_id, $id, TaxAccount::TAXATION);
  358. if (empty($taxation)) {
  359. return $this->error('记账本不存在');
  360. }
  361. // 支付宝ID
  362. $user_id = $this->getConfigService()->getAlipayUserId();
  363. // 查询税务方记账本滞留金额
  364. $service = new AlipayService($this->mall_id);
  365. $response = $service->bookQuery($taxation->book->account_book_id, $taxation->agreement_no);
  366. $amount = $response->available_amount ?? 0;
  367. if ($amount == 0) {
  368. return $this->error('账户无滞留金额, 或者查询失败');
  369. }
  370. // 退款
  371. $response = $service->transUniTransferUserByUserId(
  372. date('YmdHis') . rand(100, 999),
  373. $amount,
  374. '记账本滞留金额退回',
  375. $user_id,
  376. $taxation->book->toArray()
  377. );
  378. if ($response === false) {
  379. return $this->error($service->getError());
  380. }
  381. return true;
  382. }
  383. /**
  384. * 退款到用户账号
  385. *
  386. * @param integer $id
  387. * @param array $data
  388. * @return boolean
  389. */
  390. public function refundToAccount(int $id, array $data)
  391. {
  392. $taxation = TaxAccount::getAccountByIdAndType($this->mall_id, $id, TaxAccount::EMPLOY);
  393. if (empty($taxation)) {
  394. return $this->error('记账本不存在');
  395. }
  396. // 查询税务方记账本滞留金额
  397. $service = new AlipayService($this->mall_id);
  398. $response = $service->bookQuery($taxation->book->account_book_id, $taxation->agreement_no);
  399. $amount = $response->available_amount ?? 0;
  400. if ($amount == 0) {
  401. return $this->error('账户无滞留金额, 或者查询失败');
  402. }
  403. // 退款金额超过余额
  404. if ($data['amount'] > $amount) {
  405. return $this->error('记账本金额不足');
  406. }
  407. // 添加打款记录
  408. $refund_log = TaxRefund::addLog(array_merge([
  409. 'mall_id' => $this->mall_id,
  410. 'book_id' => $id,
  411. ], $data));
  412. if (!$refund_log) return $this->error('添加订单失败');
  413. // 退款
  414. $response = $service->bookTransUniTransferUser(
  415. date('YmdHis') . rand(100, 999),
  416. $data['amount'],
  417. '记账本滞留金额退回',
  418. [
  419. 'identity_type' => 'ALIPAY_LOGON_ID',
  420. 'identity' => $data['account_number'],
  421. 'name' => $data['account_name']
  422. ],
  423. $taxation->book
  424. );
  425. $refund_log->setResponse($response === false ? $service->getError() : $response);
  426. if ($response === false) {
  427. return $this->error($service->getError());
  428. }
  429. return true;
  430. }
  431. /**
  432. * 获取商户签约号
  433. *
  434. * @return string
  435. */
  436. protected function getExternalAgreementNo(int $book_type)
  437. {
  438. // 用工方直接返回新的签约号
  439. if ($book_type == TaxAccount::EMPLOY) {
  440. $this->isNewSign = true;
  441. return TaxAccount::getAgreementNo();
  442. }
  443. // 个人版本直接返回新的签约号
  444. if ($this->getConfigService()->isPersonalType()) {
  445. $this->isNewSign = true;
  446. return TaxAccount::getAgreementNo();
  447. }
  448. // 服务商版本
  449. if ($this->getConfigService()->isServiceType()) {
  450. // 新龙
  451. if ($this->getConfigService()->isXLApiType()) {
  452. $config = $this->getConfigService()->getXLConfig();
  453. }
  454. // 鹿用
  455. if ($this->getConfigService()->isLYApiType()) {
  456. $config = $this->getConfigService()->getLYConfig();
  457. }
  458. }
  459. // 流水方(九尾)
  460. if ($book_type == TaxAccount::PAYMENT_FLOW) {
  461. $payment_flow_id = $config['paymentFlowId'];
  462. $payment_flow = ArrayHelper::find($this->payment_flow_agreement_nos, function ($item) use ($payment_flow_id) {
  463. return $item['id'] == $payment_flow_id;
  464. });
  465. if (!empty($payment_flow['no'])) return $payment_flow['no'];
  466. $this->isNewSign = true;
  467. return TaxAccount::getAgreementNo();
  468. }
  469. // 商户名称
  470. $account = $this->getConfigService()->getPaymentFlowStatus() ? $config['taxBookAccount'] : $config['taxAccount'];;
  471. // 如果找不到已有签约号 返回新签约号
  472. if (empty($this->tax_agreement_nos[$account])) {
  473. $this->isNewSign = true;
  474. return TaxAccount::getAgreementNo();
  475. }
  476. return $this->tax_agreement_nos[$account];
  477. }
  478. /**
  479. * 过流水相关
  480. *
  481. * @return array
  482. */
  483. public function getPaymentFlow()
  484. {
  485. return [
  486. // 'has_payment_flow' => (int)Yii::$app->params['has_payment_flow'] ?? 0,
  487. 'payment_flow' => $this->payment_flow_agreement_nos,
  488. ];
  489. }
  490. public function notifyAccount($data)
  491. {
  492. $app_id = $data['app_id'] ?? '';
  493. $external_agreement_no = $data['external_agreement_no'] ?? '';
  494. $config = TaxConfig::findOne(['app_id'=>$app_id]);
  495. if(empty($config)){
  496. return "success";
  497. }
  498. $aop = $this->client($app_id,$config['private_key'],$config['alipay_public_key']);
  499. $res = $aop->rsaCheckV1($data,null,$aop->signType);
  500. var_dump($res);
  501. }
  502. private function client($app_id,$private_key,$public_key)
  503. {
  504. $aop = new AopCertClient();
  505. $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
  506. $aop->appId = $app_id;
  507. $aop->rsaPrivateKey = $private_key;
  508. $aop->alipayrsaPublicKey= $public_key;
  509. $aop->apiVersion = '1.0';
  510. $aop->signType = 'RSA2';
  511. $aop->postCharset='UTF-8';
  512. $aop->format='json';
  513. return $aop;
  514. }
  515. }