SDKs oficiais
A Moonip publica clientes oficiais para Node.js, Python, PHP 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.
Os quatro compartilham uma versão só: 0.8.0.
| Linguagem | Pacote | Instalação | Runtime |
|---|---|---|---|
| Node.js / TypeScript | @moonip/node | npm i @moonip/node | Node 18+ |
| Python | moonip | pip install moonip | Python 3.8+ |
| PHP | moonip/sdk-php | composer require moonip/sdk-php | PHP 8.1+ |
| .NET / C# | Moonip | dotnet add package Moonip | netstandard2.0, net8.0 |
#O que os quatro garantem
Os quatro 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, PHP e .NET aceitam a mesma, byte a byte.
#Famílias de recursos
Todo cliente expõe as mesmas seis famílias de recursos — as mesmas rotas, os mesmos formatos de fio da Referência da API:
| Família | Node | Python | PHP | .NET |
|---|---|---|---|---|
Cobranças (/v1/charges) | client.charges | client.charges | $client->charges | client.Charges |
Operações de pagamento (/v1/payments) | client.payments | client.payments | $client->payments | client.Payments |
Saques (/v1/withdrawals) | client.payouts | client.payouts | $client->payouts | client.Payouts |
Saldo (/v1/wallets) | client.balance | client.balance | $client->balance | client.Balance |
Webhooks (/v1/webhooks) | client.webhooks (incl. client.webhooks.endpoints) | client.webhooks (incl. client.webhooks.endpoints) | $client->webhooks (incl. $client->webhooks->endpoints) | client.Webhooks (incl. client.Webhooks.Endpoints) |
| Casos MED | client.medCases | client.med_cases | $client->medCases | client.MedCases |
Os nomes dos métodos seguem a convenção de cada linguagem (client.payments.create(input, { idempotencyKey }) em Node, client.payments.create(input, idempotency_key) em Python, $client->payments->create($input, $idempotencyKey) em PHP, client.Payments.CreateAsync(input, idempotencyKey) em .NET). payments.refund está ausente de propósito: a API responde 403 para toda requisição com chave de API — o error.code é "forbidden" e a mensagem começa com o marcador REFUND_ADMIN_ONLY (prefixo de mensagem, não um valor de code) — porque reembolsos vêm do painel administrativo.
#Autenticação
Authorization: Bearer ak_live_…Os SDKs enviam o cabeçalho canônico Authorization: Bearer; o cabeçalho legado apikey: continua aceito pela API por compatibilidade. 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 { MoonipClient } from '@moonip/node';
const moonip = new MoonipClient({ apiKey: process.env.MOONIP_API_KEY });
const cobranca = await moonip.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 moonip import MoonipClient
moonip = MoonipClient(api_key=os.environ["MOONIP_API_KEY"])
cobranca = moonip.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.
#PHP
<?php
require __DIR__ . '/vendor/autoload.php';
use Moonip\MoonipClient;
$cliente = new MoonipClient(['apiKey' => getenv('MOONIP_API_KEY')]);
$cobranca = $cliente->charges->create([
'amount' => 15000,
'currency' => 'BRL',
'payment_method' => 'pix',
'customer' => ['name' => 'Maria Silva', 'document' => '12345678901'],
], 'pedido-' . $pedidoId);
$cobranca['pix']['br_code'];#C#
using Moonip;
// Registre como SINGLETON — é thread-safe e reaproveita o HttpClient.
// Um cliente por requisição esgota portas TCP, a armadilha clássica em .NET.
var moonip = new MoonipClient(Environment.GetEnvironmentVariable("MOONIP_API_KEY")!);
var cobranca = await moonip.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/moonip', express.raw({ type: 'application/json' }), (req, res) => {
if (!moonip.webhooks.verify(req.body, req.headers['x-moonip-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-Moonip-Signature"), segredo):
return "", 401// C# — minimal API
if (!webhooks.Verify(corpo, req.Headers["X-Moonip-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 | PHP | C# |
|---|---|---|---|---|
| 401 / 403 | MoonipAuthError | MoonipAuthError | MoonipAuthError | MoonipAuthException |
| 400 / 422 | MoonipValidationError | MoonipValidationError | MoonipValidationError | MoonipValidationException |
| 409 | MoonipConflictError | MoonipConflictError | MoonipConflictError | MoonipConflictException |
| Nenhuma resposta | MoonipNetworkError | MoonipNetworkError | MoonipNetworkError | MoonipNetworkException |
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 único marcador de mensagem documentado é REFUND_ADMIN_ONLY (reembolsos são apenas no painel e respondem forbidden comum); combine-o como prefixo, não como valor de code.
O erro de rede não herda do erro de API em nenhum dos quatro, 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.