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
1 change: 1 addition & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-gradle-plugin:3.5.6'
implementation 'io.spring.gradle:dependency-management-plugin:1.1.7'
implementation 'com.google.protobuf:protobuf-gradle-plugin:0.9.4'
}

tasks.named('test') {
Expand Down
38 changes: 38 additions & 0 deletions buildSrc/src/main/groovy/myproject-grpc-base.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
id 'java'
id 'com.google.protobuf'
}

dependencies {
implementation 'javax.annotation:javax.annotation-api:1.3.2'
implementation 'io.grpc:grpc-stub:1.63.0' // gRPC 클라이언트/서버 스텁(Stub) 생성 및 실행
implementation 'io.grpc:grpc-protobuf:1.63.0' // Protobuf ↔ gRPC 연동 직렬화
implementation 'com.google.protobuf:protobuf-java:3.25.1' // Proto 메시지 구현체
implementation 'io.grpc:grpc-netty-shaded:1.63.0'
testImplementation 'io.grpc:grpc-testing:1.63.0'
}

protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.25.1'
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.63.0'
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}

sourceSets {
main {
java {
srcDirs 'build/generated/source/proto/main/grpc'
srcDirs 'build/generated/source/proto/main/java'
}
}
}
7 changes: 7 additions & 0 deletions buildSrc/src/main/groovy/myproject-grpc-client.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
id 'myproject-grpc-base'
}

dependencies {
implementation 'net.devh:grpc-client-spring-boot-starter:3.1.0.RELEASE'
}
7 changes: 7 additions & 0 deletions buildSrc/src/main/groovy/myproject-grpc-server.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
id 'myproject-grpc-base'
}

dependencies {
implementation 'net.devh:grpc-server-spring-boot-starter:3.1.0.RELEASE'
}
4 changes: 4 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#org.gradle.java.home=/Users/jeunning/Library/Java/JavaVirtualMachines/corretto-21.0.8/Contents/Home
#org.gradle.daemon=true
#org.gradle.parallel=true
#org.gradle.caching=true
9 changes: 9 additions & 0 deletions payments/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins {
id 'myproject-convention'
id 'myproject-grpc-server'
}

dependencies {
implementation project(':shared')
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.commerce.payments;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* Payments 마이크로서비스
* gRPC 서버로 동작
*/
@SpringBootApplication(scanBasePackages = {
"com.commerce.payments",
"com.commerce.shared"
})
public class PaymentsApplication {

public static void main(String[] args) {
SpringApplication.run(PaymentsApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.commerce.payments.application.port.in;


import com.commerce.payments.application.port.in.dto.PayCancelCommand;
import com.commerce.payments.application.port.in.dto.PayOrderCommand;

public interface PaymentUseCase {
void doApproval(PayOrderCommand command);
void doCancel(PayCancelCommand cancelCommand);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.commerce.payments.application.port.in.dto;

import com.commerce.payments.domain.enums.PayMethod;
import com.commerce.payments.domain.enums.PaymentStatus;
import com.commerce.payments.domain.enums.PgProvider;
import com.commerce.shared.enums.PayProvider;
import com.commerce.shared.vo.Money;
import com.commerce.shared.vo.OrderId;
import lombok.*;

/**
* 전체/부분 취소 처리 객체
*/
@Setter
@Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
public class PayCancelCommand {
private OrderId orderId;
private PaymentStatus paymentStatus;
private String cancelReason;

// 이후 계산 및 db데이터 기반으로 세팅됨]
private String pgTid; // pg 승인Tid
private Money canceledAmount;
private PayMethod payMethod;
private PayProvider payProvider;
private PgProvider pgProvider;

private RefundReceiveAccount refundReceiveAccount;

/**
* 환불 계좌 정보 (가상계좌 전용)
*/
@Getter
@AllArgsConstructor
public class RefundReceiveAccount {
private String bankCode;
private String accountNumber;
private String holderName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.commerce.payments.application.port.in.dto;

import com.commerce.payments.domain.enums.PayMethod;
import com.commerce.shared.enums.PayProvider;
import com.commerce.payments.domain.enums.PaymentStatus;
import com.commerce.shared.vo.Money;
import com.commerce.shared.vo.OrderId;
import lombok.*;

@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Builder
public class PayOrderCommand {
private OrderId orderId;
private Money approvedAmount;
private int installment;
private PayMethod payMethod;
private PayProvider payProvider;
private final PaymentStatus paymentStatus = PaymentStatus.APPROVED;
private String jsonSubData; // pg사 요구 데이터
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.commerce.platform.core.application.out;
package com.commerce.payments.application.port.out;

import com.commerce.platform.core.domain.aggreate.CardBinPromotion;

import com.commerce.payments.domain.aggregate.CardBinPromotion;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.commerce.platform.core.application.out;
package com.commerce.payments.application.port.out;

import com.commerce.platform.core.domain.aggreate.Payment;
import com.commerce.platform.core.domain.aggreate.PaymentPartCancel;
import com.commerce.platform.core.domain.vo.Money;
import com.commerce.platform.core.domain.vo.OrderId;
import com.commerce.platform.core.domain.vo.PaymentId;
import com.commerce.payments.domain.aggregate.Payment;
import com.commerce.payments.domain.aggregate.PaymentPartCancel;
import com.commerce.shared.vo.Money;
import com.commerce.shared.vo.OrderId;
import com.commerce.shared.vo.PaymentId;

import java.util.Optional;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.commerce.payments.application.port.out;


import com.commerce.payments.domain.vo.payments.PgPayCancelResponse;
import com.commerce.payments.domain.vo.payments.PgPayResponse;
import com.commerce.payments.application.port.in.dto.PayCancelCommand;
import com.commerce.payments.application.port.in.dto.PayOrderCommand;
import com.commerce.payments.domain.enums.PayMethod;
import com.commerce.payments.domain.enums.PgProvider;

/**
* PG사별 결제를 위한 메소드 정의
*/
public abstract class PgStrategy {

/**
* 승인
*/
public abstract PgPayResponse processApproval(PayOrderCommand command);

/**
* 취소
*/
public abstract PgPayCancelResponse processCancel(PayCancelCommand command);

/**
* PG사명
*/
public abstract PgProvider getPgProvider();

/**
* 결제유형
*/
public abstract PayMethod getPgPayMethod();

/**
* 결제창을 위한 초기화
*/
public abstract Object initPayment();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package com.commerce.payments.bootstrap.grpc;

import com.commerce.payments.application.port.in.PaymentUseCase;
import com.commerce.payments.application.port.in.dto.PayCancelCommand;
import com.commerce.payments.application.port.in.dto.PayOrderCommand;
import com.commerce.payments.domain.enums.PayMethod;
import com.commerce.payments.domain.enums.PaymentStatus;
import com.commerce.shared.enums.PayProvider;
import com.commerce.shared.exception.BusinessException;
import com.commerce.shared.grpc.proto.*;
import com.commerce.shared.vo.Money;
import com.commerce.shared.vo.OrderId;
import io.grpc.stub.StreamObserver;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.server.service.GrpcService;

/**
* gRPC 서버 어댑터
*/
@Slf4j
@RequiredArgsConstructor
@GrpcService
public class PaymentGrpcServiceImpl extends PaymentServiceGrpc.PaymentServiceImplBase {

private final PaymentUseCase paymentUseCase;

/**
* 결제 승인
*/
@Override
public void approvePayment(
PaymentApprovalRequest request,
StreamObserver<PaymentApprovalResponse> responseObserver) {

try {
log.info("payment approval request: {}", request);
// gRPC → Domain
PayOrderCommand command = PayOrderCommand.builder()
.orderId(OrderId.of(request.getOrderId()))
.approvedAmount(Money.of(request.getApprovedAmount()))
.installment(request.getInstallment())
.payMethod(PayMethod.valueOf(request.getPayMethod()))
.payProvider(PayProvider.getPayProviderByValue(request.getPayProvider()))
.build();

paymentUseCase.doApproval(command);

// 응답
PaymentApprovalResponse response = PaymentApprovalResponse.newBuilder()
.setSuccess(true)
.setCode("0000")
.setMessage("결제 승인 성공")
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();

} catch (BusinessException e) {
log.error("[gRPC] 결제 승인 실패: {}", e.getMessage());

PaymentApprovalResponse response = PaymentApprovalResponse.newBuilder()
.setSuccess(false)
.setCode(e.getCode())
.setMessage(e.getMessage())
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();

} catch (Exception e) {
log.error("[gRPC] 결제 승인 오류", e);

PaymentApprovalResponse response = PaymentApprovalResponse.newBuilder()
.setSuccess(false)
.setCode("9999")
.setMessage("승인 처리 중 오류가 발생했습니다")
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();
}
}

/**
* 전체/부분 취소
*/
@Override
public void cancelPayment(
PaymentCancelRequest request,
StreamObserver<PaymentCancelResponse> responseObserver) {

try {
PayCancelCommand cancelCommand = PayCancelCommand.builder()
.orderId(OrderId.of(request.getOrderId()))
.paymentStatus(PaymentStatus.valueOf(request.getPaymentStatus()))
.canceledAmount(Money.of(request.getCanceledAmount()))
.cancelReason(request.getCancelReason())
.build();

paymentUseCase.doCancel(cancelCommand);

PaymentCancelResponse response = PaymentCancelResponse.newBuilder()
.setSuccess(true)
.setCode("0000")
.setMessage("취소 성공")
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();

} catch (BusinessException e) {
log.error("[gRPC] 취소 실패: {}", e.getMessage());

PaymentCancelResponse response = PaymentCancelResponse.newBuilder()
.setSuccess(false)
.setCode(e.getCode())
.setMessage(e.getMessage())
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();

} catch (Exception e) {
log.error("[gRPC] 취소 오류", e);

PaymentCancelResponse response = PaymentCancelResponse.newBuilder()
.setSuccess(false)
.setCode("9999")
.setMessage("취소 처리 중 오류가 발생했습니다")
.build();

responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.commerce.platform.core.domain.aggreate;
package com.commerce.payments.domain.aggregate;

import com.commerce.platform.core.domain.enums.PayProvider;
import com.commerce.platform.core.domain.vo.ValidPeriod;
import com.commerce.platform.core.domain.vo.promotion.BasePromotionData;
import com.commerce.platform.infrastructure.persistence.converter.PromotionDataConverter;
import com.commerce.shared.enums.PayProvider;
import com.commerce.payments.domain.vo.promotion.BasePromotionData;
import com.commerce.payments.infrastructure.persistence.converter.PromotionDataConverter;
import com.commerce.shared.vo.ValidPeriod;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
Expand Down
Loading