包含所有线上运行的改动: - 代理编辑功能修复(码率/占成/返水率保存) - CORS跨域修复(Pcapi构造函数统一Origin) - 支付通道(ccpay/epay新增) - 代理报表洗码量显示 - AlinPay金额调整 - 代理/会员编辑页面优化 - 多语言更新
317 lines
13 KiB
PHP
317 lines
13 KiB
PHP
<?php
|
||
namespace app\onlinechip\controller\traits;
|
||
|
||
use think\Db;
|
||
use think\Log;
|
||
use think\Request;
|
||
|
||
/**
|
||
* AlinPay 越南支付 Trait
|
||
* 仅包含代收(充值)相关方法
|
||
* 代付回调在 Crypto.php 中处理
|
||
*/
|
||
trait alinPay
|
||
{
|
||
/**
|
||
* 越南代收 - 发起充值
|
||
*/
|
||
public function alin_online_order()
|
||
{
|
||
// 兼容加密请求(Pcapi)和明文请求(Order)
|
||
$post = Request::instance()->post();
|
||
if (isset($post['encryptData'])) {
|
||
$post = decrypt_data($post);
|
||
if (!$post) {
|
||
return json(['status' => 0, 'message' => '数据解密失败']);
|
||
}
|
||
}
|
||
|
||
$user_id = intval($post['user_id'] ?? 0);
|
||
$money = intval($post['price'] ?? 0);
|
||
$client = intval($post['client'] ?? 0);
|
||
$username = trim($post['username'] ?? '');
|
||
|
||
$user = Db::name('user')->where([
|
||
'id' => $user_id,
|
||
'status' => 1,
|
||
'is_delete' => 0,
|
||
'agent' => 0,
|
||
])->find();
|
||
|
||
if (!$user || $user_id <= 0 || $money <= 0 || $client <= 0) {
|
||
return json(['status' => 0, 'message' => '参数错误,请稍后再试']);
|
||
}
|
||
|
||
// 判断地区支线权限
|
||
$zdlIdArr = explode(',', $user['agent_parent_id_path']);
|
||
$area_list = config('area_list');
|
||
|
||
if (!isset($area_list[$user['area_id']])) {
|
||
return json(['status' => 0, 'message' => '所在地区暂不支持充值']);
|
||
}
|
||
|
||
$limitAgents = $area_list[$user['area_id']]['limit_agent'];
|
||
$isAllow = empty($limitAgents);
|
||
foreach ($limitAgents as $allowAgentId) {
|
||
if (in_array($allowAgentId, $zdlIdArr)) {
|
||
$isAllow = true;
|
||
}
|
||
}
|
||
if (!$isAllow) {
|
||
return json(['status' => 0, 'message' => '该账号暂不支持在线充值']);
|
||
}
|
||
|
||
// 验证总代理余额
|
||
$zdlId = $zdlIdArr[0];
|
||
$zdl = Db::name('user')->where([
|
||
'id' => $zdlId,
|
||
'status' => 1,
|
||
'is_delete' => 0,
|
||
'agent' => 1,
|
||
])->find();
|
||
|
||
if (!$zdl) {
|
||
return json(['status' => 0, 'message' => '所属代理账号异常,暂不能充值']);
|
||
}
|
||
if ($zdl['money'] < $money) {
|
||
return json(['status' => 0, 'message' => '所属代理余额不足,暂不能充值']);
|
||
}
|
||
|
||
$pay_list = config('pay_list');
|
||
$pay_orderid = 'E' . date("YmdHis") . rand(100000, 999999);
|
||
$notify_url = $pay_list['alinPay']['notify_url'];
|
||
|
||
try {
|
||
$alinPay = new \pay\AlinPay();
|
||
$result = $alinPay->createOrder($pay_orderid, sprintf('%.2f', $money), $notify_url, 'uid:' . $user_id);
|
||
|
||
// 空响应处理(curl失败/DNS/超时等)
|
||
if (!is_array($result)) {
|
||
addLogToFile('代收下单:API无响应或返回非JSON,result=' . var_export($result, true), 'alin_error', 'alin');
|
||
$data = encrypt_data(['status' => 0, 'message' => '支付通道无响应,请稍后再试']);
|
||
return json(array('Success' => 1, 'Data' => $data));
|
||
}
|
||
|
||
if (isset($result['code']) && $result['code'] == 200) {
|
||
$returnUrl = $result['data']['url'] ?? '';
|
||
|
||
if (!$returnUrl) {
|
||
$data = encrypt_data(['status' => 0, 'message' => '支付地址获取失败']);
|
||
return json(array('Success' => 1, 'Data' => $data));
|
||
}
|
||
|
||
// 记录订单
|
||
$order_record = [
|
||
'pay_channel_id' => 7,
|
||
'pay_channel_name' => 'alinPay',
|
||
'user_id' => $user_id,
|
||
'order_sn' => $pay_orderid,
|
||
'appid' => $alinPay->getConfigInfo()['app_id'],
|
||
'api_key' => '',
|
||
'money' => $money,
|
||
'sign' => '',
|
||
'create_time' => time(),
|
||
'back_order_sn' => '',
|
||
'client' => $client,
|
||
];
|
||
$order_id = Db::name('order_record')->insertGetId($order_record);
|
||
|
||
// 记录日志
|
||
Db::name('order_log')->insert([
|
||
'pay_channel_id' => 7,
|
||
'pay_channel_name' => 'alinPay',
|
||
'money' => $money,
|
||
'create_time' => time(),
|
||
'remake' => 'user_id:' . $user_id . ',用户名:' . $username . ',发起充值:' . $money . ',充值时间:' . date('Y-m-d H:i:s'),
|
||
'order_id' => $order_id,
|
||
'client' => $client,
|
||
]);
|
||
|
||
$data = encrypt_data(['status' => 1, 'message' => '发起支付成功,即将跳转...', 'url' => $returnUrl]);
|
||
return json(array('Success' => 1, 'Data' => $data));
|
||
} else {
|
||
$errMsg = isset($result['message']) ? $result['message'] : '未知错误(code=' . ($result['code'] ?? 'null') . ')';
|
||
addLogToFile('代收下单失败:' . json_encode($result, JSON_UNESCAPED_UNICODE), 'alin_error', 'alin');
|
||
$data = encrypt_data(['status' => 0, 'message' => '支付发起失败:' . $errMsg]);
|
||
return json(array('Success' => 1, 'Data' => $data));
|
||
}
|
||
} catch (\Exception $e) {
|
||
addLogToFile('代收下单异常:' . $e->getMessage() . ' | file:' . $e->getFile() . ':' . $e->getLine(), 'alin_error', 'alin');
|
||
$data = encrypt_data(['status' => 0, 'message' => '支付发起失败[' . $e->getMessage() . ']']);
|
||
return json(array('Success' => 1, 'Data' => $data));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 越南代收 - 回调处理
|
||
*/
|
||
public function alin_recharge_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'])) {
|
||
Log::record($post, 'alin_error_callback');
|
||
exit('fail');
|
||
}
|
||
|
||
// 记录回调日志
|
||
$logId = Db::name('pay_callback_log')->insertGetId([
|
||
'pay_channel' => 'ALIN_RECHARGE',
|
||
'type' => 'recharge',
|
||
'json' => $json,
|
||
'create_time' => date('Y-m-d H:i:s'),
|
||
]);
|
||
|
||
try {
|
||
$alinPay = new \pay\AlinPay();
|
||
$result = $alinPay->verifyRechargeCallback($post, $logId);
|
||
|
||
if (!$result['verified']) {
|
||
exit('fail');
|
||
}
|
||
|
||
$tradeStatus = $result['trade_status'];
|
||
|
||
// trade_status: 1=成功, -1=处理中, 2=失败
|
||
if ($tradeStatus === 1) {
|
||
$this->alin_recharge_settle($post);
|
||
exit('success');
|
||
} elseif ($tradeStatus === 2) {
|
||
// 失败,终态,确认收到
|
||
addLogToFile('代收回调失败:order=' . $post['out_trade_no'], 'alin_callback', 'alin');
|
||
exit('success');
|
||
} else {
|
||
// 处理中(-1),不返回success让网关重试
|
||
exit('fail');
|
||
}
|
||
} catch (\Exception $e) {
|
||
addLogToFile('代收回调处理异常:' . $e->getMessage(), 'alin_callback_error', 'alin');
|
||
exit('fail');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 代收成功后结算(事务保护:扣代理、写recharge表、加用户余额)
|
||
*/
|
||
private function alin_recharge_settle($data)
|
||
{
|
||
$orderNo = $data['out_trade_no'];
|
||
|
||
Db::startTrans();
|
||
try {
|
||
// 加锁读取订单,防止重复处理
|
||
$order = Db::name('order_record')->where('order_sn', $orderNo)->lock(true)->find();
|
||
if (!$order || $order['status'] == 3) {
|
||
Db::rollback();
|
||
return;
|
||
}
|
||
|
||
// 加锁读取用户
|
||
$user = Db::name('user')->where([
|
||
'id' => $order['user_id'],
|
||
'status' => 1,
|
||
'is_delete' => 0,
|
||
'agent' => 0,
|
||
])->lock(true)->find();
|
||
if (!$user) {
|
||
Db::rollback();
|
||
return;
|
||
}
|
||
|
||
$zdlIdArr = explode(',', $user['agent_parent_id_path']);
|
||
$zdlId = $zdlIdArr[0];
|
||
|
||
// 加锁读取总代理
|
||
$zdl = Db::name('user')->where([
|
||
'id' => $zdlId,
|
||
'status' => 1,
|
||
'is_delete' => 0,
|
||
'agent' => 1,
|
||
])->lock(true)->find();
|
||
|
||
if (!$zdl || $zdl['money'] < $order['money']) {
|
||
Db::rollback();
|
||
addLogToFile('代收结算失败:代理余额不足 order=' . $orderNo, 'alin_callback_error', 'alin');
|
||
return;
|
||
}
|
||
|
||
// 1. 更新订单状态为已完成
|
||
Db::name('order_record')->where('id', $order['id'])->update([
|
||
'back_money' => $data['real_amount'] ?: $data['amount'],
|
||
'back_time' => time(),
|
||
'status' => 3,
|
||
'agent_parent_id' => $user['agent_parent_id'],
|
||
'agent_parent_username' => $user['agent_parent_username'],
|
||
'zdl_id' => $zdl['id'],
|
||
'zdl_username' => $zdl['username'],
|
||
'zdl_money_before' => $zdl['money'],
|
||
'zdl_money_after' => $zdl['money'] - $order['money'],
|
||
'money_before' => $user['money'],
|
||
'money_after' => $user['money'] + $order['money'],
|
||
]);
|
||
|
||
// 2. 记录日志
|
||
Db::name('order_log')->insert([
|
||
'pay_channel_id' => 7,
|
||
'pay_channel_name' => 'alinPay',
|
||
'money' => $order['money'],
|
||
'create_time' => time(),
|
||
'remake' => '回调支付成功',
|
||
'order_id' => $order['id'],
|
||
]);
|
||
|
||
// 3. 写recharge流水表
|
||
Db::name('recharge')->insert([
|
||
'type' => 4,
|
||
'amount' => $order['money'],
|
||
'mode' => 1,
|
||
'agent_or_admin' => 3,
|
||
'controller_id' => $zdl['id'],
|
||
'controller_username' => $zdl['username'],
|
||
'controller_nickname' => $zdl['nickname'],
|
||
'controller_type' => '越南第三方充值平台充值,扣取总代理余分',
|
||
'controller_old_money' => $zdl['money'],
|
||
'controller_new_money' => $zdl['money'] - $order['money'],
|
||
'user_id' => $order['user_id'],
|
||
'user_type' => $user['agent'],
|
||
'user_agent_level' => 0,
|
||
'username_for' => $user['username'],
|
||
'nickname_for' => $user['nickname'],
|
||
'user_parent_id' => $user['agent_parent_id'],
|
||
'create_time' => time(),
|
||
'old_money' => $user['money'],
|
||
'new_money' => $user['money'] + $order['money'],
|
||
'controller_system' => 3,
|
||
'remake' => '越南AlinPay第三方充值成功',
|
||
]);
|
||
|
||
// 4. 扣代理余额
|
||
Db::name('user')->where('id', $zdl['id'])->update([
|
||
'money' => $zdl['money'] - $order['money'],
|
||
'last_recharge_out' => $order['money'],
|
||
'last_recharge_out_time' => time(),
|
||
'recharge_out_count' => $zdl['recharge_out_count'] + 1,
|
||
'recharge_out_total_amount' => $zdl['recharge_out_total_amount'] + $order['money'],
|
||
]);
|
||
|
||
// 5. 给用户加余额
|
||
Db::name('user')->where('id', $order['user_id'])->update([
|
||
'money' => $user['money'] + $order['money'],
|
||
'last_recharge' => $order['money'],
|
||
'last_recharge_time' => time(),
|
||
'recharge_count' => $user['recharge_count'] + 1,
|
||
'recharge_total_amount' => $user['recharge_total_amount'] + $order['money'],
|
||
]);
|
||
|
||
Db::commit();
|
||
addLogToFile('代收结算成功:order=' . $orderNo . ' money=' . $order['money'], 'alin_callback', 'alin');
|
||
} catch (\Exception $e) {
|
||
Db::rollback();
|
||
addLogToFile('代收结算异常:' . $e->getMessage(), 'alin_callback_error', 'alin');
|
||
}
|
||
}
|
||
}
|