๐Ÿ’ณ ํ† ์ŠคํŽ˜์ด๋จผ์ธ  ์—ฐ๋™ํ•˜๊ธฐ (2) _๊ฒฐ์ œ ์š”์ฒญ

ํ˜„์ฃผยท2023๋…„ 5์›” 31์ผ
2

๐Ÿ“Œ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„œ ํ† ์ŠคํŽ˜์ด๋จผ์ธ ๋ฅผ ์ด์šฉํ•œ ๊ฒฐ์ œ ํŒŒํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜์˜€๋Š”์ง€ ์‚ดํŽด๋ณด๋ ค๊ณ  ํ•œ๋‹ค !
๐Ÿ“Œ <๊ฒฐ์ œ ํ๋ฆ„ ์—ญํ• ๋ณ„ ์ •๋ฆฌ>์—์„œ 1~3๋ฒˆ์— ํ•ด๋‹นํ•˜๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

๋จผ์ €, ํ† ์ŠคํŽ˜์ด๋จผ์ธ ์˜ ๊ฐœ๋ฐœ์ž ํ…Œ์ŠคํŠธ์šฉ API ํ‚ค๋ฅผ ๋ฐ›์•„ ์‚ฌ์šฉํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์—

ํ† ์ŠคํŽ˜์ด๋จผ์ธ ์— ๊ฐ€์ž… ํ›„, ๊ฐœ๋ฐœ์ž์šฉ ํ…Œ์ŠคํŠธ์ƒ์ ์—์„œ API ํ‚ค๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

[ํ† ์ŠคํŽ˜์ด๋จผ์ธ  API ํ‚ค ๋ฐ›๊ธฐ ์ฐธ๊ณ ]

์ด API ํ‚ค๋Š” ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•  ๋•Œ ํ† ์ŠคํŽ˜์ด๋จผ์ธ  ๊ฒฐ์ œ์ฐฝ API์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ฏ€๋กœ

appliaction.yml์— ์œ„์—์„œ ๋ฐœ๊ธ‰๋ฐ›์€ ํ…Œ์ŠคํŠธ ํด๋ผ์ด์–ธํŠธ ํ‚ค์™€ ํ…Œ์ŠคํŠธ ์‹œํฌ๋ฆฟ ํ‚ค๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ ์–ด์ค€ ํ›„,

TossPaymentConfig ํด๋ž˜์Šค์— @Value ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ API ํ‚ค์™€ URL๋“ค์„ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด ์ค„ ๊ฒƒ์ด๋‹ค.


๐ŸŒผ ๋ฐฑ์—”๋“œ์˜ ์—ญํ• 

์„œ๋ฒ„์—์„œ ๊ฒฐ์ œ์— ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์ž…๋ ฅ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” Controller,
์ „๋‹ฌ๋ฐ›์€ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ๊ฒ€์ฆ / ํ•„์š”ํ•œ ๊ฐ’๋“ค์„ ์ƒ์„ฑํ•˜๋Š” Service,
์š”์ฒญ ๊ฐ’๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Dto,
๊ฒฐ์ œ ์š”์ฒญ ๋‚ด์—ญ์œผ๋กœ์จ DB์— ์ €์žฅ๋  Entity,
์ €์žฅ ์žฅ์†Œ์ธ Repository๋ฅผ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๋‹ค.


โœ”๏ธ ๊ฒฐ์ œ Dto

  • ๊ฒฐ์ œ ํ˜ธ์ถœ ์š”์ฒญ์„ ํ•˜๋Š” PaymentDto

    @Setter
    @Getter
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public class PaymentDto {
        @NonNull
        private PayType payType; // ๊ฒฐ์ œ ํƒ€์ž… - ์นด๋“œ/ํ˜„๊ธˆ/ํฌ์ธํŠธ
        @NonNull
        private Long amount; // ๊ฐ€๊ฒฉ ์ •๋ณด
        @NonNull
        private String orderName; // ์ฃผ๋ฌธ๋ช…
    โ €
        private String yourSuccessUrl; // ์„ฑ๊ณต ์‹œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋  URL
        private String yourFailUrl; // ์‹คํŒจ ์‹œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋  URL
    โ €
        public Payment toEntity() {
            return Payment.builder()
                    .payType(payType)
                    .amount(amount)
                    .orderName(orderName)
                    .orderId(UUID.randomUUID().toString())
                    .paySuccessYN(false)
                    .build();
        }
    }

    โžœ ์œ„ ๊ฐ’๋“ค์„ ํ”„๋ก ํŠธ์—์„œ ์ž…๋ ฅ๋ฐ›์Œ

  • ์‘๋‹ต์œผ๋กœ ์˜ฌ PaymentResDto

    @Setter
    @Getter
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public class PaymentResDto {
        private String payType; // ๊ฒฐ์ œ ํƒ€์ž… - ์นด๋“œ/ํ˜„๊ธˆ/ํฌ์ธํŠธ
        private Long amount; // ๊ฐ€๊ฒฉ ์ •๋ณด
        private String orderName; // ์ฃผ๋ฌธ๋ช…
        private String orderId; // ์ฃผ๋ฌธ Id
        private String customerEmail; // ๊ณ ๊ฐ ์ด๋ฉ”์ผ
        private String customerName; // ๊ณ ๊ฐ ์ด๋ฆ„
        private String successUrl; // ์„ฑ๊ณต ์‹œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋  URL
        private String failUrl; // ์‹คํŒจ ์‹œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋  URL
    โ €
        private String failReason; // ์‹คํŒจ ์ด์œ 
        private boolean cancelYN; // ์ทจ์†Œ YN
        private String cancelReason; // ์ทจ์†Œ ์ด์œ 
        private String createdAt; // ๊ฒฐ์ œ๊ฐ€ ์ด๋ฃจ์–ด์ง„ ์‹œ๊ฐ„
    }

    โžœ PaymentDto๋กœ ๋ฐ›์€ ์ •๋ณด๋“ค์„ ๊ฒ€์ฆ ํ›„, ์‹ค์ œ ํ† ์ŠคํŽ˜์ด๋จผ์ธ ์—์„œ ๊ฒฐ์ œ ์š”์ฒญ์„ ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ฐ’๋“ค์„ ํฌํ•จํ•˜์—ฌ PaymentResDto๋กœ ๋ฐ˜ํ™˜


โœ”๏ธ ๊ฒฐ์ œ Entity

  • Payment
    @Entity
    @Getter
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Setter
    @Table(indexes = {
            @Index(name = "idx_payment_member", columnList = "customer"),
            @Index(name = "idx_payment_paymentKey", columnList = "paymentKey" ),
    })
    public class Payment extends Auditable {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "payment_id", nullable = false, unique = true)
        private Long paymentId;
        @Column(nullable = false , name = "pay_type")
        @Enumerated(EnumType.STRING)
        private PayType payType;
        @Column(nullable = false , name = "pay_amount")
        private Long amount;
        @Column(nullable = false , name = "pay_name")
        private String orderName;
        @Column(nullable = false , name = "order_id")
        private String orderId;
    โ €
        private boolean paySuccessYN;
        @ManyToOne(cascade = CascadeType.PERSIST)
        @JoinColumn(name = "customer")
        private Member customer;
        @Column
        private String paymentKey;
        @Column
        private String failReason;
    โ €
        @Column
        private boolean cancelYN;
        @Column
        private String cancelReason;
        public PaymentResDto toPaymentResDto() { // DB์— ์ €์žฅํ•˜๊ฒŒ ๋  ๊ฒฐ์ œ ๊ด€๋ จ ์ •๋ณด๋“ค
            return PaymentResDto.builder()
                    .payType(payType.getDescription())
                    .amount(amount)
                    .orderName(orderName)
                    .orderId(orderId)
                    .customerEmail(customer.getEmail())
                    .customerName(customer.getName())
                    .createdAt(String.valueOf(getCreatedAt()))
                    .cancelYN(cancelYN)
                    .failReason(failReason)
                    .build();
        }
    }

    โžœ ์‹ค์ œ DB์— ์ €์žฅํ•˜๊ฒŒ ๋  ๊ฒฐ์ œ ๊ด€๋ จ ์ •๋ณด๋“ค
    โžœ ์‘๋‹ต ๊ฒฐ๊ณผ๋กœ๋Š” ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์•„๋‹Œ PaymentResDto ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜ํ•จ


โœ”๏ธ ๊ฒฐ์ œ Repository

  • JpaPaymentRepository

โœ”๏ธ ๊ฒฐ์ œ Controller

  • requestTossPayment

โžœ ํ”„๋ก ํŠธ์—์„œ ๊ฒฐ์ œ๋ฅผ ์š”์ฒญํ•˜๊ธฐ ์œ„ํ•ด 1์ฐจ์ ์œผ๋กœ ์š”์ฒญํ•˜๋Š” API
โ €
โžœ ์—ฌ๊ธฐ์„œ Service ๋กœ์ง์„ ํƒ€๊ณ  ๋“ค์–ด๊ฐ€ ๊ฒ€์ฆ ๊ณผ์ •์„ ๋งˆ์น˜๊ณ  ์ •์ƒ์ ์œผ๋กœ ์ง„ํ–‰์ด ๋œ๋‹ค๋ฉด ํ† ์ŠคํŽ˜์ด๋จผ์ธ ์— ์‹ค์ œ ๊ฒฐ์ œ ์š”์ฒญ์„ ๋ณด๋ƒ„


โœ”๏ธ ๊ฒฐ์ œ Service

  • requestTossPayment

โžœ ๊ฒฐ์ œ๋ฅผ ์œ„ํ•ด ์š”์ฒญํ•œ ๊ฐ’๋“ค์„ Controller๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ ๋ฐ›์•„ ๊ฒ€์ฆ์„ ํ•จ
โ €
โžœ ์กฐ๊ฑด๋“ค์ด ๋งž๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋ชจ๋“  ์กฐ๊ฑด๋“ค์ด ๋งž๋‹ค๋ฉด Entity ๊ฐ์ฒด๋กœ ๋งŒ๋“  ํ›„ DB์— ์ €์žฅํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ด์คŒ


๐Ÿšฉ ์—ฌ๊ธฐ๊นŒ์ง€ ํ•œ๋‹ค๋ฉด,
ํ”„๋ก ํŠธ์—์„œ ๊ฒฐ์ œ๋ฅผ ์š”์ฒญํ•˜๊ณ  ๋ฐฑ์—”๋“œ์—์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ฆ ํ›„, ๊ฒฐ์ œ ์š”์ฒญ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š” ๋ถ€๋ถ„์ด ์™„์„ฑ๋œ ๊ฒƒ์ด๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ h2 DB๋กœ ํ™•์ธํ•ด๋ณด์•˜๋Š”๋ฐ, ์œ„์ฒ˜๋Ÿผ ์ •๋ณด๋“ค์ด ์ž˜ ๋“ค์–ด๊ฐ„ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์ด์ œ ์•„๋ž˜์™€ ๊ฐ™์€ ์‘๋‹ต ๊ฐ’์ด ์ž˜ ์ „๋‹ฌ๋˜์—ˆ์„ ๋•Œ,
๊ทธ ๊ฐ’๋“ค์„ ๊ฐ€์ง€๊ณ  ํ† ์ŠคํŽ˜์ด๋จผ์ธ ์— ๊ฒฐ์ œ ์š”์ฒญ์„ ๋ณด๋‚ผ ๊ฒƒ์ด๋‹ค.

{
    "data": {
        "payType": "์นด๋“œ",
        "amount": 58500,
        "orderName": "ํฌ์ธํŠธ ์ถฉ์ „",
        "orderId": "bec1d544-2a34-4f44-ada0-c5213d8fd8dd",
        "customerEmail": "test1@gmail.com",
        "customerName": "์ฒซ๋ฒˆ์งธ",
        "successUrl": "http://localhost:8080/api/v1/payments/toss/success",
        "failUrl": "http://localhost:8080/api/v1/payments/toss/fail",
        "failReason": null,
        "cancelYN": false,
        "cancelReason": null,
        "createdAt": "2023-06-06T19:25:04.432591600"
    }
}

๐ŸŒผ ํ”„๋ก ํŠธ์˜ ์—ญํ• 

โœ”๏ธ ๊ฒฐ์ œ ์š”์ฒญ ํ™”๋ฉด

<!DOCTYPE html>
<html lang="ha">
<head>
    <meta charset="utf-8" />
    <title>๊ฒฐ์ œํ•˜๊ธฐ</title>
    <!-- ํ† ์ŠคํŽ˜์ด๋จผ์ธ  ๊ฒฐ์ œ์ฐฝ SDK ์ถ”๊ฐ€ -->
    <script src="https://js.tosspayments.com/v1/payment"></script>
</head>
<body>
<section>
    <!--  ์ถฉ์ „ํ•˜๊ธฐ ๋ฒ„ํŠผ ๋งŒ๋“ค๊ธฐ  -->
    <span>์ด ํฌ์ธํŠธ ์ถฉ์ „ ๊ธˆ์•ก :</span>
    <span>58500์›</span>
    <button id="payment-button">58500์› ์ถฉ์ „ํ•˜๊ธฐ</button>
</section>
<script>
    // ------ ํด๋ผ์ด์–ธํŠธ ํ‚ค๋กœ ๊ฐ์ฒด ์ดˆ๊ธฐํ™” ------
    var clientKey = 'ํ…Œ์ŠคํŠธ_ํด๋ผ์ด์–ธํŠธ_ํ‚ค'
    var tossPayments = TossPayments(clientKey)
โ €
    // ------ ์ถฉ์ „ํ•˜๊ธฐ ๋ฒ„ํŠผ์— ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ------
    var button = document.getElementById('payment-button') // ์ถฉ์ „ํ•˜๊ธฐ ๋ฒ„ํŠผ
    button.addEventListener('click', function () {
โ €
        // ------ ๊ฒฐ์ œ์ฐฝ ๋„์šฐ๊ธฐ ------
        tossPayments.requestPayment('CARD', {
            amount: 58500,
            orderId: 'bec1d544-2a34-4f44-ada0-c5213d8fd8dd',
            orderName: 'ํฌ์ธํŠธ ์ถฉ์ „',
            customerName: '์ฒซ๋ฒˆ์งธ',
            customerEmail: 'test1@gmail.com',
            successUrl: 'http://localhost:8081/api/v1/payments/toss/success',
            failUrl: 'http://localhost:8081/api/v1/payments/toss/fail'
        })
    })
</script>
</body>
</html>

โžœ ์ง€๊ธˆ์€ ์ •์ ์œผ๋กœ ๊ฐ’์„ ๋„ฃ์–ด์คฌ์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ์„œ๋ฒ„์˜ ์‘๋‹ต ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ํŒŒ์‹ฑํ•˜๊ฒŒ ๋  ๊ฒƒ
โ €

  • 'ํ…Œ์ŠคํŠธ ํด๋ผ์ด์–ธํŠธ ํ‚ค'
    โžœ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์ œ๊ณต๋ฐ›์€ API ํ‚ค
    โžœ ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ TossPayments ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์˜€๊ณ , ์ด ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  requestPayment(), requestBillingAuth() ๋“ฑ์˜ ๋ฉ”์„œ๋“ค์„ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

  • requestPayment(๊ฒฐ์ œ์ˆ˜๋‹จ,{๊ฒฐ์ œ์ •๋ณด}) - ๊ฒฐ์ œ์ฐฝ์„ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ
  • requestBillingAuth(๊ฒฐ์ œ์ˆ˜๋‹จ,{๊ฒฐ์ œ์ •๋ณด}) - ์ž๋™ ๊ฒฐ์ œ ๋“ฑ๋ก์ฐฝ์„ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ

์œ„๋ฅผ ์ ์šฉํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚˜๊ณ ,

๊ฒฐ์ œํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๊ฒฐ์ œ ์š”์ฒญ์„ ํ•˜๊ฒŒ๋˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํ† ์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒฐ์ œ ์‹œ์Šคํ…œ์ด ๋œจ๊ฒŒ ๋œ๋‹ค.

์œ„์˜ ๊ฒฐ์ œ์ฐฝ์„ ํ™•์ธํ•ด๋ณด๋ฉด ๊ฒฐ์ œ ๊ธˆ์•ก / ์ƒํ’ˆ๋ช…์ด ์š”์ฒญ ์‹œ์— ์ž‘์„ฑํ•œ ๋‚ด์šฉ๋“ค๊ณผ ๋™์ผํ•œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


๐ŸŽ€ ์ง€๊ธˆ๊นŒ์ง€ ์™„์„ฑ๋œ ๋ถ€๋ถ„

  1. ํ”„๋ก ํŠธ์—์„œ ๊ฒฐ์ œ ์š”์ฒญ
  2. ๋ฐฑ์—”๋“œ์—์„œ ์š”์ฒญ ๊ฒ€์ฆ ํ›„ ํ•„์š”ํ•œ ์ •๋ณด์™€ ํ•จ๊ป˜ ๋ฐ˜ํ™˜

๐Ÿ“Œ ๋‹ค์Œ ํฌ์ŠคํŒ…์€ ๊ฒฐ์ œ ์„ฑ๊ณต / ์‹คํŒจ์— ๋Œ€ํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.
โ €
๐Ÿ‘‰ ๐Ÿ’ณ ํ† ์ŠคํŽ˜์ด๋จผ์ธ  ์—ฐ๋™ํ•˜๊ธฐ (3) _๊ฒฐ์ œ ์„ฑ๊ณต/์‹คํŒจ

0๊ฐœ์˜ ๋Œ“๊ธ€