Skip to main content

Payment Service

Overview

The Payment Service handles payment processing and refunds for orders, supporting major Korean payment methods (KakaoPay, TossPay, KG Inicis).

ItemDetails
LanguageJava 17
FrameworkSpring Boot 3.2
DatabaseAurora PostgreSQL (Global Database)
Namespacemall-payment
Port8080
Health Check/actuator/health

Architecture

API Endpoints

MethodPathDescription
POST/api/v1/paymentsProcess payment
GET/api/v1/payments/{id}Get payment
POST/api/v1/payments/{id}/refundProcess refund

Process Payment

POST /api/v1/payments

Request:

{
"orderId": "550e8400-e29b-41d4-a716-446655440000",
"amount": 687000.00,
"currency": "KRW",
"paymentMethod": "KAKAO_PAY",
"idempotencyKey": "order-550e8400-payment-001"
}

Supported payment methods:

  • KAKAO_PAY - KakaoPay
  • TOSS_PAY - TossPay
  • KG_INICIS - KG Inicis
  • CREDIT_CARD - Credit Card

Response (201 Created):

{
"id": "660e8400-e29b-41d4-a716-446655440001",
"orderId": "550e8400-e29b-41d4-a716-446655440000",
"amount": 687000.00,
"currency": "KRW",
"status": "COMPLETED",
"paymentMethod": "KAKAO_PAY",
"idempotencyKey": "order-550e8400-payment-001",
"createdAt": "2024-01-15T10:32:00",
"updatedAt": "2024-01-15T10:32:00"
}

Get Payment

GET /api/v1/payments/{id}

Response (200 OK):

{
"id": "660e8400-e29b-41d4-a716-446655440001",
"orderId": "550e8400-e29b-41d4-a716-446655440000",
"amount": 687000.00,
"currency": "KRW",
"status": "COMPLETED",
"paymentMethod": "KAKAO_PAY",
"idempotencyKey": "order-550e8400-payment-001",
"createdAt": "2024-01-15T10:32:00",
"updatedAt": "2024-01-15T10:32:00"
}

Process Refund

POST /api/v1/payments/{id}/refund

Response (200 OK):

{
"id": "660e8400-e29b-41d4-a716-446655440001",
"orderId": "550e8400-e29b-41d4-a716-446655440000",
"amount": 687000.00,
"currency": "KRW",
"status": "REFUNDED",
"paymentMethod": "KAKAO_PAY",
"idempotencyKey": "order-550e8400-payment-001",
"createdAt": "2024-01-15T10:32:00",
"updatedAt": "2024-01-15T12:00:00"
}

Data Models

Payment Entity

@Entity
@Table(name = "payments")
public class Payment {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;

@Column(name = "order_id", nullable = false)
private UUID orderId;

@Column(precision = 12, scale = 2, nullable = false)
private BigDecimal amount;

@Column(length = 3)
private String currency = "USD";

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private PaymentStatus status = PaymentStatus.PENDING;

@Column(name = "idempotency_key", unique = true)
private String idempotencyKey;

@Column(name = "payment_method")
private String paymentMethod;

@Column(name = "created_at")
private LocalDateTime createdAt;

@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

PaymentStatus Enum

public enum PaymentStatus {
PENDING, // Pending
COMPLETED, // Completed
FAILED, // Failed
REFUNDED // Refunded
}

Database Schema

CREATE TABLE payments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
order_id UUID NOT NULL,
amount DECIMAL(12, 2) NOT NULL,
currency VARCHAR(3) DEFAULT 'KRW',
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
idempotency_key VARCHAR(255) UNIQUE,
payment_method VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_payments_order_id ON payments(order_id);
CREATE INDEX idx_payments_status ON payments(status);
CREATE INDEX idx_payments_idempotency_key ON payments(idempotency_key);

Events (Kafka)

Published Topics

Topic NameEventDescription
payments.completedpayment.completedPublished on payment completion
payments.failedpayment.failedPublished on payment failure
payments.refundedpayment.refundedPublished on refund completion

payments.completed Payload

{
"event": "payment.completed",
"payment": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"orderId": "550e8400-e29b-41d4-a716-446655440000",
"amount": 687000.00,
"currency": "KRW",
"status": "COMPLETED",
"paymentMethod": "KAKAO_PAY",
"idempotencyKey": "order-550e8400-payment-001",
"createdAt": "2024-01-15T10:32:00",
"updatedAt": "2024-01-15T10:32:00"
}
}

payments.refunded Payload

{
"event": "payment.refunded",
"payment": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"orderId": "550e8400-e29b-41d4-a716-446655440000",
"amount": 687000.00,
"currency": "KRW",
"status": "REFUNDED",
"paymentMethod": "KAKAO_PAY",
"idempotencyKey": "order-550e8400-payment-001",
"createdAt": "2024-01-15T10:32:00",
"updatedAt": "2024-01-15T12:00:00"
}
}

Environment Variables

VariableDescriptionDefault
SPRING_DATASOURCE_URLAurora PostgreSQL connection URL-
SPRING_DATASOURCE_USERNAMEDB username-
SPRING_DATASOURCE_PASSWORDDB password-
SPRING_KAFKA_BOOTSTRAP_SERVERSKafka broker address-
KAKAO_PAY_API_KEYKakaoPay API key-
TOSS_PAY_SECRET_KEYTossPay secret key-
KG_INICIS_MIDKG Inicis merchant ID-
SERVER_PORTService port8080

Service Dependencies

Idempotency

The Payment Service prevents duplicate payments through idempotencyKey:

  1. Client generates a unique idempotencyKey for each request
  2. Subsequent requests with the same key return the existing payment result
  3. Prevents duplicate payments due to network errors

Payment Flow

Error Handling

HTTP Status CodeErrorDescription
404PaymentNotFoundExceptionPayment not found
400IllegalStateExceptionInvalid state transition (e.g., refunding already refunded payment)
409DuplicatePaymentExceptionDuplicate payment attempt (same idempotencyKey)