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'); } } }