Skip to content

Developers

Payments API

Create crypto charges from your store's system, show the QR code to your customer and receive a signed notification when the payment arrives. The money goes straight to the account's wallet, with no intermediary. Start in the test environment, with no real money.

Authentication

Create the key under Charge → API and webhook (the account must have two-step verification turned on). Send it in the Authorization header and keep it on your server only. Choose the access level: “read and create charges” (store) or “read only” (reconciliation, reports). A read-only key that tries to create a charge gets a 403.

Authorization: Bearer ib_XXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Create a charge

token: USDC or SOL. valor: decimal with a dot, or omit for an open amount. validade_horas: 1, 24, 72, 168 or 720 (default 24). externo_id: your store’s order number, unique per account. Send Idempotency-Key (UUID) so you can safely retry if the network drops.

curl -X POST https://ducripto.com/api/v1/cobrancas \
  -H "Authorization: Bearer ib_XXXXXXXXXXXX_…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a8e-4b1d-4c55-9a3e-0d2b7f9e1c11" \
  -d '{"token":"USDC","valor":"25.90","descricao":"Pedido 1042","externo_id":"1042","validade_horas":24}'
{
  "id": "9b2f…", "estado": "pendente", "externo_id": "1042",
  "token": "USDC", "decimais": 6, "valor_unidades": "25900000", "valor": "25.9",
  "url_pagamento": "https://ducripto.com/pagar/9b2f…",
  "solana_pay": "solana:<carteira>?amount=25.9&spl-token=EPjF…&reference=…",
  "expira_em": "…", "pagamento": null
}

Retrieve

Amounts are always returned as strings in the token’s integer units (valor_unidades), with no loss of precision. estado: pendente, paga, expirada or cancelada. The list is paginated: use limite (1 to 100, default 50) and pass the value of proximo in depois to read the next page; a null proximo means the end. For wallets that fetch the transaction from the server (Solana Pay "transaction request"), use solana_pay_transacao; for open-amount charges, append ?quantidade= to the URL before encoding.

curl https://ducripto.com/api/v1/cobrancas/9b2f… -H "Authorization: Bearer ib_…"
curl "https://ducripto.com/api/v1/cobrancas?externo_id=1042" -H "Authorization: Bearer ib_…"
curl "https://ducripto.com/api/v1/cobrancas?limite=50&depois=<proximo>" -H "Authorization: Bearer ib_…"

Webhook: charge paid

You receive a JSON POST with tipo cobranca.paga. Verify the signature: hex HMAC-SHA256 of "timestamp.body" with your secret, and reject if the timestamp is older than 5 minutes. Reply 2xx; otherwise we retry with growing delays. Use evento_id to ignore duplicates.

import { createHmac, timingSafeEqual } from 'node:crypto';

// corpo = texto CRU da requisição (antes de JSON.parse)
// x-interbank-assinatura-anterior só vem durante uma troca de chaves da plataforma: aceite qualquer uma
export function webhookValido(corpo, cab, segredo) {
  const ts = cab['x-interbank-timestamp'];
  if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const esperado = createHmac('sha256', segredo).update(`${ts}.${corpo}`).digest();
  return ['x-interbank-assinatura', 'x-interbank-assinatura-anterior'].some((h) => {
    const veio = Buffer.from(cab[h] ?? '', 'hex');
    return veio.length === esperado.length && timingSafeEqual(veio, esperado);
  });
}

Payment button on your site (SDK)

One script tag draws the branded button and opens checkout in a new tab; once payment confirms, your site gets the checkout:paga event. The event is for the screen only: fulfil the order after confirming on your server (signed webhook or GET of the request).

<!-- data-cobranca: id devolvido por POST /api/v1/cobrancas · data-voltar: opcional, só do seu site -->
<script src="https://ducripto.com/sdk/v1/checkout.js" data-cobranca="9b2f…" data-voltar="/obrigado" async></script>
<script>
  addEventListener('checkout:paga', (e) => mostrarObrigado(e.detail.cobranca));
</script>

Receive USDC from any token

Send "aceita_conversao": true when creating a USDC charge with an amount. On the payment page the payer picks SOL, USDT, JUP or BONK, sees how much they will spend, and the same transaction converts and delivers the exact USDC amount to your wallet. The webhook and lookup are the same: what counts is the USDC that arrived. Not available in test mode.

curl -X POST https://ducripto.com/api/v1/cobrancas -H "Authorization: Bearer ib_…" -H "Content-Type: application/json" \
  -d '{"token":"USDC","valor":"49.90","externo_id":"1043","aceita_conversao":true}'
# → { "aceita_conversao": true, "url_pagamento": "https://ducripto.com/pagar/…", … }
# quem paga escolhe SOL, USDT, JUP ou BONK na página; você recebe 49.90 USDC exatos

Test environment (sandbox)

Create a key in Test mode (it starts with ib_test_). It uses the same API but only sees and creates test charges, which live on the test network (mode: "teste" in the response and the webhook) and never reach your balance. To test without a wallet, simulate the payment: POST /api/v1/cobrancas/{id}/simular-pagamento (for an open amount, send {"valor": "…"}). The cobranca.paga webhook arrives signed, just like a real one. A live key cannot simulate, and neither key sees the other one's charges.

# chave de teste (ib_test_…): mesma API, só cobranças de teste, rede de testes
curl -X POST https://ducripto.com/api/v1/cobrancas -H "Authorization: Bearer ib_test_XXXXXXXXXXXX_…" \
  -H "Content-Type: application/json" -d '{"token":"USDC","valor":"25.90","externo_id":"teste-1"}'
# → { "id": "9b2f…", "modo": "teste", "rede": "devnet", "estado": "pendente", … }

curl -X POST https://ducripto.com/api/v1/cobrancas/9b2f…/simular-pagamento -H "Authorization: Bearer ib_test_XXXXXXXXXXXX_…"
# → { "estado": "paga", "pagamento": { "assinatura": "simulado_…", … } } e o webhook cobranca.paga com "modo": "teste"

Errors and limits

Errors come as { erro: { codigo, mensagem } }. 401 invalid or revoked key · 403 the key lacks the operation's scope · 404 charge from another account or nonexistent · 409 repeated externo_id or Idempotency-Key reused with a different body · 422 invalid data · 429 over 120 calls per minute per key.

Contract (OpenAPI)

The full OpenAPI 3.1 specification (charges, errors and the webhook) is at /api/v1/openapi.json — import it into Postman, Insomnia or a client generator. It is generated from the same schemas the tests check against the real responses.

curl https://ducripto.com/api/v1/openapi.json