cprModel = new CprModel(); $this->statusModel = new StatusModel(); $this->paymentService = new PaymentService(); } public function __invoke(ServerRequestInterface $request) { $body = json_decode((string)$request->getBody(), true) ?? []; try { val::key('cpr_children_codes', val::arrayType()->notEmpty()->each(val::stringType()->notEmpty())) ->assert($body); } catch (ValidationException $e) { return ResponseLib::sendFail( 'Validation failed: ' . $e->getFullMessage(), [], 'E_VALIDATE' )->withStatus(400); } $userId = (int)($request->getAttribute('api_user_id') ?? 0); if ($userId <= 0) { return ResponseLib::sendFail('Authenticated user not found', [], 'E_VALIDATE')->withStatus(401); } $statusId = $this->statusModel->getIdByStatus('pending'); if ($statusId === null) { return ResponseLib::sendFail('Pending status not found', [], 'E_DATABASE')->withStatus(500); } try { $pixData = $this->generateDynamicQrcode(); } catch (\Throwable $e) { return ResponseLib::sendFail('Failed to generate PIX QR Code: ' . $e->getMessage(), [], 'E_INTERNAL')->withStatus(500); } try { $paymentId = $this->paymentService->createPendingPayment($pixData['item_id'], $statusId, $userId); } catch (\Throwable $e) { return ResponseLib::sendFail('Failed to create payment record: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500); } try { $record = $this->cprModel->create($body, $statusId, $paymentId); } catch (\InvalidArgumentException $e) { return ResponseLib::sendFail($e->getMessage(), [], 'E_VALIDATE')->withStatus(400); } catch (\Throwable $e) { return ResponseLib::sendFail('Failed to create CPR: ' . $e->getMessage(), [], 'E_DATABASE')->withStatus(500); } return ResponseLib::sendOk([ 'cpr' => $record, 'payment' => [ 'payment_id' => $paymentId, 'payment_external_id' => $pixData['item_id'], 'status_id' => $statusId, ], 'pix' => [ 'qrcode_url' => $pixData['qrcode_url'], ] ], 'S_CREATED'); } private function generateDynamicQrcode(): array { $cliPath = dirname(__DIR__) . '/bin/genial-cli/genial-cli'; if (!is_file($cliPath) || !is_executable($cliPath)) { throw new \RuntimeException('genial-cli executable not found or not executable'); } $amount = self::PIX_VALUE; $command = sprintf('%s qrcodedynamic %s', escapeshellarg($cliPath), escapeshellarg($amount)); $result = BashExecutor::run($command, 60); if (($result['exitCode'] ?? 1) !== 0) { $message = $result['error'] ?: $result['output'] ?: 'Unknown error'; throw new \RuntimeException($message); } $output = $result['output'] ?? ''; $itemId = $this->extractCliValue('itemId', $output); $qrcodeUrl = $this->extractCliValue('qrcodeURL', $output); if (!$itemId || !$qrcodeUrl) { throw new \RuntimeException('Unable to parse itemId or qrcodeURL from genial-cli output'); } return [ 'item_id' => $itemId, 'qrcode_url' => $qrcodeUrl, ]; } private function extractCliValue(string $key, string $content): ?string { $pattern = sprintf('#%s\s*[:=]\s*(?:"([^"]+)"|\' . "'" . '([^\' . "'" . ']+)\' . "'" . '|([^\s,{}]+))#i', preg_quote($key, '#')); if (preg_match($pattern, $content, $matches)) { foreach (array_slice($matches, 1) as $match) { if ($match !== '' && $match !== null) { return trim($match); } } } return null; } }