Files
li 79123e079f feat: 对接越南AlinPay支付网关(代收+代付)
- 新增 AlinPay 网关核心类:签名/验签/代收/代付/查询/账户接口
- 新增 alinPay trait:充值发起 + 代收回调结算(事务+行锁)
- 修改 admin/agent Report:提现审批自动路由 ALIN_VN 发起代付
- 新增 Pcapi:银行卡提现申请 + 越南银行列表接口
- 修改 Crypto:代付回调/订单查询/账户查询入口
- 新增越南区 area_list(id=3) + alinPay pay_list 配置
- DDL: user_withdraw 表增加 pay_channel/ext 字段
2026-03-12 22:24:49 +08:00

350 lines
10 KiB
PHP

<?php
namespace app\onlinechip\controller;
use pay\Ybf;
use pay\AlinPay;
use think\Cache;
use \think\Controller;
use think\Db;
use think\Request;
class Crypto Extends Controller{
public function __construct()
{
parent::__construct();
}
/**
* YBF支付回调
*/
public function callback_ybf()
{
$json = file_get_contents("php://input");
$data = json_decode($json,true);
// 记录日志
$logId = $this->addLog('YBF',$json);
$ybf = new Ybf();
$res = $ybf->callback($data,$logId);
return $res;
}
/**
* YBF反查开放接口
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function query_order_ybf()
{
$json = file_get_contents("php://input");
addLogToFile('反查报文:'.$json,'ybf_fc','ybf');
parse_str($json,$data);
// $data = Request::instance()->request();
$ybf = new Ybf();
$res = $ybf->withdraw_query($data);
$res = json_encode($res,JSON_UNESCAPED_UNICODE);
addLogToFile('反查报文返回信息:'.$res,'ybf_fc','ybf');
return $res;
}
/**
* 充值回调
*/
public function callback()
{
$json = file_get_contents("php://input");
// $json = '{"key":"transaction","merch":{"tid":1002,"user_id":1001,"type":100,"address":"TMp3mUPf4U5zsnmVSSWTHc4YQSBjxFQqbC","update_time":1681266378741,"create_time":1673600766135,"status":200},"sign":"267e31e41087f80e441bd9759fbd62fd179633b9","timestamp":1681267421305,"transaction":{"tid":1001,"type":200,"txid":"07756fb10fe41d5c342c3f79396471ce3d0d93deae2081cfe8b6e7f730492563","from_addr":"TDunwvXBwazrLdV9jPD7uy4Bq3z8Eyb4UE","to_addr":"TJtfqKmvxZTPcrQNMEoP3sPxUwbtUN8MVA","asset":"TRX","amount":"1000000","fee_energy_usage":"0","fee_energy_trx":"0","fee_net_usage":"0","fee_net_trx":"0.1","fee_used":"0.1","fee_asset":"TRX","notified":1,"update_time":1681267421298,"create_time":1681267421298,"status":200}}';
$data = json_decode($json,true);
if(!empty($data)){
$type = $data['key'];
$uniqueNo = "";
// 记录日志
$logId = $this->addLog($type,$json);
// 校验签名
$verify = $this->verifySign($data);
if($verify){
switch ($type){
// 充值上分
case "transaction":
$uniqueNo = $data['transaction']['txid'];
$res = $this->transaction($data);
break;
// 提现转账
case "processor":
$uniqueNo = $data['processor']['uuid'];
$res = $this->processor($data);
break;
default:
$res = false;
break;
}
// 更新唯一识别值
if($uniqueNo) $this->setUniqueNo($logId,$uniqueNo);
//if($res) return "success";
if($res) return json_encode(['code' => 0]);
}
}
}
/**
* 处理交易回调
* @param $data
*/
private function transaction($data)
{
$data = $data['transaction'];
// 校验状态
$status = $data['status'];
if($status != 200){
return false;
}
$toAddr = $data['to_addr'];
$fromAddr = $data['from_addr'];
$amount = $data['amount'];
$txid = $data['txid'];
$userId = Db::name('user_wallet')->where('address',$toAddr)->value('user_id');
if($userId){
Db::startTrans();
$user = Db::name('user')->where('id',$userId)->lock(true)->find();
$recharge = Db::name('user_recharge')->where('out_trade_no',$txid)->find();
if($recharge){
return false;
}
// 计算兑换比率
$system = Db::name('system')->find();
$rechargeMoney = empty($system['recharge_money']) ? 100 : $system['recharge_money'];
$money = $amount*($rechargeMoney/100);
// 上分
$userMoney = $user['money'];
$moneyAfter = $user['money'] + $money;
// 上分
$res = Db::name('user')->where('id',$userId)->update([
'money' => $moneyAfter,
'last_recharge' => $money,
'last_recharge_time' => time(),
]);
if(!$res) return;
// 充值记录
$res = Db::name('user_recharge')->insert([
'out_trade_no' => $txid,
'user_id' => $userId,
'amount' => $amount,
'money' => $money,
'old_money' => $userMoney,
'new_money' => $moneyAfter,
'from_addr' => $fromAddr,
'to_addr' => $toAddr,
'pay_channel' => 'USDT',
'status' => 'SUCCESS',
'create_time' => time(),
'pay_time' => time(),
]);
if(!$res) return;
Db::commit();
return json_encode(['code' => 0]);
}
}
/**
* 处理提现回调
* @param $data
*/
private function processor($data)
{
$data = $data['processor'];
// 校验状态
$status = $data['status'];
if($status !== 200){
return false;
}
$code = $data['result']['transfer']['code'];
if($code !== 0){
return false;
}
$uuid = $data['uuid'];
$fromAddr = $data['from_addr'];
$toAddr = $data['to_addr'];
$amount = $data['amount'];
$asset = $data['asset'];
$withdraw = Db::name('user_withdraw')->where('order_no',$uuid)->where('status','AGREE')->find();
if($withdraw){
Db::startTrans();
if($withdraw['from_address'] != $fromAddr) return false;
if($withdraw['to_address'] != $toAddr) return false;
if($withdraw['amount'] != $amount) return false;
if($withdraw['type'] != $asset) return false;
// 更改状态
$res = Db::name('user_withdraw')->where('id',$withdraw['id'])->update([
'status' => 'SUCCESS',
'update_time' => time(),
]);
if(!$res) return;
Db::commit();
return json_encode(['code' => 0]);
}
}
/**
* 校验签名
* @param $data
* @return bool
*/
private function verifySign($data)
{
$payChannelConfig = Db::name('pay_channel')->where('key','USDT')->value('value');
$config = json_decode($payChannelConfig,true);
$key = urlencode($data['key']);
$timestamp = urlencode($data['timestamp']);
$signData = "key={$key}";
$signData .= "&merch_addr={$config['merch_addr']}";
$signData .= "&merch_type={$config['merch_type']}";
$signData .= "&timestamp={$timestamp}";
$signData .= "&access_token={$config['access_token']}";
$sign = sha1($signData);
return $sign == $data['sign'];
}
/**
* AlinPay 越南代付回调
*/
public function alin_payment_callback()
{
$post = Request::instance()->post();
$json = json_encode($post, JSON_UNESCAPED_UNICODE);
addLogToFile('越南代付回调原始数据:' . $json, 'alin_callback', 'alin');
if (!is_array($post) || empty($post['out_trade_no']) || empty($post['sign'])) {
exit('fail');
}
// 记录回调日志
$logId = Db::name('pay_callback_log')->insertGetId([
'pay_channel' => 'ALIN_PAYMENT',
'type' => 'payment',
'json' => $json,
'create_time' => date('Y-m-d H:i:s'),
]);
try {
$alinPay = new AlinPay();
$res = $alinPay->paymentCallback($post, $logId);
exit($res ?: 'fail');
} catch (\Exception $e) {
addLogToFile('越南代付回调异常:' . $e->getMessage(), 'alin_callback_error', 'alin');
exit('fail');
}
}
/**
* AlinPay 越南代收查询
*/
public function alin_query_order()
{
$orderNo = Request::instance()->post('order_no');
if (!$orderNo) {
return json(['code' => 0, 'msg' => '缺少订单号']);
}
try {
$alinPay = new AlinPay();
$result = $alinPay->queryOrder($orderNo);
return json($result);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => $e->getMessage()]);
}
}
/**
* AlinPay 越南代付查询
*/
public function alin_query_payment()
{
$orderNo = Request::instance()->post('order_no');
if (!$orderNo) {
return json(['code' => 0, 'msg' => '缺少订单号']);
}
try {
$alinPay = new AlinPay();
$result = $alinPay->queryPayment($orderNo);
return json($result);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => $e->getMessage()]);
}
}
/**
* AlinPay 账户余额查询
*/
public function alin_query_account()
{
try {
$alinPay = new AlinPay();
$result = $alinPay->queryAccount();
return json($result);
} catch (\Exception $e) {
return json(['code' => 0, 'msg' => $e->getMessage()]);
}
}
/**
* 记录日志
* @param $type
* @param $json
* @return mixed
*/
private function addLog($type,$json)
{
$id = Db::name('pay_callback_log')->insertGetId([
'pay_channel' => $type,
'type' => $type,
'json' => $json,
'create_time' => date('Y-m-d H:i:s'),
]);
return $id;
}
/**
* 更新唯一识别值
* @param $id
* @param $uniqueNo
* @return mixed
*/
private function setUniqueNo($id,$uniqueNo)
{
$res = Db::name('pay_callback_log')->where('id',$id)->update([
'unique_no' => $uniqueNo,
]);
return $res;
}
}