Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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