gdias пре 2 недеља
родитељ
комит
ff2b995152
2 измењених фајлова са 283 додато и 7 уклоњено
  1. 278 6
      README.md
  2. 5 1
      bin/woovi

+ 278 - 6
README.md

@@ -1,9 +1,282 @@
-1. run ```./bin/setup``` 
+# Backend PHP – Orderbook & Pagamentos
 
-(deleta o banco existente, cria o banco de dados com as credenciais do ENV e aplica migrations)
-2. run ```composer install (instala dependencias)```
+Este documento reúne as instruções de execução e, principalmente, o contrato HTTP dos
+endpoints relacionados ao fluxo de orderbook/pagamentos. Use-o como referência ao
+implementar as chamadas no frontend.
 
-3. run ```X_LISTEN=127.0.0.1:8000 php public/index.php```
+## Como rodar localmente
+
+1. `./bin/setup` – recria o banco usando as credenciais do `.env` e aplica todas as migrations.
+2. `composer install` – instala dependências PHP.
+3. `X_LISTEN=127.0.0.1:8000 php public/index.php` – sobe a API localmente.
+
+> A aplicação também traz um `build` Deb (ver trecho fpm no final deste arquivo) caso seja
+> necessário gerar um pacote.
+
+## Convenções gerais
+
+- **Base URL**: `http://localhost:8000` (ajuste conforme ambiente).
+- **Autenticação**: todos os endpoints abaixo exigem JWT (`Authorization: Bearer <token>`),
+  exceto quando indicado.
+- **Formato de resposta**: todos usam `ResponseLib` e retornam o seguinte envelope:
+
+  ```json
+  {
+    "status": "ok" | "failed",
+    "msg": "[100] Request ok." | "<detalhe de erro>",
+    "code": "S_*" | "E_*",
+    "data": { ... } | []
+  }
+  ```
+
+## Fluxo resumido
+
+1. **/token/orderbook** – atualiza um token e cria uma ordem aberta.
+2. **/orderbook/filter** – frontend lista ordens por estado/commodity.
+3. **/orderbook/payment** – inicia o pagamento (gera PIX e IDs externos).
+4. **/orderbook/transfer** – após o pagamento concluir (Woovi → webhook atualiza status),
+   transfere o token para a carteira da empresa.
+5. **/b3/payment/confirm** – confirma status do pagamento associado à CPR e dispara registro
+   na B3/criação de token.
+
+As seções a seguir detalham os contratos JSON de cada etapa.
+
+---
+
+### 1. `POST /token/orderbook`
+Cria uma ordem no orderbook para um token existente (atualiza o valor do token e registra a
+ordem como `STATUS_OPEN`).
+
+| Campo              | Tipo / Regra                                   |
+|--------------------|------------------------------------------------|
+| `cpr_id`           | inteiro positivo                               |
+| `value`            | numérico (será arredondado para inteiro)       |
+| `state`            | string não vazia (UF, ex.: `"SP"`)             |
+| `commodity_type`   | string não vazia (ex.: `"SOJA"`)               |
+| `token_external_id`| string não vazia – precisa bater com o token   |
+
+**Exemplo de request**
+```json
+{
+  "cpr_id": 42,
+  "value": 150000.75,
+  "state": "sp",
+  "commodity_type": "SOJA",
+  "token_external_id": "TOKEN_ABC123"
+}
+```
+
+**Sucesso (`200 – S_ORDERBOOK_CREATED`)**
+```json
+{
+  "status": "ok",
+  "msg": "[100] Request ok.",
+  "code": "S_ORDERBOOK_CREATED",
+  "data": {
+    "message": "Token atualizado e ordem registrada com sucesso",
+    "token_id": 99,
+    "orderbook_id": 321
+  }
+}
+```
+
+**Principais erros**
+- `400 – E_VALIDATE`: falha de validação (`cpr_id`, campos vazios etc.).
+- `404 – E_TOKEN_NOT_FOUND`: nenhum token encontrado para a CPR informada.
+- `409 – E_TOKEN_MISMATCH`: o `token_external_id` informado não corresponde ao da CPR.
+- `500 – E_ORDERBOOK`: falha ao atualizar token/criar ordem.
+
+---
+
+### 2. `POST /orderbook/filter`
+Lista ordens abertas por estado e tipo de commodity.
+
+| Campo            | Tipo / Regra                  |
+|------------------|-------------------------------|
+| `state`          | string não vazia              |
+| `commodity_type` | string não vazia              |
+
+**Request**
+```json
+{
+  "state": "SP",
+  "commodity_type": "SOJA"
+}
+```
+
+**Sucesso (`200`)**
+- `S_ORDERBOOK_FILTER`: quando há ordens.
+- `S_ORDERBOOK_EMPTY`: lista vazia.
+
+```json
+{
+  "status": "ok",
+  "msg": "[100] Request ok.",
+  "code": "S_ORDERBOOK_FILTER",
+  "data": {
+    "state": "SP",
+    "commodity_type": "SOJA",
+    "orders": [
+      {
+        "orderbook_id": 321,
+        "orderbook_amount": "1000",
+        "token_external_id": "TOKEN_ABC123",
+        "status_id": 0,
+        "token_commodities_value": 150000,
+        "token_commodities_amount": 1000,
+        "chain_id": 1,
+        "wallet_id": 10
+      }
+    ]
+  }
+}
+```
+
+**Erros comuns**
+- `400 – E_VALIDATE`: campos ausentes/invalidos.
+- `500 – E_DATABASE`: erro ao consultar o orderbook.
+
+---
+
+### 3. `POST /orderbook/payment`
+Inicia o processo de pagamento para uma ordem aberta. Retorna o código PIX/ID externo para o
+frontend exibir ao usuário.
+
+| Campo          | Tipo / Regra      |
+|----------------|-------------------|
+| `orderbook_id` | inteiro positivo  |
+
+**Request**
+```json
+{
+  "orderbook_id": 321
+}
+```
+
+**Sucesso (`200 – S_ORDERBOOK_PAYMENT`)**
+```json
+{
+  "status": "ok",
+  "msg": "[100] Request ok.",
+  "code": "S_ORDERBOOK_PAYMENT",
+  "data": {
+    "orderbook_id": 321,
+    "payment_id": 555,
+    "payment_code": "000201...",
+    "payment_external_id": "PAY_a1b2c3d4",
+    "token_external_id": "TOKEN_ABC123"
+  }
+}
+```
+
+**Erros possíveis**
+- `400 – E_VALIDATE`: `orderbook_id` inválido.
+- `404 – E_NOT_FOUND`: orderbook inexistente.
+- `409 – E_ORDERBOOK_STATUS`: ordem não está `STATUS_OPEN`.
+- `422 – E_TOKEN_VALUE`: valor calculado do token ≤ 0.
+- `500 – E_DATABASE` ou `E_PAYMENT`: problemas ao ler base ou iniciar o pagamento.
+
+---
+
+### 4. `POST /orderbook/transfer`
+Verifica se o pagamento (Woovi) foi concluído e transfere o token para a carteira da empresa
+autenticada. Deve ser chamado após o webhook da Woovi marcar o pagamento como concluído.
+
+| Campo              | Tipo / Regra                |
+|--------------------|-----------------------------|
+| `external_id`      | string não vazia (ID Woovi) |
+| `token_external_id`| string não vazia            |
+
+**Request**
+```json
+{
+  "external_id": "PAY_a1b2c3d4",
+  "token_external_id": "TOKEN_ABC123"
+}
+```
+
+**Sucesso**
+- `200 – S_TOKEN_TRANSFERRED`: transferência executada e orderbook marcado como concluído.
+- `200 – S_TOKEN_ALREADY_TRANSFERRED`: ordem já estava concluída anteriormente.
+
+```json
+{
+  "status": "ok",
+  "msg": "[100] Request ok.",
+  "code": "S_TOKEN_TRANSFERRED",
+  "data": {
+    "orderbook_id": 321,
+    "token_external_id": "TOKEN_ABC123",
+    "destination_address": "0x8AC9...",
+    "transfer_output": "Transfer success",
+    "transfer_error": ""
+  }
+}
+```
+
+**Erros principais**
+- `400 – E_VALIDATE`: body inválido.
+- `401 – E_VALIDATE`: empresa não encontrada no contexto JWT.
+- `403 – E_FORBIDDEN`: orderbook pertence a outra empresa.
+- `404 – E_NOT_FOUND`: pagamento ou orderbook inexistente / `E_WALLET_NOT_FOUND` para carteira.
+- `409 – E_PAYMENT_PENDING`: pagamento ainda não tem `status_id = 1`.
+- `500 – E_DATABASE` ou `E_TRANSFER`: falhas ao consultar dados ou executar o comando de transferência.
+
+---
+
+### 5. `POST /b3/payment/confirm`
+Confirma o status do pagamento ligado a uma CPR (usa `payment_id`) e, se concluído, envia a
+CPR para a B3 e cria o token correspondente. Necessita header `Authorization` (JWT) e pode
+receber o token B3 via body ou headers customizados (`X-B3-Authorization`).
+
+| Campo             | Tipo / Regra                                  |
+|-------------------|-----------------------------------------------|
+| `payment_id`      | inteiro positivo                              |
+| `b3_access_token` | opcional (string). Pode vir também via header |
+
+**Request**
+```json
+{
+  "payment_id": 555,
+  "b3_access_token": "Bearer ..."
+}
+```
+
+**Sucesso (`200 – S_CPR_SENT`)**
+```json
+{
+  "status": "ok",
+  "msg": "[100] Request ok.",
+  "code": "S_CPR_SENT",
+  "data": {
+    "message": "CPR enviada e token criado com sucesso",
+    "payment_id": 555,
+    "b3_response": {"status": "OK"},
+    "token_id": 99,
+    "token_external_id": "TOKEN_ABC123",
+    "tx_hash": "0x..."
+  }
+}
+```
+
+**Erros principais**
+- `400 – E_VALIDATE`: `payment_id` ausente/≤0.
+- `404 – E_NOT_FOUND`: pagamento inexistente / `E_CPR_NOT_FOUND` (sem CPR ligada).
+- `409 – E_PAYMENT_PENDING`: pagamento ainda pendente (`status_id = 0`).
+- `409 – E_PAYMENT_STATUS`: status diferente de concluído.
+- `502 – E_EXTERNAL`: falha ao comunicar com a B3.
+- `500 – E_TOKEN_CREATE` / `E_CPR_UPDATE`: problemas internos ao gerar token ou ligar CPR → token.
+
+---
+
+## Observações adicionais
+
+- O webhook da Woovi (`POST /woovi/webhook`) marca o pagamento como concluído (`status_id = 1`).
+  Esse passo é obrigatório para que `/orderbook/transfer` aceite a transferência.
+- Todos os valores monetários são tratados em inteiros (centavos) após arredondamento.
+- Utilize os `code` (`S_*`/`E_*`) para tratar estados no frontend sem depender apenas de mensagens.
+
+## Geração de pacote Deb (opcional)
 
 ```
 fpm -s dir -t deb \
@@ -19,5 +292,4 @@ fpm -s dir -t deb \
   --config-files /etc/php-api \
   --depends "php8.2-cli | php-cli (>= 8.2)" \
   opt/php-api etc/php-api var/log/php-api
-Created package {:path=>"php-api_1.0.0_all.deb"}
-```
+Created package {:path=>"php-api_1.0.0_all.deb"}

+ 5 - 1
bin/woovi

@@ -48,10 +48,14 @@ class TokenModel
                        t.chain_id,
                        t.commodities_id,
                        t.cpr_id,
-                       t.user_id
+                       t.user_id,
+                       c.cpr_product_name
                 FROM "token" t
                 INNER JOIN "wallet" w ON w.wallet_id = t.wallet_id
+                LEFT JOIN "cpr" c ON c.cpr_id = t.cpr_id
+                LEFT JOIN "orderbook" o ON o.token_id = t.token_id AND o.status_id = 0
                 WHERE w.company_id = :company_id
+                  AND o.orderbook_id IS NULL
                 ORDER BY t.token_id';
         $stmt = $this->pdo->prepare($sql);
         $stmt->execute(['company_id' => $companyId]);