SDKs oficiais
A Demo Pay publica clientes oficiais para Node.js, Python e .NET. Eles envolvem a mesma API REST /v1 descrita na Referência da API — tudo que dá para fazer com curl, dá para fazer sem SDK. O que os SDKs acrescentam é justamente a parte fácil de errar sem perceber: idempotência, política de retry e verificação de assinatura de webhook.
| Linguagem | Pacote | Instalação | Runtime |
|---|---|---|---|
| Node.js / TypeScript | @demo/node | npm i @demo/node | Node 18+ |
| Python | demo | pip install demo | Python 3.8+ |
| .NET / C# | Demo Pay | dotnet add package DemoPay | netstandard2.0, net8.0 |
Quem integra em PHP pode usar o plugin de WooCommerce ou chamar a API direto; um pacote Composer avulso ainda não é publicado.
#O que os três garantem
Os três clientes são idênticos em comportamento, não só parecidos. A verificação de assinatura de webhook, em particular, roda contra um conjunto compartilhado de vetores de teste no CI — se o Node aceita uma assinatura, Python e .NET aceitam a mesma, byte a byte.
#Autenticação
apikey: dm_live_…O ambiente (teste ou produção) vem da própria chave. Você nunca configura ambiente, e nenhum identificador de conta ou de lojista é passado ou devolvido.
#Idempotência é obrigatória na escrita financeira
Criar cobrança ou saque exige chave de idempotência. Os SDKs recusam a chamada sem ela, em vez de mandar torcendo.
Derive a chave do seu pedido (pedido-1234), nunca de um valor aleatório. Aleatório destrói o mecanismo inteiro: se a sua requisição chegou mas a resposta se perdeu, repetir com chave nova cria uma segunda cobrança; repetir com a mesma chave devolve a original.
#Política de retry
| Situação | Repete? |
|---|---|
GET / HEAD em 5xx ou erro de rede | sim |
POST com chave de idempotência | sim — a mesma chave em toda tentativa |
POST sem chave de idempotência | nunca |
Qualquer 4xx | nunca — o pedido está errado, repetir não conserta |
O backoff é exponencial com jitter total, com teto de 8s.
A terceira linha é a que importa. Uma falha de rede não te diz se o servidor processou a requisição — só que resposta nenhuma voltou. Repetir uma escrita financeira sem chave em cima dessa ambiguidade é como um cliente é cobrado duas vezes.
#Início rápido
#Node.js
import { DemoPayClient } from '@demo/node';
const demo = new DemoPayClient({ apiKey: process.env.DEMO_API_KEY });
const cobranca = await demo.charges.create(
{
amount: 15000, // R$ 150,00 em centavos — sempre inteiro
currency: 'BRL',
payment_method: 'pix',
customer: { name: 'Maria Silva', document: '12345678901' },
},
{ idempotencyKey: `pedido-${pedidoId}` },
);
cobranca.pix.br_code; // Pix Copia e Cola#Python
import os
from demo import DemoPayClient
demo = DemoPayClient(api_key=os.environ["DEMO_API_KEY"])
cobranca = demo.charges.create(
{
"amount": 15000,
"currency": "BRL",
"payment_method": "pix",
"customer": {"name": "Maria Silva", "document": "12345678901"},
},
idempotency_key=f"pedido-{pedido_id}",
)
cobranca["pix"]["br_code"]O SDK Python não tem dependência de runtime — só a biblioteca padrão. Um SDK de pagamentos roda dentro do seu processo; cada pacote transitivo que ele trouxesse viraria superfície de supply chain que você herda de nós.
#C#
using DemoPay;
// Registre como SINGLETON — é thread-safe e reaproveita o HttpClient.
// Um cliente por requisição esgota portas TCP, a armadilha clássica em .NET.
var demo = new DemoPayClient(Environment.GetEnvironmentVariable("DEMO_API_KEY")!);
var cobranca = await demo.Charges.CreateAsync(new
{
amount = 15000,
currency = "BRL",
payment_method = "pix",
customer = new { name = "Maria Silva", document = "12345678901" },
}, idempotencyKey: $"pedido-{pedidoId}");
var brCode = cobranca!.RootElement.GetProperty("pix").GetProperty("br_code").GetString();#Verificando webhooks
A verificação não precisa de chave de API — instancie o utilitário sozinho.
// Node — Express com parser de corpo bruto
app.post('/webhooks/demo', express.raw({ type: 'application/json' }), (req, res) => {
if (!demo.webhooks.verify(req.body, req.headers['x-demo-signature'], segredo)) {
return res.status(401).end();
}
const evento = JSON.parse(req.body.toString('utf8'));
res.status(200).end(); // qualquer 2xx confirma a entrega
});# Python — Flask
if not webhooks.verify(request.get_data(), request.headers.get("X-Demo-Signature"), segredo):
return "", 401// C# — minimal API
if (!webhooks.Verify(corpo, req.Headers["X-Demo-Signature"], segredo))
return Results.Unauthorized();Passe os bytes brutos. Não desserialize e re-serialize o corpo antes de verificar: qualquer diferença de espaço ou de ordem de chave muda o HMAC e a assinatura falha. É o bug de integração de webhook mais comum, em qualquer linguagem.
Assinaturas com mais de 5 minutos são recusadas por padrão, o que impede que um POST capturado uma vez seja reenviado para sempre. Veja Webhooks para o formato da assinatura e a agenda de retentativa.
#Erros
Todos os SDKs levantam erros tipados carregando status, code, type e request_id.
| Tipo de falha | Node | Python | C# |
|---|---|---|---|
| 401 / 403 | DemoPayAuthError | DemoPayAuthError | DemoPayAuthException |
| 400 / 422 | DemoPayValidationError | DemoPayValidationError | DemoPayValidationException |
| 409 | DemoPayConflictError | DemoPayConflictError | DemoPayConflictException |
| Nenhuma resposta | DemoPayNetworkError | DemoPayNetworkError | DemoPayNetworkException |
Ramifique pelo code, nunca pela mensagem — mensagem é escrita para gente ler e muda sem aviso. O code é contrato; veja Erros para o catálogo.
O erro de rede não herda do erro de API em nenhum dos três, de propósito. Quando resposta nenhuma voltou, você não sabe se o servidor processou. Tratar os dois no mesmo ramo esconde exatamente a distinção que decide se repetir é seguro.
Cite o request_id ao falar com o suporte — ele localiza a requisição exata no nosso log.