|
|
@@ -1,452 +0,0 @@
|
|
|
-<?php
|
|
|
-
|
|
|
-namespace app\common\logic;
|
|
|
-
|
|
|
-use app\common\library\Log;
|
|
|
-use app\common\model\LedgerWalletModel;
|
|
|
-use app\common\model\OfflineRechargeRecordModel;
|
|
|
-use app\common\model\OfflineRechargeVerifyModel;
|
|
|
-use app\common\model\ParametersModel;
|
|
|
-use app\common\model\UserModel;
|
|
|
-use app\common\model\ServersModel;
|
|
|
-use Exception;
|
|
|
-use fast\Action;
|
|
|
-use fast\Asset;
|
|
|
-use fast\Http;
|
|
|
-use fast\RechargeOrderType;
|
|
|
-use fast\RechargeStatus;
|
|
|
-use fast\RechargeType;
|
|
|
-use think\Db;
|
|
|
-use think\Env;
|
|
|
-use think\Error;
|
|
|
-use think\Model;
|
|
|
-
|
|
|
-class ScanLogic
|
|
|
-{
|
|
|
-
|
|
|
- /**
|
|
|
- * 币安API地址
|
|
|
- * @var string
|
|
|
- */
|
|
|
- private string $bscApiUrl = '';
|
|
|
-
|
|
|
- /**
|
|
|
- * 币安链USDT地址
|
|
|
- * @var string
|
|
|
- */
|
|
|
- private string $bscUSDAddress = '0x55d398326f99059ff775485246999027b3197955';
|
|
|
-
|
|
|
- /**
|
|
|
- * 平台收款钱包地址
|
|
|
- * @var string
|
|
|
- */
|
|
|
- private string $collectionAddress = '';
|
|
|
-
|
|
|
- /**
|
|
|
- * 最少确认节点数量
|
|
|
- * @var int
|
|
|
- */
|
|
|
- private int $minConfirmations = 12;
|
|
|
-
|
|
|
- /**
|
|
|
- * value的放大比例 固定为: 10的18次方
|
|
|
- * @var int
|
|
|
- */
|
|
|
- private string $valueScale = '1000000000000000000';
|
|
|
-
|
|
|
- public function __construct()
|
|
|
- {
|
|
|
- $apikey = (new ParametersModel)->getValue('bscApiKey');
|
|
|
- if (empty($apikey)) {
|
|
|
- dump("读取币安apikey错误");
|
|
|
- exit(1);
|
|
|
- }
|
|
|
-
|
|
|
- $collectionAddress = Env::get('rental.pay_address');
|
|
|
- if (empty($collectionAddress)) {
|
|
|
- dump("读取平台收款钱包地址错误");
|
|
|
- exit(1);
|
|
|
- }
|
|
|
-
|
|
|
- $this->collectionAddress = strtolower($collectionAddress);
|
|
|
- $this->bscApiUrl = "https://api.bscscan.com/api?module=account&action=tokentx&contractaddress=$this->bscUSDAddress&page=1&offset=10000&endblock=99999999&apikey=$apikey&address=";
|
|
|
- $this->hashStatusUrl = "https://api.bscscan.com/api?module=transaction&action=gettxreceiptstatus&apikey=$apikey&txhash=";
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- public function scanRechargeOrders()
|
|
|
- {
|
|
|
- $recharges = (new OfflineRechargeRecordModel())->where([
|
|
|
- 'type' => RechargeType::CashFromChain,
|
|
|
- 'status' => RechargeStatus::StatusUnAuth,
|
|
|
- ])->order('id DESC')->limit(20)->select();
|
|
|
- if (!is_null($recharges)) {
|
|
|
- foreach ($recharges as $v) {
|
|
|
- $vArr = $v->toArray();
|
|
|
- // 请求api
|
|
|
- $result = $this->verifyTxHash($vArr);
|
|
|
-
|
|
|
- // 记录执行记录
|
|
|
- (new OfflineRechargeVerifyModel())->insert([
|
|
|
- 'order_id' => $vArr['id'],
|
|
|
- 'user_id' => $vArr['user_id'],
|
|
|
- 'result' => $result == '',
|
|
|
- 'fail_reason' => $result,
|
|
|
- 'create_time' => time(),
|
|
|
- ]);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * 扫描收款地址
|
|
|
- */
|
|
|
- public function scanCollectionAddress()
|
|
|
- {
|
|
|
- $max_block_number = (new OfflineRechargeRecordModel())
|
|
|
- ->max('block_number');
|
|
|
- $max_block_number ++;
|
|
|
-
|
|
|
- $url = $this->bscApiUrl . $this->collectionAddress . "&startblock=" . $max_block_number;
|
|
|
- //$user['address'] = strtolower($user['address']); // 转小写
|
|
|
- // 发起请求
|
|
|
- $body = Http::get($url);
|
|
|
- if (empty($body)) {
|
|
|
- return "api返回内容为空";
|
|
|
- }
|
|
|
-
|
|
|
- // 转成数组
|
|
|
- $rsArr = json_decode($body, true);
|
|
|
- if (empty($rsArr) || !is_array($rsArr)) {
|
|
|
- return "api返回数据异常";
|
|
|
- }
|
|
|
-
|
|
|
- if ($rsArr['status'] != '1') {
|
|
|
- return 'api返回status不为1,错误信息:' . $rsArr['message'];
|
|
|
- }
|
|
|
-
|
|
|
- $errMsg = '';
|
|
|
- $new_block_number = 0;
|
|
|
- $all_user_address = [];//钱包地址为键值的所有会员的数组
|
|
|
- $orderInfo = [];
|
|
|
- // 查询匹配交易列表
|
|
|
- (new Log())->info('开始匹配交易');
|
|
|
- (new Log())->info($rsArr['result']);
|
|
|
- foreach ($rsArr['result'] as $v) {
|
|
|
- $errMsg = '';
|
|
|
- if ($v['to'] != $this->collectionAddress) {
|
|
|
- $new_block_number = $v['blockNumber'];
|
|
|
- (new Log())->info('收款地址不是平台地址:' . $v['hash']);
|
|
|
- continue;//转载地址不是收款地址的数据直接跳过不处理
|
|
|
- }
|
|
|
-
|
|
|
- if ($v['contractAddress'] != $this->bscUSDAddress) {
|
|
|
-// $errMsg = '币种错误: ' . $v['contractAddress'];
|
|
|
- $new_block_number = $v['blockNumber'];
|
|
|
- continue;//交易币种的合约地址不是指定代币,不处理。一般币种为USDT
|
|
|
- }
|
|
|
-
|
|
|
- $orderInfo['amount'] = bcdiv($v['value'], $this->valueScale, 6);//交易金额
|
|
|
- if(!($orderInfo['amount'] > 0)){
|
|
|
- $new_block_number = $v['blockNumber'];
|
|
|
- continue;//交易金额必须大于0
|
|
|
- }else{
|
|
|
- $minUsdt = (new ParametersModel)->getValue('minUsdt') ?? '0';//最小购买金额
|
|
|
- if($minUsdt > $orderInfo['amount']){
|
|
|
- $new_block_number = $v['blockNumber'];
|
|
|
- $errMsg = '交易金额必须大于系统最小购买金额:' . $v['hash'];
|
|
|
- (new Log())->info('交易金额必须大于系统最小购买金额:' . $v['hash']);
|
|
|
- continue;//交易金额必须大于系统最小购买金额
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- $check_user = (new UserModel())->getByAddress(strtolower($v['from']));
|
|
|
- if(empty($check_user)){
|
|
|
- $new_block_number = $v['blockNumber'];
|
|
|
- (new Log())->info('转账地址不是平台用户账号:' . $v['hash']);
|
|
|
- continue;//转账地址不是平台账号的数据不处理
|
|
|
- }
|
|
|
-
|
|
|
- $orderInfo['user_id'] = $check_user['id'];
|
|
|
-
|
|
|
-// if(empty($all_user_address)){
|
|
|
-// $all_user_address = (new UserModel())->getAllAddress();
|
|
|
-// }
|
|
|
-// if(!(isset($all_user_address[$v['from']]) || isset($all_user_address[strtolower($v['from'])]))){
|
|
|
-// $new_block_number = $v['blockNumber'];
|
|
|
-// (new Log())->info('转账地址不是平台用户账号:' . $v['hash']);
|
|
|
-// continue;//转账地址不是平台账号的数据不处理
|
|
|
-// }else{
|
|
|
-// if(isset($all_user_address[$v['from']])){
|
|
|
-// $orderInfo['user_id'] = $all_user_address[$v['from']];
|
|
|
-// }else{
|
|
|
-// $orderInfo['user_id'] = $all_user_address[strtolower($v['from'])];
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
- $rsData = $this->getHashStatus($v['hash']);
|
|
|
- if($rsData['status'] == 0){//当前哈希交易状态为失败,跳出循环,等待下次判断,若真是失败交易,过几分钟后,这个数据不会再出现,这个接口拿到的都是有效的交易数据
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- //判断当前hash是否存在于充值记录
|
|
|
- $check_recharge_record = (new OfflineRechargeRecordModel())
|
|
|
- ->where('tx_hash', $v['hash'])
|
|
|
- ->find();
|
|
|
- if(empty($check_recharge_record)){//充值记录表中没有当前记录,则补充插入
|
|
|
- (new Log())->info('补单:' . $v['hash']);
|
|
|
- $orderInfo['order_type'] = RechargeOrderType::RentalPower;//订单类型
|
|
|
- //判断是否服务器购买订单
|
|
|
- $servers_list = (new ServersModel())
|
|
|
- ->field('id,title,price,power')
|
|
|
- ->select();
|
|
|
- foreach ($servers_list as $item){
|
|
|
- if($item['price'] == $orderInfo['amount']){
|
|
|
- $orderInfo['order_type'] = RechargeOrderType::RentalServer;//订单类型
|
|
|
- }
|
|
|
- }
|
|
|
- try {
|
|
|
- $orderInfo['id'] = (new OfflineRechargeRecordModel())->createRecord(RechargeType::CashFromChain, $v['hash'], $orderInfo['user_id'], $orderInfo['amount'], RechargeStatus::StatusUnAuth, $orderInfo['order_type'], $v['blockNumber'], '自动补单');
|
|
|
- } catch (Exception $e) {
|
|
|
- (new Log())->error('自动补单失败:' . $e);
|
|
|
- break;
|
|
|
- }
|
|
|
- }else if($check_recharge_record['status'] == 1){
|
|
|
- $new_block_number = $v['blockNumber'];
|
|
|
- continue;//当前交易已处理
|
|
|
- }else{
|
|
|
- $orderInfo = $check_recharge_record->toArray();
|
|
|
- if (bccomp($v['value'], bcmul($check_recharge_record['amount'], $this->valueScale, 6)) !== 0) {
|
|
|
- $errMsg = '金额错误: ' . $v['value'] . ',' . $check_recharge_record['amount'];
|
|
|
- (new Log())->info('金额错误: ' . $v['value'] . ',' . $check_recharge_record['amount']);
|
|
|
- }
|
|
|
- }
|
|
|
- $orderInfo['block_number'] = $v['blockNumber'];
|
|
|
-
|
|
|
- // 校验正确,更新订单信息为成功,并触发各项收益的发放
|
|
|
- $updateErrMsg = $this->updateOrderStatus($orderInfo, $errMsg == '');
|
|
|
- if (!empty($updateErrMsg)) {
|
|
|
- $errMsg .= '|' . $updateErrMsg;
|
|
|
- }
|
|
|
-
|
|
|
- // 记录执行记录
|
|
|
- (new OfflineRechargeVerifyModel())->insert([
|
|
|
- 'order_id' => $orderInfo['id'],
|
|
|
- 'user_id' => $orderInfo['user_id'],
|
|
|
- 'result' => $errMsg == '',
|
|
|
- 'fail_reason' => $errMsg,
|
|
|
- 'create_time' => time(),
|
|
|
- ]);
|
|
|
- }
|
|
|
-
|
|
|
- if($new_block_number > $max_block_number){
|
|
|
- //更新最新一条充值记录的区块为最新扫描过的区块
|
|
|
- $max_info = (new OfflineRechargeRecordModel())
|
|
|
- ->order('id desc')
|
|
|
- ->find();
|
|
|
- $max_info->save([
|
|
|
- 'block_number' => $new_block_number
|
|
|
- ]);
|
|
|
- }
|
|
|
- return $errMsg;
|
|
|
- }
|
|
|
-
|
|
|
- private function verifyTxHash(array $orderInfo): string
|
|
|
- {
|
|
|
-
|
|
|
- $user = (new UserModel())->getById($orderInfo['user_id']);
|
|
|
- if (empty($user)) {
|
|
|
- return "用户不存在,ID:" . $orderInfo['user_id'];
|
|
|
- }
|
|
|
-
|
|
|
- $rsData = $this->getHashStatus($orderInfo['tx_hash']);
|
|
|
- if($rsData['status'] == 0){
|
|
|
- return $rsData['msg'];
|
|
|
- }
|
|
|
-
|
|
|
- $user['address'] = strtolower($user['address']); // 转小写
|
|
|
- $url = $this->bscApiUrl . $user['address'];
|
|
|
- // 发起请求
|
|
|
- $body = Http::get($url);
|
|
|
- if (empty($body)) {
|
|
|
- return "api返回内容为空";
|
|
|
- }
|
|
|
-
|
|
|
- // 转成数组
|
|
|
- $rsArr = json_decode($body, true);
|
|
|
- if (empty($rsArr) || !is_array($rsArr)) {
|
|
|
- return "api返回数据异常";
|
|
|
- }
|
|
|
-
|
|
|
- if ($rsArr['status'] != '1') {
|
|
|
- return 'api返回status不为1,错误信息:' . $rsArr['message'];
|
|
|
- }
|
|
|
-
|
|
|
- $errMsg = '';
|
|
|
- // 查询匹配交易列表
|
|
|
- foreach ($rsArr['result'] as $v) {
|
|
|
- // 跳过hash不匹配或确认区块不足的记录
|
|
|
- if ($v['hash'] != $orderInfo['tx_hash'] || $v['confirmations'] < $this->minConfirmations) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // 开始校验
|
|
|
- if ($v['from'] != $user['address']) {
|
|
|
- $errMsg = 'from地址不是用户地址,from: ' . $v['from'];
|
|
|
- } else if ($v['to'] != $this->collectionAddress) {
|
|
|
- $errMsg = 'to地址不是平台收款地址,to: ' . $v['to'];
|
|
|
- } else if ($v['contractAddress'] != $this->bscUSDAddress) {
|
|
|
- $errMsg = '币种错误: ' . $v['contractAddress'];
|
|
|
- } else if (bccomp($v['value'], bcmul($orderInfo['amount'], $this->valueScale, 6)) !== 0) {
|
|
|
- $errMsg = '金额错误: ' . $v['value'] . ',' . $orderInfo['amount'];
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- // 校验正确,更新订单信息为成功,并触发各项收益的发放
|
|
|
- $updateErrMsg = $this->updateOrderStatus($orderInfo, $errMsg == '');
|
|
|
- if (!empty($updateErrMsg)) {
|
|
|
- $errMsg .= '|' . $updateErrMsg;
|
|
|
- }
|
|
|
- return $errMsg;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private function updateOrderStatus(array $orderInfo, bool $verify): string
|
|
|
- {
|
|
|
- if (!$verify) { // 验证失败的处理
|
|
|
- try {
|
|
|
- (new Log())->error('充值交易验证失败: ' . $verify);
|
|
|
-
|
|
|
- (new OfflineRechargeRecordModel())->updateOrderStatus($orderInfo['id'], RechargeStatus::StatusAuthFail, $orderInfo['block_number']);
|
|
|
- } catch (Exception $e) {
|
|
|
- return $e->getMessage();
|
|
|
- }
|
|
|
- return '';
|
|
|
- }
|
|
|
- if($orderInfo['order_type'] == 1){
|
|
|
- //算力租赁订单处理
|
|
|
- // 查询兑换比例
|
|
|
- $usdtToPower = (new ParametersModel)->getValue('usdtToPowerRate');
|
|
|
- $usdtToPowerFloat = floatval($usdtToPower);
|
|
|
- if (is_null($usdtToPower) || $usdtToPowerFloat <= 0) {
|
|
|
- return '获取USDT兑换算力的比例失败';
|
|
|
- }
|
|
|
-
|
|
|
- $uid = $orderInfo['user_id'];
|
|
|
- $fee = $orderInfo['amount'];
|
|
|
- $power = bcmul($fee, $usdtToPowerFloat, 6); // 该用户兑得的算力
|
|
|
-
|
|
|
- // 启动事务
|
|
|
- Db::startTrans();
|
|
|
- try {
|
|
|
- // 更新总算力和账变
|
|
|
- (new LedgerWalletModel)->changeWalletAccount($uid, Asset::POWER, $power, Action::PowerRentalPower, $orderInfo['id']);
|
|
|
-
|
|
|
- // 更新服务器算力,不账变
|
|
|
- (new LedgerWalletModel)->changeWalletOnly($uid, Asset::RENTAL_POWER, $power);
|
|
|
-
|
|
|
- // 更新自己(有效会员时间)和所有上级的信息(有效直推人数和团队总算力)
|
|
|
- (new UserModel())->updateForRental($uid, $power);
|
|
|
-
|
|
|
- // 发放直推USDT收益
|
|
|
- (new LedgerWalletModel)->sendUsdtProfit($uid, $fee);
|
|
|
-
|
|
|
- // 发放直推算力收益
|
|
|
- (new LedgerWalletModel)->sendDirectProfit($uid, $power);
|
|
|
-
|
|
|
- // 发代数收益
|
|
|
- (new LedgerWalletModel)->sendGenerateProfit($uid, $fee);
|
|
|
-
|
|
|
- // 发放见点奖
|
|
|
- (new LedgerWalletModel)->sendRegBonus($uid, $fee);
|
|
|
-
|
|
|
- // 更新购买(充值)记录
|
|
|
- (new OfflineRechargeRecordModel())->updateOrderStatus($orderInfo['id'], RechargeStatus::StatusAuthSuccess, 0, $power);
|
|
|
-
|
|
|
- // 提交事务
|
|
|
- Db::commit();
|
|
|
- } catch (Exception $e) {
|
|
|
- // 回滚事务
|
|
|
- Db::rollback();
|
|
|
- return $e->getMessage();
|
|
|
- }
|
|
|
- }else{
|
|
|
- //服务器购买订单处理
|
|
|
- $servers_info = (new ServersModel())
|
|
|
- ->where('price', $orderInfo['amount'])
|
|
|
- ->find();
|
|
|
- if(empty($servers_info)){
|
|
|
- return '支付价格和服务器价格不匹配';
|
|
|
- }
|
|
|
-
|
|
|
- // 启动事务
|
|
|
- Db::startTrans();
|
|
|
- try {
|
|
|
- // 更新服务器算力,不账变
|
|
|
- (new LedgerWalletModel)->changeWalletOnly($orderInfo['user_id'], Asset::SERVER_POWER, $servers_info['power']);
|
|
|
-
|
|
|
- // 发放服务器市场推荐相关收益
|
|
|
- //(new LedgerWalletModel)->sendMarketBonus($orderInfo['user_id'], $servers_info);
|
|
|
-
|
|
|
- // 更新购买(充值)记录
|
|
|
- (new OfflineRechargeRecordModel())->updateOrderStatus($orderInfo['id'], RechargeStatus::StatusAuthSuccess, 0, $servers_info['power']);
|
|
|
-
|
|
|
- // 提交事务
|
|
|
- Db::commit();
|
|
|
- } catch (Exception $e) {
|
|
|
- // 回滚事务
|
|
|
- Db::rollback();
|
|
|
- return $e->getMessage();
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- return '';
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取哈希地址成功状态
|
|
|
- *
|
|
|
- * api接口返回数据格式
|
|
|
- * {
|
|
|
- "status": "1",
|
|
|
- "message": "OK",
|
|
|
- "result": {
|
|
|
- "status": "1"
|
|
|
- }
|
|
|
- }
|
|
|
- * @param $orderInfo
|
|
|
- * @return array|string
|
|
|
- */
|
|
|
- private function getHashStatus($tx_hash):array
|
|
|
- {
|
|
|
- $rsData['status'] = 0;//0 失败,1 成功
|
|
|
- $rsData['msg'] = '';
|
|
|
- $hashStatusUrl = $this->hashStatusUrl . $tx_hash;//接口地址
|
|
|
- $body = Http::get($hashStatusUrl);
|
|
|
- if (empty($body)) {
|
|
|
- $rsData['msg'] = '状态api返回内容为空';
|
|
|
- return $rsData;
|
|
|
- }
|
|
|
- // 转成数组
|
|
|
- $rsArr = json_decode($body, true);
|
|
|
- if (empty($rsArr) || !is_array($rsArr)) {
|
|
|
- $rsData['msg'] = '状态api返回数据异常';
|
|
|
- return $rsData;
|
|
|
- }
|
|
|
-
|
|
|
- if ($rsArr['status'] != '1') {
|
|
|
- $rsData['msg'] = '状态api返回status不为1,当前值为:' . $rsArr['status'];
|
|
|
- return $rsData;
|
|
|
- }
|
|
|
-
|
|
|
- if ($rsArr['result']['status'] != 1) {
|
|
|
- $rsData['msg'] = '状态api返回result中的status不为1,当前值为:' . $rsArr['result']['status'];
|
|
|
- return $rsData;
|
|
|
- }
|
|
|
-
|
|
|
- $rsData['status'] = 1;
|
|
|
- return $rsData;
|
|
|
- }
|
|
|
-}
|