2025-11-07 09:56:20 +08:00
< ? php
namespace app\api\controller ;
use support\Request ;
use support\think\Db ;
use taoser\facade\Validate ;
use app\model\Recharge as RechargeModel ;
use app\model\User as UserModel ;
use hg\apidoc\annotation as Apidoc ;
/**
* 充值模块
2026-02-15 19:41:56 +08:00
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
2025-11-07 09:56:20 +08:00
*/
class RechargeController extends BaseController {
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = [ '*' ];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [ 'notify' , 'watch_list' , 'updateUserBalance' ];
/**
* 列表
* @Apidoc\Method("POST")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
public function list ()
{
$limit = ( int ) input ( 'limit' , 10 );
$status = Input ( 'status' , null );
if ( ! is_null ( $status )){
$list = RechargeModel :: where ( 'user_id' , \support\Jwt\JwtToken :: getCurrentId ()) -> where ( 'status' , $status ) -> order ( 'id desc' ) -> paginate ( $limit );
} else {
$list = RechargeModel :: where ( 'user_id' , \support\Jwt\JwtToken :: getCurrentId ()) -> order ( 'id desc' ) -> paginate ( $limit );
}
return $this -> success ( __ ( 'successful' ), $list -> toArray ());
}
/**
* 创建
* @Apidoc\Method("POST")
* @Apidoc\Param("amount", type="string", require=true, desc="金额")
* @Apidoc\Param("network", type="string", require=true, desc="网络")
*/
function create (){
return $this -> create1 ();
}
public function create2 ()
{
$user = \support\Jwt :: getUser ();
$data = [
'user_id' => $user -> id ,
'amount' => input ( 'amount' , 0 ),
'network' => input ( 'network' , '' ),
'status' => \app\enum\WithdrawlStatus :: CREATED -> value
];
//验证最小金额
if ( $data [ 'amount' ] < Config ( 'site.recharge_minimum' )){
return $this -> error ( __ ( 'Minimum recharge of %num%' ,[
'%num%' => Config ( 'site.recharge_minimum' )
]));
}
if ( ! $data [ 'network' ] || ! in_array ( $data [ 'network' ],[ 'BEP-20' , 'TRC-20' , 'wxpay' , 'alipay' , 'qqpay' ])) {
return $this -> error ( __ ( 'Network is incorrect' ));
}
if ( in_array ( $data [ 'network' ],[ 'BEP-20' , 'TRC-20' ])){
$ing_count = RechargeModel :: where ( 'user_id' , $data [ 'user_id' ]) -> where ( 'status' , \app\enum\RechargeStatus :: CREATED -> value ) -> count ( 'id' );
if ( $ing_count >= 2 ){
return $this -> error ( __ ( 'You have reached the limit of uncompleted orders ( %max% ). Please complete the existing orders before trying to create another one.' ,[
'%max%' => 2
]));
}
Db :: startTrans ();
try {
/** @var RechargeModel $order */
$order = RechargeModel :: create ( $data );
//$order->notify_url = Request()->domain().'/api/recharge/notify';
$order -> notify_url = config ( 'pay.notify_server' ) . '/api/recharge/notify' ;
$order -> out_trade_no = $order -> id ;
$order -> env = 'product' ;
$order -> appid = Config ( 'pay.appid' );
$order -> created_at = time ();
$postdata = $order -> toArray ();
unset ( $postdata [ 'id' ]);
//转换成USDT
$postdata [ 'amount' ] = bcdiv ( $order -> amount , Config ( 'site.money_to_usdt_rate' ), 4 );
//折扣
$postdata [ 'amount' ] = bcdiv ( $postdata [ 'amount' ], Config ( 'site.usdt_recharge_discount' ), 4 );
$res = post ( Config ( 'pay.server' ) . '/recharge/create' , $postdata );
\support\Log :: alert ( " create res: " . $res );
//cp($res);
$res = json_decode ( $res , true );
if ( $res [ 'code' ] === 0 ){
$order -> address = $res [ 'data' ][ 'address' ];
} else {
Db :: rollback ();
return $this -> error ( __ ( 'Failed to create recharge order, please try again later' ));
}
$order -> allowField ([ 'address' ]) -> save ();
Db :: commit ();
return $this -> success ( __ ( 'successful' ),[
'order' => $order
]);
} catch ( \Exception $e ) {
Db :: rollback ();
\support\Log :: alert ( " create error " . $e -> getMessage ());
return $this -> error ( __ ( 'Failed to create recharge order, please try again later' ));
}
} else {
2026-03-01 21:05:19 +08:00
$sign = \support\Encrypt :: aesencode ( json_encode ( $data ));
2025-11-07 09:56:20 +08:00
return $this -> success ( __ ( 'successful' ),[
'order' => [
'id' => 0 ,
'network' => $data [ 'network' ],
'amount' => $data [ 'amount' ],
" url " => 'http://43.240.74.89:9595/pay/index?sign=' . $sign
]
]);
}
}
public function create1 ()
{
$user = \support\Jwt :: getUser ();
$data = [
'user_id' => $user -> id ,
'amount' => input ( 'amount' , 0 ),
'network' => input ( 'network' , '' ),
'status' => \app\enum\WithdrawlStatus :: CREATED -> value
];
//验证最小金额
if ( $data [ 'amount' ] < Config ( 'site.recharge_minimum' )){
return $this -> error ( __ ( 'Minimum recharge of %num%' ,[
'%num%' => Config ( 'site.recharge_minimum' )
]));
}
if ( ! $data [ 'network' ] || ! in_array ( $data [ 'network' ],[ 'BEP-20' , 'TRC-20' , 'wxpay' , 'alipay' , 'qqpay' ])) {
return $this -> error ( __ ( 'Network is incorrect' ));
}
if ( in_array ( $data [ 'network' ],[ 'BEP-20' , 'TRC-20' ])){
$ing_count = RechargeModel :: where ( 'user_id' , $data [ 'user_id' ]) -> where ( 'status' , \app\enum\RechargeStatus :: CREATED -> value ) -> count ( 'id' );
if ( $ing_count >= 2 ){
return $this -> error ( __ ( 'You have reached the limit of uncompleted orders ( %max% ). Please complete the existing orders before trying to create another one.' ,[
'%max%' => 2
]));
}
}
Db :: startTrans ();
try {
/** @var RechargeModel $order */
$order = RechargeModel :: create ( $data );
if ( in_array ( $data [ 'network' ],[ 'BEP-20' , 'TRC-20' ])){
//$order->notify_url = Request()->domain().'/api/recharge/notify';
$order -> notify_url = config ( 'pay.notify_server' ) . '/api/recharge/notify' ;
$order -> out_trade_no = $order -> id ;
$order -> env = 'product' ;
$order -> appid = Config ( 'pay.appid' );
$order -> created_at = time ();
$postdata = $order -> toArray ();
unset ( $postdata [ 'id' ]);
//转换成USDT
$postdata [ 'amount' ] = bcdiv ( $order -> amount , Config ( 'site.money_to_usdt_rate' ), 4 );
//折扣
$postdata [ 'amount' ] = bcmul ( $postdata [ 'amount' ], Config ( 'site.usdt_recharge_discount' ), 4 );
$res = post ( Config ( 'pay.server' ) . '/recharge/create' , $postdata );
\support\Log :: alert ( " create res: " . $res );
//cp($res);
$res = json_decode ( $res , true );
if ( $res [ 'code' ] === 0 ){
$order -> address = $res [ 'data' ][ 'address' ];
} else {
Db :: rollback ();
return $this -> error ( __ ( 'Failed to create recharge order, please try again later' ));
}
$order -> allowField ([ 'address' ]) -> save ();
} else {
$postdata = [
" pid " => '144604' ,
" type " => $order -> network ,
" out_trade_no " => $order -> id ,
" notify_url " => Config ( 'pay.notify_server' ) . '/api/recharge/notify_ok' ,
" return_url " => Config ( 'pay.notify_server' ) . '/api/recharge/notify_ok' ,
" name " => 'VIP会员' ,
" money " => $order -> amount ,
" device " => " mobile " ,
" clientip " => request () -> getRealIp ()
];
$postdata [ 'money' ] = bcdiv ( $postdata [ 'money' ] , Config ( 'site.rmb_recharge_discount' ), 2 );
//\support\Log::alert("create postdata:".json_encode($postdata));
$this -> getsign ( $postdata );
$res = post ( 'https://pay.yf2.cn/mapi.php' , $postdata );
\support\Log :: alert ( " create res: " . $res );
$res = json_decode ( $res , true );
if ( $res [ 'code' ] === 1 ){
if ( isset ( $res [ 'payurl' ])){
$order -> address = $res [ 'qrcode' ];
} elseif ( isset ( $res [ 'qrcode' ])){
$order -> address = $res [ 'qrcode' ];
} elseif ( isset ( $res [ 'urlscheme' ])){
$order -> address = $res [ 'urlscheme' ];
}
} else {
Db :: rollback ();
\support\Log :: error ( json_encode ( $res ));
throw new \Exception ( $res [ 'msg' ]);
}
$order -> allowField ([ 'address' ]) -> save ();
}
Db :: commit ();
return $this -> success ( __ ( 'successful' ),[
'order' => $order
]);
} catch ( \Exception $e ) {
Db :: rollback ();
\support\Log :: alert ( " create error " . $e -> getMessage ());
return $this -> error ( __ ( 'Failed to create recharge order, please try again later' ));
}
}
/**
* 更新
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="int", require=true, desc="ID")
* @Apidoc\Param("amount", type="string", require=true, desc="金额")
* @Apidoc\Param("network", type="string", require=true, desc="网络")
*/
function update (){
$id = input ( 'id' );
$amount = input ( 'amount' );
$network = input ( 'network' );
$data = [];
if ( ! $id ){
return $this -> error ( __ ( 'Invalid parameters' ));
}
/** @var RechargeModel $order */
$order = RechargeModel :: where ( 'id' , $id ) -> find ();
if ( ! $order ){
return $this -> error ( __ ( 'Order not exsit' ));
}
if ( $amount && $amount != $order -> amount ){
$data [ 'amount' ] = $amount ;
//验证最小金额
if ( $data [ 'amount' ] < Config ( 'site.recharge_minimum' )){
return $this -> error ( __ ( 'Minimum recharge of %num%' ,[
'%num%' => Config ( 'site.recharge_minimum' )
]));
}
$data [ 'amount' ] = bcdiv ( $data [ 'amount' ], Config ( 'site.money_to_usdt_rate' ), 4 );
}
if ( $network && $network != $order -> network ){
$data [ 'network' ] = $network ;
if ( ! $data [ 'network' ] || ! in_array ( $data [ 'network' ],[ 'BEP-20' , 'TRC-20' ])) {
return $this -> error ( __ ( 'Network is incorrect' ));
}
}
if ( empty ( $data )){
return $this -> error ( __ ( 'Invalid parameters' ));
}
foreach ( $data as $field => $value ){
$order -> $field = $value ;
}
$data [ 'out_trade_no' ] = $order -> id ;
$data [ 'action' ] = 'update' ;
$data [ 'appid' ] = Config ( 'pay.appid' );
$res = post ( Config ( 'pay.server' ) . '/recharge/create' , $data );
//\support\Log::alert("update res:".$res);
$res = json_decode ( $res , true );
//cp($res);
if ( $res [ 'code' ] === 0 ){
if ( $order -> address != $res [ 'data' ][ 'address' ]){
$order -> address = $res [ 'data' ][ 'address' ];
}
} else {
return $this -> error ( __ ( 'Failed to create recharge order, please try again later' ));
}
$order -> save ();
return $this -> success ( __ ( 'successful' ),[
'order' => $order
]);
}
/**
* 详情
* @Apidoc\Query("id", type="string", require=true, desc="ID")
*/
public function detail (){
$appid = input ( 'id' );
$vo = RechargeModel :: where ( 'id' , $appid ) -> find ();
if ( $vo ) {
return $this -> success ( __ ( 'successful' ),[
'order' => $vo
]);
}
return $this -> error ( __ ( " Record does not exist " ));
}
/**
* 转账成功异步通知
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
public function notify (){
2026-03-01 21:05:19 +08:00
$data = \support\Encrypt :: aesdecode ( input ( 'data' ));
2025-11-07 09:56:20 +08:00
$data = json_decode ( $data , true );
/** @var RechargeModel $vo */
$vo = RechargeModel :: where ( 'id' , $data [ 'out_trade_no' ]) -> find ();
if ( $vo ){
if ( $data [ 'result' ] == 'SUCCESS' ){
if ( $vo -> status != \app\enum\RechargeStatus :: COMPLETE -> value ){
try {
$vo -> status = \app\enum\RechargeStatus :: COMPLETE -> value ;
$vo -> txid = $data [ 'txid' ];
$vo -> real_amount = isset ( $data [ 'real_amount' ]) ? $data [ 'real_amount' ] : 0 ;
$vo -> transfer_at = $data [ 'transfer_at' ] ? : time ();
$vo -> save ();
//消除USDT和人民币的误差,因为USDT支付可能不是一模一样的金额
$money = bcmul ( $vo -> amount , Config ( 'site.rmb_to_money_rate' ));
UserModel :: money ( $vo -> user_id , $money , \app\enum\BalanceType :: RECHARGE , $vo -> id );
Hook ( 'recharge.success' , $vo );
} catch ( \Exception $e ){
2026-02-21 08:21:05 +08:00
log_alert ( '充值回调失败:' . $e -> getMessage ());
log_alert ( $data );
2025-11-07 09:56:20 +08:00
}
}
} else {
$vo -> status = \app\enum\RechargeStatus :: FAIL -> value ;
$vo -> txid = $data [ 'txid' ];
$vo -> reason = $data [ 'reason' ];
$vo -> save ();
}
}
return response ( " SUCCESS " );
}
/**
* 人民币转账成功异步通知
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
public function notify_ok (){
$data = Input ( 'post.' );
$sign = get_pay_sign ( $data );
if ( $sign != $data [ 'sign' ]){
return response ( " FAIL " );
}
/** @var RechargeModel $vo */
$vo = RechargeModel :: where ( 'id' , $data [ 'out_trade_no' ]) -> find ();
if ( $vo && isset ( $data [ 'trade_status' ])){
if ( $data [ 'trade_status' ] === 'TRADE_SUCCESS' ){
if ( $vo -> status != \app\enum\RechargeStatus :: COMPLETE -> value ){
try {
$vo -> status = \app\enum\RechargeStatus :: COMPLETE -> value ;
$vo -> txid = isset ( $data [ 'trade_no' ]) ? $data [ 'trade_no' ] : '' ;
$vo -> from = 'pay.yf2.cn' ;
$vo -> real_amount = isset ( $data [ 'money' ]) ? $data [ 'money' ] : 0 ;
$vo -> save ();
$money = bcmul ( $vo -> real_amount , Config ( 'site.rmb_to_money_rate' ));
UserModel :: money ( $vo -> user_id , $money , \app\enum\BalanceType :: RECHARGE , $vo -> id );
Hook ( 'recharge.success' , $vo );
} catch ( \Exception $e ){
2026-02-21 08:21:05 +08:00
log_alert ( '充值回调失败:' . $e -> getMessage ());
log_alert ( $data );
2025-11-07 09:56:20 +08:00
return response ( " FAIL " );
}
}
}
}
return response ( " SUCCESS " );
}
/**
* 根据TXID更新用户余额,补单的功能
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
* @return \support\Response
*/
function update_balance_by_txid (){
$user_id = \support\Jwt\JwtToken :: getCurrentId ();
$data = [
'txid' => input ( 'txid' ),
'id' => input ( 'id' ),
'status' => \app\enum\WithdrawlStatus :: CREATED -> value
];
/** @var RechargeModel $vo */
$vo = RechargeModel :: where ( 'id' , $data [ 'id' ]) -> find ();
if ( $vo -> user_id != $user_id ){
return $this -> error ( __ ( 'permission denied' ));
}
//验证最小金额
if ( ! $data [ 'txid' ]){
return $this -> error ( __ ( 'Invalid parameters' ));
}
$res = get ( Config ( 'pay.server' ) . '/Util/txid?txid=' . $data [ 'txid' ]);
$res = json_decode ( $res , true );
if ( $res [ 'code' ] !== 0 ){
return $this -> error ( $res [ 'msg' ]);
}
$vo -> txid = $data [ 'txid' ];
$vo -> result = $res [ 'result' ];
if ( $res [ 'result' ] == 'SUCCESS' ){
$vo -> from = $res [ 'from' ];
$vo -> real_amount = $res [ 'real_amount' ];
$vo -> pay_time = $res [ 'timestamp' ];
$vo -> status = \app\enum\RechargeStatus :: COMPLETE -> value ;
} else {
$vo -> result = $res [ 'from' ];
$vo -> reason = $res [ 'real_amount' ];
$vo -> status = \app\enum\RechargeStatus :: FAIL -> value ;
}
$vo -> save ();
return $this -> success ( __ ( 'successful' ));
}
/**
* 根据监视上报数据更新用户余额
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
function updateUserBalance ( Request $request ){
$decimal_part = $request -> post ( 'decimal_part' );
$data = [
'user_id' => 0 ,
'amount' => $request -> post ( 'amount' ),
'network' => $request -> post ( 'chain' ),
'address' => $request -> post ( 'to' ),
'from' => $request -> post ( 'from' ),
'txid' => $request -> post ( 'txid' ),
'pay_time' => $request -> post ( 'pay_time' ),
'created_at' => $request -> post ( 'pay_time' ),
];
if ( $data [ 'network' ] == 'TRC-20' ){
$data [ 'user_id' ] = UserModel :: where ( 'trc_recharge_address' , $data [ 'address' ]) -> where ( 'decimal_part' , $decimal_part ) -> value ( 'id' );
} else {
$data [ 'user_id' ] = UserModel :: where ( 'bep_recharge_address' , $data [ 'address' ]) -> where ( 'decimal_part' , $decimal_part ) -> value ( 'id' );
}
if ( ! $data [ 'user_id' ]){
return $this -> success ( __ ( 'user not exist' ));
}
$data [ 'real_amount' ] = $data [ 'amount' ];
//$data['pay_time'] = time();
$data [ 'status' ] = 2 ;
$vo = RechargeModel :: where ( 'txid' , $data [ 'txid' ]) -> find ();
if ( $vo ){
return $this -> success ( __ ( 'exist' ));
}
Db :: startTrans ();
try {
$idata = RechargeModel :: create ( $data );
UserModel :: score ( $data [ 'user_id' ], $data [ 'amount' ], \app\enum\BalanceType :: RECHARGE , $idata [ 'id' ] . '' );
Db :: commit ();
Hook ( 'recharge.success' , $idata );
return $this -> success ( __ ( 'successful' ));
} catch ( \Exception $e ){
Db :: rollback ();
return $this -> error ( $e -> getMessage ());
}
}
/**
* 监视列表
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
function watch_list (){
//$bep = UserModel::distinct(true)->column('LOWER(bep_recharge_address)');
//$trc = UserModel::distinct(true)->column('LOWER(trc_recharge_address)');
$bep = UserModel :: distinct ( true ) -> column ( 'bep_recharge_address' );
$trc = UserModel :: distinct ( true ) -> column ( 'trc_recharge_address' );
return $this -> success ( __ ( 'successful' ),[
'BEP-20' => $bep ,
'TRC-20' => $trc ,
]);
}
protected function getsign ( & $data , $key = " 3E7551E3707DFB6B6E8A6E83B01FF437 " ) {
return get_pay_sign ( $data , $key );
}
}