|
|
@@ -3,22 +3,34 @@
|
|
|
namespace Controllers;
|
|
|
|
|
|
use Libs\ResponseLib;
|
|
|
+use Models\CommodityModel;
|
|
|
use Models\CprModel;
|
|
|
use Models\PaymentModel;
|
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
|
use Services\B3CprService;
|
|
|
+use Services\TokenCreateService;
|
|
|
|
|
|
class PaymentConfirmController
|
|
|
{
|
|
|
private PaymentModel $paymentModel;
|
|
|
private CprModel $cprModel;
|
|
|
private B3CprService $b3Service;
|
|
|
+ private CommodityModel $commodityModel;
|
|
|
+ private TokenCreateService $tokenCreateService;
|
|
|
+ private \PDO $pdo;
|
|
|
|
|
|
public function __construct()
|
|
|
{
|
|
|
+ if (!isset($GLOBALS['pdo']) || !$GLOBALS['pdo'] instanceof \PDO) {
|
|
|
+ throw new \RuntimeException('Global PDO connection not initialized');
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->pdo = $GLOBALS['pdo'];
|
|
|
$this->paymentModel = new PaymentModel();
|
|
|
$this->cprModel = new CprModel();
|
|
|
$this->b3Service = new B3CprService();
|
|
|
+ $this->commodityModel = new CommodityModel();
|
|
|
+ $this->tokenCreateService = new TokenCreateService();
|
|
|
}
|
|
|
|
|
|
public function __invoke(ServerRequestInterface $request)
|
|
|
@@ -61,10 +73,23 @@ class PaymentConfirmController
|
|
|
return ResponseLib::sendFail('cURL error during B3 CPR request', ['error' => $result['error']], 'E_EXTERNAL')->withStatus(502);
|
|
|
}
|
|
|
|
|
|
+ try {
|
|
|
+ $tokenResult = $this->createTokenFromCpr($cpr);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ return ResponseLib::sendFail(
|
|
|
+ 'Falha ao gerar token: ' . $e->getMessage(),
|
|
|
+ [],
|
|
|
+ 'E_TOKEN_CREATE'
|
|
|
+ )->withStatus(500);
|
|
|
+ }
|
|
|
+
|
|
|
return ResponseLib::sendOk([
|
|
|
- 'message' => 'CPR gerada com sucesso',
|
|
|
+ 'message' => 'CPR enviada e token criado com sucesso',
|
|
|
'payment_id' => $paymentId,
|
|
|
'b3_response' => $result['json'] ?? ($result['raw'] ?? null),
|
|
|
+ 'token_id' => $tokenResult['token_id'],
|
|
|
+ 'token_external_id' => $tokenResult['token_external_id'],
|
|
|
+ 'tx_hash' => $tokenResult['tx_hash'],
|
|
|
], 'S_CPR_SENT');
|
|
|
}
|
|
|
|
|
|
@@ -88,4 +113,195 @@ class PaymentConfirmController
|
|
|
|
|
|
return $token;
|
|
|
}
|
|
|
+
|
|
|
+ private function createTokenFromCpr(array $cpr): array
|
|
|
+ {
|
|
|
+ $inputs = $this->prepareTokenInputs($cpr);
|
|
|
+
|
|
|
+ return $this->tokenCreateService->createToken(
|
|
|
+ $inputs['token_commodities_amount'],
|
|
|
+ $inputs['token_commodities_value'],
|
|
|
+ $inputs['token_uf'],
|
|
|
+ $inputs['token_city'],
|
|
|
+ $inputs['token_content'],
|
|
|
+ $inputs['token_flag'],
|
|
|
+ $inputs['wallet_id'],
|
|
|
+ $inputs['chain_id'],
|
|
|
+ $inputs['commodities_id'],
|
|
|
+ $inputs['cpr_id'],
|
|
|
+ $inputs['user_id']
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array{
|
|
|
+ * token_commodities_amount:int,
|
|
|
+ * token_commodities_value:int,
|
|
|
+ * token_uf:string,
|
|
|
+ * token_city:string,
|
|
|
+ * token_content:string,
|
|
|
+ * token_flag:string,
|
|
|
+ * wallet_id:int,
|
|
|
+ * chain_id:int,
|
|
|
+ * commodities_id:int,
|
|
|
+ * cpr_id:int,
|
|
|
+ * user_id:int
|
|
|
+ * }
|
|
|
+ */
|
|
|
+ private function prepareTokenInputs(array $cpr): array
|
|
|
+ {
|
|
|
+ $cprId = (int)($cpr['cpr_id'] ?? 0);
|
|
|
+ if ($cprId <= 0) {
|
|
|
+ throw new \InvalidArgumentException('CPR sem identificador válido.');
|
|
|
+ }
|
|
|
+
|
|
|
+ $userId = (int)($cpr['user_id'] ?? 0);
|
|
|
+ if ($userId <= 0) {
|
|
|
+ throw new \InvalidArgumentException('CPR sem usuário associado.');
|
|
|
+ }
|
|
|
+
|
|
|
+ $companyId = (int)($cpr['company_id'] ?? 0);
|
|
|
+ if ($companyId <= 0) {
|
|
|
+ throw new \InvalidArgumentException('CPR sem empresa associada.');
|
|
|
+ }
|
|
|
+
|
|
|
+ $wallet = $this->findWalletByCompanyId($companyId);
|
|
|
+ $commoditiesName = $this->requireStringField($cpr, ['cpr_product_name'], 'cpr_product_name');
|
|
|
+ $commoditiesId = $this->resolveCommodityId($commoditiesName);
|
|
|
+
|
|
|
+ $tokenCommoditiesAmount = $this->requireNumericField(
|
|
|
+ $cpr,
|
|
|
+ ['cpr_product_quantity', 'cpr_issue_quantity'],
|
|
|
+ 'quantidade do produto'
|
|
|
+ );
|
|
|
+ $tokenCommoditiesValue = $this->requireNumericField(
|
|
|
+ $cpr,
|
|
|
+ ['cpr_issue_value', 'cpr_issue_financial_value'],
|
|
|
+ 'valor do produto'
|
|
|
+ );
|
|
|
+ $tokenUf = $this->requireStringField(
|
|
|
+ $cpr,
|
|
|
+ ['cpr_deliveryPlace_state_acronym', 'cpr_issuers_state_acronym'],
|
|
|
+ 'UF'
|
|
|
+ );
|
|
|
+ $tokenCity = $this->requireStringField(
|
|
|
+ $cpr,
|
|
|
+ ['cpr_deliveryPlace_city_name', 'cpr_issuers_city_name'],
|
|
|
+ 'cidade'
|
|
|
+ );
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'token_commodities_amount' => $tokenCommoditiesAmount,
|
|
|
+ 'token_commodities_value' => $tokenCommoditiesValue,
|
|
|
+ 'token_uf' => $tokenUf,
|
|
|
+ 'token_city' => $tokenCity,
|
|
|
+ 'token_content' => (string)$cprId,
|
|
|
+ 'token_flag' => '',
|
|
|
+ 'wallet_id' => $wallet['wallet_id'],
|
|
|
+ 'chain_id' => $wallet['chain_id'],
|
|
|
+ 'commodities_id' => $commoditiesId,
|
|
|
+ 'cpr_id' => $cprId,
|
|
|
+ '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
|
|
|
+ {
|
|
|
+ $commodityId = $this->commodityModel->getIdByName($name);
|
|
|
+ if ($commodityId === null) {
|
|
|
+ throw new \RuntimeException('Commodity não encontrada para o produto: ' . $name);
|
|
|
+ }
|
|
|
+
|
|
|
+ return $commodityId;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function requireStringField(array $cpr, array $candidates, string $label): string
|
|
|
+ {
|
|
|
+ foreach ($candidates as $field) {
|
|
|
+ if (!array_key_exists($field, $cpr)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $value = $this->normalizeStringValue($cpr[$field]);
|
|
|
+ if ($value !== '') {
|
|
|
+ return $value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $value = $this->normalizeNumericValue($cpr[$field]);
|
|
|
+ if ($value !== null) {
|
|
|
+ return $value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 '';
|
|
|
+ }
|
|
|
+
|
|
|
+ $stringValue = trim((string)$value);
|
|
|
+ if ($stringValue === '') {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ $parts = preg_split('/\s*;\s*/', $stringValue) ?: [];
|
|
|
+ $first = $parts[0] ?? $stringValue;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|