Skip to main content
POST
/
payments
curl --request POST \
  --url http://localhost:5004/payments \
  --header 'Content-Type: application/json' \
  --data '{
    "orderId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "customerId": "a8098c1a-f86e-11da-bd1a-00112444be1e",
    "reservationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "amount": 150.00,
    "currency": "USD",
    "paymentMethod": "credit_card"
  }'
{
  "success": true,
  "errorMessage": null,
  "payment": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "orderId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "customerId": "a8098c1a-f86e-11da-bd1a-00112444be1e",
    "reservationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "amount": 150.00,
    "currency": "USD",
    "paymentMethod": "credit_card",
    "status": "succeeded",
    "errorCode": null,
    "errorMessage": null,
    "failureReason": null,
    "createdAt": "2026-03-04T15:30:00Z",
    "processedAt": "2026-03-04T15:30:02Z",
    "isSimulated": true,
    "simulatedResponse": "Success - TransactionId: txn_1234567890"
  }
}
Process a payment transaction for a customer order. This endpoint validates the order state, checks the reservation if provided, simulates the payment processing, and publishes events to Kafka for downstream services.
The payment service uses a simulated payment gateway for demonstration purposes. In production, this would integrate with real payment processors like Stripe, PayPal, or other payment gateways.

Payment processing flow

When a payment is submitted, the service follows this workflow:
  1. Idempotency check - Verifies if a successful payment already exists for the order
  2. Order validation - Confirms the order exists, belongs to the customer, and has the correct amount
  3. Reservation validation - Re-validates the seat reservation if a reservationId is provided
  4. Payment record creation - Creates a payment record with pending status
  5. Payment simulation - Simulates the payment gateway processing
  6. Event publishing - Publishes either payment-succeeded or payment-failed event to Kafka
This endpoint is idempotent. Multiple requests with the same orderId will return the existing successful payment if one exists, preventing duplicate charges.

Request parameters

body.orderId
string
required
Unique identifier of the order to process payment for. Must be a valid GUID.
body.customerId
string
required
Unique identifier of the customer making the payment. Used for authorization and validation.
body.reservationId
string
Optional seat reservation identifier. If provided, the service will re-validate that the reservation is still active and belongs to the customer before processing payment.
body.amount
number
required
Payment amount in decimal format. Must be greater than zero and match the order total.
body.currency
string
default:"USD"
Three-letter ISO currency code (e.g., USD, EUR, GBP). Defaults to USD if not specified.
body.paymentMethod
string
default:"credit_card"
Payment method identifier. Supported values: credit_card, debit_card, paypal, apple_pay, google_pay.

Response fields

success
boolean
required
Indicates whether the payment processing was successful.
errorMessage
string
Human-readable error message if the payment failed. Only present when success is false.
payment
object
Payment transaction details. Only present when a payment record was created.

Kafka events

The payment service publishes events to Kafka topics to enable asynchronous processing by other services.

payment-succeeded event

Published when a payment is successfully processed. The fulfillment service listens to this event to generate and issue tickets.
{
  "paymentId": "550e8400-e29b-41d4-a716-446655440000",
  "orderId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "customerId": "a8098c1a-f86e-11da-bd1a-00112444be1e",
  "reservationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "amount": 150.00,
  "currency": "USD",
  "paymentMethod": "credit_card",
  "transactionId": "txn_1234567890",
  "processedAt": "2026-03-04T15:30:00Z",
  "status": "succeeded"
}
The fulfillment service consumes this event to automatically generate tickets and send them to customers. This asynchronous workflow ensures payment confirmation is immediate while ticket generation happens in the background.

payment-failed event

Published when a payment fails processing. Services can use this to send notifications or trigger retry logic.
{
  "paymentId": "550e8400-e29b-41d4-a716-446655440000",
  "orderId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "customerId": "a8098c1a-f86e-11da-bd1a-00112444be1e",
  "reservationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "amount": 150.00,
  "currency": "USD",
  "paymentMethod": "credit_card",
  "errorCode": "INSUFFICIENT_FUNDS",
  "errorMessage": "Card declined due to insufficient funds",
  "failureReason": "The payment gateway returned error code 51",
  "attemptedAt": "2026-03-04T15:30:00Z",
  "status": "failed"
}

Error responses

Returned when the request is malformed or validation fails.Common causes:
  • Missing required fields (orderId, customerId, amount)
  • Invalid GUID format for IDs
  • Amount is zero or negative
  • Order validation failed (order doesn’t exist or doesn’t belong to customer)
  • Reservation validation failed (expired or doesn’t belong to customer)
{
  "success": false,
  "errorMessage": "Order validation failed: Order not found or doesn't belong to customer",
  "payment": null
}
The customer is not authorized to process payment for this order.
{
  "success": false,
  "errorMessage": "Unauthorized: Customer does not own this order",
  "payment": null
}
The specified order could not be found in the system.
{
  "success": false,
  "errorMessage": "Order not found",
  "payment": null
}
The payment was processed but failed at the payment gateway level.
{
  "success": false,
  "errorMessage": "Payment failed: Card declined",
  "payment": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "failed",
    "errorCode": "CARD_DECLINED",
    "errorMessage": "Card declined",
    "failureReason": "Issuing bank rejected the transaction"
  }
}
An unexpected error occurred during payment processing.
{
  "success": false,
  "errorMessage": "Payment processing error: Database connection timeout",
  "payment": null
}
curl --request POST \
  --url http://localhost:5004/payments \
  --header 'Content-Type: application/json' \
  --data '{
    "orderId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "customerId": "a8098c1a-f86e-11da-bd1a-00112444be1e",
    "reservationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "amount": 150.00,
    "currency": "USD",
    "paymentMethod": "credit_card"
  }'
{
  "success": true,
  "errorMessage": null,
  "payment": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "orderId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "customerId": "a8098c1a-f86e-11da-bd1a-00112444be1e",
    "reservationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "amount": 150.00,
    "currency": "USD",
    "paymentMethod": "credit_card",
    "status": "succeeded",
    "errorCode": null,
    "errorMessage": null,
    "failureReason": null,
    "createdAt": "2026-03-04T15:30:00Z",
    "processedAt": "2026-03-04T15:30:02Z",
    "isSimulated": true,
    "simulatedResponse": "Success - TransactionId: txn_1234567890"
  }
}

Integration notes

Always validate the order amount on the server side before calling this endpoint. Never trust client-supplied amounts without verification.
Implement retry logic for transient failures (5xx errors), but do not retry 4xx errors as they indicate client-side issues that won’t be resolved by retrying.

Gateway integration points

In a production environment, the IPaymentSimulatorService interface (source code: services/payment/src/Application/Ports/IPaymentSimulatorService.cs) would be replaced with actual payment gateway integrations such as:
  • Stripe - For credit card processing
  • PayPal - For PayPal and credit card processing
  • Square - For point-of-sale and online payments
  • Adyen - For global payment processing
The service architecture supports multiple payment gateways through the port/adapter pattern, making it easy to add or switch payment providers.

Service configuration

The payment service runs on port 5004 and connects to:
  • PostgreSQL - bc_payment schema for payment records
  • Kafka - kafka:9092 for event publishing
  • Ordering Service - For order validation
Environment variables (from docker-compose.yml:209-229):
ASPNETCORE_URLS=http://+:5004
ConnectionStrings__Default=Host=postgres;Port=5432;Database=ticketing;Username=postgres;Password=postgres;SearchPath=bc_payment
Kafka__BootstrapServers=kafka:9092

Build docs developers (and LLMs) love