Skip to main content

Overview

The Payment service processes payments for orders. It validates order state, checks reservations, simulates payment gateway interactions, and publishes payment events to Kafka for downstream processing. Port: 5004
Database Schema: bc_payment
Dependencies: PostgreSQL, Redis, Kafka, Ordering Service, Inventory Service

Responsibilities

  • Process payment transactions for orders
  • Validate order state before payment
  • Verify reservation validity
  • Simulate payment gateway integration (development mode)
  • Track payment status (pending, succeeded, failed)
  • Publish payment-succeeded and payment-failed events to Kafka
  • Store payment records with metadata

API Endpoints

Process Payment

POST /payments
endpoint
Processes a payment for an order. Validates order state, checks reservation, simulates payment, and publishes events.
Request Body:
OrderId
Guid
required
The ID of the order to pay for
CustomerId
Guid
required
The customer making the payment
ReservationId
Guid
The associated reservation ID
Amount
decimal
required
Payment amount (must be > 0)
Currency
string
default:"USD"
Currency code
PaymentMethod
string
required
Payment method (e.g., “credit_card”, “debit_card”)
~/workspace/source/services/payment/src/Api/Controllers/PaymentsController.cs
[HttpPost]
public async Task<IActionResult> ProcessPayment([FromBody] PaymentRequest request, CancellationToken cancellationToken = default)
{
    if (request == null)
        return BadRequest("Payment request is required");

    if (request.OrderId == Guid.Empty)
        return BadRequest("Valid OrderId is required");

    if (request.CustomerId == Guid.Empty)
        return BadRequest("Valid CustomerId is required");

    if (request.Amount <= 0)
        return BadRequest("Amount must be greater than zero");

    var command = new ProcessPaymentCommand(
        request.OrderId,
        request.CustomerId,
        request.ReservationId,
        request.Amount,
        request.Currency,
        request.PaymentMethod
    );

    var response = await _mediator.Send(command, cancellationToken);

    if (!response.Success)
    {
        return response.ErrorMessage switch
        {
            var msg when msg?.Contains("Order not found") == true => NotFound(response),
            var msg when msg?.Contains("Order validation failed") == true => BadRequest(response),
            var msg when msg?.Contains("Reservation validation failed") == true => BadRequest(response),
            var msg when msg?.Contains("Unauthorized") == true => Unauthorized(response),
            var msg when msg?.Contains("Payment failed") == true => UnprocessableEntity(response),
            _ => StatusCode(500, response)
        };
    }

    return Ok(response);
}
Response (200 OK):
Success
bool
Whether the payment succeeded
Payment
Payment
The payment object with details
ErrorMessage
string?
Error message if payment failed
Error Responses:
  • 400 Bad Request: Validation errors (order state, reservation, etc.)
  • 401 Unauthorized: Customer mismatch
  • 404 Not Found: Order not found
  • 422 Unprocessable Entity: Payment gateway failure
  • 500 Internal Server Error: Other failures

Domain Models

Payment

Represents a payment transaction.
Id
Guid
Unique payment identifier
OrderId
Guid
Reference to the order being paid
CustomerId
Guid
Customer making the payment
ReservationId
Guid?
Associated reservation ID
Amount
decimal
Payment amount
Currency
string
Currency code (default: “USD”)
PaymentMethod
string
Payment method used
Status
string
Payment status: pending, succeeded, or failed
ErrorCode
string?
Error code if payment failed
ErrorMessage
string?
Error message if payment failed
FailureReason
string?
Detailed failure reason
CreatedAt
DateTime
When the payment was created
ProcessedAt
DateTime?
When the payment was processed
IsSimulated
bool
Whether this is a simulated payment (development mode)
SimulatedResponse
string?
Simulated gateway response data
~/workspace/source/services/payment/src/Domain/Entities/Payment.cs
public class Payment
{
    public const string StatusPending = "pending";
    public const string StatusSucceeded = "succeeded";
    public const string StatusFailed = "failed";

    public Guid Id { get; set; }
    public Guid OrderId { get; set; }
    public Guid CustomerId { get; set; }
    public Guid? ReservationId { get; set; }
    public decimal Amount { get; set; }
    public string Currency { get; set; } = "USD";
    public string PaymentMethod { get; set; } = null!;
    public string Status { get; set; } = StatusPending;
    public string? ErrorCode { get; set; }
    public string? ErrorMessage { get; set; }
    public string? FailureReason { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime? ProcessedAt { get; set; }
    public bool IsSimulated { get; set; } = true;
    public string? SimulatedResponse { get; set; }

    public void MarkAsSucceeded()
    {
        if (Status != StatusPending)
            throw new InvalidOperationException($"Cannot succeed payment from status: {Status}");
        Status = StatusSucceeded;
        ProcessedAt = DateTime.UtcNow;
    }

    public void MarkAsFailed(string errorCode, string errorMessage)
    {
        if (Status != StatusPending)
            throw new InvalidOperationException($"Cannot fail payment from status: {Status}");
        Status = StatusFailed;
        ErrorCode = errorCode;
        ErrorMessage = errorMessage;
        ProcessedAt = DateTime.UtcNow;
    }

    public bool IsValidForProcess()
    {
        return Amount > 0 && 
               OrderId != Guid.Empty && 
               CustomerId != Guid.Empty && 
               !string.IsNullOrWhiteSpace(PaymentMethod);
    }
}

Configuration

Database Connections

appsettings.json
{
  "ConnectionStrings": {
    "Default": "Host=postgres;Port=5432;Database=ticketing;Username=postgres;Password=postgres;SearchPath=bc_payment",
    "Redis": "redis:6379",
    "Kafka": "kafka:9092"
  }
}

Kafka Configuration

{
  "Kafka": {
    "BootstrapServers": "kafka:9092",
    "GroupId": "payment-service",
    "AutoOffsetReset": "Earliest"
  }
}

External Service URLs

{
  "ExternalServices": {
    "OrderingService": {
      "BaseUrl": "http://ordering:5003"
    },
    "InventoryService": {
      "BaseUrl": "http://inventory:5002"
    }
  }
}

Redis Configuration

{
  "Redis": {
    "ConnectionString": "redis:6379"
  }
}

Payment Processing Flow

  1. Validate request: Check OrderId, CustomerId, Amount, PaymentMethod
  2. Fetch order: Query Ordering service for order details
  3. Validate order state: Ensure order is in pending state
  4. Check reservation: Verify reservation is still active (if provided)
  5. Process payment: Simulate payment gateway call (or real gateway in production)
  6. Update payment record: Mark as succeeded or failed
  7. Publish event: Send payment-succeeded or payment-failed to Kafka
  8. Return response: Return payment result to client

Kafka Integration

Published Events:
  • payment-succeeded: Published when payment completes successfully
    • Consumed by Ordering service (updates order to paid)
    • Consumed by Fulfillment service (triggers ticket generation)
  • payment-failed: Published when payment fails
    • Consumed by Ordering service (may cancel order or allow retry)

Payment Gateway Simulation

In development mode, the service simulates payment processing:
  • All payments are marked as simulated (IsSimulated = true)
  • Success rate can be configured for testing failure scenarios
  • Simulated response data is stored in SimulatedResponse field
  • Production mode would integrate with real payment gateways (Stripe, PayPal, etc.)

Use Cases

  • ProcessPayment: Validates order, processes payment, publishes result events

Architecture Notes

  • Uses MediatR for CQRS-style command/query handling
  • Infrastructure services registered via AddInfrastructure() extension method
  • Calls external services (Ordering, Inventory) via HTTP clients
  • Uses Kafka for event-driven communication with downstream services
  • Supports simulated payment gateway for development/testing

Build docs developers (and LLMs) love