Skip to main content
Payout é a conversão Crypto → BRL. O receiver deposita crypto em um endereço fornecido pela plataforma e recebe BRL via PIX.

Etapas

  1. Cotação payout.
  2. Criação da ordem — plataforma gera endereço de depósito.
  3. Depósito crypto — receiver envia para o endereço.
  4. Conversão — swap para USDC (ou já USDC) → BRL.
  5. PIX outbound — plataforma envia BRL para a chave PIX configurada.
  6. Webhookorder.completed.

Cotação

curl -X POST https://api.astronpay.co/api/v1/quote/payout \
  -H "x-api-key: $API_KEY" -H "x-api-secret: $API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "receiverId": "rcv_...", "amountCrypto": 200.00, "sourceToken": "USDC" }'
O campo sourceToken indica o token que o receiver vai depositar. O padrão é "USDC".

Criação da ordem

curl -X POST https://api.astronpay.co/api/v1/payout \
  -H "x-api-key: $API_KEY" -H "x-api-secret: $API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "receiverId": "rcv_...",
    "quoteId": "qt_...",
    "amountCrypto": 200.00,
    "pixKey": "12345678901",
    "pixKeyType": "CPF",
    "recipientName": "João Silva",
    "recipientDocument": "12345678901"
  }'
A resposta inclui dois campos críticos para o passo seguinte:
  • depositAddress — endereço Solana da plataforma para onde o crypto deve ser enviado. É o mesmo endereço para todas as orders de payout, então não basta apenas transferir o valor: você também precisa marcar essa transferência como pertencente a esta order específica usando o paymentReference abaixo.
  • paymentReference — uma PublicKey Solana não custodial (sem chave privada, sem saldo associado) gerada exclusivamente para esta order. É um identificador no padrão Solana Pay que permite à plataforma correlacionar o depósito on-chain com a sua order.
Valores acima do cotado entram como over-deposit e ficam em conciliação manual. Valores abaixo não disparam a conversão — a ordem expira.

Construindo a transação de depósito

Caminho recomendado: POST /payout/build-transfer-tx

A maneira mais simples — e que evita as armadilhas mais comuns (esquecer o reference, blockhash expirado, instruction malformada) — é deixar a plataforma montar a transação para você:
curl -X POST https://api.astronpay.co/api/v1/payout/build-transfer-tx \
  -H "x-api-key: $API_KEY" -H "x-api-secret: $API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "orderId": "ord_01hxy..." }'
Resposta:
{
  "transaction": "<base64-tx pronta para assinar>",
  "blockhash": "Bv2nMq...",
  "lastValidBlockHeight": 312415221,
  "expiresAt": "2026-04-27T18:32:00.000Z",
  "fromAddress": "DhTfJ1x2...",
  "depositAddress": "3JcVH1j1...",
  "paymentReference": "EKFB2LCy...",
  "amountCrypto": 0.204409,
  "sourceToken": "USDC"
}
Em seguida, envie a transaction para POST /merchant/wallet/sign-and-send-transaction (se estiver usando a wallet gerenciada do merchant) ou para POST /receivers/{id}/wallet/sign-and-send-transaction. A plataforma assina, paga o gas, transmite e devolve o hash on-chain.

Caminho avançado: montar a transação por conta própria

Se você precisa de controle total (por exemplo, somar instruções extras na mesma transação ou usar uma wallet self-custodial), pode construir a transação manualmente. Para que o depósito seja detectado, a SPL Transfer precisa incluir o paymentReference como account read-only e não-signer. Sem esse account na lista de keys da instruction, a plataforma não tem como vincular a transferência on-chain à sua order — a order fica em AWAITING_DEPOSIT por 24h e expira.
import {
  Connection,
  PublicKey,
  Transaction,
} from '@solana/web3.js';
import {
  createTransferInstruction,
  getAssociatedTokenAddressSync,
} from '@solana/spl-token';

const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');

const fromWallet   = new PublicKey('<wallet de origem>');
const depositAddr  = new PublicKey(order.depositAddress);
const reference    = new PublicKey(order.paymentReference);
const amountSmallestUnit = BigInt(Math.round(order.amountCrypto * 1e6)); // USDC tem 6 decimais

const fromAta = getAssociatedTokenAddressSync(USDC_MINT, fromWallet);
const toAta   = getAssociatedTokenAddressSync(USDC_MINT, depositAddr);

const transferIx = createTransferInstruction(fromAta, toAta, fromWallet, amountSmallestUnit);

// 👇 Passo crítico: anexa o paymentReference como account não-signer
transferIx.keys.push({
  pubkey: reference,
  isSigner: false,
  isWritable: false,
});

const tx = new Transaction().add(transferIx);
// Não seta tx.feePayer aqui se você for usar uma das wallets gerenciadas
// pela plataforma (ver "Gas patrocinado" abaixo).

const connection = new Connection('https://api.mainnet-beta.solana.com');
const { blockhash } = await connection.getLatestBlockhash('finalized');
tx.recentBlockhash = blockhash;

const txBase64 = tx.serialize({ requireAllSignatures: false }).toString('base64');
Em seguida, envie txBase64 para um dos endpoints de assinatura — POST /merchant/wallet/sign-and-send-transaction se a origem for a wallet gerenciada do merchant, ou POST /receivers/{id}/wallet/sign-and-send-transaction se for a wallet gerenciada de um receiver.

Gas patrocinado

Quando você usa as wallets gerenciadas pela plataforma (createPrivyWallet: true na criação do receiver, ou a wallet do merchant), o fee da Solana é patrocinado pela plataforma. Isso significa:
  • Você não precisa manter SOL nessas wallets para pagar fees.
  • Não defina transaction.feePayer — a plataforma substitui pela sponsor wallet ao enviar.
  • Não chame connection.simulateTransaction() localmente antes de mandar pro nosso endpoint. O RPC público da Solana não enxerga a sponsorship — ele assume que o feePayer da sua tx vai pagar o fee, e a simulação falha com Blockhash not found ou insufficient lamports for fee mesmo quando a transação iria executar normalmente em produção.
  • Pegue o recentBlockhash imediatamente antes de chamar o endpoint (não reutilize um blockhash de mais de ~30s atrás — eles expiram em ~60s).
Se você gerencia a wallet por fora (passou walletAddress na criação do receiver), o gas é por sua conta — mantenha SOL suficiente na wallet pra cobrir os fees (≈ 0.000005 SOL por SPL Transfer).

Status

Mesmo conjunto do payin (ver Ciclo de vida das ordens). Os principais para payout são:
StatusQuando
PENDINGOrdem criada, aguardando depósito crypto.
AWAITING_DEPOSITEndereço gerado, monitorando blockchain.
DEPOSIT_RECEIVEDDepósito detectado; conversão em andamento.
PIX_SENDINGPIX outbound sendo enviado.
PIX_SENT_UNCONFIRMEDPIX enviado, aguardando confirmação.
COMPLETEDPIX entregue ao receiver.
FAILEDErro; ver failureReason.
EXPIREDReceiver não depositou dentro do prazo.

Chaves PIX suportadas

TipoExemplo
CPF12345678901
CNPJ12345678000190
EMAILusuario@example.com
PHONE+5511999999999
RANDOMchave aleatória (UUID v4)
QRCODEpayload EMV completo

Webhooks recebidos

  • order.deposit_received
  • order.processing
  • order.pix_sending
  • order.completed
  • order.failed
Payloads completos em Eventos de webhook.

Consultando uma ordem

curl https://api.astronpay.co/api/v1/payout/$ORDER_ID \
  -H "x-api-key: $API_KEY" -H "x-api-secret: $API_SECRET"