Pular para o conteúdo principal

Payout em lote: a outra PayoutPolicy real

Todos os capítulos até aqui rodam sob PayoutPolicy.Immediate — o padrão do Ishtaran, e o que todo exemplo funcional deste site usa: executeSettlement() paga a Bob e a comissão da Mercatto no mesmo momento, construindo um SigningRequest real contra a própria wallet de execução da Mercatto (ver Settlement e Split). Existe um segundo modo real e público — Manual — no qual executeSettlement() nunca constrói nenhum SigningRequest. Ele grava um lançamento real e balanceado no Ledger (Reserved(pagador)Payable(beneficiário)) e completa imediatamente. O dinheiro só de fato se move depois, quando a Mercatto cria explicitamente um PayoutBatch.

Quando usar cada uma

  • Immediate — o caso comum, e o padrão correto. Um beneficiário é pago no momento em que seu Settlement executa. Use este a menos que tenha um motivo específico para acumular.
  • Manual — útil quando você quer agrupar muitos Settlements pequenos em transmissões on-chain maiores e menos frequentes (menor custo total de rede), ou quando sua própria cadência de pagamento deliberadamente não é "no instante em que um Settlement acontece" (ex.: um repasse semanal a vendedores). O saldo Payable de um beneficiário (payout.getPayableSummary().accrued) pode crescer ao longo de vários Settlements antes de qualquer coisa ser de fato paga.

Threshold e Scheduled também existem no modelo de domínio (um disparo automático quando um saldo cruza um limiar, ou por agendamento cron) mas ainda não têm nenhuma rota pública para disparo — este capítulo, e esta plataforma, só criam um PayoutBatch com trigger = Manual.

Dois passos de bootstrap que este capítulo exige, nenhum necessário em mais nenhum lugar deste tutorial

A própria PayoutPolicy é uma decisão do Platform Owner, não algo que a API Key da própria Mercatto ou uma sessão de Member consigam configurar — POST /v1/admin/organizations/{organizationId}/payout-policy, autenticada com uma Platform Owner API Key. Nenhum SDK oficial expõe essa rota de propósito: não é uma capacidade de Data Plane que o backend de um integrador chamaria em tempo de execução, do mesmo jeito que nenhum SDK expõe a configuração da Pricing Policy global da plataforma. Real, achado na mesma sessão deste capítulo: antes desta rota existir, ConfigurePayoutPolicyCommand não tinha nenhum caminho HTTP, público ou administrativo — toda a superfície de payout em lote abaixo era inatingível por qualquer ator real.

ExecutionSource é a wallet/endereço que paga pela própria transmissão do lote — um conceito genuinamente diferente do ExecutionDestination de um beneficiário (onde eles recebem os fundos) e da própria wallet de execução da Mercatto usada no Settlement. Registre um por (Organization, Environment, AssetNetwork) antes do primeiro PayoutBatch real, a mesma categoria de bootstrap do NetworkCostPayerAccount (ver Self-Custody).

Passo a passo

// 1. O Platform Owner muda a PayoutPolicy para Manual nesta Organization/AssetNetwork.
await fetch(`${baseUrl}/v1/admin/organizations/${organizationId}/payout-policy`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Platform-Owner-Key': platformOwnerApiKey },
body: JSON.stringify({ assetNetworkId, mode: 3 /* Manual */ }),
});

// 2. Um NetworkExecutionQuote pode ser consultado isoladamente, a qualquer momento -- leitura pura.
const preview = await mercatto.networkExecution.quote(
environmentId, assetNetworkId,
[{ destinationAddress, amount: '1', kind: NetworkOperationKind.TRANSFER, reference: 'preview' }],
NetworkCostPayer.INTEGRATOR,
);

// 3. O Settlement acumula -- sem SigningRequest, completa de forma síncrona.
const executed = await mercatto.settlements.executeSettlement(transactionId);
const settlement = await mercatto.settlements.get(executed.settlementId);
// settlement.status.name === 'COMPLETED'; settlement.signingRequestId === null

const bobSummary = await mercatto.payout.getPayableSummary(bobAccountId, assetNetworkId);
// bobSummary.accrued > 0; bobSummary.paid === '0' -- nada se moveu ainda

// 4. Registrar o ExecutionSource que vai financiar a transmissão do lote.
await mercatto.executionSources.register(organizationId, environmentId, assetNetworkId, walletId, derivationReference, address);

// 5. Criar o lote -- trigger Manual, beneficiários explícitos com Payable real acumulado.
const created = await mercatto.payout.createBatch(organizationId, environmentId, assetNetworkId, [bobAccountId, mercattoRevenueAccountId]);
// created.payoutBatchId é null só quando nenhum dos titulares informados tinha Payable positivo -- um no-op legítimo.

// 6. Assinar cada leg com o signer da própria wallet do ExecutionSource (nunca a de um
// beneficiário, nunca a wallet de Settlement da Mercatto), submeter, simular confirmação
// (Sandbox), esperar Completed -- exatamente o mesmo protocolo de assinatura self-custody de
// Settlement/Withdrawal (ver Self-Custody).

Assim que o lote atinge Completed, payout.getPayableSummary() reflete o que mudou: accrued volta a 0 para cada beneficiário do lote, e paid cresce exatamente pelo valor que era devido — a mesma semântica de Delivered de um payout de Settlement Immediate, só que em outro cronograma.

Rode você mesmo

A versão completa e executável — mesmas chamadas, mesma ordem, HTTP real, nunca simulado — é examples/marketplace-mercatto/scenarios/payout-batch-manual.ts no repositório da plataforma (SDK TypeScript). Ela precisa de uma coisa que nenhum outro cenário do catálogo precisa: uma Platform Owner API Key real (MERCATTO_PLATFORM_OWNER_API_KEY) para executar o passo 1 acima — veja o próprio README.md do catálogo de cenários para a configuração exata.