> ## 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.

# Ambientes e sandbox

> Como testar o fluxo inteiro, inclusive webhook, sem mover um centavo.

A API tem dois ambientes e uma URL só. Quem decide onde a requisição cai é o prefixo da
chave.

<Columns cols={2}>
  <Card title="Produção" icon="building-columns">
    Chave `ps_live_`.

    Cobrança de verdade, saldo de verdade, saque de verdade.
  </Card>

  <Card title="Sandbox" icon="flask">
    Chave `ps_test_`.

    Nada é enviado a banco nenhum. O PIX gerado não é pagável.
  </Card>
</Columns>

<Warning>
  O `brCode` do sandbox não é um código PIX de verdade. Ele volta como
  `SANDBOX_PIX_<paymentId>`, uma string sem valor nenhum: não escaneia, não cola em app
  de banco, ninguém paga por acidente.

  Se você renderizar isso num QR Code na sua tela de teste, o QR vai existir e não vai
  funcionar. É esperado.
</Warning>

Os dados são isolados de ponta a ponta. Uma cobrança criada com `ps_test_` não aparece
para `ps_live_`, e o saldo de sandbox é calculado só a partir das transações de teste.
Se o prefixo da chave não bater com o ambiente em que ela foi criada, a API responde
`401` em vez de escolher um ambiente por conta própria.

## Simulando pagamento

Como ninguém consegue pagar um PIX de sandbox, você marca como pago pela API:

<CodeGroup>
  ```bash Cobrança (psc_) theme={null}
  curl -X POST https://api.purincash.com/v1/sandbox/charges/psc_a1b2c3d4/simulate-paid \
    -H "Authorization: Bearer ps_test_sua_chave"
  ```

  ```bash Pagamento (psa_) theme={null}
  curl -X POST https://api.purincash.com/v1/sandbox/payments/psa_a1b2c3d4/simulate-paid \
    -H "Authorization: Bearer ps_test_sua_chave"
  ```
</CodeGroup>

O status vira `paid` e o webhook sai para a `callbackUrl` que você informou na criação,
exatamente como aconteceria em produção.

Não existe aprovação de ninguém no meio. A chamada só funciona com chave `ps_test_` e só
alcança cobrança da sua própria conta: com chave `ps_live_` a resposta é `403`.

<Tip>
  Chame `simulate-paid` **duas vezes** na mesma cobrança. O status não muda de novo, mas o
  webhook sai outra vez com o mesmo `X-Webhook-Id`. É o jeito mais barato de verificar se o
  seu handler é idempotente antes de precisar disso em produção.
</Tip>

<Note>
  No webhook de simulação o `event` é sempre `payment.paid`, mesmo quando você simula uma
  cobrança `psc_`. O payload é enxuto: `event`, `paymentId`, `amountCents`, `status`,
  `paidAt`, `customer`, `metadata` e `sandbox: true`.
</Note>

## Conferindo o resultado

<CardGroup cols={2}>
  <Card title="GET /v1/sandbox/wallet" icon="wallet" href="/api-reference/introducao">
    Saldo simulado, somado a partir das transações de teste.
  </Card>

  <Card title="GET /v1/sandbox/transactions" icon="list" href="/api-reference/introducao">
    Extrato do que você criou em sandbox, com paginação.
  </Card>
</CardGroup>

`GET /v1/wallet` também funciona com chave de teste. Nesse caso a resposta vem com
`"sandbox": true`, e `disputeBlocked` e `cryptoBalanceLtc` ficam sempre em zero.

## O que não roda em sandbox

<AccordionGroup>
  <Accordion title="Cartão de crédito" icon="credit-card">
    `POST /v1/card-payments` depende do provedor de checkout e não é simulado. Teste o
    cartão direto em produção com um valor baixo.
  </Accordion>

  <Accordion title="Litecoin" icon="bitcoin">
    `paymentMethod: "ltc"` responde `400` com chave de teste. A cotação e o endereço vêm
    da rede real, então não há como simular.
  </Accordion>

  <Accordion title="Disputas" icon="gavel">
    Os endpoints de `/v1/disputes` respondem `404` em sandbox. Contestação nasce de uma
    transação real.
  </Accordion>

  <Accordion title="Saques" icon="arrow-up-from-bracket">
    O saque em sandbox é registrado e dispara os eventos `withdrawal.test.requested` e
    `withdrawal.test.completed`, mas nenhum valor sai.
  </Accordion>
</AccordionGroup>

## Indo para produção

Troque a variável de ambiente com a chave. Nada mais muda: mesma URL, mesmos campos,
mesmos nomes de evento.

<Tip>
  Vale a pena rodar o mesmo teste automatizado nos dois ambientes, com a chave vindo de
  variável. Se o teste passa com `ps_test_` e quebra com `ps_live_`, o problema está em
  configuração de conta e não no seu código.
</Tip>
