Primeira integração

Crie um Checkout de teste e receba a confirmação no seu servidor.

Este guia usa HTTP e um consumidor independente. Não exige chave Stripe no seu produto. Você também pode usar os SDKs para Node.js/Bun, Python e PHP/Laravel.

Webhooks habilitados

A entrega de eventos está habilitada em https://api-pay.adila.co. Este guia usa payment.succeeded; o catálogo completo lista os demais. A tela Webhooks permite configurar destinos e enviar testes sintéticos no sandbox. Encaminhamento local por CLI e automações por condições continuam fora desta entrega.

Prepare o sandbox

Você precisa de uma organização ativa, uma conta Stripe conectada no ambiente de teste e das capabilities necessárias para receber pagamentos. Um owner/admin conclui a coleta financeira pelo fluxo Stripe apresentado pela Adila. Pagamento permitido em sandbox não comprova habilitação live ou de repasses.

Configure no servidor do seu produto:

  • ADILA_PAY_BASE_URL: https://api-pay.adila.co, a API do ambiente de teste (Stripe em test mode).
  • ADILA_PAY_ORGANIZATION_ID: UUID da organização.
  • ADILA_PAY_API_KEY: chave Adila com products:write, payments:write e payments:read.

Use uma API key de teste (pk_test_): o prefixo da chave define o ambiente. Veja Autenticação e Ambientes.

Prepare o consumidor

O exemplo executável utiliza Node.js 22.13+, HTTP nativo e SQLite. Valida assinatura sobre bytes originais, timestamp, organização e sandbox; persiste evento e efeito de demonstração na mesma transação. Não libera acesso a produtos nem chama serviços externos automaticamente.

export ADILA_EXPECTED_LIVEMODE=false
export ADILA_CONSUMER_DATABASE=/caminho/privado/consumer.sqlite
# Configure ADILA_WEBHOOK_SECRET e ADILA_PAY_ORGANIZATION_ID no gerenciador de segredos.
node examples/payment-consumer/server.mjs

O servidor escuta em loopback na porta 5188. Publique /events por um proxy HTTPS com certificado válido. A Adila aceita somente destinos públicos na porta 443, sem credenciais, query strings ou redirects. Nunca exponha o arquivo SQLite ou os segredos pelo proxy.

Cadastre o destino

Um owner/admin/developer autenticado configura o destino na tela Webhooks ou na API. API keys existentes, mesmo *, não podem administrar endpoints nesta versão. Use uma sessão autorizada e a origem confiável da aplicação para:

POST /v2/organizations/{organizationId}/integrations/endpoints
Content-Type: application/json
Idempotency-Key: endpoint-produto-sandbox-001
Origin: <origem confiável da aplicação>
Cookie: <sessão obtida no login autorizado>

{
  "name": "Meu produto sandbox",
  "url": "https://seu-produto.example.com/events",
  "events": ["payment.succeeded"]
}

Guarde o signingSecret retornado em ADILA_WEBHOOK_SECRET. GETs não retornam esse segredo. Não coloque cookies ou segredos em histórico do terminal, navegador público, prints ou logs. Repetições da mesma criação usam a mesma Idempotency-Key e os mesmos parâmetros.

Teste o recebimento sem criar um pagamento

Na tela Webhooks, use Enviar teste em um endpoint ativo de sandbox. Escolha um dos eventos assinados; a Adila enfileira o envio e abre as entregas para acompanhar status HTTP, payload e tentativas. O endpoint continua exigindo HTTPS público: use um túnel HTTPS para receber no seu servidor local.

Pela API, a mesma operação é:

POST /v2/organizations/{organizationId}/integrations/endpoints/{endpointId}/test
Content-Type: application/json
Idempotency-Key: teste-recebimento-sandbox-001
Origin: <origem confiável da aplicação>
Cookie: <sessão obtida no login autorizado>

{ "type": "payment.succeeded" }

O retorno {id,eventId,state,test:true} confirma o enfileiramento. Reutilize a mesma chave e tipo após uma resposta incerta, para evitar duplicar o envio. São permitidos cinco novos testes por minuto por endpoint. Conta conectada, endpoint e instalação devem estar em sandbox; endpoints pausados e tipos não assinados são recusados.

Reconheça os eventos sintéticos

O payload tem test: true, livemode: false, origem adila_test e IDs fictícios. Seu consumidor deve reconhecer essa marca antes de aplicar efeitos de negócio. O exemplo Node/SQLite ignora eventos sintéticos após verificar a assinatura. Um teste verifica o recebimento e a assinatura; não confirma uma cobrança nem deve liberar acesso ao produto.

Continue abaixo para validar um pagamento de sandbox com recursos Stripe reais.

Crie produto e preço

O exemplo abaixo lê as credenciais do ambiente e mantém uma chave por operação lógica. Em um produto real, persista essas chaves antes da primeira tentativa e reutilize-as ao repetir a operação.

Criar catálogo e Checkout no servidor
import { randomUUID } from 'node:crypto';

const base = process.env.ADILA_PAY_BASE_URL;
const org = process.env.ADILA_PAY_ORGANIZATION_ID;
const apiKey = process.env.ADILA_PAY_API_KEY;
if (!base || !org || !apiKey) throw new Error('Configure a integração no servidor');
const prefix = `${base}/v2/organizations/${org}/stripe`;
const keys = { product: randomUUID(), price: randomUUID(), checkout: randomUUID() };

async function create(resource, body, key) {
  const response = await fetch(`${prefix}/${resource}`, {
    method: 'POST',
    headers: { 'content-type': 'application/json', 'x-api-key': apiKey, 'idempotency-key': key },
    body: JSON.stringify(body),
  });
  if (!response.ok) throw new Error(`Adila HTTP ${response.status}`);
  return response.json();
}

const product = await create('products', { name: 'Produto de teste' }, keys.product);
const price = await create('prices', {
  product: product.id, currency: 'brl', unit_amount: 500,
}, keys.price);
const session = await create('checkout/sessions', {
  mode: 'payment', line_items: [{ price: price.id, quantity: 1 }],
  client_reference_id: 'pedido-interno-de-teste',
}, keys.checkout);

// Persista session.id associado ao pedido interno e à organização.
// Entregue session.url somente ao pagador autorizado, sem registrar a URL em logs.

500 representa R$ 5,00. Use apenas cartões/dados oficiais de teste na página Stripe e confirme que ela mostra ambiente de teste. URLs de retorno são definidas pelo operador no backend, não por parâmetros arbitrários do consumidor.

Receba e confira a confirmação

O worker recebe payment_intent.succeeded real e envia payment.succeeded ao destino cadastrado. O consumidor valida assinatura, ambiente e organização, deduplica pelo ID do evento e evita duplicar o efeito pelo payment_id.

Associe esse ID ao pedido usando os registros do seu servidor e o contrato financeiro autorizado. Consulte a sessão que você persistiu:

const response = await fetch(`${prefix}/checkout/sessions/${session.id}`, {
  headers: { 'x-api-key': apiKey },
});
if (!response.ok) throw new Error(`Adila HTTP ${response.status}`);
const checkout = await response.json();
const confirmed = checkout.status === 'complete' && checkout.payment_status === 'paid';
// Confira também pedido, organização, payment_intent, valor e moeda esperados.
// Só então aplique a liberação prevista no seu produto de maneira idempotente.

Abrir Checkout, retornar ao aplicativo ou receber um evento de teste não confirma cobrança. Se o recurso ainda estiver pendente, consulte novamente ou responda 503 ao webhook para permitir retry conforme a política documentada.

Simule falha e recupere

  1. Faça seu consumidor devolver 503 antes de aplicar o efeito.
  2. Produza um pagamento de teste e consulte GET /integrations/endpoints/{id}/deliveries.
  3. Confira a tentativa, o status e a próxima execução; restaure o consumidor.
  4. Confira estado delivered, histórico e um único efeito persistido.
  5. Para uma falha definitiva, um owner/admin/developer usa POST /integrations/deliveries/{id}/retry, dentro dos limites de idade/tentativas.

Há até seis tentativas automáticas e uma janela adicional de reenvio até 12 no total, dentro de 72 horas. A entrega é pelo menos uma vez; seu consumidor precisa tolerar repetições. Contrato completo de eventos.

Finalize o teste

Estorne cobranças exclusivamente temporárias, expire sessões abertas e arquive produtos/preços de QA quando autorizado. Pause o endpoint de teste para impedir novos eventos. Confira a limpeza e mantenha os recibos/auditoria necessários. Um teste de sandbox não comprova repasse bancário nem operação com dinheiro real.

Nesta página