Skip to content

shiks2/payflow

Repository files navigation

PayFlow

PayFlow is a robust and resilient payment processing application built with Spring Boot. It demonstrates several important architectural patterns for building reliable distributed systems, including idempotency, the outbox pattern for reliable event publishing, and secure webhook handling.

Features

  • Idempotent Payment Creation: Prevents duplicate payment creation for the same request.
  • Asynchronous Event Publishing: Uses the outbox pattern with Kafka to reliably publish payment events.
  • Secure Webhook Handling: Verifies webhook signatures using HMAC-SHA256 and protects against replay attacks.
  • Database Migrations: Uses Flyway for managing database schema changes.
  • Resilient by Design: Incorporates Resilience4j for fault tolerance patterns.

Technologies Used

  • Java 17
  • Spring Boot 3
  • Maven
  • H2 Database (for local development)
  • Flyway for database migrations.
  • Apache Kafka for messaging.
  • Docker for containerization.
  • Lombok to reduce boilerplate code.
  • Resilience4j for fault tolerance.

Setup and Installation

Prerequisites

  • Docker and Docker Compose
  • Java 17 or later (for running outside of Docker)
  • Maven 3.6 or later (for running outside of Docker)

Running with Docker (Recommended)

The easiest way to run PayFlow and its dependencies (like Kafka) is by using Docker Compose.

  1. Build the application JAR:

    ./mvnw clean package
  2. Start the application with Docker Compose:

    docker-compose up --build

The application will be available at http://localhost:8080.

Running Locally (Without Docker)

If you prefer to run the application without Docker, you'll need to have Kafka running separately.

  1. Start Kafka: Ensure you have a Kafka broker running and accessible at localhost:9092.

  2. Run the Spring Boot application:

    ./mvnw spring-boot:run

The application will use an in-memory H2 database by default.

Application Configuration

The application is configured in src/main/resources/application.properties. When running with Docker Compose, the environment variables in docker-compose.yml will override these settings.

Default application.properties for local development:

spring.application.name=payflow
spring.datasource.url=jdbc:h2:mem:payflow
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.show-sql=true
flyway.enabled=true
flyway.baseline-on-migrate=true
kafka.bootstrap-servers=localhost:9092
resilience4j.retry.instances.downstream.maxAttempts=3
resilience4j.retry.instances.downstream.waitDuration=1s

resilience4j.circuitbreaker.instances.downstream.failureRateThreshold=50
resilience4j.circuitbreaker.instances.downstream.slidingWindowSize=10
resilience4j.circuitbreaker.instances.downstream.waitDurationInOpenState=10s

API Endpoints

Create a Payment

  • URL: /payments
  • Method: POST
  • Headers:
    • Idempotency-Key: A unique key for the request (e.g., a UUID).
  • Request Body:
    {
        "amount": 1000,
        "currency": "USD"
    }
  • Success Response:
    {
        "paymentId": "...",
        "status": "PENDING"
    }

Update Payment Status

  • URL: /payments/{id}/status
  • Method: PUT
  • Path Variables:
    • id: The ID of the payment to update.
  • Query Parameters:
    • status: The new status (e.g., "COMPLETED", "FAILED").
  • Success Response:
    {
        "paymentId": "...",
        "status": "COMPLETED"
    }

PSP Webhook

  • URL: /webhooks/psp
  • Method: POST
  • Headers:
    • X-Signature: The HMAC-SHA256 signature of the request body.
    • X-Timestamp: The timestamp of the request.
  • Request Body:
    {
        "event_id": "...",
        "payment_id": "...",
        "status": "COMPLETED",
        "ts": 1678886400
    }

Architectural Patterns

Idempotency

The POST /payments endpoint is idempotent. If the same request is sent multiple times with the same Idempotency-Key header, the payment will only be created once, and subsequent requests will receive the same response.

Outbox Pattern

When a payment is created or its status is updated, an event is written to the events_outbox table in the same transaction. A background process (OutboxPublisher) periodically polls this table and publishes the events to a Kafka topic (payments.events). This ensures that events are published reliably, even if the application crashes after the database transaction is committed but before the event is sent to Kafka.

Inbox Pattern

The PaymentEventConsumer consumes events from the payments.events Kafka topic. It uses the events_inbox table to ensure that each event is processed exactly once, preventing duplicate processing in case of message redelivery from Kafka.

Webhook Security

The /webhooks/psp endpoint is secured using the following mechanisms:

  1. HMAC Signature Verification: The X-Signature header is verified to ensure that the webhook was sent by the trusted PSP and that the payload has not been tampered with.
  2. Timestamp Verification: The X-Timestamp header is checked to prevent replay attacks. Webhooks that are too old are rejected.
  3. Replay Guard: The webhook_replay_guard table is used to store the IDs of processed webhook events, preventing the same event from being processed multiple times.

About

PayFlow is a sample payment processing application built with Spring Boot and Kafka, demonstrating best practices for building resilient, distributed, event-driven microservices. It features idempotent APIs, the outbox/inbox pattern for reliable messaging, secure webhooks, and fault tolerance using Resilience4j.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors