'充值', self::TYPE_WITHDRAW => '提现', ]; const STATUS_WAITING_QUEUE = 1; const STATUS_CODE_PROCESSING = 2; const STATUS_CHAIN_WAITING = 3; const STATUS_SUCCESS = 4; const STATUS_FAIL = 5; const STATUS = [ self::STATUS_WAITING_QUEUE => '等待队列', self::STATUS_CODE_PROCESSING => '程序处理中', self::STATUS_CHAIN_WAITING => '链上等待', self::STATUS_SUCCESS => '成功', self::STATUS_FAIL => '失败', ]; const DESC_KEY_NOT_FOUND_WITHDRAW_AMOUNT_WALLET_ADDR = 'notFoundWithdrawAmountWalletAddr'; const DESC_KEY_WITHDRAW_WALLET_AMOUNT_LOW = 'withdrawWalletAmountLow'; const DESC_KEY_WITHDRAW_CALL_CHAIN_WRONG = 'withdrawCallChainWrong'; const DESC = [ self::DESC_KEY_NOT_FOUND_WITHDRAW_AMOUNT_WALLET_ADDR => '未找到足额提现金额钱包地址', self::DESC_KEY_WITHDRAW_WALLET_AMOUNT_LOW => '提现钱包金额不足', self::DESC_KEY_WITHDRAW_CALL_CHAIN_WRONG => '提现调用链上接口失败', ]; //请求提现发起 function withdrawFirst($amount, $platform_id, $uid, $currency_code, $desc_key = null, $remark = ''): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|bool { try { Db::beginTransaction(); $amount = abs($amount); //检查平台余额 $oWalletPlatformBalanceModel = new WalletPlatformBalanceModel(); $resWalletPlatformBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalance($platform_id, $currency_code); if (!$resWalletPlatformBalanceModel) throw new ModelException('platform_id or currency_code error'); if (Math::bcComp($resWalletPlatformBalanceModel->available_amount, $amount) == -1) throw new ModelException('platform balance not enough'); //冻结平台余额并增加账变 $oWalletPlatformBalanceTransactionModel = new WalletPlatformBalanceTransactionModel(); $resWalletPlatformBalanceTransactionModel = $oWalletPlatformBalanceTransactionModel->newWithdrawPlatformTransaction($amount, $platform_id, $currency_code); if (!$resWalletPlatformBalanceTransactionModel) throw new ModelException('newWithdrawPlatformTransaction error'); //根据币种计算费率 $oWalletCurrencyModel = new WalletCurrencyModel(); $oWalletCurrencyModel = $oWalletCurrencyModel->findByCode($currency_code); if (!$oWalletCurrencyModel) throw new ModelException('currency_code error'); $fee_amount = $oWalletCurrencyModel->computeTransferRate($amount, $oWalletCurrencyModel->transfer_rate); //预计费率计算 if ($fee_amount <= 0) throw new ModelException('fee_amount error'); $desc = isset(self::DESC[$desc_key]) ? self::DESC[$desc_key] : ''; $resModel = $this->addWithdrawTransaction($platform_id, $uid, $oWalletCurrencyModel, $amount, $fee_amount, $desc_key, $desc, $remark); if (!$resModel) throw new ModelException('addItem error'); $oWalletPlatformBalanceTransactionModel->updateItem([ 'id' => $resWalletPlatformBalanceTransactionModel->id, 'from_user_transaction_id' => $resModel->id, ]); $this->putWithdrawToQueue($resModel->id); //投递到队列处理 Db::commit(); Log::info('newWithdrawSucc', ['platform_id' => $platform_id, 'uid' => $uid, 'currency_code' => $currency_code, 'amount' => $amount]); return $resModel; } catch (\Exception $e) { Db::rollBack(); Log::error('newWithdrawErr', ['platform_id' => $platform_id, 'uid' => $uid, 'currency_code' => $currency_code, 'amount' => $amount, 'msg' => $e->getMessage()]); return false; } } function addWithdrawTransaction($platform_id, $uid, $oWalletCurrencyModel, $amount, $fee_amount, $desc_key = null, $desc = '', $remark = ''): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|bool { $insert = [ 'type' => self::TYPE_WITHDRAW, 'status' => self::STATUS_WAITING_QUEUE, 'platform_id' => $platform_id, 'uid' => $uid, 'currency_id' => $oWalletCurrencyModel->id, 'currency_code' => $oWalletCurrencyModel->code, 'currency_type' => $oWalletCurrencyModel->type, 'received_amount' => abs($amount), // 'entered_amount' => abs($entered_amount), 'fee_amount' => abs($fee_amount), 'desc_key' => $desc_key ?? '', 'desc' => $desc, 'remark' => $remark, 'created_at' => date('Y-m-d H:i:s'), ]; return $this->addItem($insert); } function addRechargeTransaction($platform_id, $uid, $oWalletCurrencyModel, $amount, $desc_key = null, $desc = '', $remark = ''): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|bool { $amount = abs($amount); $insert = [ 'type' => self::TYPE_RECHARGE, 'status' => self::STATUS_SUCCESS, //充提成功 'platform_id' => $platform_id, 'uid' => $uid, 'currency_id' => $oWalletCurrencyModel->id, 'currency_code' => $oWalletCurrencyModel->code, 'currency_type' => $oWalletCurrencyModel->type, 'received_amount' => $amount, 'entered_amount' => $amount, 'desc_key' => $desc_key ?? '', 'desc' => $desc, 'remark' => $remark, 'created_at' => date('Y-m-d H:i:s'), ]; return $this->addItem($insert); } function addTransactionWithPlatformBalance($platform_id, $uid, $oWalletCurrencyModel, $amount, $desc_key = null, $desc = '', $remark = '') { try { Db::beginTransaction(); $resModel = $this->addRechargeTransaction($platform_id, $uid, $oWalletCurrencyModel, $amount, $desc_key, $desc, $remark); if(!$resModel) throw new ModelException('addRechargeTransaction error'); $oWalletPlatformBalanceTransactionModel = new WalletPlatformBalanceTransactionModel(); $res = $oWalletPlatformBalanceTransactionModel->newRechargePlatformTransaction($amount, $platform_id, $oWalletCurrencyModel->code); if(!$res) throw new ModelException('newRechargePlatformTransaction error'); Db::commit(); return true; }catch (\Exception $e) { Db::rollBack(); Log::error('addTransactionWithBalance', ['platform_id' => $platform_id, 'uid' => $uid, 'amount' => $amount, 'msg' => $e->getMessage()]); return false; } } //@@投递到rabbitmq队列处理 function putWithdrawToQueue($wallet_platform_user_transaction_id): void { $params = [ 'wallet_platform_user_transaction_id' => $wallet_platform_user_transaction_id, ]; WalletPlatformUserWithdrawQueue::dispatch($params)->onQueue(QueueWalletPlatformUserWithdrawStruct::QUEUE_NAME); } //处理用户提现队列,发起链上提现 function withdrawConsumer($id): void { //@新增任务处理表 //查询前先检查redis中锁 $oWalletPlatformUserTransactionLock = new WalletPlatformUserTransactionLock(); $isLock = $oWalletPlatformUserTransactionLock->checkLock($oWalletPlatformUserTransactionLock->getWithdrawKey($id)); if ($isLock) return; //有锁就跳过 //加锁 $isLock = $oWalletPlatformUserTransactionLock->setLock($oWalletPlatformUserTransactionLock->getWithdrawKey($id)); if (!$isLock) return; //加锁失败 //查询任务 $resWalletPlatformUserTransactionModel = $this->findItem($id); if (!$resWalletPlatformUserTransactionModel) throw new ModelException('id error'); if ($resWalletPlatformUserTransactionModel->type != self::TYPE_WITHDRAW) throw new ModelException('type error'); if ($resWalletPlatformUserTransactionModel->status != self::STATUS_WAITING_QUEUE) throw new ModelException('status error'); $resWalletPlatformUserTransactionModel->status = self::STATUS_CODE_PROCESSING; $resWalletPlatformUserTransactionModel->save(); //获取提现地址 $oWalletAddrModel = new WalletAddrModel(); try { $resWalletAddrModel = $oWalletAddrModel->findWithdrawAddrWithAmount($resWalletPlatformUserTransactionModel->received_amount, $resWalletPlatformUserTransactionModel->currency_code); if (!$resWalletAddrModel) throw new ModelException('not found withdraw amount wallet addr'); } catch (ModelException $e) { //未找到足额提现金额钱包地址 $code = $e->getCode(); $msg = $e->getMessage(); $resWalletPlatformUserTransactionModel->status = self::STATUS_FAIL; if ($code == ModelException::CODE_WALLET_AMOUNT_ADDR_NOT_FOUND) { //未找到足额提现金额钱包地址 $resWalletPlatformUserTransactionModel->desc_key = self::DESC_KEY_NOT_FOUND_WITHDRAW_AMOUNT_WALLET_ADDR; $resWalletPlatformUserTransactionModel->desc = self::DESC[self::DESC_KEY_NOT_FOUND_WITHDRAW_AMOUNT_WALLET_ADDR]; } if ($code == ModelException::CODE_WALLET_ADDR_BALANCE_LOW) { //提现钱包金额不足 $resWalletPlatformUserTransactionModel->desc_key = self::DESC_KEY_WITHDRAW_WALLET_AMOUNT_LOW; $resWalletPlatformUserTransactionModel->desc = self::DESC[self::DESC_KEY_WITHDRAW_WALLET_AMOUNT_LOW]; } $resWalletPlatformUserTransactionModel->save(); $this->withdrawFailUnLock($id); //解锁 return; } try { $resWalletPlatformUserTransactionModel->from_wallet_addr_id = $resWalletAddrModel->id; $resWalletPlatformUserTransactionModel->wallet_addr = $resWalletAddrModel->addr; $resWalletPlatformUserTransactionModel->save(); //增加钱包账变 $oWalletAddrTransactionModel = new WalletAddrTransactionModel(); $resWalletAddrTransactionModel = $oWalletAddrTransactionModel->newPlatformUserWithdraw( $resWalletAddrModel->id, $resWalletPlatformUserTransactionModel->entered_amount, $resWalletPlatformUserTransactionModel->id ); if (!$resWalletAddrTransactionModel) { $this->withdrawFailUnLock($id); //解锁 return; } //调用链上接口 $resChain = $oWalletAddrModel->callWalletAddrChainTransferApi($resWalletPlatformUserTransactionModel->id, $resWalletAddrModel->id); if (!$resChain) { //链上提现失败 $resWalletPlatformUserTransactionModel->status = self::STATUS_FAIL; $resWalletPlatformUserTransactionModel->desc_key = self::DESC_KEY_WITHDRAW_CALL_CHAIN_WRONG; $resWalletPlatformUserTransactionModel->desc = self::DESC[self::DESC_KEY_WITHDRAW_CALL_CHAIN_WRONG]; $resWalletPlatformUserTransactionModel->save(); //解冻钱包余额 $oWalletAddrTransactionModel->platformUserWithdrawCallback($resWalletAddrTransactionModel->id, $resWalletAddrTransactionModel->received_amount, WalletAddrTransactionModel::STATUS_FAIL); $this->withdrawFailUnLock($id); //解锁 return; } $resWalletPlatformUserTransactionModel->status = self::STATUS_CHAIN_WAITING; $resWalletPlatformUserTransactionModel->save(); $this->withdrawFailUnLock($id); //解锁 } catch (\Exception $e) { Log::error('withdrawConsumer', ['id' => $id, 'msg' => $e->getMessage()]); $this->withdrawFailUnLock($id); //解锁 } } //用户提现失败解锁并返还平台余额 function withdrawFailUnLock($id): void { //返还用户余额 $oWalletPlatformBalanceTransactionModel = new WalletPlatformBalanceTransactionModel(); $oWalletPlatformBalanceTransactionModel->withdrawErrByUserTransactionId($id); //解锁 $oWalletPlatformUserTransactionLock = new WalletPlatformUserTransactionLock(); $oWalletPlatformUserTransactionLock->unLock($oWalletPlatformUserTransactionLock->getWithdrawKey($id)); } //提现链上回调监听 function listenWithdrawCallback($wallet_addr_transaction_id): void { Log::info('listenWithdrawCallback', ['wallet_addr_transaction_id' => $wallet_addr_transaction_id]); //查找钱包账变 $oWalletAddrTransactionModel = new WalletAddrTransactionModel(); $resWalletAddrTransactionModel = $oWalletAddrTransactionModel->findItem($wallet_addr_transaction_id); if (!$resWalletAddrTransactionModel) throw new ModelException('wallet_addr_transaction_id error'); if ($resWalletAddrTransactionModel->type !== WalletAddrTransactionModel::TYPE_TRANSFER) throw new ModelException('wallet_addr_transaction_type error'); if (!in_array($resWalletAddrTransactionModel->status, [WalletAddrTransactionModel::STATUS_SUCCESS, WalletAddrTransactionModel::STATUS_FAIL])) throw new ModelException('status error'); if ($resWalletAddrTransactionModel->status == WalletAddrTransactionModel::STATUS_SUCCESS) $status = self::STATUS_SUCCESS; if ($resWalletAddrTransactionModel->status == WalletAddrTransactionModel::STATUS_FAIL) $status = self::STATUS_FAIL; if (empty($resWalletAddrTransactionModel->platform_user_transaction_id)) throw new ModelException('platform_user_transaction_id error'); $id = $resWalletAddrTransactionModel->platform_user_transaction_id; if (!in_array($status, [self::STATUS_SUCCESS, self::STATUS_FAIL])) throw new ModelException('status error'); $resWalletPlatformUserTransactionModel = $this->findItem($id); if (!$resWalletPlatformUserTransactionModel) throw new ModelException('id error'); if ($resWalletPlatformUserTransactionModel->type != self::TYPE_WITHDRAW) throw new ModelException('type error'); if ($resWalletPlatformUserTransactionModel->status != self::STATUS_CHAIN_WAITING) throw new ModelException('status error'); //用户账变处理 $resWalletPlatformUserTransactionModel->status = $status; $resWalletPlatformUserTransactionModel->save(); //平台余额和账变处理 $oWalletPlatformBalanceTransactionModel = new WalletPlatformBalanceTransactionModel(); if ($status == self::STATUS_SUCCESS) { //成功 $oWalletPlatformBalanceTransactionModel->withdrawSuccByUserTransactionId($id); } else { //失败 $oWalletPlatformBalanceTransactionModel->withdrawErrByUserTransactionId($id); } } function listenRechargeCallback($wallet_addr_transaction_id): void { Log::info('listenRechargeCallback', ['wallet_addr_transaction_id' => $wallet_addr_transaction_id]); //查找钱包账变 $oWalletAddrTransactionModel = new WalletAddrTransactionModel(); $resWalletAddrTransactionModel = $oWalletAddrTransactionModel->findItem($wallet_addr_transaction_id); if (!$resWalletAddrTransactionModel) throw new ModelException('wallet_addr_transaction_id error'); if ($resWalletAddrTransactionModel->type !== WalletAddrTransactionModel::TYPE_RECHARGE) throw new ModelException('wallet_addr_transaction_type error'); if (!in_array($resWalletAddrTransactionModel->status, [WalletAddrTransactionModel::STATUS_SUCCESS, WalletAddrTransactionModel::STATUS_FAIL])) throw new ModelException('status error'); if ($resWalletAddrTransactionModel->status == WalletAddrTransactionModel::STATUS_SUCCESS) $status = self::STATUS_SUCCESS; if ($resWalletAddrTransactionModel->status == WalletAddrTransactionModel::STATUS_FAIL) $status = self::STATUS_FAIL; if ($status != self::STATUS_SUCCESS) throw new ModelException('status not success'); $oWalletCurrencyModel = new WalletCurrencyModel(); $resWalletCurrencyModel = $oWalletCurrencyModel->findItem($resWalletAddrTransactionModel->currency_id); //查找用户绑定钱包地址 $oWalletPlatformUserWalletAddrModel = new WalletPlatformUserWalletAddrModel(); $resWalletPlatformUserWalletAddrModel = $oWalletPlatformUserWalletAddrModel->findByWalletAddrId($resWalletAddrTransactionModel->wallet_addr_id); if (!$resWalletPlatformUserWalletAddrModel) throw new ModelException('找不到wallet_addr_id绑定用户'); //给用户增加账变/平台增加账变增加余额 $this->addTransactionWithPlatformBalance( $resWalletPlatformUserWalletAddrModel->platform_id, $resWalletPlatformUserWalletAddrModel->uid, $resWalletCurrencyModel, $resWalletAddrTransactionModel->received_amount, ); } }