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.