paymentModel = new PaymentModel(); $this->walletModel = new WalletModel(); $this->orderbookTransferModel = new OrderbookTransferModel(); $this->tokenTransferService = new TokenTransferService(); } public function __invoke(ServerRequestInterface $request) { $body = json_decode((string)$request->getBody(), true) ?? []; try { val::key('external_id', val::stringType()->notEmpty()) ->key('token_external_id', val::stringType()->notEmpty()) ->assert($body); } catch (ValidationException $e) { return ResponseLib::sendFail('Validation failed: ' . $e->getFullMessage(), [], 'E_VALIDATE')->withStatus(400); } $externalId = trim((string)$body['external_id']); $tokenExternalId = trim((string)$body['token_external_id']); $companyId = (int)($request->getAttribute('api_company_id') ?? 0); if ($companyId <= 0) { return ResponseLib::sendFail('Empresa autenticada não encontrada', [], 'E_VALIDATE')->withStatus(401); } try { $payment = $this->paymentModel->findByExternalId($externalId); } catch (\Throwable $e) { return ResponseLib::sendFail('Falha ao consultar pagamento: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500); } if (!$payment) { return ResponseLib::sendFail('Pagamento não encontrado', ['external_id' => $externalId], 'E_NOT_FOUND')->withStatus(404); } if ((int)$payment['status_id'] !== PaymentModel::STATUS_COMPLETED) { return ResponseLib::sendFail('Pagamento ainda não concluído', ['external_id' => $externalId, 'status_id' => $payment['status_id']], 'E_PAYMENT_PENDING')->withStatus(409); } try { $orderbook = $this->orderbookTransferModel->getByTokenExternalId($tokenExternalId); } catch (\Throwable $e) { return ResponseLib::sendFail('Falha ao consultar orderbook: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500); } if (!$orderbook) { return ResponseLib::sendFail('Orderbook não encontrado', ['token_external_id' => $tokenExternalId], 'E_NOT_FOUND')->withStatus(404); } if ((int)$orderbook['company_id'] !== $companyId) { return ResponseLib::sendFail('Orderbook não pertence à empresa autenticada', [], 'E_FORBIDDEN')->withStatus(403); } if ((int)$orderbook['status_id'] === OrderbookModel::STATUS_COMPLETED) { return ResponseLib::sendOk([ 'orderbook_id' => (int)$orderbook['orderbook_id'], 'token_external_id' => $tokenExternalId, 'message' => 'Orderbook já transferido anteriormente', ], 'S_TOKEN_ALREADY_TRANSFERRED'); } try { $wallet = $this->walletModel->getPrimaryWalletByCompanyId($companyId); } catch (\Throwable $e) { return ResponseLib::sendFail('Falha ao consultar carteira da empresa: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500); } if (!$wallet || empty($wallet['wallet_address'])) { return ResponseLib::sendFail('Carteira da empresa não encontrada', [], 'E_WALLET_NOT_FOUND')->withStatus(404); } $serverAddress = $this->resolveServerAddress(); try { $transferResult = $this->tokenTransferService->transferFrom($serverAddress, (string)$wallet['wallet_address'], $tokenExternalId); $this->orderbookTransferModel->markCompleted((int)$orderbook['orderbook_id']); } catch (\Throwable $e) { return ResponseLib::sendFail('Falha ao transferir token: ' . $e->getMessage(), [], 'E_TRANSFER')->withStatus(500); } return ResponseLib::sendOk([ 'orderbook_id' => (int)$orderbook['orderbook_id'], 'token_external_id' => $tokenExternalId, 'destination_address' => (string)$wallet['wallet_address'], 'transfer_output' => $transferResult['output'] ?? '', 'transfer_error' => $transferResult['error'] ?? '', ], 'S_TOKEN_TRANSFERRED'); } private function resolveServerAddress(): string { $address = $_ENV['SERVER_WALLET_ADDRESS'] ?? $_ENV['EASY_ADMIM_PUBLIC_KEY'] ?? ''; $trimmed = trim((string)$address); if ($trimmed === '') { throw new \RuntimeException('Endereço do servidor (SERVER_WALLET_ADDRESS) não configurado.'); } return $trimmed; } }