TransFundCommand.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. namespace App\Console\Pay;
  3. use App\Cache\UserCache;
  4. use App\Libs\Utils;
  5. use Illuminate\Console\Command;
  6. use Illuminate\Support\Facades\DB;
  7. use Illuminate\Support\Facades\Redis;
  8. use Ycpay\AliPay;
  9. use App\Servicess\WithdrawCashService;
  10. /**
  11. * 转账脚本
  12. */
  13. class TransFundCommand extends Command
  14. {
  15. /**
  16. * @var string
  17. */
  18. protected $signature = 'trans_fund';
  19. /**
  20. * The console command description.
  21. *
  22. * @var string
  23. */
  24. protected $description = '定时转账';
  25. /**
  26. * handle
  27. */
  28. public function handle()
  29. {
  30. // 读取近2小时内待转账的任务 (不读取全表,防止逻辑异常产生大影响)
  31. $alipay = new AliPay();
  32. $fund_account_res = $alipay->queryFundAccount();
  33. \Log::info('queryFundAccount_res:'.$fund_account_res->alipay_fund_account_query_response->available_amount);
  34. \Log::info(json_encode($fund_account_res));
  35. //{"alipay_fund_account_query_response":{"code":"10000","msg":"Success","available_amount":"499.70","freeze_amount":"0.00"},"alipay_cert_sn":"c0a0f43f741ae05c456b45aa20c231e4","sign":"BFQQ8Qqj2sT82vZAvPUlxfXrETSt9Mwhfche8ao5DRJNNP4f9cDPafDMeeixhtbeqlEHtGpjCTW\/vcaCk+UdKs7pZTCv17MZyzS1V1t9G1a7Y5hSbqHBNldTFrwsB\/yvFNbk2MGNd\/cX5Yp1kWjljPE8UEfSzpTNA3IQip8fNuzkJQbGomqFZjPbhD8pMcOvhKWRguImMc0fxOiWXnV0lFkfjtN5zXZNeZZhalskfDL9lnq0mn3aEamNZM8lya6nor7D0o0k2K\/ZuDqoX+clOA580LD6\/zu8Il+ZwSWE37RY7oCah7ggxbuSwKS0t3kwHwhiLcKm\/Ynzs1zT4aX4kw=="}
  36. // 钱不够直接返回
  37. $before_limit_minute = 120;
  38. $toTransUserWithdrawCashes = WithdrawCashService::getToTransUserWithdrawCashes($before_limit_minute);
  39. \Log::info('trans_fund_toTransUserWithdrawCashes,count:'.count($toTransUserWithdrawCashes));
  40. if(!empty($toTransUserWithdrawCashes)){
  41. foreach($toTransUserWithdrawCashes as $toTransUserWithdrawCashe){
  42. $redis_lock_key = 'trans_fund_lock:'.$toTransUserWithdrawCashe['id'];
  43. try{
  44. if(Redis::get($redis_lock_key)){
  45. \Log::info('trans_fund_one_lock_return:'.json_encode($toTransUserWithdrawCashe));
  46. continue;
  47. }
  48. # 加redis锁,防止并发,有错误就改状态为待打款
  49. Redis::set($redis_lock_key,date('Y-m-d H:i:s'));
  50. \Log::info('trans_fund_one_toTransUserWithdrawCashes:'.$toTransUserWithdrawCashe['uid']);
  51. $risk_manage_param = [
  52. 'uid'=>$toTransUserWithdrawCashe['uid'],
  53. 'distribution_channel_id'=>$toTransUserWithdrawCashe['distribution_channel_id'],
  54. 'alipay_username'=>$toTransUserWithdrawCashe['alipay_username'],
  55. 'alipay_account'=>$toTransUserWithdrawCashe['alipay_account'],
  56. 'amount'=>$toTransUserWithdrawCashe['amount'],
  57. ];
  58. // 风控策略
  59. $risk_check = $this->risk_manage($risk_manage_param);
  60. if($risk_check['code'] == 0){
  61. \Log::info('withdraw_risk_manage_error:'.$toTransUserWithdrawCashe['uid']);
  62. $toTransUserWithdrawCashe['status'] = '打款失败';
  63. $toTransUserWithdrawCashe['trans_fund_result'] = '触发风控策略:'.$risk_check['msg'];
  64. $toTransUserWithdrawCashe->save();
  65. # 删锁
  66. Redis::del($redis_lock_key);
  67. continue;
  68. }
  69. // FIXME 测试期间,统一1分提现
  70. // $toTransUserWithdrawCashe['amount'] = 0.1;
  71. $trans_param = [
  72. 'out_biz_no'=>generateOrderSn('tf-'),// 平台订单号
  73. 'account'=>$toTransUserWithdrawCashe['alipay_account'],
  74. 'account_name'=>$toTransUserWithdrawCashe['alipay_username'],
  75. 'trans_amount'=>$toTransUserWithdrawCashe['amount'],
  76. 'order_title'=>'火猫小说',
  77. 'remark'=>'收益提现',
  78. ];
  79. \Log::info('start_transfund:'.json_encode($trans_param));
  80. // continue;
  81. // 执行转账 ,记录下转账的结果
  82. $transFund_res = $alipay->transFund($trans_param);
  83. // {"alipay_fund_trans_uni_transfer_response":{"code":"10000","msg":"Success","order_id":"20230529020070011500080081135133","out_biz_no":"tf-2023052917470116853536215518734","pay_fund_order_id":"20230529020070011500080081135133","status":"SUCCESS","trans_date":"2023-05-29 17:47:02"},"alipay_cert_sn":"c0a0f43f741ae05c456b45aa20c231e4","sign":"NP6p5hn9NLsED2MdaaxnD3HXtyE2QSGo3RkV7kOwnoHEK6GXwhjLiW9cgBfk63VxCTrtKy1\/AoKq8PzPNIc1CJs4XHRfeIgh1ptkDVv+AfTNIpgBjrXB8ijEHukfEkdOEeRNHYQ3wrBt7sfObFek11ospCktxXutDv2mLy3QOv4AgZD5DF0CDyfRrTYMuOvL2l5zNexTrq5XFwa4sb1QRtVP0Pj25T0Sn\/Sz6O0Y3sEv1woHP3jPdqiSS1E6T\/fdnSF5VYc2syzL4mnaEf0aLDnHxwi8NnW8NqDRdB7PrnwOjsTwsktvkvKvnsfDtjPG4AgT3S\/EE6i1GFfl1w7oLA=="}
  84. \Log::info('transFund_res');
  85. \Log::info(json_encode($transFund_res));
  86. if(isset($transFund_res->alipay_fund_trans_uni_transfer_response) && $transFund_res->alipay_fund_trans_uni_transfer_response->code=='10000'){
  87. $toTransUserWithdrawCashe['status'] = '系统已打款';
  88. }else{
  89. $toTransUserWithdrawCashe['status'] = '打款失败';
  90. }
  91. $toTransUserWithdrawCashe['trans_time'] = date('Y-m-d H:i:s');
  92. $toTransUserWithdrawCashe['trans_trade_no'] = $trans_param['out_biz_no'];
  93. $toTransUserWithdrawCashe['tiktok_trade_no'] = isset($transFund_res->alipay_fund_trans_uni_transfer_response->order_id)?$transFund_res->alipay_fund_trans_uni_transfer_response->order_id:'';
  94. $toTransUserWithdrawCashe['trans_fund_result'] = json_encode($transFund_res,JSON_UNESCAPED_UNICODE);
  95. $toTransUserWithdrawCashe->save();
  96. // TODO 如果打款成功,做钉钉通知
  97. }catch(\Exception $e){
  98. \Log::info('trans_ept:'.$e);
  99. # 删锁
  100. Redis::del($redis_lock_key);
  101. }
  102. // 重要,防止上一个循环数据库没执行成功,影响风控判断
  103. sleep(5);
  104. }
  105. }
  106. //
  107. else{
  108. \Log::info('trans_fund_toTransUserWithdrawCashes,nothing todo:');
  109. }
  110. }
  111. // 风控
  112. public function risk_manage($param){
  113. $result = ['code'=>1,'msg'=>''];
  114. \Log::info('risk_manage_start:'.json_encode($param));
  115. // 账户不允许空
  116. if(empty($param['alipay_username']) || empty($param['alipay_account'])){
  117. \Log::info('withdraw_account_error:'.json_encode($param));
  118. $result['code'] = 0;
  119. $result['msg'] = '支付宝姓名或者账户是空';
  120. return $result;
  121. }
  122. // TODO 金额不能超过100,待定
  123. $limit_amount = 1;
  124. if($param['amount'] > $limit_amount){
  125. \Log::info('withdraw_amount_error:'.json_encode($param).' account:'.$param['alipay_account']);
  126. $result['code'] = 0;
  127. $result['msg'] = '打款金额超过:'.$limit_amount;
  128. return $result;
  129. }
  130. // 提现账户角度:今天提现过2次,则不能再提,防止爆破改账号风险
  131. $query_data = [
  132. 'alipay_account'=>$param['alipay_account']
  133. ];
  134. $user_withdraw_cashes = WithdrawCashService::getUserWithdrawCashes($query_data);
  135. \Log::info('$user_withdraw_cashes_check_alipay_account_acount:');
  136. \Log::info($user_withdraw_cashes);
  137. $limit_trans_num = 1;
  138. if(count($user_withdraw_cashes) >=$limit_trans_num){
  139. \Log::info('withdraw_account_today_has_over_limit_error:'.$param['alipay_account'].' count:'.count($user_withdraw_cashes));
  140. $result['code'] = 0;
  141. $result['msg'] = '支付宝账户'.$param['alipay_account'].'今日打款超过次数:'.$limit_trans_num;
  142. return $result;
  143. }
  144. // uid角度:今天1个uid提现过1次,则不能再提
  145. $query_data = [
  146. 'uid'=>$param['uid']
  147. ];
  148. $user_withdraw_cashes = WithdrawCashService::getUserWithdrawCashes($query_data);
  149. \Log::info('$user_withdraw_cashes_check_user_count:');
  150. \Log::info($user_withdraw_cashes);
  151. if(count($user_withdraw_cashes) >=1){
  152. \Log::info('withdraw_uid_today_has_over_limit_error:'.$param['uid'].' count:'.count($user_withdraw_cashes));
  153. $result['code'] = 0;
  154. $result['msg'] = '用户id:'.$param['uid'].'今日打款超过次数:'.$limit_trans_num;
  155. return $result;
  156. }
  157. return $result;
  158. }
  159. }