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.
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
- 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" }
}
}
- 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" }
}
}
- 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,
})
}
- 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
- Subir serviços:
docker compose -f infra/docker-compose.yml up --build -d kafka payment-service
- 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"}'
- 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"}'
- 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
reasonCodecom enum para facilitar analytics. - Inclua
occurredAteeventIdem 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
orderIdno 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.