Ver código fonte

完成URS提取和余额检查功能实现

主要功能:
1. URS提取功能(UrsWithdrawWebhook)
   - 实现完整的提取处理逻辑
   - 支持用户映射查找和资金转移
   - 集成Transfer模块进行钻石扣除
   - 完善的错误处理和日志记录

2. URS余额检查功能(UrsCheckWebhook)
   - 实现余额查询和手续费计算
   - 支持提取前预检查
   - 返回详细的余额和费用信息
   - 智能判断余额是否充足

3. Transfer模块扩展
   - 新增TransferOutThirdPartyValidation验证类
   - 添加createTransferOutForThirdParty方法
   - 支持第三方应用跳过密码验证
   - 完善TransferThirdPartyService服务

4. 测试验证
   - 创建完整的测试脚本
   - 验证提取和余额检查功能
   - 测试多种场景和边界情况
   - 确保功能稳定可靠

技术特点:
- 模块化设计,代码结构清晰
- 完善的异常处理机制
- 详细的操作日志记录
- 支持签名验证和安全检查
- 精确的数值计算和格式转换
AI Assistant 6 meses atrás
pai
commit
07f34905e8

+ 131 - 0
AiWork/202506/181432-URS提取功能实现.md

@@ -0,0 +1,131 @@
+# URS提取功能实现
+
+## 任务概述
+实现URS系统的提取(withdraw)功能,允许URS用户从农场系统提取钻石到URS系统。
+
+## 实现内容
+
+### 1. 创建URS提取Webhook处理器
+**文件**: `ThirdParty/Urs/Webhook/UrsWithdrawWebhook.php`
+
+**功能**:
+- 处理URS系统发送的提取通知
+- 验证请求参数(user_id, amount, order_id)
+- 查找URS用户对应的农场用户
+- 调用Transfer模块完成资金转移
+- 返回提取结果
+
+**关键方法**:
+- `handler()`: 主处理方法,验证参数并调用核心逻辑
+- `processWithdrawNotification()`: 核心提取处理逻辑
+- `validateWithdrawRequest()`: 请求参数验证
+
+### 2. 创建第三方应用专用验证类
+**文件**: `app/Module/Transfer/Validations/TransferOutThirdPartyValidation.php`
+
+**功能**:
+- 专门为第三方应用转出请求设计
+- 跳过密码验证(第三方应用不需要用户密码)
+- 验证基础字段:transfer_app_id, user_id, amount等
+
+### 3. 扩展Transfer模块支持第三方应用
+**文件**: `app/Module/Transfer/Logics/TransferLogic.php`
+
+**新增方法**:
+- `createTransferOutForThirdParty()`: 第三方应用转出接口
+- `createTransferOutFromArrayForThirdParty()`: 第三方应用转出内部实现
+
+**特点**:
+- 使用专用验证类跳过密码验证
+- 保持与原有转出逻辑一致的业务流程
+- 支持手续费计算和收取
+
+### 4. 更新TransferThirdPartyService
+**文件**: `app/Module/Transfer/Services/TransferThirdPartyService.php`
+
+**修改**:
+- 将`createTransferOut`调用改为`createTransferOutForThirdParty`
+- 移除不再需要的密码参数处理
+
+## 测试验证
+
+### 测试脚本
+**文件**: `test_urs_withdraw_webhook.php`
+
+**功能**:
+- 模拟URS系统发送提取请求
+- 正确生成签名验证
+- 验证响应结果
+
+### 测试结果
+✅ **测试成功**
+- URS用户10003(对应农场用户39026)成功提取50钻石
+- 签名验证通过
+- 资金正确扣除
+- Transfer订单正常创建
+- 返回正确的订单ID和状态
+
+**测试数据**:
+```
+用户ID: 10003 (URS) -> 39026 (农场)
+提取金额: 50.0000 钻石
+订单ID: URS_WITHDRAW_TEST_1750257155
+返回订单ID: OUT20250618223235mQTN1p
+Transfer订单ID: 11
+```
+
+## 技术要点
+
+### 1. 签名验证
+- 使用与WebhookDispatchService一致的签名算法
+- 参数按字母顺序排序
+- 使用HMAC-SHA256生成签名
+
+### 2. 密码验证跳过
+- 创建专用验证类`TransferOutThirdPartyValidation`
+- 移除password字段的必需验证
+- 保持其他验证逻辑不变
+
+### 3. 错误处理
+- 完整的异常捕获和日志记录
+- 详细的错误信息返回
+- 支持调试和问题排查
+
+### 4. 数据一致性
+- 使用数据库事务确保操作原子性
+- 正确的资金流转记录
+- 完整的审计日志
+
+## 配置要求
+
+### Transfer应用配置
+- 应用ID: 2 (urs)
+- 关联第三方应用ID: 11 (out_id3字段)
+- 支持转出操作
+- 手续费配置(可选)
+
+### URS用户映射
+- 表: `kku_urs_promotion_user_mappings`
+- 字段: `urs_user_id` -> `farm_user_id`
+- 状态: `status = 1` (激活)
+
+## 部署说明
+
+1. **代码部署**: 确保所有新增文件和修改文件已部署
+2. **配置检查**: 验证Transfer应用配置正确
+3. **权限验证**: 确保Webhook路由可访问
+4. **测试验证**: 运行测试脚本验证功能正常
+
+## 后续优化
+
+1. **手续费配置**: 根据业务需求调整手续费率
+2. **限额控制**: 添加单次和日累计提取限额
+3. **风控机制**: 添加异常检测和风险控制
+4. **监控告警**: 添加关键指标监控和异常告警
+
+## 相关文档
+
+- Transfer模块文档
+- URS推广系统文档
+- Webhook处理机制文档
+- 第三方服务集成规范

+ 178 - 0
AiWork/202506/181445-URS余额检查功能实现.md

@@ -0,0 +1,178 @@
+# URS余额检查功能实现
+
+## 任务概述
+完成URS系统的余额检查(check)功能,允许URS系统查询用户在农场系统中的钻石余额和提取费用信息。
+
+## 实现内容
+
+### 1. 完善UrsCheckWebhook处理器
+**文件**: `ThirdParty/Urs/Webhook/UrsCheckWebhook.php`
+
+**功能**:
+- 处理URS系统发送的余额检查请求
+- 验证请求参数(user_id, amount)
+- 查找URS用户对应的农场用户
+- 获取用户钻石余额
+- 计算提取手续费
+- 检查余额是否足够提取
+- 返回详细的余额和费用信息
+
+**关键方法**:
+- `handler()`: 主处理方法,验证参数并调用核心逻辑
+- `processCheck()`: 核心余额检查逻辑
+- `validateCheckRequest()`: 请求参数验证
+
+### 2. 核心业务逻辑
+**processCheck方法实现**:
+
+1. **用户映射查找**: 根据URS用户ID查找对应的农场用户ID
+2. **手续费计算**: 使用TransferThirdPartyService计算提取手续费
+3. **余额获取**: 获取用户钻石余额(fund_id=2)
+4. **余额检查**: 判断余额是否足够支付本金+手续费
+5. **结果返回**: 返回详细的检查结果
+
+### 3. 返回数据格式
+```json
+{
+    "check": true/false,           // 是否允许提取
+    "diamond_balance": "49.9950",  // 钻石余额
+    "principal_total": "10.0000",  // 本金总数
+    "fee_total": "0.0000",         // 手续费总数
+    "required_total": "10.0000",   // 所需总数
+    "message": "余额充足,允许提取"  // 状态消息
+}
+```
+
+## 测试验证
+
+### 测试脚本
+**文件**: `test_urs_check_webhook.php`
+
+**功能**:
+- 模拟URS系统发送余额检查请求
+- 正确生成签名验证
+- 测试多种场景
+- 验证响应结果
+
+### 测试场景和结果
+
+#### 场景1:余额充足
+```
+用户ID: 10003 (URS) -> 39026 (农场)
+检查金额: 10.0000 钻石
+用户余额: 49.9950 钻石
+手续费: 0.0000 钻石
+所需总额: 10.0000 钻石
+结果: ✅ 允许提取
+```
+
+#### 场景2:余额不足
+```
+用户ID: 10003 (URS) -> 39026 (农场)
+检查金额: 1000000.0000 钻石
+用户余额: 49.9950 钻石
+手续费: 0.0000 钻石
+所需总额: 1000000.0000 钻石
+结果: ❌ 不允许提取(余额不足)
+```
+
+#### 场景3:零余额用户
+```
+用户ID: 88888 (URS) -> 39036 (农场)
+检查金额: 50.0000 钻石
+用户余额: 0.0000 钻石
+手续费: 0.0000 钻石
+所需总额: 50.0000 钻石
+结果: ❌ 不允许提取(余额不足)
+```
+
+## 技术要点
+
+### 1. 签名验证
+- 使用与WebhookDispatchService一致的签名算法
+- 参数按字母顺序排序
+- 使用HMAC-SHA256生成签名
+
+### 2. 余额计算
+- 钻石余额从存储格式(分)转换为显示格式(元)
+- 使用`number_format($balance / 10000, 4)`进行格式化
+- 保持4位小数精度
+
+### 3. 手续费计算
+- 调用TransferThirdPartyService::calculateWithdrawFee方法
+- 支持汇率转换和手续费率计算
+- 返回详细的费用信息
+
+### 4. 错误处理
+- 完整的异常捕获和日志记录
+- 详细的错误信息返回
+- 支持调试和问题排查
+
+### 5. 用户映射处理
+- 自动查找URS用户到农场用户的映射关系
+- 处理用户不存在的情况
+- 记录详细的查询日志
+
+## 配置要求
+
+### Transfer应用配置
+- 应用ID: 2 (urs)
+- 关联第三方应用ID: 11 (out_id3字段)
+- 手续费配置(当前为0%)
+- 汇率配置(当前为1:1)
+
+### URS用户映射
+- 表: `kku_urs_promotion_user_mappings`
+- 字段: `urs_user_id` -> `farm_user_id`
+- 状态: `status = 1` (激活)
+
+### 钻石账户
+- 资金类型ID: 2 (钻石)
+- 存储格式: 分(整数)
+- 显示格式: 元(4位小数)
+
+## 部署说明
+
+1. **代码部署**: 确保UrsCheckWebhook文件已更新
+2. **配置检查**: 验证Transfer应用配置正确
+3. **权限验证**: 确保Webhook路由可访问
+4. **测试验证**: 运行测试脚本验证功能正常
+
+## 日志记录
+
+### 成功日志
+```
+URS余额检查完成 {
+    "urs_user_id": "10003",
+    "farm_user_id": 39026,
+    "amount": "10.0000",
+    "diamond_balance": "49.9950",
+    "fee_amount": "0.0000",
+    "required_total": "10.0000",
+    "is_allowed": true
+}
+```
+
+### 警告日志
+```
+URS余额检查失败:未找到用户映射关系 {
+    "urs_user_id": "99999",
+    "amount": "50.0000"
+}
+```
+
+## 后续优化
+
+1. **缓存机制**: 添加余额查询缓存,提高响应速度
+2. **限额控制**: 添加单次和日累计提取限额检查
+3. **风控机制**: 添加异常检测和风险控制
+4. **监控告警**: 添加关键指标监控和异常告警
+5. **性能优化**: 优化数据库查询和计算逻辑
+
+## 相关文档
+
+- Transfer模块文档
+- URS推广系统文档
+- Webhook处理机制文档
+- 第三方服务集成规范
+- URS提取功能实现文档

+ 109 - 11
ThirdParty/Urs/Webhook/UrsCheckWebhook.php

@@ -4,9 +4,10 @@ namespace ThirdParty\Urs\Webhook;
 
 use App\Module\ThirdParty\Models\ThirdPartyService as ServiceModel;
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Log;
 
 /**
- * URS余额检查Webhook处理器
+ * URS转出 检查Webhook处理器
  *
  * 专门处理余额检查相关的Webhook通知
  */
@@ -69,21 +70,118 @@ class UrsCheckWebhook extends WebhookReceiver
             throw new \Exception('用户ID格式无效');
         }
 
-
+        // 验证金额
+        $amount = $request->input('amount');
+        if (!is_numeric($amount) || $amount <= 0) {
+            throw new \Exception('提取金额必须大于0');
+        }
     }
 
-    public function processCheck()
+    /**
+     * 处理余额检查的核心逻辑
+     *
+     * @param Request $request 请求对象
+     * @return array
+     * @throws \Exception
+     */
+    protected function processCheck(Request $request): array
     {
-        // 是否允许,钻石余额 ,本金总数,手续费总数,所需总数
+        try {
+            // 获取请求参数
+            $userId = $request->input('user_id');
+            $amount = $request->input('amount');
+
+            /**
+             * 三方应用ID
+             * @var int $thirdPartyAppId
+             */
+            $thirdPartyAppId = $this->service->id;
+
+            // 1. 根据URS用户ID获取农场用户ID
+            $farmUserId = \App\Module\UrsPromotion\Services\UrsUserMappingService::getFarmUserId($userId);
+
+            if (!$farmUserId) {
+                Log::warning("URS余额检查失败:未找到用户映射关系", [
+                    'urs_user_id' => $userId,
+                    'amount' => $amount,
+                ]);
+
+                return [
+                    'check' => false,
+                    'diamond_balance' => '0.0000',
+                    'principal_total' => $amount,
+                    'fee_total' => '0.0000',
+                    'required_total' => $amount,
+                    'message' => '用户未进入农场系统'
+                ];
+            }
 
-        return [
-            'check' => false,           // 是否允许
-            'diamond_balance' => 100,  // 钻石余额
-            'principal_total' => 500,  // 本金总数
-            'fee_total' => 50,         // 手续费总数
-            'required_total' => 550,   // 所需总数
-        ];
+            // 2. 使用TransferThirdPartyService计算提取费用
+            $feeInfo = \App\Module\Transfer\Services\TransferThirdPartyService::calculateWithdrawFee(
+                $thirdPartyAppId,
+                $amount
+            );
+
+            if (isset($feeInfo['error'])) {
+                Log::warning("URS余额检查失败:无法计算手续费", [
+                    'urs_user_id' => $userId,
+                    'farm_user_id' => $farmUserId,
+                    'amount' => $amount,
+                    'error' => $feeInfo['error']
+                ]);
+
+                return [
+                    'check' => false,
+                    'diamond_balance' => '0.0000',
+                    'principal_total' => $amount,
+                    'fee_total' => '0.0000',
+                    'required_total' => $amount,
+                    'message' => $feeInfo['error']
+                ];
+            }
 
+            // 3. 获取用户钻石余额
+            $userFundService = new \App\Module\Fund\Services\FundService($farmUserId, 2); // 2是钻石的fund_id
+            $diamondBalance = $userFundService->balance();
+            $diamondBalanceFormatted = number_format($diamondBalance / 10000, 4); // 转换为显示格式
+
+            // 4. 计算所需总金额(本金 + 手续费)
+            $principalAmount = $amount;
+            $feeAmount = $feeInfo['fee_amount'];
+            $requiredTotal = bcadd($principalAmount, $feeAmount, 4);
+
+            // 5. 检查余额是否足够
+            $requiredTotalInCents = bcmul($requiredTotal, '10000', 0); // 转换为存储格式(分)
+            $isAllowed = $diamondBalance >= $requiredTotalInCents;
+
+            Log::info("URS余额检查完成", [
+                'urs_user_id' => $userId,
+                'farm_user_id' => $farmUserId,
+                'amount' => $amount,
+                'diamond_balance' => $diamondBalanceFormatted,
+                'fee_amount' => $feeAmount,
+                'required_total' => $requiredTotal,
+                'is_allowed' => $isAllowed
+            ]);
+
+            return [
+                'check' => $isAllowed,
+                'diamond_balance' => $diamondBalanceFormatted,
+                'principal_total' => $principalAmount,
+                'fee_total' => $feeAmount,
+                'required_total' => $requiredTotal,
+                'message' => $isAllowed ? '余额充足,允许提取' : '余额不足,无法提取'
+            ];
+
+        } catch (\Exception $e) {
+            Log::error("URS余额检查处理异常", [
+                'urs_user_id' => $userId ?? 'unknown',
+                'amount' => $amount ?? 'unknown',
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+            throw $e;
+        }
     }
 
 }

+ 96 - 4
ThirdParty/Urs/Webhook/UrsWithdrawWebhook.php

@@ -5,6 +5,7 @@ namespace ThirdParty\Urs\Webhook;
 use App\Module\ThirdParty\Models\ThirdPartyService as ServiceModel;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\DB;
 
 /**
  * URS提取通知Webhook处理器
@@ -42,11 +43,102 @@ class UrsWithdrawWebhook extends WebhookReceiver
 
         // 验证必需字段
         $this->validateWithdrawRequest($request);
+
+        // 获取请求参数
+        $userId = $request->input('user_id');
+        $amount = $request->input('amount');
+        $orderId = $request->input('order_id');
+
         // 处理提取通知
-        $rorder_id = time();
-        return [
-            'rorder_id'=>$rorder_id
-        ];
+        return $this->processWithdrawNotification($userId, $amount, $orderId);
+    }
+
+    /**
+     * 处理提取通知的核心逻辑
+     *
+     * @param string $userId URS用户ID
+     * @param string $amount 提取金额
+     * @param string $orderId URS订单ID
+     * @return array
+     * @throws \Exception
+     */
+    protected function processWithdrawNotification(string $userId, string $amount, string $orderId): array
+    {
+        try {
+            /**
+             * 三方应用ID
+             * @var int $thirdPartyAppId
+             */
+            $thirdPartyAppId = $this->service->id;
+
+            // 1. 根据URS用户ID获取农场用户ID
+            $farmUserId = \App\Module\UrsPromotion\Services\UrsUserMappingService::getFarmUserId($userId);
+
+            if (!$farmUserId) {
+                Log::error("URS提取失败:未找到用户映射关系", [
+                    'urs_user_id' => $userId,
+                    'order_id'    => $orderId,
+                ]);
+                throw new \Exception("用户未进入农场系统,无法完成提取");
+            }
+
+            // 2. 开启数据库事务并使用Transfer模块完成提取钻石操作
+            $result = DB::transaction(function () use ($thirdPartyAppId, $farmUserId, $userId, $amount, $orderId) {
+                return \App\Module\Transfer\Services\TransferThirdPartyService::createWithdrawOrder(
+                    thirdPartyAppId: $thirdPartyAppId,
+                    farmUserId: $farmUserId,
+                    thirdPartyUserId: (string)$userId,
+                    thirdPartyAmount: $amount,
+                    remark: "URS提取 - 订单号: {$orderId}",
+                    callbackData: [
+                        'urs_order_id' => $orderId,
+                        'urs_user_id' => $userId,
+                        'source' => 'urs_withdraw_webhook'
+                    ]
+                );
+            });
+
+            if (is_string($result)) {
+                // 提取失败
+                Log::error("URS提取失败", [
+                    'urs_user_id' => $userId,
+                    'farm_user_id' => $farmUserId,
+                    'third_party_app_id' => $thirdPartyAppId,
+                    'amount' => $amount,
+                    'order_id' => $orderId,
+                    'error' => $result
+                ]);
+                throw new \Exception("提取失败: {$result}");
+            }
+
+            // 提取成功
+            Log::info("URS提取成功", [
+                'urs_user_id' => $userId,
+                'farm_user_id' => $farmUserId,
+                'third_party_app_id' => $thirdPartyAppId,
+                'amount' => $amount,
+                'order_id' => $orderId,
+                'transfer_order_id' => $result->id,
+                'rorder_id' => $result->out_order_id
+            ]);
+
+            return [
+                'rorder_id' => $result->out_order_id,
+                'transfer_order_id' => $result->id,
+                'actual_amount' => $result->actual_amount,
+                'status' => 'success'
+            ];
+
+        } catch (\Exception $e) {
+            Log::error("URS提取处理异常", [
+                'urs_user_id' => $userId,
+                'amount' => $amount,
+                'order_id' => $orderId,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+            throw $e;
+        }
     }
 
     /**

+ 152 - 0
app/Module/Transfer/Logics/TransferLogic.php

@@ -56,6 +56,158 @@ class TransferLogic
         return self::createTransferOutFromArray($data);
     }
 
+    /**
+     * 创建第三方应用转出订单(跳过密码验证)
+     *
+     * @param int $transferAppId 划转应用ID
+     * @param int $userId 用户ID
+     * @param string $amount 转出金额
+     * @param string|null $outUserId 外部用户ID
+     * @param string|null $remark 备注
+     * @param array $callbackData 回调数据
+     * @return TransferOrder
+     * @throws \Exception
+     */
+    public static function createTransferOutForThirdParty(
+        int $transferAppId,
+        int $userId,
+        string $amount,
+        ?string $outUserId = null,
+        ?string $remark = null,
+        array $callbackData = []
+    ): TransferOrder {
+        $data = [
+            'transfer_app_id' => $transferAppId,
+            'user_id' => $userId,
+            'amount' => $amount,
+            'out_user_id' => $outUserId,
+            'remark' => $remark,
+            'callback_data' => $callbackData,
+        ];
+
+        return self::createTransferOutFromArrayForThirdParty($data);
+    }
+
+    /**
+     * 创建第三方应用转出订单(内部实现,跳过密码验证)
+     *
+     * @param array $data 订单数据
+     * @return TransferOrder
+     * @throws \Exception
+     */
+    public static function createTransferOutFromArrayForThirdParty(array $data): TransferOrder
+    {
+        // 使用第三方应用专用验证类(跳过密码验证)
+        $validation = new \App\Module\Transfer\Validations\TransferOutThirdPartyValidation($data);
+        $validation->validated();
+
+        // 获取应用配置
+        $app = TransferApp::findOrFail($data['transfer_app_id']);
+        if (!$app->is_enabled) {
+            throw new \Exception('应用已禁用');
+        }
+
+        // 计算金额
+        $outAmount = (string) $data['amount'];
+        $amount = bcmul($outAmount, (string) $app->exchange_rate, 10);
+
+        // 计算手续费
+        $feeInfo = $app->calculateOutFee($amount);
+
+        // 生成外部订单ID
+        $outOrderId = self::generateOutOrderId('OUT');
+
+        // 创建订单
+        $order = TransferOrder::create([
+            'transfer_app_id' => $app->id,
+            'out_id' => $app->out_id2 ?? 0,
+            'out_order_id' => $outOrderId,
+            'out_user_id' => $data['out_user_id'] ?? null,
+            'user_id' => $data['user_id'],
+            'currency_id' => $app->currency_id,
+            'fund_id' => $app->fund_id,
+            'type' => TransferType::OUT,
+            'status' => TransferStatus::CREATED,
+            'out_amount' => $outAmount,
+            'amount' => $amount,
+            'exchange_rate' => $app->exchange_rate,
+            'fee_rate' => $feeInfo['fee_rate'],
+            'fee_amount' => $feeInfo['fee_amount'],
+            'actual_amount' => $feeInfo['actual_amount'],
+            'callback_data' => $data['callback_data'] ?? [],
+            'remark' => $data['remark'] ?? null,
+        ]);
+
+        // 验证fund_to_uid必须存在
+        if (empty($app->fund_to_uid)) {
+            $order->updateStatus(TransferStatus::FAILED, '应用配置错误:未配置转入目标账户');
+            throw new \Exception('应用配置错误:未配置转入目标账户(fund_to_uid)');
+        }
+
+        // 验证目标账户是否存在
+        $targetFundService = new FundService($app->fund_to_uid, $app->fund_id);
+        if (!$targetFundService->getAccount()) {
+            $order->updateStatus(TransferStatus::FAILED, '目标账户不存在');
+            throw new \Exception('目标账户不存在');
+        }
+
+        // 使用trade方法从用户转账到目标账户(实际到账金额)
+        $userFundService = new FundService($data['user_id'], $app->fund_id);
+        $tradeResult = $userFundService->trade(
+            $app->fund_to_uid,
+            $order->actual_amount, // 转出实际到账金额(扣除手续费后)
+            'transfer_out',
+            $order->id,
+            "转出到{$app->title}"
+        );
+
+        // 如果有手续费,处理手续费收取
+        if ($order->hasFee()) {
+            // 获取手续费收取账户UID(默认为1)
+            $feeAccountUid = $app->getFeeAccountUid();
+
+            $feeTradeResult = $userFundService->trade(
+                $feeAccountUid,
+                $order->fee_amount,
+                'transfer_out_fee',
+                $order->id,
+                "转出手续费-{$app->title}"
+            );
+
+            if (is_string($feeTradeResult)) {
+                \Illuminate\Support\Facades\Log::warning('Transfer out fee collection failed', [
+                    'order_id' => $order->id,
+                    'fee_amount' => $order->fee_amount,
+                    'fee_account_uid' => $feeAccountUid,
+                    'error' => $feeTradeResult
+                ]);
+            } else {
+                \Illuminate\Support\Facades\Log::info('Transfer out fee collected successfully', [
+                    'order_id' => $order->id,
+                    'fee_amount' => $order->fee_amount,
+                    'fee_account_uid' => $feeAccountUid,
+                    'configured_account' => $app->fee_account_uid
+                ]);
+            }
+        }
+
+        if (is_string($tradeResult)) {
+            $order->updateStatus(TransferStatus::FAILED, '资金转移失败: ' . $tradeResult);
+            throw new \Exception('余额不足或资金操作失败: ' . $tradeResult);
+        }
+
+        // 检查是否需要调用外部API
+        if (!empty($app->order_out_create_url)) {
+            // 配置了外部转出API,调用外部API处理
+            OrderLogic::processTransferOut($order);
+        } else {
+            // 没有配置外部API,直接完成订单
+            $order->updateStatus(TransferStatus::COMPLETED);
+        }
+
+        return $order;
+    }
+
     /**
      * 创建转出订单(内部实现)
      *

+ 5 - 11
app/Module/Transfer/Services/TransferThirdPartyService.php

@@ -67,14 +67,14 @@ class TransferThirdPartyService
 
             return TransferOrderDto::fromModel($order);
         } catch (\Exception $e) {
-            \Log::error('ThirdParty recharge order creation failed', [
+            \Illuminate\Support\Facades\Log::error('ThirdParty recharge order creation failed', [
                 'third_party_app_id' => $thirdPartyAppId,
                 'farm_user_id' => $farmUserId,
                 'third_party_user_id' => $thirdPartyUserId,
                 'amount' => $thirdPartyAmount,
                 'error' => $e->getMessage()
             ]);
-            
+
             return $e->getMessage();
         }
     }
@@ -114,17 +114,11 @@ class TransferThirdPartyService
                 return '该应用不支持提现功能或提现功能已被禁用';
             }
 
-            // 对于第三方提现,我们需要一个默认密码或者跳过密码验证
-            // 这里使用空密码,在实际业务逻辑中可能需要特殊处理
-            $password = '';
-
-            // 调用Transfer模块的转出逻辑
-            $order = TransferLogic::createTransferOut(
+            // 调用Transfer模块的第三方转出逻辑(跳过密码验证)
+            $order = TransferLogic::createTransferOutForThirdParty(
                 transferAppId: $transferApp->id,
                 userId: $farmUserId,
                 amount: $thirdPartyAmount,
-                password: $password,
-                googleCode: null,
                 outUserId: $thirdPartyUserId,
                 remark: $remark,
                 callbackData: array_merge($callbackData, [
@@ -136,7 +130,7 @@ class TransferThirdPartyService
 
             return TransferOrderDto::fromModel($order);
         } catch (\Exception $e) {
-            \Log::error('ThirdParty withdraw order creation failed', [
+            \Illuminate\Support\Facades\Log::error('ThirdParty withdraw order creation failed', [
                 'third_party_app_id' => $thirdPartyAppId,
                 'farm_user_id' => $farmUserId,
                 'third_party_user_id' => $thirdPartyUserId,

+ 41 - 0
app/Module/Transfer/Validations/TransferOutThirdPartyValidation.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace App\Module\Transfer\Validations;
+
+use UCore\ValidationCore;
+
+/**
+ * 第三方应用转出验证类
+ * 专门为第三方应用提取请求设计,跳过密码验证
+ */
+class TransferOutThirdPartyValidation extends ValidationCore
+{
+    /** @var \App\Module\Transfer\Models\TransferApp|null 转账应用对象,由验证器设置 */
+    public ?\App\Module\Transfer\Models\TransferApp $transfer_app = null;
+
+    /**
+     * 验证规则
+     */
+    public function rules($rules = []): array
+    {
+        return [
+            // 基础验证
+            ['transfer_app_id,user_id', 'required'],
+            ['transfer_app_id,user_id', 'integer', 'min' => 1],
+            ['amount', 'required'],
+            // 注意:这里不要求password字段,因为第三方应用不需要密码验证
+            ['google_code', 'string', 'size' => 6],
+            ['out_user_id', 'string', 'max' => 50],
+            ['remark', 'string', 'max' => 255],
+            ['callback_data', 'array'],
+        ];
+    }
+
+    /**
+     * 默认值
+     */
+    public function default(): array
+    {
+        return [];
+    }
+}