-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into feature/JT-28
- Loading branch information
Showing
12 changed files
with
396 additions
and
0 deletions.
There are no files selected for viewing
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
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
30 changes: 30 additions & 0 deletions
30
...plication/src/main/java/com/devtoon/jtoon/payment/presentation/PaymentInfoController.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,30 @@ | ||
package com.devtoon.jtoon.payment.presentation; | ||
|
||
import java.io.IOException; | ||
|
||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.devtoon.jtoon.payment.application.PaymentInfoService; | ||
import com.devtoon.jtoon.payment.request.PaymentInfoDto; | ||
import com.siot.IamportRestClient.exception.IamportResponseException; | ||
import com.siot.IamportRestClient.response.IamportResponse; | ||
import com.siot.IamportRestClient.response.Payment; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/payments") | ||
public class PaymentInfoController { | ||
private final PaymentInfoService paymentInfoService; | ||
|
||
@PostMapping | ||
public IamportResponse<Payment> verifyIamport(@RequestBody PaymentInfoDto paymentInfoDto) | ||
throws IamportResponseException, IOException { | ||
IamportResponse<Payment> iamportResponse = paymentInfoService.paymentLookup(paymentInfoDto.impUid()); | ||
return paymentInfoService.verifyIamport(iamportResponse, paymentInfoDto); | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
module-application/src/main/resources/templates/payments.html
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,91 @@ | ||
<!DOCTYPE html> | ||
<html lang="ko"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Sample Payment</title> | ||
|
||
<!-- jQuery --> | ||
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js"></script> | ||
<!-- iamport.payment.js --> | ||
<script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.2.0.js"></script> | ||
|
||
<script> | ||
var IMP = window.IMP; | ||
IMP.init("imp27504174"); | ||
|
||
function requestPay() { | ||
IMP.request_pay({ | ||
pg: $("#pg").val(), | ||
pay_method: $("#pay-method").val(), | ||
merchant_uid: "IMP" + new Date().getTime(), | ||
name: '쿠키', | ||
amount: Number($("#amount").val()), | ||
buyer_email: $("#buyer-email").val(), | ||
buyer_name: $("#buyer-name").val(), | ||
buyer_tel: $("#buyer-tel").val() | ||
}, function (rsp) { | ||
// 결제 성공 시 | ||
if (rsp.success) { | ||
console.log(rsp); | ||
let data = { | ||
impUid: rsp.imp_uid, | ||
merchantUid: rsp.merchant_uid, | ||
pg: rsp.pg, | ||
payMethod: rsp.pay_method, | ||
productName: rsp.name, | ||
amount: rsp.amount, | ||
buyerEmail: rsp.buyer_email, | ||
buyerName: rsp.buyer_name, | ||
buyerPhone: rsp.buyer_tel | ||
}; | ||
|
||
// 서버로 해당 정보로 검증 및 주문서 생성 요청 | ||
$.ajax({ | ||
type: "POST", | ||
url: 'api/v1/payments', | ||
data: JSON.stringify(data), | ||
contentType: "application/json", | ||
dataType: "json", | ||
success: function (result) { | ||
alert("결제 검증 완료"); | ||
}, error: function (result) { | ||
console.log(jqXHR.status + ", " + textStatus + ", " + errorThrown); | ||
alert("2. 결제 실패 : " + jqXHR.status + ", " + errorThrown); | ||
} | ||
}); | ||
} else { | ||
// 결제 실패 시, | ||
console.log(rsp); | ||
alert('1. 결제 실패 : ' + rsp.error_msg); | ||
} | ||
}); | ||
} | ||
</script> | ||
</head> | ||
<body> | ||
이름: <input type="text" id="buyer-name" value="hhj"><br> | ||
이메일: <input type="text" id="buyer-email" value="example@naver.com"><br> | ||
전화번호: <input type="text" id="buyer-tel" value="010-1717-1237"><br> | ||
|
||
상품: | ||
<select id='amount'> | ||
<option value='1000'>쿠키 10개 - 1000원</option> | ||
<option value='2000'>쿠키 20개 - 2000원</option> | ||
<option value='3000'>쿠키 30개 - 3000원</option> | ||
<option value='5000'>쿠키 50개 - 5000원</option> | ||
<option value='10000'>쿠키 100개 - 10000원</option> | ||
</select><br> | ||
|
||
결제 방법: | ||
<select id='pay-method'> | ||
<option value='card'>카드</option> | ||
</select><br> | ||
|
||
결제사: | ||
<select id='pg'> | ||
<option value='kcp'>KG이니시스</option> | ||
</select><br><br> | ||
|
||
<button onclick="requestPay()">결제하기</button> | ||
</body> | ||
</html> |
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
2 changes: 2 additions & 0 deletions
2
module-domain/src/main/java/com/devtoon/jtoon/member/repository/MemberRepository.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
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
76 changes: 76 additions & 0 deletions
76
module-domain/src/main/java/com/devtoon/jtoon/payment/application/PaymentInfoService.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,76 @@ | ||
package com.devtoon.jtoon.payment.application; | ||
|
||
import java.io.IOException; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import com.devtoon.jtoon.member.entity.Member; | ||
import com.devtoon.jtoon.member.repository.MemberRepository; | ||
import com.devtoon.jtoon.payment.entity.PaymentInfo; | ||
import com.devtoon.jtoon.payment.repository.PaymentInfoRepository; | ||
import com.devtoon.jtoon.payment.request.PaymentInfoDto; | ||
import com.siot.IamportRestClient.IamportClient; | ||
import com.siot.IamportRestClient.exception.IamportResponseException; | ||
import com.siot.IamportRestClient.response.IamportResponse; | ||
import com.siot.IamportRestClient.response.Payment; | ||
|
||
@Service | ||
@Transactional(readOnly = true) | ||
public class PaymentInfoService { | ||
|
||
@Value("${pg.kg-inicis.rest-api-key}") | ||
private String REST_API_KEY; | ||
|
||
@Value("${pg.kg-inicis.rest-api-secret}") | ||
private String REST_API_SECRET; | ||
|
||
private final IamportClient iamportClient; | ||
private final MemberRepository memberRepository; | ||
private final PaymentInfoRepository paymentInfoRepository; | ||
|
||
public PaymentInfoService(MemberRepository memberRepository, PaymentInfoRepository paymentInfoRepository) { | ||
this.iamportClient = new IamportClient(REST_API_KEY, REST_API_SECRET); | ||
this.paymentInfoRepository = paymentInfoRepository; | ||
this.memberRepository = memberRepository; | ||
} | ||
|
||
public IamportResponse<Payment> paymentLookup(String impUid) throws IamportResponseException, IOException { | ||
return iamportClient.paymentByImpUid(impUid); | ||
} | ||
|
||
@Transactional | ||
public IamportResponse<Payment> verifyIamport( | ||
IamportResponse<Payment> iamportResponse, | ||
PaymentInfoDto paymentInfoDto | ||
) { | ||
validateImpUid(paymentInfoDto); | ||
validateMerchantUid(paymentInfoDto); | ||
validateAmount(iamportResponse, paymentInfoDto.amount()); | ||
Member member = memberRepository.findByPhone(paymentInfoDto.buyerPhone()) | ||
.orElseThrow(() -> new RuntimeException("member is not found")); | ||
PaymentInfo paymentInfo = paymentInfoDto.toEntity(member); | ||
paymentInfoRepository.save(paymentInfo); | ||
|
||
return iamportResponse; | ||
} | ||
|
||
private void validateAmount(IamportResponse<Payment> iamportResponse, int amount) { | ||
if (iamportResponse.getResponse().getAmount().intValue() != amount) { | ||
throw new RuntimeException("verify iamport exception"); | ||
} | ||
} | ||
|
||
private void validateMerchantUid(PaymentInfoDto paymentInfoDto) { | ||
if (paymentInfoRepository.existsByMerchantUid(paymentInfoDto.merchantUid())) { | ||
throw new RuntimeException("merchantUid duplicate"); | ||
} | ||
} | ||
|
||
private void validateImpUid(PaymentInfoDto paymentInfoDto) { | ||
if (paymentInfoRepository.existsByImpUid(paymentInfoDto.impUid())) { | ||
throw new RuntimeException("impUid duplicate"); | ||
} | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
module-domain/src/main/java/com/devtoon/jtoon/payment/entity/PG.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,32 @@ | ||
package com.devtoon.jtoon.payment.entity; | ||
|
||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
public enum PG { | ||
KCP("KG_이니시스"); | ||
|
||
private String name; | ||
|
||
private static final Map<String, PG> PG_MAP; | ||
|
||
static { | ||
PG_MAP = Collections.unmodifiableMap(Arrays.stream(values()) | ||
.collect(Collectors.toMap(PG::getName, Function.identity()))); | ||
} | ||
|
||
public static PG from(String name) { | ||
return Optional.ofNullable(PG_MAP.get(name)) | ||
.orElseThrow(() -> new RuntimeException("현재 서버에 등록되지 않은 Payment Gateway 입니다.")); | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
module-domain/src/main/java/com/devtoon/jtoon/payment/entity/PaymentInfo.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,94 @@ | ||
package com.devtoon.jtoon.payment.entity; | ||
|
||
import java.util.Objects; | ||
|
||
import com.devtoon.jtoon.member.entity.Member; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.FetchType; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import jakarta.persistence.Table; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@Entity | ||
@Table(name = "payments") | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class PaymentInfo { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
@Column(name = "payment_id") | ||
private Long id; | ||
|
||
@Column(name = "imp_uid", length = 100, nullable = false, unique = true, updatable = false) | ||
private String impUid; // 포트원 결제 고유번호 | ||
|
||
@Column(name = "pay_method", length = 100, nullable = false, unique = true, updatable = false) | ||
private String merchantUid; // 가맹점 주문번호 | ||
|
||
@Column(name = "pg", length = 20, nullable = false) | ||
private PG pg; // 결제사 | ||
|
||
@Column(name = "pay_method", length = 20, nullable = false) | ||
private String payMethod; // 결제 방법 | ||
|
||
@Column(name = "product_name", length = 15, nullable = false) | ||
private String productName; // 상품명 | ||
|
||
@Column(name = "amount", nullable = false) | ||
private int amount; // 결제 금액 | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "member_id", nullable = false) | ||
private Member member; | ||
|
||
@Builder | ||
private PaymentInfo( | ||
String impUid, | ||
String merchantUid, | ||
PG pg, | ||
String payMethod, | ||
String productName, | ||
int amount, | ||
Member member | ||
) { | ||
validateFieldNotNull(impUid, merchantUid, pg, payMethod, productName, amount, member); | ||
this.impUid = impUid; | ||
this.pg = pg; | ||
this.payMethod = payMethod; | ||
this.merchantUid = merchantUid; | ||
this.productName = productName; | ||
this.amount = amount; | ||
this.member = member; | ||
} | ||
|
||
private void validateFieldNotNull( | ||
String impUid, | ||
String merchantUid, | ||
PG pg, | ||
String payMethod, | ||
String productName, | ||
int amount, | ||
Member member | ||
) { | ||
Objects.requireNonNull(impUid, "impUid is null"); | ||
Objects.requireNonNull(merchantUid, "merchantUid is null"); | ||
Objects.requireNonNull(pg, "pg is null"); | ||
Objects.requireNonNull(payMethod, "payMethod is null"); | ||
Objects.requireNonNull(productName, "productName is null"); | ||
Objects.requireNonNull(member, "member is null"); | ||
|
||
if (amount <= 0) { | ||
throw new RuntimeException("amount is zero or negative number"); | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
module-domain/src/main/java/com/devtoon/jtoon/payment/repository/PaymentInfoRepository.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,14 @@ | ||
package com.devtoon.jtoon.payment.repository; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.stereotype.Repository; | ||
|
||
import com.devtoon.jtoon.payment.entity.PaymentInfo; | ||
|
||
@Repository | ||
public interface PaymentInfoRepository extends JpaRepository<PaymentInfo, Long> { | ||
|
||
boolean existsByImpUid(String impUid); | ||
|
||
boolean existsByMerchantUid(String merchantUid); | ||
} |
Oops, something went wrong.