-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44 from 9oormthon-univ/32-feature-integrate-kakao…
…-pay-open-api-for-point-payment-feature 32 feature integrate kakao pay open api for point payment feature
- Loading branch information
Showing
11 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
59 changes: 59 additions & 0 deletions
59
src/main/java/com/example/mymoo/domain/payment/controller/PaymentController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package com.example.mymoo.domain.payment.controller; | ||
|
||
import com.example.mymoo.domain.payment.dto.api.KakaoPayReadyResponse; | ||
import com.example.mymoo.domain.payment.dto.request.PayRequestDTO; | ||
import com.example.mymoo.domain.payment.dto.response.PayResponseDTO; | ||
import com.example.mymoo.domain.payment.service.PaymentService; | ||
import com.example.mymoo.global.security.CustomUserDetails; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@RestController | ||
@RequestMapping("api/v1/payment") | ||
@RequiredArgsConstructor | ||
public class PaymentController { | ||
|
||
private final PaymentService paymentService; | ||
|
||
@PostMapping("ready") | ||
public ResponseEntity<KakaoPayReadyResponse> payReady( | ||
@AuthenticationPrincipal CustomUserDetails userDetails, | ||
@RequestBody PayRequestDTO req | ||
){ | ||
KakaoPayReadyResponse res = paymentService.payReady(req.getName(), req.getTotalPrice(), userDetails.getAccountId()); | ||
return ResponseEntity.status(HttpStatus.ACCEPTED).body(res); | ||
} | ||
|
||
@GetMapping("approve") | ||
public ResponseEntity<PayResponseDTO> approve( | ||
@AuthenticationPrincipal CustomUserDetails userDetails, | ||
@RequestParam("pg_token") String pgToken, | ||
@RequestParam("tid") String tid | ||
) { | ||
//승인 처리 - 이 부분은 프론트에서 1차적으로 리다이렉트 | ||
// 프론트에서 받은 결제 정보(pg_token)를 해당 api에 넘겨주면 서버에 반영됨 | ||
return ResponseEntity.status(HttpStatus.ACCEPTED) | ||
.body(paymentService.approve(pgToken, tid, userDetails.getAccountId())); | ||
} | ||
|
||
@GetMapping("cancel") | ||
public String cancel() { | ||
// 주문건이 진짜 취소되었는지 확인 후 취소 처리 | ||
// 결제내역조회(/v1/payment/status) api에서 status를 확인한다. | ||
// To prevent the unwanted request cancellation caused by attack, | ||
// the “show payment status” API is called and then check if the status is QUIT_PAYMENT before suspending the payment | ||
return "cancel"; | ||
} | ||
|
||
@GetMapping("fail") | ||
public String fail() { | ||
// 주문건이 진짜 실패되었는지 확인 후 실패 처리 | ||
// 결제내역조회(/v1/payment/status) api에서 status를 확인한다. | ||
// To prevent the unwanted request cancellation caused by attack, | ||
// the “show payment status” API is called and then check if the status is FAIL_PAYMENT before suspending the payment | ||
return "fail"; | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/com/example/mymoo/domain/payment/dto/api/KakaoPayApproveRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.example.mymoo.domain.payment.dto.api; | ||
|
||
import com.fasterxml.jackson.databind.PropertyNamingStrategy; | ||
import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
|
||
@Data | ||
@Builder | ||
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | ||
public class KakaoPayApproveRequest { | ||
private String tid; | ||
private String cid; | ||
private String partnerOrderId; | ||
private String partnerUserId; | ||
private String pgToken; | ||
} |
31 changes: 31 additions & 0 deletions
31
src/main/java/com/example/mymoo/domain/payment/dto/api/KakaoPayApproveResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.example.mymoo.domain.payment.dto.api; | ||
|
||
import lombok.Data; | ||
|
||
@Data | ||
public class KakaoPayApproveResponse { | ||
private String aid; // 요청 고유 번호 | ||
private String tid; // 결제 고유 번호 | ||
private String cid; // 가맹점 코드 | ||
private String sid; // 정기결제용 ID | ||
private String partner_order_id; // 가맹점 주문 번호 | ||
private String partner_user_id; // 가맹점 회원 id | ||
private String payment_method_type; // 결제 수단 | ||
private Amount amount; // 결제 금액 정보 | ||
private String item_name; // 상품명 | ||
private String item_code; // 상품 코드 | ||
private int quantity; // 상품 수량 | ||
private String created_at; // 결제 요청 시간 | ||
private String approved_at; // 결제 승인 시간 | ||
private String payload; // 결제 승인 요청에 대해 저장 값, 요청 시 전달 내용 | ||
|
||
@Data | ||
public class Amount{ | ||
private int total; // 총 결제 금액 | ||
private int tax_free; // 비과세 금액 | ||
private int tax; // 부가세 금액 | ||
private int point; // 사용한 포인트 | ||
private int discount; // 할인금액 | ||
private int green_deposit; // 컵 보증금 | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
src/main/java/com/example/mymoo/domain/payment/dto/api/KakaoPayReadyRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.example.mymoo.domain.payment.dto.api; | ||
|
||
import com.fasterxml.jackson.databind.PropertyNamingStrategy; | ||
import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
|
||
@Data @Builder | ||
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) | ||
public class KakaoPayReadyRequest { | ||
private String cid; | ||
private String partnerOrderId; | ||
private String partnerUserId; | ||
private String itemName; | ||
private Integer quantity; | ||
private Integer totalAmount; | ||
private Integer taxFreeAmount; | ||
private Integer vatAmount; | ||
private String approvalUrl; | ||
private String cancelUrl; | ||
private String failUrl; | ||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/com/example/mymoo/domain/payment/dto/api/KakaoPayReadyResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.example.mymoo.domain.payment.dto.api; | ||
|
||
import lombok.Data; | ||
import lombok.ToString; | ||
|
||
@Data | ||
@ToString | ||
public class KakaoPayReadyResponse { | ||
private String tid; | ||
private Boolean tms_result; | ||
private String created_at; | ||
private String next_redirect_pc_url; | ||
private String next_redirect_mobile_url; | ||
private String next_redirect_app_url; | ||
private String android_app_scheme; | ||
private String ios_app_scheme; | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/example/mymoo/domain/payment/dto/request/PayRequestDTO.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.example.mymoo.domain.payment.dto.request; | ||
|
||
import lombok.Data; | ||
|
||
@Data | ||
public class PayRequestDTO { | ||
private String name; | ||
private Integer totalPrice; | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/com/example/mymoo/domain/payment/dto/response/PayResponseDTO.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.example.mymoo.domain.payment.dto.response; | ||
|
||
import lombok.Builder; | ||
import lombok.Data; | ||
|
||
@Data @Builder | ||
public class PayResponseDTO { | ||
private String item_name; | ||
private String account_name; | ||
private int total; | ||
private String created_at; // 결제 요청 시간 | ||
private String approved_at; // 결제 승인 시간 | ||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/com/example/mymoo/domain/payment/exception/PaymentException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.example.mymoo.domain.payment.exception; | ||
|
||
import com.example.mymoo.domain.store.exception.StoreExceptionDetails; | ||
import com.example.mymoo.global.exception.CustomException; | ||
|
||
public class PaymentException extends CustomException { | ||
public PaymentException(PaymentExceptionDetails paymentExceptionDetails){ | ||
super(paymentExceptionDetails); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/com/example/mymoo/domain/payment/exception/PaymentExceptionDetails.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.example.mymoo.domain.payment.exception; | ||
|
||
import com.example.mymoo.global.exception.ExceptionDetails; | ||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public enum PaymentExceptionDetails implements ExceptionDetails { | ||
// 가게 id가 store 테이블에 존재하지 않을 때 | ||
APPROVE_FAILED(HttpStatus.BAD_REQUEST, "승인 요청이 실패했습니다."), | ||
; | ||
|
||
private final HttpStatus status; | ||
private final String message; | ||
} |
125 changes: 125 additions & 0 deletions
125
src/main/java/com/example/mymoo/domain/payment/service/Impl/PaymentServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package com.example.mymoo.domain.payment.service.Impl; | ||
|
||
import com.example.mymoo.domain.account.entity.Account; | ||
import com.example.mymoo.domain.account.exception.AccountException; | ||
import com.example.mymoo.domain.account.exception.AccountExceptionDetails; | ||
import com.example.mymoo.domain.account.repository.AccountRepository; | ||
import com.example.mymoo.domain.payment.dto.api.KakaoPayApproveRequest; | ||
import com.example.mymoo.domain.payment.dto.api.KakaoPayApproveResponse; | ||
import com.example.mymoo.domain.payment.dto.api.KakaoPayReadyRequest; | ||
import com.example.mymoo.domain.payment.dto.api.KakaoPayReadyResponse; | ||
import com.example.mymoo.domain.payment.dto.response.PayResponseDTO; | ||
import com.example.mymoo.domain.payment.exception.PaymentException; | ||
import com.example.mymoo.domain.payment.exception.PaymentExceptionDetails; | ||
import com.example.mymoo.domain.payment.service.PaymentService; | ||
import com.example.mymoo.global.aop.LogExecutionTime; | ||
import jakarta.transaction.Transactional; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.*; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.client.HttpStatusCodeException; | ||
import org.springframework.web.client.RestTemplate; | ||
import org.springframework.web.reactive.function.client.WebClient; | ||
|
||
|
||
@Service @LogExecutionTime | ||
@RequiredArgsConstructor | ||
public class PaymentServiceImpl implements PaymentService { | ||
|
||
private final AccountRepository accountRepository; | ||
|
||
@Value("${kakao.pay.secret-key}") | ||
private String secretKey; | ||
@Value("${kakao.pay.uri}") | ||
private String uri; | ||
@Value("${kakao.pay.cid}") | ||
private String cid; | ||
@Value("${kakao.pay.approve-Url}") | ||
private String approvalUrl; | ||
@Value("${kakao.pay.partner-order-id}") | ||
private String partnerOrderId; | ||
|
||
private String tid; | ||
|
||
public KakaoPayReadyResponse payReady(String name, Integer totalPrice, Long accountId){ | ||
// Request header | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.add("Authorization", "DEV_SECRET_KEY " + secretKey); | ||
headers.setContentType(MediaType.APPLICATION_JSON); | ||
|
||
// Request param | ||
KakaoPayReadyRequest readyRequest = KakaoPayReadyRequest.builder() | ||
.cid(cid) | ||
.partnerOrderId(partnerOrderId) | ||
.partnerUserId(String.valueOf(accountId)) | ||
.itemName(name) | ||
.quantity(1) | ||
.totalAmount(totalPrice) | ||
.taxFreeAmount(0) | ||
.vatAmount(100) | ||
.approvalUrl(approvalUrl) | ||
.cancelUrl("http://localhost:8080/payment/cancel") | ||
.failUrl("http://localhost:8080/payment/fail") | ||
.build(); | ||
|
||
// Send reqeust | ||
HttpEntity<KakaoPayReadyRequest> entityMap = new HttpEntity<>(readyRequest, headers); | ||
ResponseEntity<KakaoPayReadyResponse> response = new RestTemplate().postForEntity( | ||
uri, | ||
entityMap, | ||
KakaoPayReadyResponse.class | ||
); | ||
this.tid = response.getBody().getTid(); | ||
// 주문번호와 TID를 매핑해서 저장해놓는다. | ||
// Mapping TID with partner_order_id then save it to use for approval request. | ||
return response.getBody(); | ||
} | ||
|
||
@Transactional | ||
public PayResponseDTO approve(String pgToken, String tida, Long accountId){ | ||
// ready할 때 저장해놓은 TID로 승인 요청 | ||
// Call “Execute approved payment” API by pg_token, TID mapping to the current payment transaction and other parameters. | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.add("Authorization", "SECRET_KEY " + secretKey); | ||
headers.setContentType(MediaType.APPLICATION_JSON); | ||
// Request param | ||
KakaoPayApproveRequest approveRequest = KakaoPayApproveRequest.builder() | ||
.cid(cid) | ||
.tid(tid) | ||
.partnerOrderId(partnerOrderId) | ||
.partnerUserId(String.valueOf(accountId)) | ||
.pgToken(pgToken) | ||
.build(); | ||
|
||
// Send Request | ||
HttpEntity<KakaoPayApproveRequest> entityMap = new HttpEntity<>(approveRequest, headers); | ||
try { | ||
ResponseEntity<KakaoPayApproveResponse> response = new RestTemplate().postForEntity( | ||
"https://open-api.kakaopay.com/online/v1/payment/approve", | ||
entityMap, | ||
KakaoPayApproveResponse.class | ||
); | ||
|
||
// 승인 결과를 저장한다. | ||
// save the result of approval | ||
KakaoPayApproveResponse res = response.getBody(); | ||
// account 계정에 결제금액 만큼 포인트 충전 | ||
Account foundAccount = accountRepository.findById(Long.valueOf(res.getPartner_user_id())) | ||
.orElseThrow(() -> new AccountException(AccountExceptionDetails.ACCOUNT_NOT_FOUND)); | ||
System.out.println(Long.valueOf(res.getAmount().getTotal())); | ||
foundAccount.chargePoint(Long.valueOf(res.getAmount().getTotal())); | ||
|
||
return PayResponseDTO.builder() | ||
.item_name(res.getItem_name()) | ||
.account_name(foundAccount.getNickname()) | ||
.total(res.getAmount().getTotal()) | ||
.created_at(res.getCreated_at()) | ||
.approved_at(res.getApproved_at()) | ||
.build(); | ||
|
||
} catch (HttpStatusCodeException ex) { | ||
throw new PaymentException(PaymentExceptionDetails.APPROVE_FAILED); | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/example/mymoo/domain/payment/service/PaymentService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.example.mymoo.domain.payment.service; | ||
|
||
import com.example.mymoo.domain.payment.dto.api.KakaoPayReadyResponse; | ||
import com.example.mymoo.domain.payment.dto.response.PayResponseDTO; | ||
|
||
public interface PaymentService { | ||
KakaoPayReadyResponse payReady(String name, Integer totalPrice, Long accountId); | ||
PayResponseDTO approve(String pgToken, String tid, Long accountId); | ||
} |