|
|
@@ -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;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 创建转出订单(内部实现)
|
|
|
*
|