Minstrel

Roteamento

Fluxos de roteamento

Como o construtor visual decide por qual adquirente cada transacao passa, e como testar antes de publicar.

Um fluxo decide o caminho de cada transacao: qual adquirente tenta primeiro, quando passar por antifraude, quando dividir o valor e o que fazer na recusa.

Fluxos sao desenhados no painel, arrastando etapas. Esta pagina explica o modelo por tras do desenho - util para quem vai montar o fluxo e essencial para quem vai criar fluxos por API.

O modelo

Um fluxo e um grafo aciclico de nos ligados por arestas. Cada no tem portas de saida nomeadas, e cada porta leva a uma proxima etapa.

 Gatilho ──▶ Faixa de ticket ──alto──▶ Antifraude ──aprovar──▶ Adquirente Y ──▶ Aprovar
                   │                        │
                   │                     recusar ──────────────────────────────▶ Recusar
                   └──demais──▶ Adquirente X ──aprovado──▶ Aprovar

Por que aciclico: um ciclo no caminho de autorizacao poderia girar indefinidamente enquanto o comprador espera. Para repetir uma cobranca em outra data existe o no de Retentativa, que agenda em vez de voltar no grafo.

Tipos de no

GrupoNoO que fazPortas
InicioGatilhoEvento que inicia o fluxonext
LogicaCondicaoDivide em verdadeiro ou falsotrue, false
LogicaMultiplos caminhosAvalia casos em ordemids dos casos, default
LogicaDefinir variavelGuarda um valor para as proximas etapasnext
RiscoAntifraudeConsulta o provedor e ramificaapprove, review, decline, error
RiscoValidacao de cartaoConfere o cartao sem cobrar (Zero Auth)valid, invalid, unavailable, error
RiscoAutenticacao 3DSAutentica o portadorauthenticated, not_authenticated, unavailable
RiscoRevisao manualPausa ate a decisao de um analistaapproved, declined
ProcessamentoRotearEscolhe o conector (cartao nao troca)approved, declined, error
ProcessamentoCapturarCaptura o valor autorizadocaptured, failed
ProcessamentoSplitDivide entre recebedoresnext
CobrancaRetentativaReagenda conforme a politicascheduled, exhausted
CobrancaAguardarPausa e retoma depois do prazonext
IntegracaoWebhookEmite flow.notificationnext
FimAprovar / RecusarEncerra o fluxonenhuma

Cada porta aceita uma ligacao. Duas setas saindo de "Aprovado" tornariam o fluxo ambiguo, e o motor nao teria como escolher. E toda porta precisa de destino, inclusive error: uma porta solta nao e "nada acontece", e o pagamento terminaria sem decisao explicita.

No painel, um duplo clique na etapa abre a configuracao no painel lateral. As etapas que dependem de parceiro (antifraude, rota, validacao de cartao, 3DS) escolhem o provedor e a conta ali mesmo, com cadastro de chaves e Testar conexao - ver Conectores.

Antifraude: score ou recomendacao

O score de todo provedor e normalizado para 0 a 100 (100 = risco maximo), para que trocar Konduto por ClearSale nao exija recalibrar o fluxo. O no decide de um de dois jeitos (decisionMode):

ModoQuem decideConfiguracao
scoreAs faixas do no: aprovar abaixo de X, recusar a partir de YapproveBelow, declineAbove
recommendationA recomendacao do provedor (aprovar/revisar/recusar)-

Quando o provedor decide sem devolver score (a ClearSale decide por status), vale a recomendacao dele nos dois modos - o Minstrel nunca inventa um numero. Timeout (timeoutMs) ou provedor fora do ar saem pela porta error; ligue-a a uma revisao manual para nao perder a venda.

Validacao de cartao (Zero Auth)

O no Validacao de cartao (zero_auth) usa a autorizacao de valor zero (Zero Auth) para conferir se o cartao esta ativo. Nao cobra, nao reserva limite e nao avisa o portador. Serve para barrar um cartao morto antes de gastar uma tentativa de venda, por exemplo na primeira cobranca de uma assinatura.

PortaQuando sai por elaSugestao
validValido: o emissor confirmou que o cartao esta ativo.Seguir para a rota.
invalidInvalido: o emissor disse que o cartao nao serve.Recusar. Nao envie ao emissor.
unavailableIndisponivel: bandeira sem suporte, recurso desligado na conta ou token de outra conta.Seguir sem validar, se o seu risco permitir.
errorFalha tecnica (timeout, parceiro fora do ar).Seguir sem validar ou desviar para uma revisao manual.

Separar invalid de unavailable e a razao de o no existir: so o primeiro justifica recusar a venda. unavailable nao diz nada sobre o cartao, e um servico fora do ar ou nao habilitado na conta nunca deve virar "cartao ruim". Enviar ao emissor um cartao invalid so geraria outra recusa, que ainda conta nos programas de retentativa das bandeiras.

CampoPadraoEfeito
Conta (connectorAccountId)A conta onde o cartao foi salvoOpcional. O token da Cielo so vale na conta que o criou, entao deixar em branco e quase sempre o certo. Outra conta, que nao e a do cartao, sai por unavailable.
reuseWithinDays30 (de 0 a 365)Reaproveita uma validacao valid recente do mesmo cartao, sem consultar de novo. 0 valida sempre.

O reaproveitamento existe porque o Zero Auth conta nos programas de retentativa das bandeiras e pode ter custo no adquirente: validar o mesmo cartao a cada pagamento seria pagar, e arriscar tarifa, por uma resposta que ja se tem. So uma validacao valid e reaproveitada.

No editor, so aparecem as contas com a validacao de cartao ligada (Plugins, conta, Recursos - ver Conectores). Quando a validacao e aprovada num cartao salvo, ela abre a cadeia da rede (IssuerTransactionId e, na Mastercard, o TLID) como se fosse uma primeira transacao: as cobrancas sem o cliente que vierem depois partem dela.

Exemplo: validar so a primeira compra do cliente.

 Gatilho ──▶ Condicao: customer.is_first_purchase = true
                 │
                 ├── true ──▶ Validacao de cartao ──valid──▶ Rotear ──approved──▶ Aprovar
                 │                   │
                 │                   └── invalid ──▶ Recusar
                 └── false ──▶ Rotear

As portas unavailable e error tambem precisam de destino, como qualquer porta: no exemplo acima, ligue-as a um segundo Rotear para seguir sem validar.

O resultado fica disponivel para as regras seguintes em card.verification_status e card.verified_days_ago (veja Campos disponiveis nas regras). Para validar o cartao na hora de salva-lo, sem fluxo, use o parametro verify descrito em Clientes e grupos.

Captura imediata ou adiada

O no Capturar tem dois modos. Em immediate (padrao), captura assim que a autorizacao e aprovada. Em delayed, agenda a captura para delayHours horas depois (captureScheduledAt) e quem a executa e o worker, nao o fluxo: o pagamento fica authorized ate la.

A bandeira limita quanto tempo uma pre-autorizacao pode esperar, e o agendamento respeita isso: se delayHours passar do prazo, a captura e marcada para 1 hora antes do vencimento (authorizationExpiresAt), nunca depois. Veja os prazos e o aviso de vencimento em Transacoes.

Pausas: Aguardar e Revisao manual

Essas etapas nunca seguram a autorizacao. A execucao pausa, o pagamento fica requires_action e a resposta da API volta na hora. Depois:

  • Aguardar retoma sozinho pela porta next quando o prazo vence;
  • Revisao manual retoma pela decisao de um analista ou, se ninguem decidir no prazo (slaHours), pela decisao automatica onSlaBreach.

A decisao e uma chamada idempotente:

curl -X POST http://localhost:3333/v1/payments/pay_.../review \
  -H "Authorization: Bearer $MINSTREL_API_KEY" \
  -H "Idempotency-Key: revisao-pay_..." \
  -H "Content-Type: application/json" \
  -d '{ "decision": "approve", "note": "Cliente confirmou por telefone." }'

O fluxo continua de onde parou - aprovar pode disparar a autorizacao no adquirente - e a resposta traz o pagamento ja com o status final. Se a revisao ja foi decidida (por outra pessoa ou pelo prazo), a API responde 409.

Notificar o seu sistema

O no Notificar webhook emite o evento flow.notification ao fim da execucao, com o nome escolhido no no em data.name. Assine esse evento uma vez e use o nome para saber em que ponto do fluxo o pagamento passou:

{
  "type": "flow.notification",
  "data": {
    "name": "antifraude_em_revisao",
    "paymentId": "pay_...",
    "flowId": "flw_...",
    "flowRunId": "frun_..."
  }
}

Campos disponiveis nas regras

As condicoes comparam campos de um catalogo fechado:

CampoExemplo de uso
payment.amountValor em centavos. > 10000 e acima de R$ 100,00
payment.installmentsNumero de parcelas
payment.initiatorcustomer (cliente presente) ou merchant (automatica)
card.brandvisa, mastercard, elo, amex, hiper
card.binPrimeiros digitos, para regra por emissor
card.fundingcredit, debit, prepaid
card.countryPais emissor, para regra de cartao estrangeiro
card.verification_statusResultado da validacao de cartao: valid, invalid ou unavailable. Vazio se o cartao nunca foi validado
card.verified_days_agoDias desde a ultima validacao do cartao. Vazio se nunca foi validado; > 30 pega validacao antiga
customer.tagsEtiquetas do cliente
customer.groupsGrupos do cliente (ids cgr_...); o editor mostra os nomes
customer.is_first_purchasePrimeira compra
risk.scoreScore do antifraude (0 a 100)
attempt.numberNumero da tentativa
attempt.previous_decline_reasonMotivo da recusa anterior
subscription.is_recurringE cobranca de assinatura
metadata.<chave>Qualquer metadado que voce enviou

O catalogo e fechado de proposito. Expressao livre tornaria o fluxo impossivel de validar antes de publicar - e um fluxo invalido em producao decide o roteamento de dinheiro real.

Nao existe operador de expressao regular, tambem por decisao: regex fornecida pelo usuario, avaliada no caminho critico de autorizacao, e vetor de ataque de negacao de servico. contains, starts_with e ends_with cobrem os casos reais.

Estrategias de roteamento

O no Rotear aceita varios conectores candidatos e quatro estrategias:

EstrategiaComportamentoQuando usar
priorityTenta na ordem definidaTem um adquirente preferido
weightedSorteia pelo peso (%)Teste A/B ou divisao de volume (80/20)
least_costUsa o custo cadastradoOtimizar taxa
round_robinAlterna igualmenteDistribuir carga

Cartao: uma adquirente por transacao

O token de um cartao e emitido por UMA adquirente - o CardToken da Cielo, o tokenizationId da Rede - e so vale nela. Por isso, no cartao:

  • o cartao salvo vai sempre para a conta onde foi salvo (connectorAccountId informado no cadastro do cartao), mesmo que a estrategia do no aponte outra. Se o no de rota nao tiver essa conta, o pagamento falha com no_connector_available e a mensagem diz qual conta incluir;
  • recusa ou falha nao trocam de adquirente. A nova tentativa e da politica de retentativa (Retentativa), que respeita a ABECS e o intervalo pedido pelo emissor, na mesma adquirente. O detalhe da transacao mostra o motivo card_single_acquirer.

A excecao e o token de bandeira proprio (card.isNetworkToken: true) quando todas as contas do no aceitam token de bandeira externo: ai o fallback abaixo vale tambem para o cartao.

Fallback

Vale para Pix e boleto, que nao dependem de token, e para cartao com token de bandeira proprio. Duas opcoes por no de roteamento:

  • fallbackOnConnectorFailure - tenta o proximo em timeout ou erro do provedor. Recomendado deixar ligado: a chance de pagamento segue intacta.
  • fallbackOnReversibleDecline - tenta o proximo em recusa reversivel (so cartao com token de bandeira proprio).

Recusa irreversivel sempre interrompe o fallback, independentemente da configuracao. O emissor nunca aprovaria, portanto tentar outro adquirente seria custo garantido sem chance de aprovacao. Ver Retentativa.

Simule antes de publicar

No editor, o botao Simular percorre o grafo com um contexto hipotetico e mostra o caminho que a transacao seguiria, com a explicacao de cada decisao. Nenhum conector e acionado e nada e gravado.

Por API:

curl -X POST http://localhost:3333/v1/flows/simulate \
  -H "Authorization: Bearer $MINSTREL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "graph": { "nodes": [], "edges": [] },
    "context": {
      "payment.amount": 1500000,
      "card.brand": "visa",
      "risk.score": 20
    }
  }'

Resposta:

{
  "outcome": "aprovado",
  "path": [
    { "nodeId": "trigger", "explanation": "Gatilho: payment.authorization_requested." },
    {
      "nodeId": "faixas",
      "handle": "alto",
      "explanation": "Caso \"Acima de R$ 10.000,00\": payment.amount (1500000) maior que 1000000 => sim"
    },
    {
      "nodeId": "antifraude",
      "handle": "approve",
      "explanation": "Score simulado 20 resultaria em approve."
    },
    {
      "nodeId": "rota-alto",
      "handle": "approved",
      "explanation": "Rotearia para ca_adquirente_y (estrategia priority)."
    }
  ]
}

Etapas que dependem de terceiros seguem o caminho feliz por padrao. Para ver o outro caminho, informe o resultado suposto em assume, por tipo de no ou por id: "assume": { "route": "declined" } responde "e se o adquirente recusar?". Regras (condicao, multiplos caminhos) nunca sao supostas: sao elas que se quer testar.

Versionamento

Publicar cria uma versao imutavel e move o ponteiro da versao ativa.

Isso permite duas coisas que importam em operacao:

  • auditar por que uma transacao de tres meses atras foi roteada de certo jeito - o grafo exato daquele momento esta preservado;
  • voltar atras instantaneamente, trocando o ponteiro para a versao anterior.

Cada execucao grava a trilha passo a passo em flow_run_steps, com a explicacao de cada decisao em portugues. Quando o cliente pergunta por que a transacao foi para o adquirente X, a resposta esta no banco.

Validacao na publicacao

Antes de uma versao entrar em vigor, verificamos:

  1. Schema - estrutura de cada no e de cada regra;
  2. Grafo - exatamente um gatilho, nenhum ciclo, nenhuma porta sem ligacao, nenhum no terminal com saida, ao menos um no de Aprovar ou Recusar;
  3. Referencias - toda conta de conector e todo recebedor citados existem, pertencem a sua organizacao, estao no mesmo ambiente e estao habilitados.

A terceira camada evita o erro mais silencioso: publicar um fluxo que aponta para uma conta desabilitada recusaria transacoes reais sem nenhuma pista.

Rascunhos aceitam configuracao incompleta - salvar nunca perde o trabalho em andamento. A resposta do rascunho e de POST /v1/flows/validate lista cada problema com o no (nodeId) e o campo (field), e o painel marca os dois.

Um fluxo em vigor por gatilho

Cada gatilho tem um fluxo em vigor por ambiente. Publicar coloca o fluxo em vigor para o gatilho desenhado nele e tira o anterior - a resposta informa quais sairam em replacedFlows. Para voltar ao fluxo antigo, publique-o de novo.

A cobranca de fatura da recorrencia usa o fluxo do gatilho invoice.payment_due; sem ele publicado, usa o de payment.authorization_requested. Quem so desenhou o roteamento cobra a recorrencia pelo mesmo caminho; quem quer tratar fatura de outro jeito (outro adquirente, outra regra) publica um fluxo proprio para ela. Nos dois casos o contexto traz subscription.is_recurring verdadeiro.