Skip to content

Commit 128ce3a

Browse files
Merge pull request #465 from Podo-Store/develop
[FEAT] 나이스페이
2 parents 2214401 + 62e85fe commit 128ce3a

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

src/main/java/PodoeMarket/podoemarket/common/entity/OrdersEntity.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ public class OrdersEntity {
3535
@Column(nullable = false)
3636
private OrderStatus orderStatus = OrderStatus.WAIT;
3737

38+
@Column
39+
private String tid;
40+
3841
@Column(nullable = false, updatable = false)
3942
private LocalDateTime createdAt;
4043

src/main/java/PodoeMarket/podoemarket/order/controller/OrderController.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
import PodoeMarket.podoemarket.order.dto.response.OrderInfoResponseDTO;
99
import PodoeMarket.podoemarket.order.dto.response.OrderItemResponseDTO;
1010
import PodoeMarket.podoemarket.order.service.OrderService;
11+
import jakarta.servlet.http.HttpServletResponse;
1112
import lombok.RequiredArgsConstructor;
1213
import lombok.extern.slf4j.Slf4j;
1314
import org.springframework.http.ResponseEntity;
1415
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1516
import org.springframework.web.bind.annotation.*;
1617

18+
import java.io.IOException;
1719
import java.util.List;
20+
import java.util.Map;
1821

1922
@RequiredArgsConstructor
2023
@RestController
@@ -62,4 +65,18 @@ public ResponseEntity<?> purchaseSuccess(@RequestParam Long orderId) {
6265
return ResponseEntity.badRequest().body(resDTO);
6366
}
6467
}
68+
69+
@PostMapping("/return")
70+
public void nicePaySuccess(@RequestParam Map<String, String> params, HttpServletResponse response) throws IOException {
71+
try {
72+
// Service가 redirect URL(String)만 반환함
73+
String redirectURL = orderService.handleNicepayReturn(params);
74+
75+
// 성공 시 프론트 성공 페이지로 리다이렉트
76+
response.sendRedirect(redirectURL);
77+
} catch(Exception e) {
78+
// 실패 시 프론트 실패 페이지로 리다이렉트
79+
response.sendRedirect("https://www.podo-store.com/purchase/abort");
80+
}
81+
}
6582
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package PodoeMarket.podoemarket.order.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class NicepayApproveResponseDTO {
13+
private String resultCode; // "0000"이면 승인 성공
14+
private String resultMsg;
15+
private String tid; // 거래 ID
16+
private String orderId;
17+
private Integer amount;
18+
private String payMethod;
19+
private String authDate;
20+
}

src/main/java/PodoeMarket/podoemarket/order/service/OrderService.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import PodoeMarket.podoemarket.common.entity.*;
44
import PodoeMarket.podoemarket.common.entity.type.OrderStatus;
55
import PodoeMarket.podoemarket.order.dto.request.OrderInfoRequestDTO;
6+
import PodoeMarket.podoemarket.order.dto.response.NicepayApproveResponseDTO;
67
import PodoeMarket.podoemarket.order.dto.response.OrderCompleteResponseDTO;
78
import PodoeMarket.podoemarket.order.dto.request.OrderRequestDTO;
89
import PodoeMarket.podoemarket.common.repository.ApplicantRepository;
@@ -13,15 +14,23 @@
1314
import PodoeMarket.podoemarket.order.dto.response.OrderItemResponseDTO;
1415
import PodoeMarket.podoemarket.service.MailSendService;
1516
import org.springframework.beans.factory.annotation.Value;
17+
import org.springframework.http.HttpEntity;
18+
import org.springframework.http.HttpHeaders;
19+
import org.springframework.http.MediaType;
20+
import org.springframework.http.ResponseEntity;
1621
import org.springframework.transaction.annotation.Transactional;
1722
import lombok.RequiredArgsConstructor;
1823
import lombok.extern.slf4j.Slf4j;
1924
import org.springframework.stereotype.Service;
25+
import org.springframework.util.LinkedMultiValueMap;
26+
import org.springframework.util.MultiValueMap;
27+
import org.springframework.web.client.RestTemplate;
2028

2129
import java.net.URLEncoder;
2230
import java.nio.charset.StandardCharsets;
2331
import java.text.DecimalFormat;
2432
import java.util.List;
33+
import java.util.Map;
2534
import java.util.UUID;
2635

2736
@RequiredArgsConstructor
@@ -38,6 +47,12 @@ public class OrderService {
3847
@Value("${cloud.aws.s3.url}")
3948
private String bucketURL;
4049

50+
@Value("${nicepay.client-id}")
51+
private String clientId;
52+
53+
@Value("${nicepay.secret-key}")
54+
private String secretKey;
55+
4156
public OrderItemResponseDTO getOrderItemInfo(UserEntity userInfo, OrderInfoRequestDTO dto) {
4257
try {
4358
final ProductEntity orderProduct = getProduct(dto.getProductId());
@@ -121,6 +136,75 @@ public OrderInfoResponseDTO orderSuccess(Long orderId) {
121136
}
122137
}
123138

139+
@Transactional
140+
public String handleNicepayReturn(Map<String, String> params) {
141+
log.info("NICEPAY RETURN PARAMS = {}", params);
142+
143+
try {
144+
String resultCode = params.get("authResultCode");
145+
String authToken = params.get("authToken");
146+
String tid = params.get("tid");
147+
String orderIdStr = params.get("orderId");
148+
String amount = params.get("amount") != null ? params.get("amt") : params.get("amount");
149+
150+
if (orderIdStr == null) {
151+
throw new RuntimeException("orderId가 존재하지 않음");
152+
}
153+
154+
Long orderId = Long.valueOf(orderIdStr);
155+
156+
// 1) 인증 결과 실패
157+
if (!"0000".equals(resultCode)) {
158+
throw new RuntimeException("인증 실패");
159+
}
160+
161+
// 2) NICEPAY 서버 승인 API 호출
162+
NicepayApproveResponseDTO approveResult = callApprove(authToken, amount);
163+
164+
if (approveResult == null || !"0000".equals(approveResult.getResultCode())) {
165+
throw new RuntimeException("승인 API 실패");
166+
}
167+
168+
// 3) DB 주문 상태 업데이트
169+
OrdersEntity order = orderRepo.findById(orderId).orElseThrow(() -> new RuntimeException("Order not found"));
170+
171+
order.setTid(tid);
172+
order.setOrderStatus(OrderStatus.PASS);
173+
174+
log.info("결제 완료 처리됨: orderId={}, tid={}", orderId, tid);
175+
176+
// 4) 성공 redirect URL 반환
177+
return "https://www.podo-store.com/purchase/success?orderId=" + orderId;
178+
} catch (Exception e) {
179+
throw e;
180+
}
181+
}
182+
183+
public NicepayApproveResponseDTO callApprove(String tid, String amount) {
184+
RestTemplate restTemplate = new RestTemplate();
185+
String url = "https://webapi.nicepay.co.kr/webapi/payments/approve";
186+
187+
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
188+
body.add("clientId", clientId);
189+
body.add("secretKey", secretKey);
190+
body.add("tid", tid);
191+
body.add("amount", amount);
192+
193+
HttpHeaders headers = new HttpHeaders();
194+
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
195+
196+
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(body, headers);
197+
198+
try {
199+
ResponseEntity<NicepayApproveResponseDTO> res = restTemplate.postForEntity(url, entity, NicepayApproveResponseDTO.class);
200+
201+
return res.getBody();
202+
} catch (Exception e) {
203+
log.error("승인 API 호출 실패", e);
204+
return null;
205+
}
206+
}
207+
124208
// =============== private (protected) method ===============
125209
private ProductEntity getProduct(UUID id) {
126210
try {

0 commit comments

Comments
 (0)