pdo = $GLOBALS['pdo']; $this->cprModel = new CprModel(); $this->commodityModel = new CommodityModel(); $this->statusModel = new StatusModel(); $this->paymentModel = new PaymentModel(); $this->tokenCreateService = new TokenCreateService(); } public function __invoke(ServerRequestInterface $request) { $body = json_decode((string)$request->getBody(), true) ?? []; $cprPayload = isset($body['cpr']) && is_array($body['cpr']) ? $body['cpr'] : $body; $userId = isset($body['user_id']) ? (int)$body['user_id'] : 0; $companyId = isset($body['company_id']) ? (int)$body['company_id'] : 0; if ($userId <= 0 || $companyId <= 0) { $pair = $this->findAnyActiveUser(); $userId = $pair['user_id']; $companyId = $pair['company_id']; } try { $statusId = $this->statusModel->getIdByStatus('pending'); if ($statusId === null) { throw new \RuntimeException('Pending status not found'); } $paymentExternal = 'FAST_' . bin2hex(random_bytes(6)); $paymentId = $this->paymentModel->create( $paymentExternal, PaymentModel::STATUS_COMPLETED, $userId, time(), '', 'fast-track' ); $cpr = $this->cprModel->create($cprPayload, $statusId, $paymentId, $userId, $companyId); $inputs = $this->prepareTokenInputs($cpr, $companyId, $userId); $token = $this->tokenCreateService->createToken( $inputs['token_commodities_amount'], $inputs['token_commodities_value'], $inputs['token_uf'], $inputs['token_city'], (string)$cpr['cpr_id'], '', $inputs['wallet_id'], $inputs['chain_id'], $inputs['commodities_id'], (int)$cpr['cpr_id'], $userId ); $this->cprModel->updateTokenId((int)$cpr['cpr_id'], (int)$token['token_id']); return ResponseLib::sendOk([ 'message' => 'CPR registrada e token mintado (fast-track)', 'cpr_id' => (int)$cpr['cpr_id'], 'payment_id' => $paymentId, 'token_id' => (int)$token['token_id'], 'token_external_id' => $token['token_external_id'], 'tx_hash' => $token['tx_hash'], ], 'S_CPR_FAST'); } catch (\Throwable $e) { return ResponseLib::sendFail('Falha no fast-track: ' . $e->getMessage(), [], 'E_FAST_TRACK')->withStatus(500); } } private function findAnyActiveUser(): array { $stmt = $this->pdo->query("SELECT user_id, company_id FROM \"user\" WHERE user_flag = 'a' ORDER BY user_id LIMIT 1"); $row = $stmt->fetch(\PDO::FETCH_ASSOC); if (!$row) { throw new \RuntimeException('Nenhum usuário ativo encontrado'); } return ['user_id' => (int)$row['user_id'], 'company_id' => (int)$row['company_id']]; } private function prepareTokenInputs(array $cpr, int $companyId, int $userId): array { $wallet = $this->findWalletByCompanyId($companyId); $commoditiesName = $this->requireStringField($cpr, ['cpr_product_name'], 'cpr_product_name'); $commoditiesId = $this->resolveCommodityId($commoditiesName); $amount = $this->requireNumericField($cpr, ['cpr_product_quantity', 'cpr_issue_quantity'], 'quantidade do produto'); $value = $this->requireNumericField($cpr, ['cpr_issue_value', 'cpr_issue_financial_value'], 'valor do produto'); $uf = $this->requireStringField($cpr, ['cpr_deliveryPlace_state_acronym', 'cpr_issuers_state_acronym'], 'UF'); $city = $this->requireStringField($cpr, ['cpr_deliveryPlace_city_name', 'cpr_issuers_city_name'], 'cidade'); return [ 'token_commodities_amount' => $amount, 'token_commodities_value' => $value, 'token_uf' => $uf, 'token_city' => $city, 'wallet_id' => $wallet['wallet_id'], 'chain_id' => $wallet['chain_id'], 'commodities_id' => $commoditiesId, 'user_id' => $userId, ]; } private function findWalletByCompanyId(int $companyId): array { $stmt = $this->pdo->prepare('SELECT wallet_id, chain_id FROM "wallet" WHERE company_id = :company_id ORDER BY wallet_id ASC LIMIT 1'); $stmt->execute(['company_id' => $companyId]); $wallet = $stmt->fetch(\PDO::FETCH_ASSOC); if (!$wallet) { throw new \RuntimeException('Nenhuma carteira encontrada para a empresa informada'); } return ['wallet_id' => (int)$wallet['wallet_id'], 'chain_id' => (int)$wallet['chain_id']]; } private function resolveCommodityId(string $name): int { $id = $this->commodityModel->getIdByName($name); if ($id === null) { throw new \RuntimeException('Commodity não encontrada para o produto: ' . $name); } return $id; } private function requireStringField(array $cpr, array $candidates, string $label): string { foreach ($candidates as $field) { if (array_key_exists($field, $cpr)) { $v = $this->normalizeStringValue($cpr[$field]); if ($v !== '') { return $v; } } } throw new \InvalidArgumentException("Campo {$label} ausente ou inválido na CPR."); } private function requireNumericField(array $cpr, array $candidates, string $label): int { foreach ($candidates as $field) { if (array_key_exists($field, $cpr)) { $v = $this->normalizeNumericValue($cpr[$field]); if ($v !== null) { return $v; } } } throw new \InvalidArgumentException("Campo {$label} ausente ou inválido na CPR."); } private function normalizeStringValue($value): string { if (is_array($value)) { $value = reset($value); } if (!is_scalar($value)) { return ''; } $s = trim((string)$value); if ($s === '') { return ''; } $parts = preg_split('/\s*;\s*/', $s) ?: []; $first = $parts[0] ?? $s; return trim((string)$first); } private function normalizeNumericValue($value): ?int { if (is_array($value)) { $value = reset($value); } if (is_string($value)) { $value = str_replace([' ', ','], ['', '.'], $value); } if (is_numeric($value)) { return (int)round((float)$value); } return null; } }