浏览代码

fix to cli and and payments

ljoaquim 2 周之前
父节点
当前提交
7db0ee62f5
共有 7 个文件被更改,包括 172 次插入5 次删除
  1. 二进制
      bin/easycli
  2. 1 0
      bin/genial-cli
  3. 79 2
      controllers/RegisterCprController.php
  4. 4 1
      migrations/migrations_v1.sql
  5. 12 2
      models/CprModel.php
  6. 56 0
      models/PaymentModel.php
  7. 20 0
      services/PaymentService.php

二进制
bin/easycli


+ 1 - 0
bin/genial-cli

@@ -0,0 +1 @@
+Subproject commit ee0a402a2b12c23e471b01460752bf838a6e80c7

+ 79 - 2
controllers/RegisterCprController.php

@@ -2,22 +2,28 @@
 
 namespace Controllers;
 
+use Libs\BashExecutor;
 use Libs\ResponseLib;
 use Models\CprModel;
 use Models\StatusModel;
 use Psr\Http\Message\ServerRequestInterface;
 use Respect\Validation\Exceptions\ValidationException;
 use Respect\Validation\Validator as val;
+use Services\PaymentService;
 
 class RegisterCprController
 {
     private CprModel $cprModel;
     private StatusModel $statusModel;
+    private PaymentService $paymentService;
+
+    private const PIX_VALUE = '1000.00';
 
     public function __construct()
     {
         $this->cprModel = new CprModel();
         $this->statusModel = new StatusModel();
+        $this->paymentService = new PaymentService();
     }
 
     public function __invoke(ServerRequestInterface $request)
@@ -35,19 +41,90 @@ class RegisterCprController
             )->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 {
-            $record = $this->cprModel->create($body, $statusId);
+            $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($record, 'S_CREATED');
+        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;
     }
 }

+ 4 - 1
migrations/migrations_v1.sql

@@ -148,7 +148,9 @@ CREATE TABLE "cpr" (
     "cpr_mother_code" TEXT NOT NULL,
     "cpr_children_codes" TEXT NOT NULL, -- text array
     "status_id" INTEGER NOT NULL,
-    FOREIGN KEY ("status_id") REFERENCES "status" ("status_id")
+    "payment_id" INTEGER NOT NULL,
+    FOREIGN KEY ("status_id") REFERENCES "status" ("status_id"),
+    FOREIGN KEY ("payment_id") REFERENCES "payment" ("payment_id")
 );
 
 CREATE TABLE "commodities" (
@@ -263,6 +265,7 @@ CREATE TABLE "orderbook" (
 
 CREATE TABLE payment (
     "payment_id" SERIAL PRIMARY KEY,
+    "payment_external_id" TEXT NOT NULL,
     "status_id" INTEGER NOT NULL,
     "user_id" INTEGER NOT NULL,
     "payment_ts" INTEGER NOT NULL,

+ 12 - 2
models/CprModel.php

@@ -61,10 +61,10 @@ class CprModel
         $meta = $this->getColumnsMeta();
         unset($meta['cpr_id']);
 
-        return array_diff_key($meta, ['status_id' => true]);
+        return array_diff_key($meta, ['status_id' => true, 'payment_id' => true]);
     }
 
-    public function create(array $data, int $statusId): array
+    public function create(array $data, int $statusId, int $paymentId): array
     {
         $meta = $this->getColumnsMeta();
 
@@ -84,6 +84,13 @@ class CprModel
                 continue;
             }
 
+            if ($column === 'payment_id') {
+                $columns[] = '"payment_id"';
+                $placeholders[] = ':payment_id';
+                $params['payment_id'] = $paymentId;
+                continue;
+            }
+
             if (!array_key_exists($column, $data)) {
                 if ($info['nullable']) {
                     $columns[] = '"' . $column . '"';
@@ -125,6 +132,9 @@ class CprModel
         if (isset($record['status_id'])) {
             $record['status_id'] = (int)$record['status_id'];
         }
+        if (isset($record['payment_id'])) {
+            $record['payment_id'] = (int)$record['payment_id'];
+        }
 
         return $record;
     }

+ 56 - 0
models/PaymentModel.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace Models;
+
+class PaymentModel
+{
+    private \PDO $pdo;
+
+    public function __construct()
+    {
+        if (isset($GLOBALS['pdo']) && $GLOBALS['pdo'] instanceof \PDO) {
+            $this->pdo = $GLOBALS['pdo'];
+            return;
+        }
+
+        throw new \RuntimeException('Global PDO connection not initialized');
+    }
+
+    public function create(
+        string $paymentExternalId,
+        int $statusId,
+        int $userId,
+        string $paymentTs = '',
+        string $paymentE2e = '',
+        string $paymentFlag = ''
+    ): int {
+        $stmt = $this->pdo->prepare(
+            'INSERT INTO "payment" (
+                payment_external_id,
+                status_id,
+                user_id,
+                payment_ts,
+                payment_e2e,
+                payment_flag
+            ) VALUES (
+                :external_id,
+                :status_id,
+                :user_id,
+                :payment_ts,
+                :payment_e2e,
+                :payment_flag
+            ) RETURNING payment_id'
+        );
+
+        $stmt->execute([
+            'external_id' => $paymentExternalId,
+            'status_id' => $statusId,
+            'user_id' => $userId,
+            'payment_ts' => $paymentTs,
+            'payment_e2e' => $paymentE2e,
+            'payment_flag' => $paymentFlag,
+        ]);
+
+        return (int)$stmt->fetchColumn();
+    }
+}

+ 20 - 0
services/PaymentService.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Services;
+
+use Models\PaymentModel;
+
+class PaymentService
+{
+    private PaymentModel $paymentModel;
+
+    public function __construct()
+    {
+        $this->paymentModel = new PaymentModel();
+    }
+
+    public function createPendingPayment(string $externalId, int $statusId, int $userId): int
+    {
+        return $this->paymentModel->create($externalId, $statusId, $userId, '', '', '');
+    }
+}