Skip to content
4 changes: 3 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
MYSQL_DATABASE=commerce_dev_db
MYSQL_ROOT_PASSWORD=je1234
MYSQL_PORT=3308
MYSQL_PORT=3308
REDIS_PORT=6379
REDIS_PASSWORD=je1234
1 change: 1 addition & 0 deletions buildSrc/src/main/groovy/myproject-convention.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
runtimeOnly("com.mysql:mysql-connector-j")

compileOnly 'org.projectlombok:lombok'
Expand Down
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ services:
volumes:
- db-data:/var/lib/mysql

redis:
image: redis:7.2-alpine
container_name: commerce-redis
ports:
- "${REDIS_PORT}:6379"
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data

# 데이터 영속성을 위함
volumes:
db-data:
redis-data:
Original file line number Diff line number Diff line change
@@ -1,11 +1,100 @@
package com.commerce.platform.bootstrap.customer;

import com.commerce.platform.bootstrap.dto.payment.PaymentCancelRequest;
import com.commerce.platform.bootstrap.dto.payment.PaymentRequest;
import com.commerce.platform.core.application.in.PaymentUseCase;
import com.commerce.platform.core.application.in.dto.PayCancelCommand;
import com.commerce.platform.core.application.in.dto.PayOrderCommand;
import com.commerce.platform.core.application.in.dto.PayResult;
import com.commerce.platform.core.domain.enums.PaymentStatus;
import com.commerce.platform.core.domain.vo.OrderId;
import com.commerce.platform.shared.exception.BusinessException;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RequiredArgsConstructor
@RequestMapping("/payment")
@RequestMapping("/payments")
@RestController
public class PaymentController {
private final PaymentUseCase paymentUseCase;

@PostMapping("/approval")
public ResponseEntity<PayResult> createPayment(@Valid @RequestBody PaymentRequest paymentRequest) {
PayResult result = null;

try {
PayOrderCommand command = new PayOrderCommand(
OrderId.of(paymentRequest.orderId()),
null,
paymentRequest.installment(),
paymentRequest.payMethod(),
paymentRequest.payProvider()
);

paymentUseCase.doApproval(command);
} catch (BusinessException e) {
result = new PayResult.Failed(e.getCode(), e.getMessage());
} catch (Exception e) {
result = new PayResult.Failed("9999", "승인 처리 중 오류가 발생했습니다");
}

return ResponseEntity.ok(result);
}

@PatchMapping("/cancel")
public ResponseEntity<PayResult> fullCancel(@Valid @RequestBody PaymentCancelRequest cancelRequest) {
PayResult result = null;

try {
PayCancelCommand cancelCommand = PayCancelCommand.builder()
.orderId(cancelRequest.orderId())
.paymentStatus(PaymentStatus.FULL_CANCELED)
.build();

paymentUseCase.doCancel(cancelCommand);
} catch (BusinessException e) {
result = new PayResult.Failed(e.getCode(), e.getMessage());
} catch (Exception e) {
result = new PayResult.Failed("9999", "전체취소 처리 중 오류가 발생했습니다");
}

return ResponseEntity.ok(result);
}

@PatchMapping("/partial-cancel")
public ResponseEntity<PayResult> partialCancel(@Valid @RequestBody PaymentCancelRequest cancelRequest) {
PayResult result = null;

try {
PayCancelCommand cancelCommand = PayCancelCommand.builder()
.orderId(cancelRequest.orderId())
.orderItemId(cancelRequest.orderItemId())
.canceledQuantity(cancelRequest.canceledQuantity())
.paymentStatus(PaymentStatus.FULL_CANCELED)
.build();

paymentUseCase.doPartCancel(cancelCommand);
} catch (BusinessException e) {
result = new PayResult.Failed(e.getCode(), e.getMessage());
} catch (Exception e) {
result = new PayResult.Failed("9999", "부분취소 처리 중 오류가 발생했습니다");
}

return ResponseEntity.ok(result);
}

@PostMapping("/registry-card/{cardId}")
public ResponseEntity<String> createPaymentWithRegistryCard(
@PathVariable Long cardId,
@RequestBody Map<String, String> body) {

// todo
paymentUseCase.doApprovalWithCardId(cardId);

return ResponseEntity.ok("성공");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.commerce.platform.bootstrap.dto.payment;

import com.commerce.platform.core.domain.enums.PaymentStatus;
import com.commerce.platform.core.domain.vo.OrderId;
import com.commerce.platform.core.domain.vo.Quantity;
import jakarta.validation.constraints.NotBlank;

/**
* 전채/부분 취소 요청 dto
*/
public record PaymentCancelRequest(
@NotBlank
OrderId orderId,

Long orderItemId, // 취소할 orderItem

Quantity canceledQuantity, // 해당 orderItem의 취소 개수

@NotBlank
PaymentStatus paymentStatus
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.commerce.platform.bootstrap.dto.payment;

import com.commerce.platform.core.domain.enums.PayMethod;
import com.commerce.platform.core.domain.enums.PayProvider;
import jakarta.validation.constraints.NotBlank;

/**
* 승인 요청 dto
*/
public record PaymentRequest(
@NotBlank
String orderId,

@NotBlank
PayMethod payMethod,

@NotBlank
PayProvider payProvider,

@NotBlank
String installment
) { }
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public OrderDetailResponse getOrder(OrderId orderId) {
List<OrderItem> orderItems = orderItemOutPort.findByOrderId(order.getOrderId());

List<ProductId> productIds = orderItems.stream()
.map(oi -> oi.getOrderItemId().productId())
.map(oi -> oi.getProductId())
.toList();

Map<ProductId, Product> productMap = productOutputPort.findByIdIn(productIds).stream()
Expand Down Expand Up @@ -156,15 +156,15 @@ private Money applyCoupon(Order order, CouponId couponId) {
*/
private Money calculateTotalAmountFromProducts(List<OrderItem> orderItems) {
List<ProductId> productIds = orderItems.stream()
.map(oi -> oi.getOrderItemId().productId())
.map(oi -> oi.getProductId())
.toList();

Map<ProductId, Product> productMap = productOutputPort.findByIdIn(productIds).stream()
.collect(Collectors.toMap(Product::getProductId, Function.identity()));

return orderItems.stream()
.map(item -> {
Product product = productMap.get(item.getOrderItemId().productId());
Product product = productMap.get(item.getProductId());
product.decreaseStock(item.getQuantity()); // todo 재고 소진 테스트
return product.getPrice()
.multiply(item.getQuantity());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.commerce.platform.core.application.in;

import com.commerce.platform.core.application.in.dto.PayCancelCommand;
import com.commerce.platform.core.application.in.dto.PayOrderCommand;

public interface PaymentUseCase {
void doApproval(PayOrderCommand command);
void doCancel(PayCancelCommand cancelCommand);
Long doPartCancel(PayCancelCommand cancelCommand);
void doApprovalWithCardId(Long cardId); // 등록된 카드로 결제
}
Loading
Loading