UrsCheckWebhook.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <?php
  2. namespace ThirdParty\Urs\Webhook;
  3. use App\Module\ThirdParty\Models\ThirdPartyService as ServiceModel;
  4. use Illuminate\Http\Request;
  5. use Illuminate\Support\Facades\Log;
  6. /**
  7. * URS转出 检查Webhook处理器
  8. *
  9. * 专门处理余额检查相关的Webhook通知
  10. */
  11. class UrsCheckWebhook extends WebhookReceiver
  12. {
  13. /**
  14. * 构造函数
  15. *
  16. * @param string $serviceCode 服务代码
  17. * @param Request $request 请求对象
  18. * @param ServiceModel $service 服务配置对象
  19. */
  20. public function __construct(string $serviceCode, Request $request, ServiceModel $service)
  21. {
  22. parent::__construct($serviceCode, $request, $service);
  23. }
  24. /**
  25. * 处理余额检查通知
  26. *
  27. * @param string $action 操作类型(固定为check)
  28. * @param Request $request 请求对象
  29. * @return array
  30. * @throws \Exception
  31. */
  32. protected function handler(string $action, Request $request): array
  33. {
  34. // 验证操作类型
  35. if ($action !== 'check') {
  36. throw new \Exception("此处理器只能处理check操作,当前操作: {$action}");
  37. }
  38. // 验证必需字段
  39. $this->validateCheckRequest($request);
  40. // 处理余额检查通知
  41. return $this->processCheck($request);
  42. }
  43. /**
  44. * 验证余额检查请求
  45. *
  46. * @param Request $request 请求对象
  47. * @throws \Exception
  48. */
  49. protected function validateCheckRequest(Request $request): void
  50. {
  51. $requiredFields = [ 'user_id', 'amount' ];
  52. foreach ($requiredFields as $field) {
  53. if (!$request->has($field)) {
  54. throw new \Exception("缺少必需字段: {$field}");
  55. }
  56. }
  57. // 验证用户ID
  58. $userId = $request->input('user_id');
  59. if (!is_numeric($userId) || $userId <= 0) {
  60. throw new \Exception('用户ID格式无效');
  61. }
  62. // 验证金额
  63. $amount = $request->input('amount');
  64. if (!is_numeric($amount) || $amount < 0) {
  65. throw new \Exception('提取金额不能为负数');
  66. }
  67. }
  68. /**
  69. * 处理余额检查的核心逻辑
  70. *
  71. * @param Request $request 请求对象
  72. * @return array
  73. * @throws \Exception
  74. */
  75. protected function processCheck(Request $request): array
  76. {
  77. try {
  78. // 获取请求参数
  79. $userId = $request->input('user_id');
  80. $amount = $request->input('amount');
  81. /**
  82. * 三方应用ID
  83. *
  84. * @var int $thirdPartyAppId
  85. */
  86. $thirdPartyAppId = $this->service->id;
  87. // 1. 根据URS用户ID获取农场用户ID
  88. $farmUserId = \App\Module\UrsPromotion\Services\UrsUserMappingService::getFarmUserId($userId);
  89. if (!$farmUserId) {
  90. Log::warning("URS余额检查失败:未找到用户映射关系", [
  91. 'urs_user_id' => $userId,
  92. 'amount' => $amount,
  93. ]);
  94. return [
  95. 'check' => false,
  96. 'diamond_balance' => '0.0000',
  97. 'principal_total' => $amount,
  98. 'fee_total' => '0.0000',
  99. 'required_total' => $amount,
  100. 'message' => '用户未进入农场系统'
  101. ];
  102. }
  103. // 2. 获取用户钻石余额
  104. $userFundService = new \App\Module\Fund\Services\FundService($farmUserId, 2); // 2是钻石的fund_id
  105. $diamondBalance = $userFundService->balance();
  106. $diamondBalanceFormatted = number_format($diamondBalance, 4); // 直接格式化,无需转换
  107. // 3. 特殊处理:如果金额为0,只返回余额信息,不计算手续费
  108. if (bccomp($amount, '0', 4) === 0) {
  109. Log::info("URS余额查询(金额为0)", [
  110. 'urs_user_id' => $userId,
  111. 'farm_user_id' => $farmUserId,
  112. 'diamond_balance' => $diamondBalanceFormatted,
  113. ]);
  114. return [
  115. 'check' => true,
  116. 'diamond_balance' => $diamondBalanceFormatted,
  117. 'principal_total' => '0.0000',
  118. 'fee_total' => '0.0000',
  119. 'required_total' => '0.0000',
  120. 'message' => '余额查询成功'
  121. ];
  122. }
  123. // 6. 使用TransferThirdPartyService计算提取费用,传递用户ID以便URS推广模块计算正确的手续费率
  124. $feeDto = \App\Module\Transfer\Services\TransferThirdPartyService::calculateWithdrawFeeWithContext(
  125. $thirdPartyAppId,
  126. $amount,
  127. [ 'user_id' => $farmUserId ] // 传递农场用户ID,让URS推广模块能够根据房屋等级和达人等级计算手续费
  128. );
  129. if ($feeDto->hasError) {
  130. Log::warning("URS余额检查失败:无法计算手续费", [
  131. 'urs_user_id' => $userId,
  132. 'farm_user_id' => $farmUserId,
  133. 'amount' => $amount,
  134. 'internal_amount' => $feeDto->feeRate,
  135. 'error' => $feeDto->errorMessage
  136. ]);
  137. return [
  138. 'check' => false,
  139. 'diamond_balance' => $diamondBalanceFormatted,
  140. 'principal_total' => $feeDto->actualAmount,
  141. 'fee_total' => '0.0000',
  142. 'required_total' => $feeDto->totleAmount,
  143. 'message' => $feeDto->errorMessage
  144. ];
  145. }
  146. // 6. 检查余额是否足够(直接比较小数值)
  147. $isAllowed = bccomp($diamondBalance, $feeDto->totleAmount, 10) >= 0;
  148. Log::info("URS余额检查完成", [
  149. 'urs_user_id' => $userId,
  150. 'farm_user_id' => $farmUserId,
  151. 'amount' => $amount,
  152. 'diamond_balance' => $diamondBalanceFormatted,
  153. 'fee_amount' => $feeDto->feeAmount,
  154. 'required_total' => $feeDto->totleAmount,
  155. 'is_allowed' => $isAllowed
  156. ]);
  157. return [
  158. 'check' => $isAllowed,
  159. 'diamond_balance' => $diamondBalanceFormatted,
  160. 'principal_total' => $feeDto->actualAmount,
  161. 'fee_total' => $feeDto->feeAmount,
  162. 'required_total' => $feeDto->totleAmount,
  163. 'message' => $isAllowed ? '余额充足,允许提取' : '余额不足,无法提取'
  164. ];
  165. } catch (\Exception $e) {
  166. Log::error("URS余额检查处理异常", [
  167. 'urs_user_id' => $userId ?? 'unknown',
  168. 'amount' => $amount ?? 'unknown',
  169. 'error' => $e->getMessage(),
  170. 'trace' => $e->getTraceAsString()
  171. ]);
  172. throw $e;
  173. }
  174. }
  175. }