> ## Documentation Index
> Fetch the complete documentation index at: https://docs.purincash.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Produtos

> Existem dois tipos de produto com o mesmo nome. Aqui está qual é qual.

Esta é a parte da API que mais gera ticket de suporte, então vale começar pelo ponto:

<Warning>
  `GET /v1/products` pode devolver uma lista vazia com a sua loja cheia de produtos. Não é
  bug. São duas coleções diferentes.
</Warning>

<Columns cols={2}>
  <Card title="Produto de cobrança" icon="code" horizontal>
    Criado **pela API**, com `POST /v1/products`.

    Tem `priceCents` (número inteiro).

    É o único aceito em `POST /v1/payments` e `POST /v1/subscriptions`.

    Vive em `/v1/products`.
  </Card>

  <Card title="Produto da loja" icon="store" horizontal>
    Criado **no painel**, na sua loja do Discord.

    Tem `price` (texto tipo `"49,90"`), categoria, variações e estoque.

    Não serve para gerar cobrança pela API.

    Vive em `/v1/store/products`, somente leitura.
  </Card>
</Columns>

Os dois não vêm juntos por padrão porque a forma é diferente. Código fazendo
`priceCents / 100` viraria `NaN` ao receber um produto de loja, e quem repassasse um id
de loja para criar cobrança levaria `404` sem entender o motivo.

## Pedindo os dois de uma vez

| Chamada                          | Devolve                                   |
| -------------------------------- | ----------------------------------------- |
| `GET /v1/products`               | Só os de cobrança. É o padrão e não mudou |
| `GET /v1/products?include=all`   | Os dois, com um campo `type` em cada item |
| `GET /v1/products?include=store` | Só os da loja                             |
| `GET /v1/store/products`         | Só os da loja                             |

```json GET /v1/products?include=all theme={null}
{
  "products": [
    { "type": "api",   "_id": "60f7...", "name": "Plano Premium", "priceCents": 4990 },
    { "type": "store", "id": "70a2...", "name": "Conta Full", "price": "249,90", "variations": [] }
  ]
}
```

Todas aceitam `?includeInactive=true` para incluir os desativados.

<Tip>
  Se `GET /v1/products` vier vazio e você tiver produtos na loja, a resposta traz um campo
  `hint` apontando a rota certa. Ele é aditivo, então quem só lê `products` não sente
  diferença.
</Tip>

## Produtos de cobrança

```bash Criar theme={null}
curl -X POST https://api.purincash.com/v1/products \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Plano Premium",
    "description": "Acesso completo por 30 dias",
    "priceCents": 4990,
    "metadata": "{\"sku\":\"premium-01\"}"
  }'
```

<ParamField body="name" type="string" required>
  Até 200 caracteres.
</ParamField>

<ParamField body="priceCents" type="integer" required>
  Preço em centavos. Mínimo de 100, ou seja R\$ 1,00.
</ParamField>

<ParamField body="description" type="string">
  Até 500 caracteres.
</ParamField>

<ParamField body="currency" type="string" default="BRL">
  Produto em outra moeda é convertido para real na hora de gerar a cobrança.
</ParamField>

<ParamField body="metadata" type="string">
  String JSON de até 2 KB, devolvida sem alteração.
</ParamField>

O `PUT` aceita os mesmos campos, todos opcionais, mais `active` para ativar ou desativar.
Mande apenas o que quer mudar.

Limite de 500 produtos por conta e por ambiente.

<Note>
  Repare que o mínimo de um produto (R\$ 1,00) é diferente do mínimo de uma cobrança avulsa
  (R\$ 0,80, ajustável pela loja). Não é inconsistência: produto é catálogo, cobrança é
  transação.
</Note>

## Produtos da loja

Leitura apenas. Para criar ou editar, use o painel.

```bash theme={null}
curl "https://api.purincash.com/v1/store/products?includeInactive=true" \
  -H "Authorization: Bearer $PURINCASH_KEY"
```

```json theme={null}
{
  "products": [
    {
      "type": "store",
      "id": "70a2b1c3d4e5f6a7b8c9d0e1",
      "publicId": "plano-premium",
      "name": "Plano Premium",
      "description": "Acesso completo por 30 dias",
      "price": "49,90",
      "category": "Assinaturas",
      "active": true,
      "deliveryType": "automatica",
      "variations": [
        { "id": "a1b2...", "name": "Mensal", "price": "49,90", "stock": 42, "unlimited": false },
        { "id": "c3d4...", "name": "Anual", "price": "499,00", "stock": null, "unlimited": true }
      ]
    }
  ]
}
```

### Estoque ilimitado vem como `null`

<Warning>
  Variação com fornecedor externo, estoque via API ou estoque fictício infinito não tem um
  número para contar. Nesses casos `stock` vem `null` e `unlimited` vem `true`.

  O valor **não** é `0`, justamente porque zero você leria como esgotado e esconderia um
  produto que está à venda.
</Warning>

Na prática, a checagem correta é:

```js theme={null}
const disponivel = variacao.unlimited || variacao.stock > 0;
```

### O que não sai nessa rota

<Info>
  O conteúdo do estoque (a chave, a conta ou o link que o comprador recebe) e as
  instruções de entrega não são retornados. Variações de seller ainda não aprovadas também
  ficam de fora, porque não são vendáveis.
</Info>

Para ver o que foi entregue em um pagamento específico, existe a rota de
[entrega](/guias/entrega).
