'充值', self::TYPE_WITHDRAW_DEC => '提现', self::TYPE_TRANSACTION_FEE_SUB => '交易手续费扣款', self::TYPE_TRANSACTION_FEE_ADD => '交易手续费加款', self::TYPE_ADMIN_ADD => '管理员加款', self::TYPE_ADMIN_SUB => '管理员扣款', ]; const STATUS_WAIT = 1; const STATUS_PROCESSING = 2; const STATUS_SUCCESS = 3; const STATUS_FAIL = 4; const STATUS = [ self::STATUS_WAIT => '等待处理', self::STATUS_PROCESSING => '处理中', self::STATUS_SUCCESS => '成功', self::STATUS_FAIL => '失败', ]; const IS_NOTIFY_WAIT = 1; const IS_NOTIFY_PROCESS = 2; const IS_NOTIFY_FINISH = 3; const IS_NOTIFY_FAIL = 4; const IS_NOTIFY = [ self::IS_NOTIFY_WAIT => '待处理', self::IS_NOTIFY_PROCESS => '处理中', self::IS_NOTIFY_FINISH => '处理完成', self::IS_NOTIFY_FAIL => '处理失败', ]; //管理员扣款 function typeAdminSub($platform_id, $uid, $currency_code, $amount, $remark = null): bool { $bean = new WalletPlatformBalanceTransactionBean(); $bean->setPlatformId($platform_id); $bean->setUid($uid); $bean->setCurrencyCode($currency_code); $bean->setAmount(-abs($amount)); //负数 $bean->setType(self::TYPE_ADMIN_SUB); $bean->setStatus(self::STATUS_SUCCESS); $bean->setRemark($remark); try { DB::beginTransaction(); //查询平台余额 $oWalletPlatformBalanceModel = new WalletPlatformBalanceModel(); $resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code); if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error'); //加悲观锁 $resBalanceModel = $oWalletPlatformBalanceModel->findByWhereWithLock($resBalanceModel->id); $bean->setBalanceId($resBalanceModel->id); $bean->setBeforeTotalAmount($resBalanceModel->total_amount); $bean->setAfterTotalAmount(Math::bcSub($resBalanceModel->total_amount, abs($amount))); //新增账变 $res = $this->addTransaction($bean); if (!$res) throw new ModelException('addTransaction fail'); //变更余额 $res = $oWalletPlatformBalanceModel->subAvailableAmount($bean->getBalanceId(), abs($amount)); if (!$res) throw new ModelException('subAvailableAmount fail'); DB::commit(); Logs::SuccLog(__FUNCTION__, func_get_args()); return true; } catch (\Exception $e) { DB::rollBack(); Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args()); return false; } } //管理员加款 function typeAdminAdd($platform_id, $uid, $currency_code, $amount, $remark = null): bool { $bean = new WalletPlatformBalanceTransactionBean(); $bean->setPlatformId($platform_id); $bean->setUid($uid); $bean->setCurrencyCode($currency_code); $bean->setAmount(abs($amount)); //正数 $bean->setType(self::TYPE_ADMIN_ADD); $bean->setStatus(self::STATUS_SUCCESS); $bean->setRemark($remark); try { DB::beginTransaction(); //查询平台余额 $oWalletPlatformBalanceModel = new WalletPlatformBalanceModel(); $resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code); if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error'); //加悲观锁 $resBalanceModel = $oWalletPlatformBalanceModel->findByWhereWithLock($resBalanceModel->id); $bean->setBalanceId($resBalanceModel->id); $bean->setBeforeTotalAmount($resBalanceModel->total_amount); $bean->setAfterTotalAmount(Math::bcAdd($resBalanceModel->total_amount, $bean->getAmount())); //新增账变 $res = $this->addTransaction($bean); if (!$res) throw new ModelException('addTransaction fail'); //变更余额 $res = $oWalletPlatformBalanceModel->addAvailableAmount($bean->getBalanceId(), $bean->getAmount()); if (!$res) throw new ModelException('addAvailableAmount fail'); DB::commit(); Logs::SuccLog(__FUNCTION__, func_get_args()); return true; } catch (\Exception $e) { DB::rollBack(); Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args()); return false; } } //手续费扣除 function typeTransactionFeeSub($platform_id, $uid, $currency_code, $amount, $remark = null): bool { $bean = new WalletPlatformBalanceTransactionBean(); $bean->setPlatformId($platform_id); $bean->setUid($uid); $bean->setCurrencyCode($currency_code); $bean->setAmount(-abs($amount)); //负数 $bean->setType(self::TYPE_TRANSACTION_FEE_SUB); $bean->setStatus(self::STATUS_SUCCESS); $bean->setRemark($remark); try { DB::beginTransaction(); //查询平台余额 $oWalletPlatformBalanceModel = new WalletPlatformBalanceModel(); $resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code); if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error'); //加悲观锁 $resBalanceModel = $oWalletPlatformBalanceModel->findByWhereWithLock($resBalanceModel->id); $bean->setBalanceId($resBalanceModel->id); $bean->setBeforeTotalAmount($resBalanceModel->total_amount); $bean->setAfterTotalAmount(Math::bcSub($resBalanceModel->total_amount, abs($amount))); //新增账变 $res = $this->addTransaction($bean); if (!$res) throw new ModelException('addTransaction fail'); //变更余额 $res = $oWalletPlatformBalanceModel->subAvailableAmount($bean->getBalanceId(), abs($amount)); if (!$res) throw new ModelException('subAvailableAmount fail'); DB::commit(); Logs::SuccLog(__FUNCTION__, func_get_args()); return true; } catch (\Exception $e) { DB::rollBack(); Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args()); return false; } } //手续费增加 function typeTransactionFeeAdd($platform_id, $uid, $currency_code, $amount, $remark = null): bool { $bean = new WalletPlatformBalanceTransactionBean(); $bean->setPlatformId($platform_id); $bean->setUid($uid); $bean->setCurrencyCode($currency_code); $bean->setAmount(abs($amount)); //正数 $bean->setType(self::TYPE_TRANSACTION_FEE_ADD); $bean->setStatus(self::STATUS_SUCCESS); $bean->setRemark($remark); try { DB::beginTransaction(); //查询平台余额 $oWalletPlatformBalanceModel = new WalletPlatformBalanceModel(); $resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code); if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error'); //加悲观锁 $resBalanceModel = $oWalletPlatformBalanceModel->findByWhereWithLock($resBalanceModel->id); $bean->setBalanceId($resBalanceModel->id); $bean->setBeforeTotalAmount($resBalanceModel->total_amount); $bean->setAfterTotalAmount(Math::bcAdd($resBalanceModel->total_amount, $bean->getAmount())); //新增账变 $res = $this->addTransaction($bean); if (!$res) throw new ModelException('addTransaction fail'); //变更余额 $res = $oWalletPlatformBalanceModel->addAvailableAmount($bean->getBalanceId(), $bean->getAmount()); if (!$res) throw new ModelException('addAvailableAmount fail'); DB::commit(); Logs::SuccLog(__FUNCTION__, func_get_args()); return true; } catch (\Exception $e) { DB::rollBack(); Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args()); return false; } } //充值 function typeRecharge($platform_id, $uid, $currency_code, $amount, $to_wallet_address_id = null, $tid = null, $callback_wallet_address_transaction_id = null): bool { $bean = new WalletPlatformBalanceTransactionBean(); $bean->setBlockTransactionId($tid); $bean->setPlatformId($platform_id); $bean->setUid($uid); $bean->setCurrencyCode($currency_code); $bean->setAmount(abs($amount)); //正数 $bean->setType(self::TYPE_RECHARGE_ADD); $bean->setStatus(self::STATUS_SUCCESS); $bean->setCallbackWalletAddressTransactionId($callback_wallet_address_transaction_id); $bean->setToWalletAddressId($to_wallet_address_id); try { DB::beginTransaction(); //检测钱包地址和当前绑定地址是否一致 $oWalletPlatformBindModel = new WalletPlatformBindModel(); $resBindModel = $oWalletPlatformBindModel->findBind($platform_id, $uid, $currency_code); if (!$resBindModel) throw new ModelException('WalletPlatformBindModel findBind error'); //查找钱包地址 $oWalletAddressModel = new WalletAddressModel(); $resWalletAddressModel = $oWalletAddressModel->findItem($resBindModel->wallet_address_id); if (!$resWalletAddressModel) throw new ModelException('WalletAddressModel findItem error'); //检测异步到账地址是否和绑定地址一致 if (!empty($wallet_address_id)) { if ($resWalletAddressModel->id != $to_wallet_address_id) throw new ModelException('wallet_address_id not same'); } $bean->setBindWalletAddressId($resWalletAddressModel->id); $bean->setBindWalletAddress($resWalletAddressModel->address_base58); //查询平台余额 $oWalletPlatformBalanceModel = new WalletPlatformBalanceModel(); $resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code); if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error'); //加悲观锁 $resBalanceModel = $oWalletPlatformBalanceModel->findByWhereWithLock($resBalanceModel->id); $bean->setBalanceId($resBalanceModel->id); $bean->setBeforeTotalAmount($resBalanceModel->total_amount); $bean->setAfterTotalAmount(Math::bcAdd($resBalanceModel->total_amount, $bean->getAmount())); //新增账变 $res = $this->addTransaction($bean); if (!$res) throw new ModelException('addTransaction fail'); //变更余额 $res = $oWalletPlatformBalanceModel->addAvailableAmount($bean->getBalanceId(), $bean->getAmount()); if (!$res) throw new ModelException('addAvailableAmount fail'); DB::commit(); Logs::SuccLog(__FUNCTION__, func_get_args()); return true; } catch (\Exception $e) { DB::rollBack(); Logs::ErrLog(__FUNCTION__ . ' rollBack', $e); return false; } } //发起提现 function typeWithdrawFirst($platform_id, $uid, $currency_code, $amount, $to_address = null): bool { $bean = new WalletPlatformBalanceTransactionBean(); $bean->setPlatformId($platform_id); $bean->setUid($uid); $bean->setCurrencyCode($currency_code); $bean->setAmount(-abs($amount)); //负数 $bean->setType(self::TYPE_WITHDRAW_DEC); $bean->setStatus(self::STATUS_WAIT); $bean->setToWalletAddress($to_address); try { DB::beginTransaction(); //获取提现手续费 $oTableWalletSettingCache = new TableWalletSettingCache(); $withdraw_fee = $oTableWalletSettingCache->getWithdrawFeeAmount(); //查询平台余额 $oWalletPlatformBalanceModel = new WalletPlatformBalanceModel(); $resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code); if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error'); //加悲观锁 $resBalanceModel = $oWalletPlatformBalanceModel->findByWhereWithLock($resBalanceModel->id); //检查余额是否足够 if (Math::bcComp(Math::bcSub($resBalanceModel->available_amount, $withdraw_fee), abs($bean->getAmount())) == -1) throw new ModelException('available_amount not enough'); $bean->setBalanceId($resBalanceModel->id); $bean->setBeforeTotalAmount($resBalanceModel->total_amount); $bean->setAfterTotalAmount(Math::bcSub($resBalanceModel->total_amount, $bean->getAmount())); $bean->setFeeAmount($withdraw_fee); //查询当前绑定地址 $oWalletPlatformBindModel = new WalletPlatformBindModel(); $resBindModel = $oWalletPlatformBindModel->findBind($platform_id, $uid, $currency_code); if (!$resBindModel) throw new ModelException('findBind error'); //查找钱包地址 $oWalletAddressModel = new WalletAddressModel(); $resWalletAddressModel = $oWalletAddressModel->findItem($resBindModel->wallet_address_id); if (!$resWalletAddressModel) throw new ModelException('WalletAddressModel findItem error'); $bean->setBindWalletAddressId($resWalletAddressModel->id); $bean->setBindWalletAddress($resWalletAddressModel->address_base58); //新增账变 $res = $this->addTransaction($bean); if (!$res) throw new ModelException('addTransaction fail'); //先扣除手续费 $res = $this->typeTransactionFeeSub($platform_id, $uid, $currency_code, $withdraw_fee, $res->id .'withdraw fee'); if (!$res) throw new ModelException('withdraw fee fail'); //变更余额 //先冻结,等回调成功再扣减 $res = $oWalletPlatformBalanceModel->frozenAmount($bean->getBalanceId(), $bean->getAmount()); if (!$res) throw new ModelException('frozenAmount fail'); //投递到提现处理队列 $oQueueWalletPlatformWithdrawBean = new QueueWalletPlatformWithdrawTransferBean(); $oQueueWalletPlatformWithdrawBean->setTransactionId($bean->getId()); WalletPlatformWithdrawTransferQueue::putToQueue($oQueueWalletPlatformWithdrawBean); DB::commit(); Logs::SuccLog(__FUNCTION__, func_get_args()); return true; } catch (\Exception $e) { DB::rollBack(); Logs::ErrLog(__FUNCTION__ . 'rollBack', $e); return false; } } //提现回调 function typeWithdrawSecondCallback($id, $status, $remark = null, $callback_wallet_address_transaction_id = null): bool { try { DB::beginTransaction(); //获取账变 $resModel = $this->findItem($id); if (!$resModel) throw new ModelException('findItem error'); if ($resModel->status != self::STATUS_PROCESSING) throw new ModelException('status error'); //查询平台余额 $oWalletPlatformBalanceModel = new WalletPlatformBalanceModel(); $resBalanceModel = $oWalletPlatformBalanceModel->findItem($resModel->balance_id); if (!$resBalanceModel) throw new ModelException('WalletPlatformBalanceModel findItem error'); //加悲观锁 $resBalanceModel = $oWalletPlatformBalanceModel->findByWhereWithLock($resBalanceModel->id); if ($status == self::STATUS_SUCCESS) { //变更余额 $res = $oWalletPlatformBalanceModel->subFrozenAmount($resModel->balance_id, $resModel->amount); if (!$res) throw new ModelException('subFrozenAmount error'); //更新账变状态 $updateItem = [ 'id' => $id, 'status' => self::STATUS_SUCCESS, 'remark' => $remark, 'updated_at' => Times::getNowDateTime(), 'callback_time' => Times::getNowDateTime(), 'after_total_amount' => Math::bcAdd($resBalanceModel->total_amount, $resModel->amount), 'before_total_amount' => $resBalanceModel->total_amount, 'callback_wallet_address_transaction_id' => $callback_wallet_address_transaction_id, ]; if (!empty($remark)) $updateItem['remark'] = $remark; $res = $this->updateItem($updateItem); if (!$res) throw new ModelException('updateItem error'); //投递到通知平台队列 $oQueueNotifyToPlatformBean = new QueueNotifyToPlatformBean(); $oQueueNotifyToPlatformBean->setType(QueueNotifyToPlatformBean::TYPE_WITHDRAW); $oQueueNotifyToPlatformBean->setPlatformId($resModel->platform_id); $oQueueNotifyToPlatformBean->setCurrencyCode($resModel->currency_code); $oQueueNotifyToPlatformBean->setAmount($resModel->amount); $oQueueNotifyToPlatformBean->setStatus(self::STATUS_SUCCESS); $oQueueNotifyToPlatformBean->setSn($resModel->sn); WalletNotifyToPlatformQueue::putToQueue($oQueueNotifyToPlatformBean); } else { //变更余额 $res = $oWalletPlatformBalanceModel->unFrozenAmount($resModel->balance_id, $resModel->amount); if (!$res) throw new ModelException('unFrozenAmount error'); //更新账变状态 $updateItem = [ 'id' => $id, 'status' => self::STATUS_FAIL, 'updated_at' => Times::getNowDateTime(), 'callback_time' => Times::getNowDateTime(), 'callback_wallet_address_transaction_id' => $callback_wallet_address_transaction_id, ]; if (!empty($remark)) $updateItem['remark'] = $remark; $res = $this->updateItem($updateItem); if (!$res) throw new ModelException('updateItem error'); //返还手续费 $res = $this->typeTransactionFeeAdd($resModel->platform_id, $resModel->uid, $resModel->currency_code, $resModel->fee_amount, $resModel->id . 'withdraw fail return fee'); if (!$res) throw new ModelException('withdraw fail return fee fail'); } DB::commit(); Logs::SuccLog(__FUNCTION__, func_get_args()); return true; } catch (\Exception $e) { DB::rollBack(); Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args()); return false; } } function addTransaction(WalletPlatformBalanceTransactionBean &$bean): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|bool { if (!$bean->getPlatformId()) throw new ModelException('addTransaction getPlatformId is required'); if (!$bean->getCurrencyCode()) throw new ModelException('addTransaction getCurrencyCode is required'); if (!$bean->getType()) throw new ModelException('addTransaction getType is required'); if (!$bean->getStatus()) throw new ModelException('addTransaction getStatus is required'); if (!$bean->getBalanceId()) throw new ModelException('addTransaction getBalanceId is required'); if (!$bean->getBindWalletAddressId()) throw new ModelException('addTransaction getBindWalletAddressId is required'); if (empty($bean->getSn())) $bean->setSn(Tools::genUuid()); $bean->setCreatedAt(Times::getNowDateTime()); return $this->addItem($bean->toArrayNotNull()); } function findByBlockTransactionId(string $tid, $type): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Builder|array|null { if (!in_array($type, [self::TYPE_RECHARGE_ADD, self::TYPE_WITHDRAW_DEC])) throw new ModelException('type error'); return $this->findItemByWhere(['block_transaction_id' => $tid, 'type' => $type]); } }