Fluxo de Pagamento e Notificação

payment.approved e payment.failed

Você implementa contratos e publicação dos eventos de aprovação e falha de pagamento.

Avançado 45 min 25 pontos Leitura 0%

Nesta aula você vai

  • Modelar dois eventos com semântica explícita de resultado
  • Publicar contratos versionados no payment-service
  • Padronizar motivo de falha para consumidores downstream

payment.approved e payment.failed

Objetivos

  • Modelar dois eventos com semântica explícita de resultado
  • Publicar contratos versionados no payment-service
  • Padronizar motivo de falha para consumidores downstream

Pré-requisitos

  • Fluxo order->payment concluído
  • payment-service consumindo order.created
  • Kafka operacional

Conceito

Quando você representa sucesso e falha no mesmo evento genérico, o consumidor precisa interpretar combinações de campos e a regra fica frágil. Separar payment.approved e payment.failed simplifica o processamento e reduz erro de interpretação.

Cada evento deve contar uma história clara do domínio financeiro: qual pedido, qual resultado, quando aconteceu e por que falhou (quando falhar). Isso dá base para notificação, analytics e auditoria.

Nesta aula, você cria schemas dedicados e publica ambos a partir do payment-service em Go.

Estrutura de arquivos

contracts/events/payment-approved.v1.json
contracts/events/payment-failed.v1.json
services/payment-service/internal/publishers/payment_result_publisher.go
services/payment-service/internal/service/payment_service.go

Passo a passo

  1. Criar contrato payment-approved.v1.json
{
  "title": "payment.approved.v1",
  "type": "object",
  "required": ["eventId", "occurredAt", "paymentId", "orderId", "customerId", "amount", "currency"],
  "properties": {
    "eventId": { "type": "string" },
    "occurredAt": { "type": "string", "format": "date-time" },
    "paymentId": { "type": "string" },
    "orderId": { "type": "string" },
    "customerId": { "type": "string" },
    "amount": { "type": "number" },
    "currency": { "type": "string" }
  }
}
  1. Criar contrato payment-failed.v1.json
{
  "title": "payment.failed.v1",
  "type": "object",
  "required": ["eventId", "occurredAt", "paymentId", "orderId", "customerId", "reasonCode"],
  "properties": {
    "eventId": { "type": "string" },
    "occurredAt": { "type": "string", "format": "date-time" },
    "paymentId": { "type": "string" },
    "orderId": { "type": "string" },
    "customerId": { "type": "string" },
    "reasonCode": { "type": "string", "enum": ["INSUFFICIENT_FUNDS", "CARD_DECLINED", "ANTI_FRAUD"] },
    "reasonMessage": { "type": "string" }
  }
}
  1. Implementar publisher em Go
type Publisher struct {
  writer *kafka.Writer
}

func (p *Publisher) Publish(topic string, key string, payload any) error {
  b, _ := json.Marshal(payload)
  return p.writer.WriteMessages(context.Background(), kafka.Message{
    Topic: topic,
    Key:   []byte(key),
    Value: b,
  })
}
  1. Publicar evento correto no service
if approved {
  _ = publisher.Publish("payment.approved.v1", payment.OrderID, approvedEvent)
} else {
  _ = publisher.Publish("payment.failed.v1", payment.OrderID, failedEvent)
}

Como testar

  1. Subir serviços:
docker compose -f infra/docker-compose.yml up --build -d kafka payment-service
  1. Simular pagamento aprovado:
curl -s -X POST http://localhost:8080/payments \
  -H "Content-Type: application/json" \
  -d '{"orderId":"ord-200","customerId":"cli-200","amount":50.0,"currency":"BRL","simulate":"approved"}'
  1. Simular pagamento recusado:
curl -s -X POST http://localhost:8080/payments \
  -H "Content-Type: application/json" \
  -d '{"orderId":"ord-201","customerId":"cli-201","amount":9999.0,"currency":"BRL","simulate":"failed"}'
  1. Ler eventos:
docker compose -f infra/docker-compose.yml exec kafka \
  kafka-console-consumer.sh --bootstrap-server kafka:9092 --topic payment.approved.v1 --from-beginning --max-messages 1

docker compose -f infra/docker-compose.yml exec kafka \
  kafka-console-consumer.sh --bootstrap-server kafka:9092 --topic payment.failed.v1 --from-beginning --max-messages 1

Saída esperada: um evento em cada tópico com campos obrigatórios.

Dicas de projeto

  • Use eventos diferentes para resultados opostos.
  • Padronize reasonCode com enum para facilitar analytics.
  • Inclua occurredAt e eventId em todos os eventos.
  • Versione contratos desde a primeira publicação.

Erros comuns

  • Publicar evento sem persistir o estado do pagamento.
  • Reutilizar mesmo tópico para approved e failed sem distinção.
  • Não incluir orderId no payload.
  • Colocar dados sensíveis de cartão no evento.

Resumo

Você definiu e publicou eventos financeiros com semântica clara, separando sucesso e falha. Isso simplifica consumidores e melhora rastreabilidade do fluxo de pagamento.