Pagamentos
Split de pagamento
Divida o valor de um pagamento entre recebedores com parcelas fixas e percentuais, saiba para onde vao os centavos e simule antes de cobrar.
O split divide o valor de um pagamento entre recebedores: o vendedor de um marketplace, um parceiro, um afiliado. Cada recebedor leva exatamente a parcela configurada para ele. O que nao foi dividido fica com a conta principal - a do estabelecimento que cobra - e nao gera linha de split.
Por isso nao e preciso cadastrar a plataforma como recebedor para ficar com a comissao: "90% para o vendedor" basta, e os outros 10% ficam com voce.
Cadastre os recebedores
curl -X POST http://localhost:3333/v1/recipients \
-H "Authorization: Bearer $MINSTREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Loja do Vendedor ME",
"document": "12.345.678/0001-95",
"email": "financeiro@lojadovendedor.com.br",
"externalRecipientId": "rec_8812",
"bankCode": "341",
"bankBranch": "1234",
"bankAccount": "56789-0",
"pixKey": "12345678000195",
"defaultSplitType": "percentage",
"defaultSplitValue": 9000,
"liableForChargeback": true
}'
| Campo | Para que serve |
|---|---|
document, documentType | CPF ou CNPJ (inclusive o alfanumerico), com digito verificador conferido. O tipo e deduzido pelo tamanho; para outros documentos, informe passport ou other. |
externalRecipientId | Id do recebedor no parceiro (subadquirente). E o que vai nas instrucoes de split. Unico por ambiente. |
connectorAccountId | Conta de conector em que o recebedor esta registrado. |
bankCode, bankBranch, bankAccount | Conta para repasse: codigo COMPE do banco (3 digitos), agencia e conta com digito. Os tres vem juntos; bankAccountType e checking (padrao) ou savings. |
pixKey | CPF, CNPJ, e-mail, celular com +55 ou chave aleatoria. |
defaultSplitType, defaultSplitValue | Regra padrao do recebedor, usada pelo simulador e sugerida ao montar o no Split. Vem em par. |
liableForChargeback | O recebedor arca com o chargeback da parcela dele. Padrao false. |
chargeProcessingFee | O recebedor paga parte da tarifa de processamento. Padrao false (veja Tarifa de processamento). |
- O recebedor pertence ao ambiente da credencial que o criou: um split de producao nunca paga um recebedor de sandbox, e vice-versa.
- Celular sem o +55 e recusado:
11987654321seria indistinguivel de um CPF, e repasse para a chave errada nao tem estorno automatico. - A listagem e os eventos de webhook trazem documento, conta e chave Pix
mascarados (
masked: true). Os dados completos so voltam emGET /v1/recipients/:ide na resposta de quem acabou de envia-los.
Gerenciar recebedores
| Endpoint | O que faz |
|---|---|
GET /v1/recipients | Lista com filtros (active, search, document, splitType, destination, liableForChargeback) e o volume dividido no periodo (from e to, padrao 30 dias). |
GET /v1/recipients/summary | Recebedores ativos, volume dividido contra o periodo anterior, total a repassar e maiores recebedores. |
GET /v1/recipients/:id | Dados completos, totais e os fluxos publicados que usam o recebedor (usedByFlows). |
GET /v1/recipients/:id/splits | Parcelas do recebedor, pagamento a pagamento, da mais recente para a mais antiga. |
GET /v1/recipients/:id/ledger | Lancamentos da conta do recebedor no razao contabil, com o saldo. |
GET /v1/recipients/:id/payouts | Repasses do recebedor. |
PATCH /v1/recipients/:id | Edicao parcial: campo ausente fica como esta, null apaga. |
DELETE /v1/recipients/:id | Remocao logica. Os splits ja gravados continuam no historico (preservedSplits informa quantos). |
Leitura exige o escopo recipients:read; cadastro, edicao e remocao,
recipients:write.
Desativar (active: false) ou remover um recebedor que esta no split de um
fluxo publicado e recusado com 409, com os fluxos em meta.flows: os
pagamentos desse fluxo passariam a falhar. Retire o recebedor do no Split e
publique o fluxo antes. Com repasse em andamento, a remocao tambem e recusada.
Toda troca de banco ou chave Pix fica na auditoria como
recipient.payout_destination_changed - trocar a conta de repasse e a fraude
classica de marketplace.
Divida o pagamento
Envie splits na criacao do pagamento:
curl -X POST http://localhost:3333/v1/payments \
-H "Authorization: Bearer $MINSTREL_API_KEY" \
-H "Idempotency-Key: pedido-7781" \
-H "Content-Type: application/json" \
-d '{
"amount": 10003,
"customerId": "cus_01HQ...",
"paymentMethodId": "pm_01HQ...",
"splits": [
{ "recipientId": "rcp_vendedor", "type": "percentage", "value": 2500 },
{ "recipientId": "rcp_parceiro", "type": "percentage", "value": 2500 }
],
"splitRemainderRecipientId": "rcp_parceiro"
}'
Campo de splits[] | Obrigatorio | Significado |
|---|---|---|
recipientId | sim | Recebedor cadastrado, ativo e do mesmo ambiente da chave. |
type | sim | percentage ou fixed. |
value | sim | Inteiro. Em percentage, basis points: 1000 e 10%, 250 e 2,5%. Em fixed, centavos: 1500 e R$ 15,00. |
liableForChargeback | nao | Omitido, vale o padrao cadastrado no recebedor. |
chargeProcessingFee | nao | Omitido, vale o padrao cadastrado no recebedor. |
- No maximo 20 parcelas, uma por recebedor.
typeevaluesao obrigatorios na API. A regra padrao do recebedor (defaultSplitTypeedefaultSplitValue) so e usada pelo simulador.splitRemainderRecipientIde opcional. Precisa ser um dos recebedores desplitse fica com os centavos de arredondamento. Sem ele, os centavos ficam com a conta principal.
Como o valor e calculado
A conta segue quatro regras, nesta ordem:
- As parcelas fixas sao reservadas primeiro.
- Os percentuais valem sobre o que sobra (valor menos fixas), literalmente: 10% sao 10%. Cada parte e truncada no centavo, entao nenhum recebedor leva mais do que configurou.
- Centavos de arredondamento sao a diferenca entre o grupo percentual
truncado uma unica vez e a soma das partes truncadas uma a uma. Vao para
splitRemainderRecipientIdou ficam com a conta principal. - O que nao foi dividido fica com a conta principal, sem linha de split.
A soma sempre fecha ao centavo: parcelas mais conta principal da exatamente o valor do pagamento. O banco reforca a regra com um trigger que impede gravar splits que somem mais do que o pagamento.
10% sozinho
R$ 100,00 com uma unica parcela de 10% (value: 1000):
| Quem | Valor |
|---|---|
| Recebedor | R$ 10,00 |
| Conta principal | R$ 90,00 |
Dois de 50% com centavo impar
R$ 100,01 em duas parcelas de 50% (value: 5000). Cada metade, truncada, da
R$ 50,00; o grupo de 100% vale R$ 100,01. Sobra 1 centavo:
| Quem | Sem recebedor dos centavos | Centavos para A |
|---|---|---|
| Recebedor A | R$ 50,00 | R$ 50,01 |
| Recebedor B | R$ 50,00 | R$ 50,00 |
| Conta principal | R$ 0,01 | R$ 0,00 |
"Centavos para A" e splitRemainderRecipientId apontando para o recebedor A.
25% mais 25% com os centavos para B
E o pagamento do exemplo acima: R$ 100,03 com duas parcelas de 25% e
splitRemainderRecipientId apontando para B. As partes truncadas dao R$ 25,00
cada; o grupo de 50%, truncado uma vez, da R$ 50,01:
| Quem | Valor |
|---|---|
| Recebedor A | R$ 25,00 |
| Recebedor B | R$ 25,01 (R$ 25,00 mais 1 centavo) |
| Conta principal | R$ 50,02 |
Sem splitRemainderRecipientId, B ficaria com R$ 25,00 e a conta principal com
R$ 50,03.
Fixo mais percentual
R$ 100,00 com R$ 15,00 fixos para A (type: fixed, value: 1500) e 20% para
B. A parcela fixa sai primeiro, e os 20% valem sobre os R$ 85,00 restantes:
| Quem | Valor |
|---|---|
| Recebedor A | R$ 15,00 |
| Recebedor B | R$ 17,00 (20% de R$ 85,00) |
| Conta principal | R$ 68,00 |
Parcela zerada
0,5% (value: 50) de uma venda de R$ 1,50 da R$ 0,0075, truncado para zero. A
parcela e gravada com valor zero, mas nao vai ao parceiro.
Erros de validacao
O split da requisicao e validado antes de o pagamento ser criado:
configuracao invalida recusa a chamada inteira e nao deixa pagamento pela
metade. Todos os casos respondem 422, com type: invalid_request_error,
code: validation_failed e param: splits - menos o ultimo, em que param e
o proprio splitRemainderRecipientId:
| Situacao | Em meta |
|---|---|
| Parcelas fixas somam mais que o valor do pagamento | reason: fixed_exceeds_amount |
Percentuais somam mais de 100% (10000) | reason: percentage_over_100 |
| O mesmo recebedor em duas parcelas | recipientId |
| Recebedor inexistente, inativo ou de outro ambiente | missingRecipientIds |
| Recebedor dos centavos fora das parcelas | - |
{
"error": {
"type": "invalid_request_error",
"code": "validation_failed",
"message": "A soma dos percentuais do split e 110.00%, acima de 100%.",
"param": "splits",
"requestId": "req_01HQ...",
"meta": { "reason": "percentage_over_100" }
}
}
Mais de 20 parcelas, type desconhecido e value negativo ou fracionado sao
barrados antes, na validacao do corpo, tambem com 422.
Na resposta do pagamento
O pagamento devolvido traz as parcelas calculadas:
{
"object": "payment",
"id": "pay_01HQ...",
"amount": 10003,
"splits": [
{ "recipientId": "rcp_vendedor", "amount": 2500, "type": "percentage", "liableForChargeback": true },
{ "recipientId": "rcp_parceiro", "amount": 2501, "type": "percentage", "liableForChargeback": false }
]
}
Em GET /v1/payments/:id, cada parcela tambem traz id, o nome do recebedor
em recipient, o valor configurado em configuredValue (basis points ou
centavos), chargeProcessingFee e feeAmount. Veja
Transacoes.
Precedencia e envio ao parceiro
- O split da requisicao vence o do fluxo. Com split gravado, o no Split nao recalcula; a etapa registra "O pagamento ja tem split gravado (da requisicao ou de uma execucao anterior); as regras deste no nao foram reaplicadas.". O mesmo vale quando o fluxo do pagamento e retomado (revisao manual, espera): a divisao de um pagamento e decidida uma unica vez.
- Sem no Split no fluxo, o split da requisicao segue para o conector do mesmo jeito - inclusive no roteamento direto, sem fluxo publicado.
- A autorizacao e a captura total levam as instrucoes. Para cada parcela,
o conector recebe o
externalRecipientIddo recebedor (ou o id dele no Minstrel, quando nao ha id externo), o valor,liableForChargebackechargeProcessingFee. - Captura parcial segue sem split. As parcelas gravadas foram calculadas sobre o valor autorizado, e o recalculo para o valor capturado ainda nao existe. E uma limitacao conhecida: se a divisao importa, capture o total.
- Parcela de valor zero fica gravada, mas nao vai ao parceiro.
Quem liquida o dinheiro de cada recebedor e o parceiro. O catalogo
(GET /v1/connectors) indica a capacidade split em capabilities. Os
adquirentes disponiveis hoje - Minstrel Sandbox e Cielo - nao tem essa
capacidade e nao repassam as instrucoes: o valor e liquidado inteiro para o
estabelecimento, e a divisao fica registrada no Minstrel para consulta e
conciliacao.
No construtor de fluxos
O no Split divide o valor com as mesmas regras e o mesmo calculo. Ele
precisa vir antes da rota, porque as instrucoes viajam com a autorizacao.
Um Split depois da rota grava a divisao, mas a autorizacao ja saiu sem ela: a
validacao avisa com split_after_route. E um aviso, nao um erro - a
publicacao segue e devolve o aviso em warnings.
A publicacao recusa com 422 (flow_error, codigo flow_invalid_graph):
- recebedor repetido no mesmo no;
- percentuais acima de 100%;
- recebedor dos centavos (
remainderRecipientId) fora das regras; - recebedor inexistente no ambiente ou removido;
- recebedor inativo.
Os tres primeiros vem em meta.issues, com o no e o campo de cada problema; os
dois ultimos, com os ids em meta.missingRecipientIds e
meta.inactiveRecipientIds.
So uma coisa aparece apenas na execucao: parcelas fixas acima do valor, que so
e conhecido no pagamento. Nesse caso a requisicao falha com o mesmo 422 da
tabela de erros e o pagamento fecha como failed, com o evento
payment.failed - nunca fica preso em processing. Confira as regras no
simulador antes de publicar.
No editor do painel, ao escolher o recebedor, a regra padrao, o chargeback e a
taxa vem do cadastro dele. No grafo salvo os valores ficam explicitos: um no
criado pela API sem liableForChargeback e chargeProcessingFee usa false.
O modelo Marketplace com split ("template": "marketplace-split" em
POST /v1/flows) ja vem na ordem certa: gatilho, Split, rota e captura. Ele
divide 10% para a plataforma e 90% para o vendedor, com os centavos para a
plataforma. Troque os ids de exemplo (rcp_plataforma, rcp_vendedor e
ca_subadquirente_split) pelos seus antes de publicar. Como o que nao e
dividido fica com a conta principal, voce tambem pode manter so a parcela do
vendedor e deixar a comissao com a conta principal.
Tarifa de processamento
chargeProcessingFee e gravado em cada parcela e enviado ao conector, mas o
rateio da tarifa ainda nao e calculado: o feeAmount de toda parcela e
0. O simulador avisa com fees_not_calculated quando alguma regra marca o
campo.
Simule antes de cobrar
POST /v1/recipients/split-preview calcula com a mesma funcao do motor de
pagamentos, sem gravar nada (escopo recipients:read):
curl -X POST http://localhost:3333/v1/recipients/split-preview \
-H "Authorization: Bearer $MINSTREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 10003,
"rules": [
{ "recipientId": "rcp_vendedor", "type": "percentage", "value": 2500 },
{ "recipientId": "rcp_parceiro", "type": "percentage", "value": 2500 }
],
"remainderRecipientId": "rcp_parceiro"
}'
Resposta (resumida):
{
"object": "split_preview",
"amount": 10003,
"currency": "BRL",
"valid": true,
"parts": [
{ "recipientId": "rcp_vendedor", "name": "Loja do Vendedor ME", "source": "request", "amount": 2500, "effectiveBps": 2499, "roundingCents": 0 },
{ "recipientId": "rcp_parceiro", "name": "Parceiro Exemplo", "source": "request", "amount": 2501, "effectiveBps": 2500, "roundingCents": 1 }
],
"total": 5001,
"fixedTotal": 0,
"percentageTotalBps": 5000,
"remainingForPercentages": 10003,
"mainAccountAmount": 5002,
"roundingCents": 1,
"issues": [
{ "severity": "info", "code": "main_account_share", "message": "A conta principal fica com 5002 centavos: a parte que nao foi dividida." },
{ "severity": "info", "code": "rounding_cents", "ruleIndex": 1, "message": "Os percentuais nao dividem a venda em centavos exatos: 1 centavo(s) de arredondamento vao para Parceiro Exemplo." }
]
}
Diferencas em relacao ao pagamento, pensadas para a tela de simulacao:
- regra sem
typeevalueusa a regra padrao do recebedor (source: "default"); - regra com
labele semrecipientIdvale so para simular ("Plataforma", por exemplo); - configuracao invalida nao vira erro HTTP: a resposta e
200comvalid: false, os valores nulos e os problemas emissues. So o corpo mal formado (mais de 20 regras,typesemvalue) responde422.
code | Severidade | Significado |
|---|---|---|
recipient_not_found | error | Recebedor inexistente no ambiente ou removido. |
recipient_inactive | error | Recebedor inativo: o pagamento seria recusado. |
rule_incomplete | error | Regra sem type e value, e o recebedor nao tem regra padrao. |
duplicate_recipient | error | Recebedor em mais de uma parcela. |
remainder_not_in_rules | error | Recebedor dos centavos fora das parcelas. |
fixed_exceeds_amount | error | Parcelas fixas acima do valor. |
percentage_over_100 | error | Percentuais acima de 100%. |
fixed_first | info | Ha fixas e percentuais: as fixas saem primeiro e os percentuais valem sobre o resto. |
main_account_share | info | Quanto fica com a conta principal (meta.amount e meta.bps). |
rounding_cents | info | Centavos de arredondamento e quem fica com eles. |
unregistered_rules | info | Ha regra sem recebedor cadastrado: serve para simular, nao para cobrar. |
fees_not_calculated | info | Alguma regra marca chargeProcessingFee, cujo rateio ainda nao e calculado. |
effectiveBps e a participacao efetiva de cada parcela na venda, em basis
points, so para exibicao.
Saldo do recebedor
GET /v1/recipients e GET /v1/recipients/:id trazem o valor a receber em
stats.toReceive, com a origem do numero em source:
ledger: saldo do razao contabil (creditos menos debitos da conta do recebedor);estimate: estimativa - parcelas de pagamentos capturados, menos as revertidas, as tarifas e os repasses registrados.
Hoje nenhum processo grava lancamentos de recebedor no razao nem repasses, entao
o numero vem como estimate. Na estimativa, estorno total reverte a parcela
inteira; chargeback reverte so a parcela de quem tem liableForChargeback; e
estorno parcial e captura parcial nao sao rateados. Em
GET /v1/recipients/:id/splits, cada parcela diz como entrou na conta em
effect: counted, reversed, awaiting_capture, in_progress ou
not_captured.
Eventos
| Evento | Quando acontece |
|---|---|
recipient.created | Recebedor cadastrado. |
recipient.updated | Dados alterados. previousAttributes traz os valores anteriores, com documento, conta e chave Pix mascarados. |
recipient.deleted | Recebedor removido. Os splits gravados continuam no historico; pagamentos novos nao podem usa-lo. |
O data.object desses eventos traz o recebedor na versao mascarada: webhook
sai da nossa infraestrutura e nao precisa do numero da conta para cumprir o seu
papel. Veja Webhooks.