# Autenticação e permissões (/api/autenticacao)
Toda chamada leva a sua chave de API, de um destes dois jeitos:
```bash
# No header x-api-key
curl https://api.wysepay.com.br/api/v1/balance \
-H "x-api-key: wyp_test_sua_chave"
# Ou como Bearer
curl https://api.wysepay.com.br/api/v1/balance \
-H "Authorization: Bearer wyp_test_sua_chave"
```
Sem a chave, ou com uma chave revogada, a resposta é `401`.
## Teste e produção [#teste-e-produção]
O modo é definido pela chave, não pelo endereço: as duas usam `https://api.wysepay.com.br`.
| | Chave de teste | Chave de produção |
| --------------------------- | ---------------------------- | ----------------------------- |
| Começa com | `wyp_test_` | `wyp_` |
| Dinheiro real | Nunca | Sim |
| Precisa de conta verificada | Não | Sim |
| Pagamentos e saques | [Simulados](/api/modo-teste) | Reais |
| Webhooks | Só para endpoints de teste | Só para endpoints de produção |
| `livemode` nas respostas | `false` | `true` |
Os dados de teste ficam separados dos reais: uma chave de teste não enxerga cobranças, clientes ou saques de produção, e o contrário também vale.
## Criar uma chave [#criar-uma-chave]
1. No [painel](https://app.wysepay.com.br), vá em **Desenvolvedores → Chaves de API**.
2. Para uma chave de teste, ligue antes o **Modo teste** no topo da tela.
3. Dê um nome que diga onde ela será usada (ex.: "Servidor da loja"), escolha as permissões e, se quiser, os IPs.
4. Clique em **Criar chave** e copie na hora: ela aparece uma única vez.
Chaves de produção só podem ser criadas depois que a conta é verificada. Veja [Criar conta e liberar produção](/painel/criar-conta).
## Permissões [#permissões]
Cada chave tem permissões por recurso. Dê a cada sistema só o que ele usa: um painel de BI, por exemplo, só precisa ler.
| Recurso | Ler | Criar e alterar |
| ------------------ | -------------------- | --------------------- |
| Cobranças Pix | | `charges:write` |
| Transações | `transactions:read` | |
| Links de pagamento | `payment_links:read` | `payment_links:write` |
| Clientes | `customers:read` | `customers:write` |
| Saldo | `balance:read` | |
| Saques | `withdrawals:read` | `withdrawals:write` |
* **Chaves novas** vêm com tudo, **menos `withdrawals:write`** (sacar).
* **Chaves criadas antes das permissões** ficaram com tudo, menos sacar: as integrações continuam funcionando.
* **Mudar as permissões** vale na hora: em **Chaves de API**, clique no ícone de ajustes da chave.
Sem a permissão da rota, a resposta é `403` com `code: "API_KEY_PERMISSION"`, dizendo qual falta. A permissão de cada rota aparece na [referência](/api/referencia/gerar-pix).
## IPs permitidos [#ips-permitidos]
Você pode limitar a chave aos IPs do seu servidor. Com a lista preenchida, uma chamada de outro IP responde `403` com `code: "API_KEY_IP"`, mesmo com a chave certa.
* Um IP por linha, IPv4 ou IPv6.
* Faixas em CIDR: `203.0.113.0/24` libera de `203.0.113.0` a `203.0.113.255`.
* Lista vazia: qualquer IP.
Uma chave com `withdrawals:write` tira dinheiro da conta. Libere só o IP do servidor que a usa e defina um limite diário. Veja [Saques](/api/saques).
## Guarde a chave no servidor [#guarde-a-chave-no-servidor]
Quem tem a chave age em nome da sua conta, dentro das permissões dela. A chave deve ficar só no seu servidor, numa variável de ambiente, nunca no código do site, do aplicativo ou num repositório.
Se uma chave vazar, revogue em **Chaves de API** e crie outra. A chave revogada para de funcionar na hora.
# Clientes (/api/clientes)
Cada cliente é identificado pelo CPF ou CNPJ: há um por documento, em cada modo (teste e produção). Ele é criado sozinho quando alguém paga ou preenche o checkout, e você também pode cadastrar e consultar pela API.
## Cadastrar [#cadastrar]
[`POST /api/v1/customers`](/api/referencia/cadastrar-cliente) (permissão `customers:write`):
```bash
curl -X POST https://api.wysepay.com.br/api/v1/customers \
-H "x-api-key: wyp_test_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"name": "Ana Souza",
"document": "123.456.789-09",
"email": "ana@exemplo.com.br",
"phone": "11912345678",
"address": { "zipCode": "01310-100", "number": "1000" }
}'
```
Se já existe um cliente com esse CPF ou CNPJ, ele é **atualizado** em vez de duplicado. Campos que você não envia ficam como estavam. Um documento inválido responde `400`.
## Achar pelo CPF ou CNPJ [#achar-pelo-cpf-ou-cnpj]
```bash
curl "https://api.wysepay.com.br/api/v1/customers?document=12345678909" \
-H "x-api-key: wyp_test_sua_chave"
```
A resposta é uma [lista](/api/paginacao) com no máximo um cliente.
## Atualizar [#atualizar]
[`PATCH /api/v1/customers/{id}`](/api/referencia/atualizar-cliente) muda só os campos enviados. O CPF ou CNPJ não muda.
## O que fica de fora [#o-que-fica-de-fora]
As notas internas e as tags que você escreve no painel não aparecem na API.
# Cobranças Pix (/api/cobrancas-pix)
Uma cobrança Pix é o que o cliente paga: um QR Code e um código copia e cola com o valor certo. Você cria pela API e mostra na sua tela.
## Criar [#criar]
[`POST /api/v1/charges/pix`](/api/referencia/gerar-pix) com:
| Campo | Obrigatório | O que é |
| ------------------ | ----------- | --------------------------------------------------------------------------------------------- |
| `amountCents` | Sim | Valor em centavos. Mínimo `500` (R$ 5,00). |
| `description` | Sim | O que está sendo cobrado (3 a 180 caracteres). Aparece para o pagador. |
| `customer` | Sim | Quem paga: `name`, `email`, `phone` (com DDD), `documentType` (`CPF` ou `CNPJ`) e `document`. |
| `customer.address` | Não | Endereço, salvo no cadastro do cliente. |
| `expiresAt` | Não | Até quando o Pix aceita pagamento (ISO 8601). |
| `metadata` | Não | Dados seus, como o id do pedido. Voltam iguais na consulta e nos webhooks. |
O CPF ou CNPJ é validado pelos dígitos. Um documento inválido responde `400` e a cobrança não é criada.
Use sempre o header `idempotency-key` (ex.: o id do pedido). Se a chamada for repetida por uma falha de rede, a mesma cobrança volta, sem duplicar. Veja [Idempotência](/api/limites-e-idempotencia#idempotência).
## Mostrar ao cliente [#mostrar-ao-cliente]
A resposta traz em `pix`:
* `copyPaste`: o código copia e cola. Mostre com um botão de copiar.
* `qrCode`: o mesmo código, para você gerar a imagem do QR Code com qualquer biblioteca de QR.
Mostre também o prazo: `expiresAt` diz até quando o Pix pode ser pago.
## Saber que foi pago [#saber-que-foi-pago]
Use o webhook [`transaction.paid`](/api/webhooks): ele chega em segundos, com a cobrança inteira, incluindo o seu `metadata`. Libere o pedido quando ele chegar.
Se precisar conferir, consulte [`GET /api/v1/transactions/{id}`](/api/referencia/consultar-transacao). Evite consultar em loop: o webhook é mais rápido e não gasta o seu [limite de requisições](/api/limites-e-idempotencia).
## Situações (`status`) [#situações-status]
| Status | Quer dizer |
| --------------------- | -------------------------------------------------------------------------------------------- |
| `WAITING_PAYMENT` | Pix gerado, aguardando o pagamento. |
| `PAID` | Pago e confirmado. Pode liberar o pedido. |
| `EXPIRED` | Passou do `expiresAt` sem pagamento. Gere uma cobrança nova se o cliente ainda quiser pagar. |
| `CANCELLED`, `FAILED` | Não pode mais ser paga. |
Outros status aparecem em situações raras (como `PENDING` ou `PROCESSING`, enquanto o Pix é gerado). Trate tudo que não é `PAID` como "não pago".
## Valores [#valores]
* `amountCents`: o que o cliente paga.
* `feeCents`: a taxa da WysePay, definida pelo seu plano no momento em que a cobrança é criada.
* `netAmountCents`: o que entra no seu saldo (`amountCents − feeCents`).
Se depois de pago o cliente contestar pelo banco (MED), a cobrança ganha o campo `dispute`. Veja [MED](/painel/med).
# Começo rápido (/api/comeco-rapido)
Em 5 minutos você faz o caminho completo de um pagamento no modo teste. Nada aqui movimenta dinheiro, e o modo teste não precisa de verificação da conta.
### Crie uma chave de teste [#crie-uma-chave-de-teste]
1. Entre no [painel](https://app.wysepay.com.br) e ligue **Modo teste**, no topo da tela.
2. Vá em **Desenvolvedores → Chaves de API** e clique em **Criar chave**.
3. Copie a chave. Ela começa com `wyp_test_` e só aparece uma vez.
### Gere um Pix [#gere-um-pix]
Troque `wyp_test_sua_chave` pela chave que você copiou.
```bash
curl -X POST https://api.wysepay.com.br/api/v1/charges/pix \
-H "x-api-key: wyp_test_sua_chave" \
-H "Content-Type: application/json" \
-H "idempotency-key: pedido-1042" \
-d '{
"amountCents": 8990,
"description": "Pedido #1042",
"customer": {
"name": "Ana Souza",
"email": "ana@exemplo.com.br",
"phone": "11912345678",
"documentType": "CPF",
"document": "12345678909"
},
"metadata": { "orderId": "1042" }
}'
```
```js
const response = await fetch("https://api.wysepay.com.br/api/v1/charges/pix", {
method: "POST",
headers: {
"x-api-key": process.env.WYSEPAY_API_KEY,
"Content-Type": "application/json",
"idempotency-key": "pedido-1042",
},
body: JSON.stringify({
amountCents: 8990,
description: "Pedido #1042",
customer: {
name: "Ana Souza",
email: "ana@exemplo.com.br",
phone: "11912345678",
documentType: "CPF",
document: "12345678909",
},
metadata: { orderId: "1042" },
}),
});
const charge = await response.json();
console.log(charge.pix.copyPaste);
```
```python
import os
import requests
response = requests.post(
"https://api.wysepay.com.br/api/v1/charges/pix",
headers={
"x-api-key": os.environ["WYSEPAY_API_KEY"],
"idempotency-key": "pedido-1042",
},
json={
"amountCents": 8990,
"description": "Pedido #1042",
"customer": {
"name": "Ana Souza",
"email": "ana@exemplo.com.br",
"phone": "11912345678",
"documentType": "CPF",
"document": "12345678909",
},
"metadata": {"orderId": "1042"},
},
)
charge = response.json()
print(charge["pix"]["copyPaste"])
```
```php
true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-api-key: ' . getenv('WYSEPAY_API_KEY'),
'Content-Type: application/json',
'idempotency-key: pedido-1042',
],
CURLOPT_POSTFIELDS => json_encode([
'amountCents' => 8990,
'description' => 'Pedido #1042',
'customer' => [
'name' => 'Ana Souza',
'email' => 'ana@exemplo.com.br',
'phone' => '11912345678',
'documentType' => 'CPF',
'document' => '12345678909',
],
'metadata' => ['orderId' => '1042'],
]),
]);
$charge = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $charge['pix']['copyPaste'];
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{
"amountCents": 8990,
"description": "Pedido #1042",
"customer": map[string]any{
"name": "Ana Souza",
"email": "ana@exemplo.com.br",
"phone": "11912345678",
"documentType": "CPF",
"document": "12345678909",
},
"metadata": map[string]any{"orderId": "1042"},
})
req, _ := http.NewRequest("POST", "https://api.wysepay.com.br/api/v1/charges/pix", bytes.NewReader(body))
req.Header.Set("x-api-key", os.Getenv("WYSEPAY_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("idempotency-key", "pedido-1042")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var charge struct {
Pix struct {
CopyPaste string `json:"copyPaste"`
} `json:"pix"`
}
json.NewDecoder(res.Body).Decode(&charge)
fmt.Println(charge.Pix.CopyPaste)
}
```
```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class GerarPix {
public static void main(String[] args) throws Exception {
String body = """
{
"amountCents": 8990,
"description": "Pedido #1042",
"customer": {
"name": "Ana Souza",
"email": "ana@exemplo.com.br",
"phone": "11912345678",
"documentType": "CPF",
"document": "12345678909"
},
"metadata": { "orderId": "1042" }
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.wysepay.com.br/api/v1/charges/pix"))
.header("x-api-key", System.getenv("WYSEPAY_API_KEY"))
.header("Content-Type", "application/json")
.header("idempotency-key", "pedido-1042")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// A cobrança em JSON: o código está em pix.copyPaste.
System.out.println(response.body());
}
}
```
```ruby
require 'json'
require 'net/http'
uri = URI('https://api.wysepay.com.br/api/v1/charges/pix')
request = Net::HTTP::Post.new(uri)
request['x-api-key'] = ENV.fetch('WYSEPAY_API_KEY')
request['Content-Type'] = 'application/json'
request['idempotency-key'] = 'pedido-1042'
request.body = {
amountCents: 8990,
description: 'Pedido #1042',
customer: {
name: 'Ana Souza',
email: 'ana@exemplo.com.br',
phone: '11912345678',
documentType: 'CPF',
document: '12345678909'
},
metadata: { orderId: '1042' }
}.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
charge = JSON.parse(response.body)
puts charge['pix']['copyPaste']
```
A resposta é a cobrança criada, com o código Pix para mostrar ao cliente:
```json
{
"id": "txn_01k6g8m2v4x7z9b3c5d7f9h0j2",
"status": "WAITING_PAYMENT",
"amountCents": 8990,
"feeCents": 90,
"netAmountCents": 8900,
"description": "Pedido #1042",
"pix": { "copyPaste": "…", "qrCode": "…" },
"customer": { "id": "cus_…", "name": "Ana Souza", "documentType": "CPF", "document": "12345678909", "…": "…" },
"metadata": { "orderId": "1042" },
"livemode": false,
"expiresAt": "2026-10-04T16:00:00.000Z",
"paidAt": null,
"…": "…"
}
```
A API confere os dígitos do CPF ou CNPJ, inclusive no modo teste. Use um CPF válido, como o `123.456.789-09` dos exemplos.
### Cadastre um webhook [#cadastre-um-webhook]
No painel, ainda no modo teste, vá em **Desenvolvedores → Webhooks**, cadastre a URL do seu servidor e marque o evento `transaction.paid`.
Ainda não tem um servidor no ar? Use um serviço como o [webhook.site](https://webhook.site) para ver os eventos chegando.
### Simule o pagamento [#simule-o-pagamento]
No modo teste ninguém paga de verdade: você simula. Use o `id` da cobrança:
```bash
curl -X POST https://api.wysepay.com.br/api/v1/sandbox/transactions/txn_01k6g8m2v4x7z9b3c5d7f9h0j2/pay \
-H "x-api-key: wyp_test_sua_chave"
```
A cobrança passa para `PAID` e o webhook `transaction.paid` chega na sua URL, igual a um pagamento real.
### Vá para produção [#vá-para-produção]
1. Complete o perfil e a verificação da conta no painel (veja [Criar conta e liberar produção](/painel/criar-conta)).
2. Desligue o modo teste e crie uma chave de produção. Ela começa com `wyp_`.
3. Cadastre o webhook de produção. Endpoints de teste e de produção são separados.
4. Troque a chave no seu servidor. O código é o mesmo.
## Próximos passos [#próximos-passos]
* [Validar a assinatura dos webhooks](/api/webhooks#validar-a-assinatura), antes de confiar num evento.
* [Evitar cobranças duplicadas](/api/limites-e-idempotencia) com a `idempotency-key`.
* [Links de pagamento](/api/links-de-pagamento), se você não quer montar a tela de pagamento.
# Erros (/api/erros)
Toda resposta de erro tem o mesmo formato:
```json
{
"ok": false,
"code": "VALIDATION_ERROR",
"message": "Revise os campos informados.",
"errors": [
{ "path": "customer.email", "message": "E-mail inválido." }
],
"path": "/api/v1/charges/pix",
"timestamp": "2026-10-04T15:30:00.000Z"
}
```
* `code`: para o seu código decidir o que fazer.
* `message`: em português, pode ser mostrada a quem opera o seu sistema.
* `errors`: só em `VALIDATION_ERROR`, com cada campo e o motivo.
## Códigos [#códigos]
| `code` | Quando |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `VALIDATION_ERROR` | Algum campo do corpo está faltando ou inválido. Veja `errors`. |
| `REQUEST_ERROR` | O pedido não pôde ser atendido. A `message` diz o motivo (ex.: "CPF ou CNPJ do pagador inválido."). |
| `API_KEY_PERMISSION` | A chave não tem a permissão da rota. Ajuste em **Chaves de API**. Veja [Permissões](/api/autenticacao#permissões). |
| `API_KEY_IP` | A chamada veio de um IP que a chave não libera. Veja [IPs permitidos](/api/autenticacao#ips-permitidos). |
| `WITHDRAW_DAILY_LIMIT` | O saque passaria do limite diário da chave. A `message` diz quanto ainda resta hoje. |
| `INTERNAL_ERROR` | Falha do nosso lado. Tente de novo em instantes. |
## Status HTTP [#status-http]
| Status | Quer dizer | O que fazer |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `400` | Dados inválidos. | Corrija o pedido; repetir igual dá o mesmo erro. |
| `401` | Chave ausente, inválida ou revogada. | Confira o header `x-api-key`. |
| `403` | A chave não pode fazer isso: falta permissão, o IP não está liberado, o limite diário de saque acabou, a conta ainda não opera em produção ou a simulação foi feita com chave de produção. | Veja o `code` e a `message`. |
| `404` | Não encontrado nesta conta e neste modo. | Confira o id e se a chave é do mesmo modo (teste ou produção). |
| `429` | Muitas requisições. | Espere o tempo do header `Retry-After`. Veja [Limites](/api/limites-e-idempotencia). |
| `5xx` | Falha nossa ou da adquirente. | Tente de novo com a mesma `idempotency-key`. |
# Visão geral da API (/api)
Com a API você cobra por Pix a partir do seu sistema: gera o QR Code e o código copia e cola para o cliente, cria links de pagamento e é avisado por webhook quando o dinheiro cai.
## O que dá para fazer [#o-que-dá-para-fazer]
| Você quer… | Use |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Mostrar um Pix no seu site ou app | [`POST /api/v1/charges/pix`](/api/referencia/gerar-pix) |
| Mandar um link para o cliente pagar | [`POST /api/v1/payment-links`](/api/referencia/criar-link) |
| Saber se uma cobrança foi paga | [Webhook `transaction.paid`](/api/webhooks) ou [`GET /api/v1/transactions/{id}`](/api/referencia/consultar-transacao) |
| Conciliar vendas | [`GET /api/v1/transactions`](/api/referencia/listar-transacoes), com [paginação](/api/paginacao) |
| Manter os seus clientes | [`/api/v1/customers`](/api/clientes) |
| Ver o saldo e sacar | [`/api/v1/balance` e `/api/v1/withdrawals`](/api/saques) |
| Testar sem dinheiro real | [Modo teste](/api/modo-teste) |
## Como a API funciona [#como-a-api-funciona]
* **Endereço:** `https://api.wysepay.com.br`. Todas as rotas começam com `/api/v1`.
* **Autenticação:** a sua chave de API no header `x-api-key` (ou `Authorization: Bearer`), com as permissões que você der a ela. Veja [Autenticação e permissões](/api/autenticacao).
* **Formato:** JSON no corpo e nas respostas, com `Content-Type: application/json`.
* **Valores em centavos:** `8990` é R$ 89,90. O mínimo de uma cobrança é `500` (R$ 5,00).
* **Datas em ISO 8601, UTC:** por exemplo, `2026-10-04T15:30:00.000Z`.
* **Teste e produção com o mesmo endereço:** o que define o modo é a chave. Uma chave `wyp_test_` nunca movimenta dinheiro.
## O caminho de um pagamento [#o-caminho-de-um-pagamento]
**Você cria a cobrança.** Pela API, com o valor, a descrição e os dados de quem paga. A resposta traz o código Pix.
**O cliente paga** pelo app do banco, lendo o QR Code ou colando o código.
**A WysePay avisa o seu sistema.** O webhook `transaction.paid` chega na sua URL em segundos. É nele que você libera o pedido.
**O dinheiro entra no seu saldo**, já descontada a taxa, e fica disponível para saque conforme o prazo de liberação da sua conta.
Pronto para tentar? Vá ao [Começo rápido](/api/comeco-rapido).
# Limites e idempotência (/api/limites-e-idempotencia)
## Limites de requisições [#limites-de-requisições]
Os limites contam por endereço IP, a cada minuto:
| Rotas | Limite |
| --------------------------------- | -------------- |
| Consultas e listas (`GET`) | 120 por minuto |
| Criar e alterar (`POST`, `PATCH`) | 60 por minuto |
| `POST /api/v1/withdrawals` | 20 por minuto |
Acima do limite a resposta é `429`, com o header `Retry-After` dizendo quantos segundos esperar. Para saber se algo foi pago, prefira os [webhooks](/api/webhooks) a consultar a API em loop.
Precisa de mais? Fale com o comercial pelo e-mail [comercial@wysepay.com.br](mailto:comercial@wysepay.com.br).
## Idempotência [#idempotência]
Uma falha de rede pode deixar você sem saber se a cobrança (ou o saque) foi criada. Para repetir sem risco de duplicar, envie o header `idempotency-key` ao criar um Pix ou um saque:
```bash
curl -X POST https://api.wysepay.com.br/api/v1/charges/pix \
-H "x-api-key: wyp_test_sua_chave" \
-H "idempotency-key: pedido-1042" \
-H "Content-Type: application/json" \
-d '{ … }'
```
* Use um valor único por cobrança, como o id do pedido no seu sistema.
* Repetindo com a mesma chave em até **24 horas**, a WysePay devolve a mesma cobrança (ou o mesmo saque), sem criar outro.
* A chave vale por conta e por modo: a mesma `idempotency-key` em teste e em produção são cobranças diferentes.
* Para cobrar de novo o mesmo pedido (ex.: o Pix venceu), use uma chave nova, como `pedido-1042-2`.
# Links de pagamento (/api/links-de-pagamento)
Com um link de pagamento você não monta tela nenhuma: cria o link, envia a `url` ao cliente, e ele preenche os dados, paga com Pix e vê o comprovante num checkout com a sua marca.
## Criar [#criar]
[`POST /api/v1/payment-links`](/api/referencia/criar-link):
```bash
curl -X POST https://api.wysepay.com.br/api/v1/payment-links \
-H "x-api-key: wyp_test_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"amountCents": 15900,
"description": "Caneca WysePay",
"maxPayments": 1,
"askAddress": true,
"returnUrl": "https://loja.com.br/obrigado?pedido=1042",
"metadata": { "orderId": "1042" }
}'
```
A resposta traz a `url` do checkout, como `https://app.wysepay.com.br/checkout/pay_…`. Envie por WhatsApp, e-mail ou redirecione o cliente para ela.
## Opções [#opções]
| Campo | O que faz |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `maxPayments` | Quantos pagamentos o link aceita. `1` = uso único; omitido ou `null` = ilimitado. Depois do limite, o link mostra "esgotado". |
| `expiresAt` | Até quando o link aceita pagamentos. |
| `askAddress` | O checkout pede o endereço, na mesma tela dos dados. O CEP preenche rua, bairro e cidade. |
| `returnUrl` | Para onde o cliente volta depois de pagar (precisa ser `https`). |
| `metadata` | Dados seus. Cada cobrança gerada pelo link aponta para ele em `paymentLinkId`. |
## Voltar para a sua loja [#voltar-para-a-sua-loja]
Com `returnUrl`, o comprovante mostra o botão **Voltar para a loja** e, logo depois do pagamento, leva o cliente de volta sozinho em 8 segundos (ele pode ficar, se quiser). A URL recebe o id da cobrança paga:
```
https://loja.com.br/obrigado?pedido=1042&wysepay_transaction_id=txn_01k6g8m2v4x7z9b3c5d7f9h0j2
```
Qualquer pessoa pode abrir essa URL com um id inventado. Use o retorno só para mostrar a página de "obrigado". Libere o pedido quando chegar o webhook `transaction.paid`, ou consultando a transação pela API com a sua chave.
## Como o pagamento aparece para você [#como-o-pagamento-aparece-para-você]
Cada vez que alguém gera um Pix no link, nasce uma cobrança (`transaction.created`). Quando paga, chega `transaction.paid`, com `paymentLinkId` apontando para o link e os dados que o cliente preencheu em `customer`.
## Listar, consultar e desativar [#listar-consultar-e-desativar]
* [`GET /api/v1/payment-links`](/api/referencia/listar-links): os links, com o filtro `active`.
* [`GET /api/v1/payment-links/{id}`](/api/referencia/consultar-link): um link e se ainda aceita pagamentos (`active`).
* [`POST /api/v1/payment-links/{id}/deactivate`](/api/referencia/desativar-link): o link para de aceitar pagamentos. Os Pix já gerados por ele continuam valendo até vencer.
## A marca do checkout [#a-marca-do-checkout]
Logo, cores, fonte e textos do checkout são configurados no painel, em **Cobranças → Personalizar checkout**, e valem para todos os links. Veja [Personalizar o checkout](/painel/personalizar-checkout).
# Modo teste (/api/modo-teste)
No modo teste tudo funciona como em produção, mas nada movimenta dinheiro: você cria cobranças e links, recebe webhooks e simula pagamentos. Ele não precisa de conta verificada.
## Como usar [#como-usar]
* **Pela API:** use uma chave `wyp_test_`. Tudo que ela cria é de teste (`livemode: false`).
* **No painel:** ligue **Modo teste** no topo da tela. Os dados de teste ficam separados dos reais.
## O Pix de teste não pode ser pago [#o-pix-de-teste-não-pode-ser-pago]
O código de um Pix de teste é de mentira de propósito: nenhum app de banco aceita. Para "pagar", simule:
```bash
curl -X POST https://api.wysepay.com.br/api/v1/sandbox/transactions/{id}/pay \
-H "x-api-key: wyp_test_sua_chave"
```
Também dá para simular pelo painel, no botão **Simular pagamento** da tela do Pix ou da venda.
A simulação:
* muda a cobrança para `PAID`;
* envia o webhook `transaction.paid` aos seus endpoints de teste;
* credita o saldo de teste, para você testar também o extrato e os saques no painel.
Só cobranças de teste podem ser simuladas. Com uma chave de produção, a rota responde `403`.
## Prazo [#prazo]
Um Pix de teste vence em 30 minutos, como um Pix real vence no prazo dele. Depois disso ele aparece como `EXPIRED`, e a simulação responde `400`: gere outro.
## Webhooks de teste [#webhooks-de-teste]
Cadastre o endpoint com o modo teste ligado. Ele recebe só eventos de teste; os de produção vão só para endpoints de produção.
# OpenAPI, Postman e IA (/api/openapi)
## OpenAPI [#openapi]
A especificação completa da API, no formato OpenAPI 3.1, gerada do mesmo código que valida as chamadas:
* [openapi.json](/openapi.json)
Importe no Insomnia, no Postman ou no gerador de código da sua linguagem.
## Coleção do Postman [#coleção-do-postman]
* [wysepay.postman_collection.json](/wysepay.postman_collection.json)
No Postman, use **Import** e depois preencha as variáveis da coleção: `apiKey` com a sua chave e, se precisar, `baseUrl`.
## Para assistentes de código (IA) [#para-assistentes-de-código-ia]
* Em cada página, o botão **Copiar Markdown** copia o conteúdo para colar no seu assistente.
* [/llms.txt](/llms.txt) lista todas as páginas da documentação.
* [/llms-full.txt](/llms-full.txt) traz toda a documentação num arquivo só.
Lembre-se: nunca cole a sua chave de produção em conversas com assistentes.
# Paginação (/api/paginacao)
As listas vêm da mais nova para a mais antiga, em páginas:
```json
{
"data": [ { "id": "txn_01k6…", "…": "…" }, { "id": "txn_01k5…", "…": "…" } ],
"hasMore": true
}
```
* `limit`: quantos itens por página, de 1 a 100 (padrão 20).
* `startingAfter`: o `id` do último item que você recebeu. A próxima página começa depois dele.
* `hasMore`: `true` quando há mais páginas.
```bash
# Primeira página
curl "https://api.wysepay.com.br/api/v1/transactions?limit=50" \
-H "x-api-key: wyp_test_sua_chave"
# Próxima: startingAfter = o id do último item da página anterior
curl "https://api.wysepay.com.br/api/v1/transactions?limit=50&startingAfter=txn_01k5…" \
-H "x-api-key: wyp_test_sua_chave"
```
Diferente de "página 2, página 3", o cursor não pula nem repete itens quando chegam pagamentos novos enquanto você percorre a lista.
## Filtros [#filtros]
| Lista | Filtros |
| ----------------------------------------------- | ----------------------------------------------------- |
| [Transações](/api/referencia/listar-transacoes) | `status`, `paymentLinkId`, `createdFrom`, `createdTo` |
| [Links](/api/referencia/listar-links) | `active` (`true` ou `false`) |
| [Clientes](/api/referencia/listar-clientes) | `document` (CPF ou CNPJ) |
| [Saques](/api/referencia/listar-saques) | — |
O filtro `status=EXPIRED` inclui os Pix que venceram sem pagamento, do mesmo jeito que a consulta de uma transação mostra.
# Saldo e saques (/api/saques)
## Saldo [#saldo]
[`GET /api/v1/balance`](/api/referencia/consultar-saldo) (permissão `balance:read`):
```json
{
"availableCents": 125000,
"pendingCents": 8900,
"blockedCents": 0,
"withdrawableNowCents": 124900,
"withdrawFeeCents": 100,
"livemode": true
}
```
* `availableCents`: o que já pode sair.
* `pendingCents`: vendas pagas ainda no prazo de liberação.
* `blockedCents`: retido, por exemplo, por um [MED](/painel/med).
* `withdrawableNowCents`: quanto dá para sacar agora, num saque só.
## Sacar [#sacar]
[`POST /api/v1/withdrawals`](/api/referencia/sacar) tira o valor do saldo e envia para uma chave Pix.
`withdrawals:write` vem **desligada** em toda chave. Antes de ligar, em **Chaves de API**:
* libere só o IP do servidor que vai sacar;
* defina um **limite diário** de saque para a chave;
* use essa chave só para sacar, separada da que cria cobranças.
```bash
curl -X POST https://api.wysepay.com.br/api/v1/withdrawals \
-H "x-api-key: wyp_test_sua_chave_de_saque" \
-H "Content-Type: application/json" \
-H "idempotency-key: repasse-2026-10-05" \
-d '{
"amountCents": 50000,
"pixKey": "12345678000190",
"pixKeyType": "CNPJ"
}'
```
| Campo | O que é |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `amountCents` | Quanto sai do saldo, **já incluindo a taxa de saque**. Mínimo `600` (R$ 6,00). O que chega no destino é `amountCents − taxa`. |
| `pixKey` | A chave Pix de destino. |
| `pixKeyType` | `CPF`, `CNPJ`, `PHONE` (celular com DDD), `EMAIL` ou `EVP` (chave aleatória). |
* **Idempotência:** envie sempre a `idempotency-key`. Repetindo com a mesma chave, o mesmo saque volta e nada sai duas vezes.
* **Conta verificada:** em produção, o saque exige a conta verificada, como no painel.
* **Aviso por e-mail:** o dono da conta recebe o mesmo e-mail de um saque feito no painel.
## Limite diário [#limite-diário]
Com limite definido, a chave só saca até esse valor por dia, contado no horário de Brasília (o dia vira à meia-noite). Entram na conta os saques daquela chave no dia, menos os que falharam. Passou do limite, a resposta é `403` com `code: "WITHDRAW_DAILY_LIMIT"` e diz quanto ainda resta.
## Acompanhar [#acompanhar]
O saque começa `PENDING` e termina `PAID`, `PARTIALLY_PAID` (parte voltou ao saldo) ou `FAILED` (tudo voltou). Ele pode ser pago em mais de uma transferência Pix. Use os webhooks `withdrawal.paid`, `withdrawal.partially_paid` e `withdrawal.failed`, ou consulte [`GET /api/v1/withdrawals/{id}`](/api/referencia/consultar-saque).
A chave de destino aparece parcialmente escondida nas respostas.
## No modo teste [#no-modo-teste]
Com uma chave `wyp_test_`, o saque é simulado: sai do saldo de teste, fica `PAID` na hora e envia `withdrawal.created` e `withdrawal.paid` aos endpoints de teste. Para ter saldo de teste, [simule pagamentos](/api/modo-teste).
# Webhooks (/api/webhooks)
Um webhook é uma chamada que a WysePay faz para uma URL sua quando algo acontece, como um pagamento confirmado. É o jeito certo de saber que foi pago: chega em segundos e você não precisa consultar a API em loop.
## Cadastrar [#cadastrar]
1. No painel, vá em **Desenvolvedores → Webhooks**.
2. Informe a URL (use `https`) e marque os eventos.
3. Guarde o **segredo** que aparece: ele valida a assinatura.
4. Use **Testar** para conferir se a sua URL responde.
Endpoints de teste (cadastrados com o modo teste ligado) só recebem eventos de teste, e os de produção só os reais.
## Eventos [#eventos]
| Evento | Quando | Corpo |
| --------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------- |
| `transaction.created` | Um Pix foi gerado (API, painel ou link). | [Transação](/api/referencia/eventos/transaction-created) |
| `transaction.paid` | O pagamento foi confirmado. **Libere o pedido aqui.** | [Transação](/api/referencia/eventos/transaction-paid) |
| `transaction.expired` | O Pix venceu sem pagamento. Cancele o pedido ou gere outro Pix. | [Transação](/api/referencia/eventos/transaction-expired) |
| `dispute.created` | O pagador contestou o pagamento pelo banco (MED). | [MED](/api/referencia/eventos/dispute-created) |
| `dispute.responded` | A sua defesa do MED foi enviada. | [MED](/api/referencia/eventos/dispute-responded) |
| `dispute.won` | A contestação foi negada: o valor volta a ficar disponível. | [MED](/api/referencia/eventos/dispute-won) |
| `dispute.lost` | A devolução foi confirmada: o valor voltou ao pagador. | [MED](/api/referencia/eventos/dispute-lost) |
| `withdrawal.created` | Um saque foi pedido, pelo painel ou pela API. | [Saque](/api/referencia/eventos/withdrawal-created) |
| `withdrawal.paid` | O saque chegou ao destino. | [Saque](/api/referencia/eventos/withdrawal-paid) |
| `withdrawal.partially_paid` | Parte do saque não foi concluída e voltou ao saldo. | [Saque](/api/referencia/eventos/withdrawal-partially-paid) |
| `withdrawal.failed` | O saque não foi feito e o valor voltou ao saldo. | [Saque](/api/referencia/eventos/withdrawal-failed) |
| `webhook.test` | Você clicou em **Testar** no painel. | `{ message, sentAt }` |
Os eventos de transação mandam a mesma transação que a API devolve, incluindo o seu `metadata`. Use-o para achar o pedido no seu sistema.
## O que chega [#o-que-chega]
Um `POST` com o corpo em JSON e estes headers:
| Header | O que é |
| --------------------- | ------------------------------------------------------------ |
| `wysepay-event` | O tipo do evento, como `transaction.paid`. |
| `wysepay-signature` | A assinatura, para você conferir que veio da WysePay. |
| `wysepay-timestamp` | Quando foi assinado, em milissegundos. |
| `wysepay-delivery-id` | O id da entrega. É o mesmo nas novas tentativas automáticas. |
| `wysepay-attempt` | O número da tentativa, de 1 a 6. |
## Validar a assinatura [#validar-a-assinatura]
A assinatura é um HMAC-SHA256, em hexadecimal, feito com o segredo do endpoint sobre o texto `{wysepay-timestamp}.{corpo}`. Calcule o mesmo no seu servidor e compare. Se não bater, recuse com `401`.
Assine o texto bruto da requisição, antes de transformar em objeto. Reescrever o JSON muda espaços e a ordem dos campos, e a assinatura deixa de bater.
```js
import crypto from "node:crypto";
import express from "express";
const app = express();
// O corpo cru (Buffer), não o objeto já interpretado.
app.post("/webhooks/wysepay", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.header("wysepay-timestamp");
const signature = req.header("wysepay-signature") ?? "";
const expected = crypto
.createHmac("sha256", process.env.WYSEPAY_WEBHOOK_SECRET)
.update(`${timestamp}.${req.body}`)
.digest("hex");
const valid =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) return res.sendStatus(401);
const event = req.header("wysepay-event");
const data = JSON.parse(req.body.toString("utf8"));
if (event === "transaction.paid") {
// Libere o pedido data.metadata.orderId (uma vez só: veja "Entregas repetidas").
}
res.sendStatus(200);
});
```
```python
import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
@app.post("/webhooks/wysepay")
def wysepay_webhook():
body = request.get_data() # bytes crus
timestamp = request.headers.get("wysepay-timestamp", "")
signature = request.headers.get("wysepay-signature", "")
expected = hmac.new(
os.environ["WYSEPAY_WEBHOOK_SECRET"].encode(),
f"{timestamp}.".encode() + body,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(signature, expected):
abort(401)
data = json.loads(body)
if request.headers.get("wysepay-event") == "transaction.paid":
pass # libere o pedido data["metadata"]["orderId"]
return "", 200
```
```php
```go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"os"
)
func wysepayWebhook(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body) // corpo cru
if err != nil {
http.Error(w, "", http.StatusBadRequest)
return
}
mac := hmac.New(sha256.New, []byte(os.Getenv("WYSEPAY_WEBHOOK_SECRET")))
mac.Write([]byte(r.Header.Get("wysepay-timestamp") + "."))
mac.Write(body)
expected := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(expected), []byte(r.Header.Get("wysepay-signature"))) {
w.WriteHeader(http.StatusUnauthorized)
return
}
if r.Header.Get("wysepay-event") == "transaction.paid" {
var data struct {
Metadata map[string]any `json:"metadata"`
}
json.Unmarshal(body, &data)
// libere o pedido data.Metadata["orderId"] (uma vez só)
}
w.WriteHeader(http.StatusOK)
}
```
```java
// Spring Boot. HexFormat precisa do Java 17+.
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
public class WysePayWebhook {
@PostMapping("/webhooks/wysepay")
public ResponseEntity receive(
@RequestBody byte[] body, // corpo cru
@RequestHeader("wysepay-timestamp") String timestamp,
@RequestHeader("wysepay-signature") String signature,
@RequestHeader("wysepay-event") String event) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(
System.getenv("WYSEPAY_WEBHOOK_SECRET").getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
mac.update((timestamp + ".").getBytes(StandardCharsets.UTF_8));
String expected = HexFormat.of().formatHex(mac.doFinal(body));
if (!MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8), signature.getBytes(StandardCharsets.UTF_8))) {
return ResponseEntity.status(401).build();
}
if ("transaction.paid".equals(event)) {
// libere o pedido: leia metadata.orderId do JSON em body (uma vez só)
}
return ResponseEntity.ok().build();
}
}
```
```ruby
# Sinatra
require 'json'
require 'openssl'
require 'sinatra'
post '/webhooks/wysepay' do
body = request.body.read # corpo cru
timestamp = request.env['HTTP_WYSEPAY_TIMESTAMP'].to_s
signature = request.env['HTTP_WYSEPAY_SIGNATURE'].to_s
expected = OpenSSL::HMAC.hexdigest('SHA256', ENV.fetch('WYSEPAY_WEBHOOK_SECRET'), "#{timestamp}.#{body}")
halt 401 unless Rack::Utils.secure_compare(expected, signature)
data = JSON.parse(body)
if request.env['HTTP_WYSEPAY_EVENT'] == 'transaction.paid'
# libere o pedido data['metadata']['orderId'] (uma vez só)
end
status 200
end
```
## Responda rápido [#responda-rápido]
Responda com qualquer status `2xx` em até **8 segundos**. Se o trabalho for demorado (e-mail, nota fiscal), guarde o evento, responda `200` e processe depois.
## Novas tentativas [#novas-tentativas]
Se a sua URL não responder `2xx` a tempo, a WysePay tenta de novo depois de **1 minuto, 5 minutos, 30 minutos, 2 horas e 6 horas**: até 6 tentativas no total. Em **Webhooks**, no painel, você vê cada entrega e a resposta do seu servidor, e pode usar **Reenviar evento**: ele cria uma entrega nova, com outro `wysepay-delivery-id` e uma assinatura nova.
Se todas as tentativas falharem, você pode ser avisado por e-mail (ligue em **Configurações → Notificações**).
## Entregas repetidas [#entregas-repetidas]
O mesmo evento pode chegar mais de uma vez (por exemplo, se a sua resposta se perdeu no caminho). Trate os eventos de forma idempotente: antes de liberar, confira se o pedido daquela transação (`id`) já foi liberado. O `wysepay-delivery-id` ajuda a reconhecer tentativas repetidas, mas um reenvio manual chega com um id novo.
# Documentação da WysePay (/)
Escolha por onde começar:
Gere seu primeiro Pix em 5 minutos, com uma chave de teste e sem dinheiro real.
Crie links de pagamento, acompanhe vendas e clientes, saque e responda MEDs.
## Para quem integra [#para-quem-integra]
* [Autenticação e permissões](/api/autenticacao): chaves `wyp_test_` e `wyp_`, permissões por recurso e IPs permitidos.
* [Webhooks](/api/webhooks): seja avisado quando um pagamento é confirmado, e valide a assinatura.
* [Referência da API](/api/referencia/gerar-pix): cada endpoint, com o botão **Testar** para fazer a chamada pela própria página.
* [OpenAPI e Postman](/api/openapi): baixe a especificação e a coleção pronta.
## Para quem vende [#para-quem-vende]
* [Criar conta e liberar produção](/painel/criar-conta)
* [Links de pagamento](/painel/links-de-pagamento) e [personalizar o checkout](/painel/personalizar-checkout)
* [Saldo e saques](/painel/saldo-e-saques)
* [MED: quando um pagamento é contestado](/painel/med)
Dúvidas? Fale com a gente na [comunidade do Discord](https://discord.gg/t7a2ppxjpu) ou pelo chat do painel.
# Clientes (/painel/clientes)
Em **Clientes** fica a sua carteira, um cliente por CPF ou CNPJ.
## Como alguém vira cliente [#como-alguém-vira-cliente]
* Ao preencher um CPF ou CNPJ válido no checkout, mesmo sem pagar.
* Quando você cadastra, em **Clientes** ou na hora de gerar um Pix direto.
Os dados são atualizados a cada compra.
## O que você vê e faz [#o-que-você-vê-e-faz]
* Todas as compras do cliente, pagas ou não.
* Editar os dados e o endereço.
* Escrever **notas internas** e adicionar **tags** (ex.: VIP) para filtrar a lista. O cliente nunca vê as notas nem as tags.
* Gerar um Pix direto para ele.
## Exportar [#exportar]
O botão **Exportar CSV** segue os filtros da tela, inclusive a tag escolhida.
# Criar conta e liberar produção (/painel/criar-conta)
## Quem pode ter conta [#quem-pode-ter-conta]
Pessoas físicas (CPF) maiores de 18 anos e empresas (CNPJ), inclusive MEI. Cada CPF ou CNPJ pode ter uma conta.
## Criar a conta [#criar-a-conta]
1. No [painel](https://app.wysepay.com.br), clique em **Criar conta**.
2. Informe nome completo, e-mail, CPF ou CNPJ e uma senha, e aceite os Termos de Uso e a Política de Privacidade.
3. Digite o código de 6 números que chega no seu e-mail. Não chegou? Confira o spam e peça um novo na mesma tela.
Pronto: você já entra no painel.
## Comece no modo teste [#comece-no-modo-teste]
Logo depois do cadastro o **modo teste** está liberado: você cria links, testa o checkout, simula pagamentos e integra a API, sem dinheiro real e sem verificação. Ligue pela chave **Modo teste** no topo da tela. Veja [Modo teste](/api/modo-teste).
## Liberar a produção [#liberar-a-produção]
Para receber pagamentos reais, criar chaves de API de produção e sacar, faltam três etapas, todas pelo painel:
**Complete o perfil** em **Configurações → Conta**: nome fantasia, telefone, site ou Instagram, segmento e faturamento estimado.
**Verifique a sua identidade**: um documento com foto e uma selfie, em cerca de 2 minutos. Veja [Verificação](/painel/verificacao).
**Verifique a empresa** (só para CNPJ): os dados da Receita e, quando for preciso, documentos como o contrato social.
Quando tudo é aprovado, a conta passa a operar em produção e você é avisado no painel e por e-mail.
# Guias do painel (/painel)
O [painel](https://app.wysepay.com.br) é onde você cria cobranças, acompanha as vendas e cuida do dinheiro. Não precisa de site nem de programação: um link de pagamento enviado pelo WhatsApp já basta para vender.
Cadastro, modo teste e o que falta para receber pagamentos reais.
Crie um link, envie ao cliente e receba por Pix num checkout com a sua marca.
Cada venda, paga ou não, e como recuperar as abandonadas.
Quando o dinheiro fica disponível e como sacar.
## Instale como aplicativo [#instale-como-aplicativo]
O painel funciona como app no celular e no computador, direto do navegador:
* **Android e computador:** use **Instalar aplicativo** no menu do navegador.
* **iPhone e iPad:** abra no Safari, toque em **Compartilhar** e depois em **Adicionar à Tela de Início**.
Instalado, ele abre em tela cheia e recebe [notificações de venda](/painel/notificacoes).
# Links de pagamento (/painel/links-de-pagamento)
## Criar um link [#criar-um-link]
1. Clique em **Nova cobrança** e deixe selecionado **Link de pagamento**.
2. Informe o valor (mínimo R$ 5,00) e a descrição.
3. Em **Mais opções**, se precisar:
* **Pedir endereço do cliente**: o checkout pede o endereço, na mesma tela dos dados. O CEP preenche rua, bairro e cidade.
* **Expiração**: até quando o link aceita pagamentos.
* **Pagamentos aceitos**: uso único, limitado (a quantidade que você definir) ou ilimitado.
* **URL de retorno**: para onde o cliente volta depois de pagar.
4. Copie o link ou baixe o QR Code e envie ao cliente.
## Como o cliente paga [#como-o-cliente-paga]
1. Abre o link e preenche, numa tela só, CPF ou CNPJ, nome, e-mail, celular e, se o link pedir, o endereço.
2. Toca em **Pagar com Pix** e recebe o QR Code e o copia e cola, com o prazo em contagem regressiva.
3. Paga pelo app do banco. A tela acompanha sozinha e mostra o comprovante quando confirma. O comprovante também chega por e-mail.
Se ele fechar a página e voltar depois no mesmo aparelho, o checkout continua de onde parou. Se o Pix vencer, ele pode gerar outro pelo mesmo link, enquanto o link estiver ativo.
## URL de retorno [#url-de-retorno]
Com uma URL de retorno, o comprovante mostra **Voltar para a loja** e, logo depois do pagamento, leva o cliente de volta sozinho em 8 segundos (ele pode escolher ficar). A URL recebe o código da transação no parâmetro `wysepay_transaction_id`.
Qualquer pessoa pode abrir a URL de retorno. Confirme o pagamento no painel ou pelo [webhook](/api/webhooks) `transaction.paid` antes de entregar.
## Esgotado [#esgotado]
Quando um link atinge o limite de pagamentos, ele mostra "Link esgotado" e não aceita mais pagamentos.
# MED (/painel/med)
O MED (Mecanismo Especial de Devolução) é a forma, criada pelo Banco Central, de um pagador contestar um Pix pelo banco dele, por exemplo em caso de suspeita de golpe. Quando isso acontece com uma venda sua, o valor contestado fica **bloqueado** no saldo até a análise terminar.
## Como você fica sabendo [#como-você-fica-sabendo]
* A contestação aparece em **MEDs**, com um contador no menu.
* Por e-mail, se o aviso de MED estiver ligado em **Configurações → Notificações**.
* Pelo webhook `dispute.created`, se você usa a API.
## Responder [#responder]
Responda assim que receber. Recomendamos manter o aviso de MED sempre ligado.
1. Em **MEDs**, abra a contestação.
2. Responda com evidências da venda: o que foi vendido, quando foi entregue e qualquer comprovante, como nota fiscal, rastreio ou conversas com o cliente.
3. Envie.
## O resultado [#o-resultado]
* **Devolução confirmada:** o valor é debitado do seu saldo.
* **Devolução negada:** o valor é liberado de novo.
# Notificações (/painel/notificacoes)
Tudo fica em **Configurações → Notificações**.
## Por e-mail [#por-e-mail]
Escolha receber avisos de:
* pagamento recebido (com valor, taxa e líquido);
* MED aberto (recomendamos manter ligado: o prazo de resposta é curto);
* saque solicitado;
* webhook com falha.
## No celular e no computador [#no-celular-e-no-computador]
Em **Neste dispositivo**, ligue as notificações push. Você escolhe, em cada aparelho, o que receber: venda aprovada, venda pendente (Pix gerado aguardando pagamento, desligada por padrão), MEDs, saques e webhook com falha. A verificação da conta e os saques não concluídos sempre avisam.
Em **O que aparece na notificação**, ligue ou desligue separadamente o nome do cliente, o produto e o valor, por exemplo se outras pessoas veem a sua tela de bloqueio.
As notificações só funcionam com o painel instalado na tela de início. Veja [como instalar](/painel#instale-como-aplicativo).
# Nova cobrança (/painel/nova-cobranca)
O botão **Nova cobrança**, no topo do menu, oferece dois caminhos:
| | Pix direto | Link de pagamento |
| ---------------------- | ---------------------------------- | -------------------------------------------------- |
| O que gera | QR Code e copia e cola, na hora | Um checkout para enviar ao cliente |
| Quem preenche os dados | Você | O cliente |
| Bom para | Atendimento presencial ou por chat | Vender à distância, vários clientes, redes sociais |
O valor mínimo é R$ 5,00 nos dois.
## Pix direto [#pix-direto]
1. Escolha **Pix direto**.
2. Informe o valor e a descrição.
3. Escolha o cliente: busque por nome, CPF ou e-mail, ou cadastre um novo ali mesmo. O Pix precisa de nome, CPF ou CNPJ, e-mail e celular.
4. Mostre o QR Code ao cliente ou envie o copia e cola.
A tela avisa quando o pagamento cai.
## Link de pagamento [#link-de-pagamento]
Veja [Links de pagamento](/painel/links-de-pagamento).
# Personalizar o checkout (/painel/personalizar-checkout)
Em **Cobranças → Personalizar checkout** você deixa o checkout com a cara da sua marca. Vale para todos os seus links, e a prévia mostra as telas de dados, do Pix e do comprovante, no computador e no celular.
## Logo [#logo]
PNG, JPG, WebP ou SVG. Fundo transparente fica melhor. Imagens grandes são reduzidas automaticamente. Sem logo, aparece o nome da conta.
## Cores [#cores]
Cada parte tem a sua cor:
* **Resumo**: a coluna com a logo, a descrição e o valor (fundo e texto).
* **Formulário**: onde o cliente preenche os dados e vê o Pix (fundo e texto).
* **Campos**: fundo, borda e a cor de destaque, que também aparece no foco e na barra do prazo do Pix.
* **Botão**: fundo e texto do **Pagar com Pix**.
Se uma combinação deixar o texto difícil de ler, aparece um aviso com o botão **Corrigir**.
## Fonte e cantos [#fonte-e-cantos]
Fontes: Geist, Inter, Poppins, Manrope, Lora ou Pacifico. A Pacifico é cursiva, por isso vale só para o nome da loja e o título; o resto fica numa fonte de leitura fácil.
Os cantos podem ser retos, leves, médios ou redondos.
## Textos [#textos]
* **Título**: aparece acima da descrição, no resumo.
* **Texto do botão**: por padrão, "Pagar com Pix".
* **Nota de rodapé**: uma linha abaixo do botão, como uma política de troca.
Clique em **Salvar** para aplicar. Para voltar ao visual original, use **Restaurar padrão WysePay**.
# Extrato e relatórios (/painel/relatorios)
## Extrato [#extrato]
Em **Extrato** você vê todas as entradas e saídas do saldo, por período: vendas pagas, taxas, saques e valores de MED.
## Exportar em CSV [#exportar-em-csv]
Para conciliar ou mandar à contabilidade, exporte em CSV:
* **Transações**, **saques** e **extrato**, por período.
* **Vendas** e **clientes**, seguindo os filtros da tela.
Nos seletores de período há opções como **Este mês** e **Mês anterior**, que segue o mês do calendário no horário de Brasília.
# Saldo e saques (/painel/saldo-e-saques)
## Os três saldos [#os-três-saldos]
Na **Visão geral** do painel:
| Saldo | O que é |
| -------------- | ------------------------------------------------------------------ |
| **Disponível** | O que você já pode sacar. |
| **Pendente** | Vendas pagas que ainda estão no prazo de liberação. |
| **Bloqueado** | Valor retido, por exemplo, por um [MED](/painel/med) em andamento. |
Cada venda entra no saldo já descontada a taxa. Hoje o padrão é liberar no mesmo dia (D+0); o prazo pode ser maior para uma conta específica, quando o risco das operações justificar.
## Sacar [#sacar]
1. Vá em **Saques**.
2. Informe o valor e a chave Pix de destino: CPF, CNPJ, celular, e-mail ou chave aleatória.
3. Confirme.
* O mínimo é **R$ 6,00**, e o valor precisa ser maior que a taxa de saque (veja [Taxas e planos](/painel/taxas-e-planos)).
* Não pode passar do saldo disponível.
* A conta precisa estar verificada.
## Sacar pela API [#sacar-pela-api]
Também dá para sacar pela API, com uma chave que tenha a permissão de saques (ela vem desligada). Defina um limite diário e libere só o IP do seu servidor. Veja [Saldo e saques na API](/api/saques).
## Saque em mais de uma transferência [#saque-em-mais-de-uma-transferência]
Um saque pode ser pago em mais de uma transferência Pix. Isso é normal: acompanhe em **Saques**, inclusive quando ele é pago só em parte.
## Saque não concluído [#saque-não-concluído]
Se um saque (ou parte dele) não for concluído, o valor volta para o seu saldo e você é avisado. Com dúvidas sobre um saque, fale com o suporte informando a data e o valor.
## Saldo negativo [#saldo-negativo]
Pode acontecer quando uma devolução de MED é confirmada depois que o valor da venda já foi sacado. O valor devido é descontado das vendas seguintes.
# Taxas e planos (/painel/taxas-e-planos)
Você paga uma taxa só nas vendas aprovadas. Criar links e testar a integração não custa nada.
## Escolher o plano [#escolher-o-plano]
Em **Configurações → Taxas** aparecem os planos lado a lado, com a taxa por venda e por saque de cada um. Clique em **Usar este plano** e confirme. Você pode trocar quando quiser.
* Um plano **percentual** costuma sair mais barato em vendas de valor baixo.
* Um plano de **valor fixo** costuma sair mais barato em vendas de valor alto.
Compare com o valor médio das suas vendas.
## Quando a taxa muda [#quando-a-taxa-muda]
A taxa é definida quando a cobrança é criada. Trocar de plano vale para as cobranças criadas depois da troca; as que já existem mantêm a taxa de quando foram criadas.
As taxas podem mudar com aviso prévio, e a nova vale para as cobranças criadas depois da mudança.
## Taxa de saque [#taxa-de-saque]
Pode existir, conforme o plano da sua conta, e aparece em **Configurações → Taxas**. O valor do saque precisa ser maior que ela.
## Taxa negociada [#taxa-negociada]
Contas com taxa negociada pagam a taxa combinada e não veem a escolha de planos. Para volumes maiores, fale com o comercial: [comercial@wysepay.com.br](mailto:comercial@wysepay.com.br).
# Vendas (/painel/vendas)
Em **Vendas** aparecem todas as vendas, de qualquer origem: link de pagamento, Pix pelo painel ou API.
## Situação e etapa [#situação-e-etapa]
Cada venda mostra a situação e, se não foi paga, a etapa em que o cliente parou:
| Situação | Quer dizer |
| ---------------- | ---------------------------------------------------------------------- |
| **Paga** | O pagamento foi confirmado. |
| **Em andamento** | O cliente ainda está no checkout ou o Pix ainda não venceu. |
| **Abandonada** | O cliente ficou 30 minutos sem avançar, ou o Pix venceu sem pagamento. |
As etapas vão de **Abriu o checkout** e **Preenchendo os dados** até **Pix gerado** e **Pago**, passando por **Preenchendo o endereço** quando o link pede endereço.
## Funil [#funil]
No topo fica o gráfico do funil dos links de pagamento, da esquerda para a direita: quantos abriram o checkout, começaram a preencher, geraram o Pix e pagaram. Em cada etapa aparece quantos seguiram da etapa anterior; na última, também quanto de quem abriu chegou a pagar. Onde a faixa mais afina é onde os clientes mais desistem. No celular, as etapas aparecem uma embaixo da outra.
Use o seletor de período ao lado para comparar, por exemplo, este mês com o mês anterior.
## Recuperar uma venda abandonada [#recuperar-uma-venda-abandonada]
Abra a venda. Se o cliente já informou um contato, aparece o bloco **Recuperar venda**, com uma mensagem pronta e botões para enviar no WhatsApp, por e-mail ou copiar o link.
O link abre o checkout com os dados que o cliente já tinha preenchido, para ele só terminar.
## Exportar [#exportar]
O botão **Exportar CSV** segue os filtros da tela. Veja [Relatórios](/painel/relatorios).
# Verificação (/painel/verificacao)
## Identidade [#identidade]
Leva cerca de 2 minutos. Tenha em mãos um documento com foto (RG, CNH ou passaporte):
1. Fotografe o documento.
2. Tire uma selfie, que é comparada com a foto do documento.
A verificação é feita por um parceiro especializado, com apoio de sistema automatizado. O resultado aparece no painel e chega por e-mail.
* **Parou no meio?** Continue de onde parou quando quiser, pelo painel.
* **Faltou uma etapa ou a foto ficou ilegível?** Refaça pelo painel.
* **Foi recusada?** Fale com o suporte para entender o motivo. Você também pode pedir que uma pessoa revise a decisão automatizada.
## Empresa (CNPJ) [#empresa-cnpj]
Consultamos os dados públicos da empresa na Receita Federal, incluindo o quadro de sócios. Podemos pedir:
* o **contrato social** (a última alteração contratual consolidada) ou o estatuto com a ata de eleição da diretoria;
* uma **procuração**, quando o responsável pela conta não aparece entre os sócios.
Os arquivos podem ter até 10 MB. A análise é feita pela equipe da WysePay, normalmente em até 1 dia útil. Se faltar algo, o pedido de ajuste aparece no painel, explicando o que é necessário.
Para MEI, a verificação usa os dados do CNPJ na Receita e a verificação de identidade do responsável.
# Gerar uma cobrança Pix (/api/referencia/gerar-pix)
Cria a cobrança e devolve o código Pix (copia e cola e QR Code). O status começa em `WAITING_PAYMENT`; quando o cliente pagar, chega o webhook `transaction.paid`.
Permissão da chave: `charges:write`.
# Listar transações (/api/referencia/listar-transacoes)
As cobranças da conta, da mais nova para a mais antiga, com filtros. Veja Paginação.
Permissão da chave: `transactions:read`.
# Consultar uma transação (/api/referencia/consultar-transacao)
A situação atual da cobrança. Um Pix vencido sem pagamento aparece como `EXPIRED`.
Permissão da chave: `transactions:read`.
# Listar links de pagamento (/api/referencia/listar-links)
Os links da conta, do mais novo para o mais antigo.
Permissão da chave: `payment_links:read`.
# Criar um link de pagamento (/api/referencia/criar-link)
Cria um checkout com o valor e a descrição. Envie a `url` ao cliente: ele preenche os dados, paga com Pix e vê o comprovante.
Permissão da chave: `payment_links:write`.
# Consultar um link (/api/referencia/consultar-link)
Um link de pagamento e se ele ainda aceita pagamentos (`active`).
Permissão da chave: `payment_links:read`.
# Desativar um link (/api/referencia/desativar-link)
O link para de aceitar pagamentos e o checkout mostra que ele não está disponível. Cobranças já geradas continuam valendo.
Permissão da chave: `payment_links:write`.
# Listar clientes (/api/referencia/listar-clientes)
Os clientes da conta, do mais novo para o mais antigo. Filtre por `document` para achar um CPF ou CNPJ.
Permissão da chave: `customers:read`.
# Cadastrar um cliente (/api/referencia/cadastrar-cliente)
Cadastra o cliente, ou atualiza o que já tem esse CPF ou CNPJ (há um cliente por documento). Campos não enviados ficam como estavam.
Permissão da chave: `customers:write`.
# Consultar um cliente (/api/referencia/consultar-cliente)
Os dados e o endereço de um cliente.
Permissão da chave: `customers:read`.
# Atualizar um cliente (/api/referencia/atualizar-cliente)
Muda os campos enviados. O CPF ou CNPJ não muda.
Permissão da chave: `customers:write`.
# Consultar o saldo (/api/referencia/consultar-saldo)
Disponível, a liberar e bloqueado, e quanto dá para sacar agora.
Permissão da chave: `balance:read`.
# Listar saques (/api/referencia/listar-saques)
Os saques da conta (pelo painel e pela API), do mais novo para o mais antigo.
Permissão da chave: `withdrawals:read`.
# Sacar para uma chave Pix (/api/referencia/sacar)
Tira o valor do saldo e envia para a chave Pix. Precisa da permissão `withdrawals:write`, que vem desligada, e respeita o limite diário e os IPs da chave. Com chave de teste, o saque é simulado e pago na hora.
Permissão da chave: `withdrawals:write`.
# Consultar um saque (/api/referencia/consultar-saque)
A situação de um saque.
Permissão da chave: `withdrawals:read`.
# Simular o pagamento (/api/referencia/simular-pagamento)
Só com chave de teste: marca a cobrança de teste como paga e envia o webhook `transaction.paid`, como num pagamento real.
Permissão da chave: `charges:write`.
# Cobrança criada (/api/referencia/eventos/transaction-created)
Um Pix foi gerado (pela API, pelo painel ou por um link).
# Cobrança paga (/api/referencia/eventos/transaction-paid)
O pagamento foi confirmado. Use este evento para liberar o pedido.
# Cobrança vencida (/api/referencia/eventos/transaction-expired)
O Pix passou do `expiresAt` sem pagamento. Cancele o pedido ou gere uma cobrança nova.
# MED aberto (/api/referencia/eventos/dispute-created)
O pagador contestou um pagamento pelo banco dele.
# MED respondido (/api/referencia/eventos/dispute-responded)
A sua defesa do MED foi enviada.
# MED ganho (/api/referencia/eventos/dispute-won)
A contestação foi negada: o valor volta a ficar disponível para você.
# MED perdido (/api/referencia/eventos/dispute-lost)
A devolução foi confirmada: o valor foi devolvido ao pagador.
# Saque criado (/api/referencia/eventos/withdrawal-created)
Um saque foi pedido, pelo painel ou pela API.
# Saque pago (/api/referencia/eventos/withdrawal-paid)
O dinheiro chegou na chave Pix de destino.
# Saque pago em parte (/api/referencia/eventos/withdrawal-partially-paid)
Parte das transferências do saque não foi concluída; o valor dela voltou ao saldo.
# Saque não concluído (/api/referencia/eventos/withdrawal-failed)
O saque não foi feito; o valor voltou ao saldo.