Autenticação
Toda requisição usa HTTP Basic com seu client_id como usuário e client_secret como senha. Crie chaves em /voren-api.
curl https://appvoren.com.br/api/public/v1/ping \
-u "vrn_id_xxx:vrn_live_yyy"Quickstart em 3 passos
- Ative seu recebedor Pagar.me e crie uma chave em /voren-api.
- Crie um produto via API (ou use um existente).
- Gere uma cobrança e receba o resultado por webhook.
# 1) Criar produto
curl -X POST https://appvoren.com.br/api/public/v1/products \
-u "$KEY_ID:$KEY_SECRET" \
-H "Content-Type: application/json" \
-d '{"name":"Curso Voren","price_cents":9700,"status":"active","delivery_methods":["link"],"delivery_url":"https://area.example.com"}'
# 2) Gerar Pix
curl -X POST https://appvoren.com.br/api/public/v1/charges \
-u "$KEY_ID:$KEY_SECRET" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1001" \
-d '{
"product_id":"<uuid>",
"payment_method":"pix",
"customer":{"name":"Ana Silva","email":"ana@example.com","document":"12345678909","phone":"11999998888"}
}'Escopos e ambientes
Cada chave tem escopos e ambiente (live ou test). Chame apenas o mínimo necessário.
products.read,products.writesales.read,sales.write(inclui cobranças e estornos)subscriptions.read,customers.readwebhooks.manage
Idempotência
Envie um header Idempotency-Key em POST. Requisições repetidas com a mesma chave retornam o mesmo recurso e o header Idempotency-Replayed: true.
Rate limits
300 requisições por minuto por chave. Ao exceder, respondemos 429 com header Retry-After em segundos.
Formato de erro
Todos os erros usam JSON estável:
{
"error": "invalid_request",
"message": "Descrição legível do problema.",
"request_id": "req_ab12cd34...",
"details": [{ "path": "customer.email", "message": "Invalid email" }]
}Códigos comuns: unauthorized, invalid_credentials, insufficient_scope, invalid_request, not_found, invalid_state, rate_limited, provider_error, internal_error.
Products
/v1/productsscope: products.read/v1/productsscope: products.write/v1/products/{id}scope: products.read/v1/products/{id}scope: products.writecurl -X POST https://appvoren.com.br/api/public/v1/products \
-u "$KEY_ID:$KEY_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name":"Mentoria Voren",
"description":"3 meses de acompanhamento",
"price_cents":149700,
"status":"active",
"delivery_methods":["email"],
"support_email":"suporte@example.com"
}'Charges (cobranças)
/v1/chargesscope: sales.write/v1/chargesscope: sales.read/v1/charges/{id}scope: sales.readPreço e divisão (split) são sempre calculados no servidor — o campo amount nunca vem do cliente. Métodos: pix, boleto, credit_card.
curl -X POST https://appvoren.com.br/api/public/v1/charges \
-u "$KEY_ID:$KEY_SECRET" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cli-2025-0001" \
-d '{
"product_id":"<uuid>",
"offer_id":"<uuid>",
"payment_method":"credit_card",
"card_token":"card_token_xxx",
"installments":3,
"customer":{"name":"Ana","email":"ana@x.com","document":"12345678909","phone":"11999998888"},
"coupon_code":"BEMVINDO10",
"ref_code":"abc123",
"metadata":{"origin":"landing-a"}
}'Refunds (estornos)
/v1/refundsscope: sales.writeSuporta estorno total ou parcial. Envie sale_id (ou charge_id) e opcionalmente amount_cents para parcial.
# Estorno total
curl -X POST https://appvoren.com.br/api/public/v1/refunds \
-u "$KEY_ID:$KEY_SECRET" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-2025-0001" \
-d '{"sale_id":"<uuid>","reason":"solicitação do cliente"}'
# Estorno parcial (R$ 30,00)
curl -X POST https://appvoren.com.br/api/public/v1/refunds \
-u "$KEY_ID:$KEY_SECRET" \
-H "Content-Type: application/json" \
-d '{"sale_id":"<uuid>","amount_cents":3000}'Sales
/v1/salesscope: sales.read/v1/sales/{id}scope: sales.readFiltros: status, from, to, limit (máx 100), cursor.
Webhooks
/v1/webhooksscope: webhooks.manage/v1/webhooksscope: webhooks.manage/v1/webhooks/{id}scope: webhooks.manage/v1/webhooks/{id}scope: webhooks.manage/v1/webhooks/{id}/testscope: webhooks.manageEventos: sale.paid, sale.failed, sale.refunded, sale.chargeback, sale.canceled, subscription.updated, test.ping. Use * para assinar todos.
Verificar assinatura de webhook
Cada entrega inclui o header X-Voren-Signature: t=<timestamp>,v1=<sha256_hex>. Calcule HMAC-SHA256 sobre ${timestamp}.${raw_body} com seu secret e compare em tempo constante. Rejeite entregas com timestamp com mais de 5 minutos.
// Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyVorenSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
const timestamp = parts.t;
const v1 = parts.v1;
if (!timestamp || !v1) return false;
if (Math.abs(Date.now()/1000 - Number(timestamp)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const a = Buffer.from(v1, "hex");
const b = Buffer.from(expected, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}