|
@@ -0,0 +1,208 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+
|
|
|
|
|
+namespace Controllers;
|
|
|
|
|
+
|
|
|
|
|
+use Libs\ResponseLib;
|
|
|
|
|
+use Models\CprModel;
|
|
|
|
|
+use Models\CommodityModel;
|
|
|
|
|
+use Models\StatusModel;
|
|
|
|
|
+use Models\PaymentModel;
|
|
|
|
|
+use Psr\Http\Message\ServerRequestInterface;
|
|
|
|
|
+use Services\TokenCreateService;
|
|
|
|
|
+
|
|
|
|
|
+class CprFastTrackController
|
|
|
|
|
+{
|
|
|
|
|
+ private \PDO $pdo;
|
|
|
|
|
+ private CprModel $cprModel;
|
|
|
|
|
+ private CommodityModel $commodityModel;
|
|
|
|
|
+ private StatusModel $statusModel;
|
|
|
|
|
+ private PaymentModel $paymentModel;
|
|
|
|
|
+ private TokenCreateService $tokenCreateService;
|
|
|
|
|
+
|
|
|
|
|
+ public function __construct()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!isset($GLOBALS['pdo']) || !$GLOBALS['pdo'] instanceof \PDO) {
|
|
|
|
|
+ throw new \RuntimeException('Global PDO connection not initialized');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $this->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;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|