์ด์ ์ ์กธ์ ์ํ์ผ๋ก ์งํํ๋ Ignis ํ๋ก์ ํธ์์ ๊ฒฐ์ ๊ธฐ๋ฅ์ ๊ตฌํํ ๊ฒฝํ์ด ์์๋ค. ๋น์ ํฌํธ์(PortOne)์ ํ์ฉํ์ฌ ๊ฒฐ์ ์ฐ๋์ ์๋ฃํ์ง๋ง, ๊ฒฐ์ ์์คํ ์ ๋ด๋ถ ๋์ ์๋ฆฌ์ ์ ์ฒด์ ์ธ ํ๋ฆ์ ๋ํด ๋ ๊น์ด ์ดํดํ๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋ค์๋ค.
์ต๊ทผ ๋๋ถ๋ถ์ ์น ์๋น์ค๋ ์จ๋ผ์ธ ๊ฒฐ์ ๊ธฐ๋ฅ์ ํ์์ ์ผ๋ก ํฌํจํ๊ณ ์์ผ๋ฉฐ, ๊ฒฐ์ ๊ณผ์ ์ ์ฌ์ฉ์ ์ธ์ฆ, ๊ฒฐ์ ์น์ธ, ๊ฒฐ์ ์ํ ์ ๋ฐ์ดํธ, ์ ์ฐ ๋ฑ ์ฌ๋ฌ ๋จ๊ณ๋ก ๊ตฌ์ฑ๋๋ค. ์ด๋ฌํ ๊ฒฐ์ ์์คํ ์ ์ค์ ๋ก PG(Payment Gateway) ์ ์ฒด๋ฅผ ํตํด ๋ณต์กํ ์ฐ๋ ๊ณผ์ ์ ๊ฑฐ์ณ ๋์ํ๋ฉฐ, ๋ด๋ถ ๊ตฌ์กฐ๊ฐ ๊ณต๊ฐ๋์ง ์๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ ํ์ต์๊ฐ ์ ๋ฐ์ ์ธ ํ๋ฆ์ ์ดํดํ๊ธฐ ์ด๋ ต๋ค.
์ด์ ๋ฐ๋ผ ๋ณธ ๋ณด๊ณ ์๋ ๊ฒฐ์ ํ๋ก์ธ์ค์ ํต์ฌ ์๋ ๋ฐฉ์๊ณผ ๋ฐ์ดํฐ ํ๋ฆ์ ์ง์ ์ฒดํํ๊ณ ์ดํดํ๊ธฐ ์ํด, ๊ฒฐ์ ๊ธฐ๋ฅ์ ๋จ์ํํ ํ์ต์ฉ ํ ์ด ํ๋ก์ ํธ PayFlow๋ฅผ ์กฐ์ฌ ๋ฐ ๊ตฌํ ๋์์ผ๋ก ์ ์ ํ์๋ค. ๋ณธ ์กฐ์ฌ๋ PayFlow ํ๋ก์ ํธ๋ฅผ ํตํด ๊ฒฐ์ ๋ชจ๋ธ์ ๊ตฌ์ฑ ์์, ๋ฐฑ์๋ ๊ตฌ์กฐ, ์ํ ๊ด๋ฆฌ ๋ฐฉ์ ๋ฑ์ ๋ถ์ํ๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ค.
๋ณธ ์กฐ์ฌ์ ๋ชฉ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ค์ ๊ฒฐ์ ์์คํ ์ด ๊ฐ์ถ์ด์ผ ํ๋ ๊ธฐ๋ณธ ๊ธฐ๋ฅ๊ณผ ๊ตฌ์กฐ๋ฅผ ์ดํดํ๋ค.
- ๊ฒฐ์ ์์ฑ โ PG ์น์ธ โ ์ํ ์กฐํ ๊ณผ์ ์ ํ๋ฆ์ ์ง์ ๊ตฌ์ฑํ์ฌ ๋ฐฑ์๋ ๋์ ์๋ฆฌ๋ฅผ ๋ถ์ํ๋ค.
- ๊ฐ๋จํ Payment ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์ค๊ณํจ์ผ๋ก์จ ์ํ ๊ธฐ๋ฐ(State-based) ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๊ฐ๋ ์ ํ์ตํ๋ค.
- Spring Boot ๊ธฐ๋ฐ์ ๊ฒฐ์ ์ฒ๋ฆฌ ์์คํ ์ ์ฒด ๊ตฌ์กฐ๋ฅผ ์กฐ์ฌํ๊ณ ๊ตฌํํ์ฌ ์ค๋ฌด์ ์ธ ๊ฐ๊ฐ์ ๊ธฐ๋ฅธ๋ค.
- ํฌํธ์(PortOne) V2 Checkout SDK ์ค์ ์ฐ๋์ ํตํด PG ์ฐ๋ ๊ฒฝํ์ ํ๋ณดํ๋ค.
PayFlow๋ Spring Boot 3.2.2 + Java 17 ๊ธฐ๋ฐ์ ๊ฐ๋จํ ๊ฒฐ์ ์ฒ๋ฆฌ ์์คํ ์ผ๋ก, ์ฌ์ฉ์๊ฐ ๊ฒฐ์ ์ ๋ณด๋ฅผ ์ ๋ ฅํ๋ฉด ์๋ฒ์์ ๊ฒฐ์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ณ ํฌํธ์(PortOne) V2 Checkout SDK๋ฅผ ํตํด ์ค์ ๊ฒฐ์ ์น์ธ์ ์ฒ๋ฆฌํ๋ค. ์ดํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํด๋น ๋ด์ญ์ ์ ์ฅํ๊ณ , ๊ฒฐ์ ์์ธ ํ์ด์ง์์ ๊ฒฐ๊ณผ๋ฅผ ์กฐํํ ์ ์๋๋ก ํ๋ค.
๋ณธ ํ๋ก์ ํธ๋ "๊ฒฐ์ ์์ฒญ โ PG ์น์ธ ์ฒ๋ฆฌ โ ์ํ ๋ณ๊ฒฝ" ๊ณผ์ ์ ์ค์ฌ์ผ๋ก ๊ตฌ์ฑํ์์ผ๋ฉฐ, ์ค์ ํฌํธ์ V2 API๋ฅผ ํตํ ์ค๊ฒฐ์ ์ฐ๋์ ๊ตฌํํ์๋ค.
| ๊ตฌ๋ถ | ๊ธฐ์ |
|---|---|
| Backend | Spring Boot 3.2.2, Java 17 |
| ORM | Spring Data JPA, Hibernate |
| Database | MySQL 8.x |
| Template Engine | Thymeleaf |
| Build Tool | Gradle |
| PG ์ฐ๋ | ํฌํธ์ V2 Checkout SDK |
| ๋ณด์ | Spring Security Crypto (BCrypt) |
๊ฒฐ์ ์์คํ ๊ตฌํ ์ ํ์ฉํ ์ ์๋ ๋ค์ํ PG์ฌ๋ค์ด ์กด์ฌํ๋ค. ๊ฐ PG์ฌ๋ณ ํน์ง์ ์กฐ์ฌํ์๋ค.
| PG์ฌ | ํน์ง | ์ฐ๋ ๋ฐฉ์ |
|---|---|---|
| ํฌํธ์(PortOne) | ์ฌ๋ฌ PG์ฌ๋ฅผ ํตํฉ ์ฐ๋ํ ์ ์๋ ๊ฒฐ์ ๋ํ ์๋น์ค. ํ๋์ API๋ก ๋ค์ํ PG์ฌ ์ฐ๋ ๊ฐ๋ฅ. Checkout V2 SDK ์ ๊ณต | REST API + JavaScript SDK |
| ํ ์คํ์ด๋จผ์ธ | ํ ์ค ๊ณ์ด PG์ฌ. ๊น๋ํ API ๋ฌธ์์ ๊ฐ๋ฐ์ ์นํ์ ํ๊ฒฝ ์ ๊ณต. ๊ฒฐ์ ์์ ฏ(Payment Widget) ๋ฐฉ์ ์ง์ | REST API + JavaScript SDK |
| NHN KCP | ์ค๋๋ PG์ฌ ์ค ํ๋๋ก ์์ ์ ์ธ ์๋น์ค ์ ๊ณต. ๋ค์ํ ๊ฒฐ์ ์๋จ ์ง์ | REST API + Server-to-Server |
| ์ด๋์์ค(INICIS) | KG์ด๋์์ค. ๊ตญ๋ด ์ ์ ์จ 1์ PG์ฌ. ๋ํ ์ผํ๋ชฐ์์ ๋ง์ด ์ฌ์ฉ | REST API + JavaScript SDK |
| ๋ค๋ | ํด๋ํฐ ๊ฒฐ์ ์ ๊ฐ์ . ์์ก๊ฒฐ์ ์๋น์ค ์ ๋ฌธ | REST API |
| ์นด์นด์คํ์ด | ๊ฐํธ๊ฒฐ์ ์๋น์ค. ์นด์นด์คํก ์ฑ ์ฐ๋. ๋น ๋ฅธ ๊ฒฐ์ ๊ฒฝํ ์ ๊ณต | REST API + Redirect |
| ๋ค์ด๋ฒํ์ด | ๋ค์ด๋ฒ ๊ฐํธ๊ฒฐ์ . ๋ค์ด๋ฒ ์ผํ๊ณผ ์ฐ๊ณ ์ ์ ๋ฆฌ | REST API + JavaScript SDK |
๋ณธ ํ๋ก์ ํธ์์ **ํฌํธ์(PortOne)**์ ์ ํํ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ๋ค:
- ํตํฉ ์ฐ๋: ํ๋์ API๋ก ํ ์คํ์ด๋จผ์ธ , ์ด๋์์ค, KCP ๋ฑ ๋ค์ํ PG์ฌ๋ฅผ ์ฐ๋ํ ์ ์์
- V2 Checkout SDK: ์ต์ JavaScript SDK๋ก ๊ฐํธํ๊ฒ ๊ฒฐ์ ์ฐฝ ํธ์ถ ๊ฐ๋ฅ
- ํ ์คํธ ํ๊ฒฝ: ๋ณ๋์ ์ฌ์ ์๋ฑ๋ก ์์ด ํ ์คํธ ๊ฒฐ์ ์งํ ๊ฐ๋ฅ
- ๋ฌธ์ํ: ๊ฐ๋ฐ์ ์นํ์ ์ธ API ๋ฌธ์ ๋ฐ ์์ ์ฝ๋ ์ ๊ณต
์ฐธ๊ณ : ํ ์คํ์ด๋จผ์ธ ๋ ๋ณธ ํ๋ก์ ํธ์์ ์ง์ ์ฌ์ฉํ์ง ์์์ง๋ง, ํฅํ ๋น๊ต ์ฐ๋์ ์ํด ๊ด์ฌ ์๋ PG์ฌ๋ก ์กฐ์ฌํ์๋ค. ํ ์คํ์ด๋จผ์ธ ๋ Payment Widget ๋ฐฉ์์ ์ ๊ณตํ์ฌ ํ๋ก ํธ์๋์์ ๊ฐํธํ๊ฒ ๊ฒฐ์ ์ฐฝ์ ๊ตฌํํ ์ ์๋ค๋ ํน์ง์ด ์๋ค.
flowchart LR
subgraph Direct["์ง์ ์ฐ๋ ๋ฐฉ์"]
A[ํ ์คํ์ด๋จผ์ธ ] --> B[๊ฐ PG์ฌ๋ณ ๊ฐ๋ณ ์ฐ๋]
C[์ด๋์์ค] --> B
D[KCP] --> B
end
subgraph Aggregator["ํตํฉ ์ฐ๋ ๋ฐฉ์"]
E[ํฌํธ์] --> F[๋จ์ผ API๋ก ํตํฉ ๊ด๋ฆฌ]
F --> G[ํ ์คํ์ด๋จผ์ธ ]
F --> H[์ด๋์์ค]
F --> I[KCP]
end
flowchart TB
subgraph Client["๐ค ํด๋ผ์ด์ธํธ"]
A[์ฌ์ฉ์ ์์ฒญ]
end
subgraph Controller["โ Controller ๊ณ์ธต"]
B[PaymentController - ํ์ด์ง ๋ ๋๋ง]
C[PaymentRestController - API ์์ ]
end
subgraph BO["โก BO ๊ณ์ธต (Business Object)"]
E[PaymentBO - ๊ฒฐ์ ์์ฑ/์ํ ์
๋ฐ์ดํธ]
end
subgraph Client_Layer["โข PG Client ๊ณ์ธต"]
F[PgClient Interface]
G[PortOneClient]
end
subgraph Repository["โฃ Repository ๊ณ์ธต"]
I[PaymentRepository]
J[UserRepository]
end
subgraph DB["โค DB ๊ณ์ธต (MySQL)"]
K[(payflow Database)]
end
subgraph PG["โฅ PG์ฌ"]
L[ํฌํธ์ V2 API]
M[์ด๋์์ค ์ค๊ฒฐ์ ]
end
A --> B
A --> C
B --> E
C --> E
E --> F
F --> G
G --> L
L --> M
E --> I
E --> J
I --> K
J --> K
| ๊ณ์ธต | ์ญํ | ์ฃผ์ ํด๋์ค |
|---|---|---|
| โ Controller ๊ณ์ธต | ํ์ด์ง ๋ ๋๋ง ๋ฐ API ์์ฒญ ์ฒ๋ฆฌ | PaymentController, PaymentRestController |
| โก BO ๊ณ์ธต | ๋น์ฆ๋์ค ๋ก์ง ๋ด๋น | PaymentBO |
| โข PG Client ๊ณ์ธต | PG์ฌ ์ฐ๋ ์ถ์ํ | PgClient, PortOneClient |
| โฃ Repository ๊ณ์ธต | ๋ฐ์ดํฐ ์์์ฑ ๋ด๋น | PaymentRepository, UserRepository |
| โค DB ๊ณ์ธต | ๋ฐ์ดํฐ ์ ์ฅ | MySQL (payflow DB) |
| โฅ PG์ฌ | ์ค์ ๊ฒฐ์ ์ฒ๋ฆฌ | ํฌํธ์ โ ์ด๋์์ค |
ํฌํธ์ V2 Checkout SDK๋ฅผ ์ฌ์ฉํ ์ค์ ๊ฒฐ์ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค:
sequenceDiagram
participant Client as ๐ค ํด๋ผ์ด์ธํธ
participant Server as ๐ฅ๏ธ PayFlow ์๋ฒ
participant PortOne as ๐ณ ํฌํธ์ V2 SDK
participant PG as ๐ฆ PG์ฌ (์ด๋์์ค)
Client->>Server: 1. ๊ฒฐ์ ์์ฑ ์์ฒญ (POST /api/pay/create)
Note over Server: userId, amount, method
Server->>Server: 2. Payment ์ํฐํฐ ์์ฑ<br/>(status: ready, orderId ์์ฑ)
Server->>Client: 3. orderId ๋ฐํ ๋ฐ ๋ฆฌ๋ค์ด๋ ํธ
Client->>Server: 4. ๊ฒฐ์ ์์ฒญ ํ์ด์ง (GET /pay/request/{orderId})
Server->>Server: 5. ํฌํธ์ ํ๋ผ๋ฏธํฐ ์ค๋น<br/>(storeId, channelKey, amount ๋ฑ)
Server->>Client: 6. ํฌํธ์ V2 SDK ํ๋ผ๋ฏธํฐ ์ ๋ฌ
Client->>PortOne: 7. PortOne.requestPayment() ํธ์ถ
Note over Client,PortOne: ๊ฒฐ์ ์ฐฝ ํ์
PortOne->>PG: 8. ๊ฒฐ์ ์น์ธ ์์ฒญ
PG->>PortOne: 9. ๊ฒฐ์ ์น์ธ ๊ฒฐ๊ณผ
PortOne->>Client: 10. paymentId ๋ฐํ (์ฑ๊ณต ์)
Client->>Server: 11. ๊ฒฐ์ ์ฑ๊ณต ์ฝ๋ฐฑ (GET /pay/success)
Note over Server: paymentId, orderId
Server->>Server: 12. ๊ฒฐ์ ์ํ ์
๋ฐ์ดํธ<br/>(status: paid, pg_tid ์ ์ฅ)
Server->>Client: 13. ๊ฒฐ์ ์๋ฃ ํ์ด์ง ๋ฆฌ๋ค์ด๋ ํธ
์์ธ ๋จ๊ณ ์ค๋ช :
-
๊ฒฐ์ ์์ฑ ์์ฒญ: ์ฌ์ฉ์๊ฐ ๊ธ์ก๊ณผ ๊ฒฐ์ ์๋จ์ ์ ํํ์ฌ
POST /api/pay/create๋ก ๊ฒฐ์ ์์ฑ ์์ฒญ- ์ธ์
์์
userId์๋ ์ถ์ถ amount,methodํ๋ผ๋ฏธํฐ ์์
- ์ธ์
์์
-
Payment ์ํฐํฐ ์์ฑ: ์๋ฒ์์
ORD-{timestamp}ํ์์ ์ฃผ๋ฌธ๋ฒํธ ์์ฑ- ์ด๊ธฐ ์ํ:
ready createdAt,updatedAt์๋ ์ค์
- ์ด๊ธฐ ์ํ:
-
๊ฒฐ์ ์์ฒญ ํ์ด์ง:
GET /pay/request/{orderId}๋ก ๊ฒฐ์ ์ฐฝ ํธ์ถ ํ์ด์ง ๋ ๋๋ง- ํฌํธ์ V2 SDK ํ๋ผ๋ฏธํฐ ์ค๋น:
storeId: ํฌํธ์ ์คํ ์ด IDchannelKey: ํฌํธ์ ์ฑ๋ ํค (V2 ํ์)orderId,amount,orderNamepayMethod: CARD ๋๋ VBANKcustomer: ๊ตฌ๋งค์ ์ ๋ณด (fullName, phoneNumber, email)successUrl,failUrl: ์ฝ๋ฐฑ URL
- ํฌํธ์ V2 SDK ํ๋ผ๋ฏธํฐ ์ค๋น:
-
๊ฒฐ์ ์ฐฝ ํธ์ถ: ํ๋ก ํธ์๋์์
PortOne.requestPayment()ํจ์ ์คํ- ํฌํธ์ V2 SDK๊ฐ ๊ฒฐ์ ์ฐฝ์ ์๋์ผ๋ก ํ์
- ์ฌ์ฉ์๊ฐ ๊ฒฐ์ ์๋จ ์ ํ ๋ฐ ์ธ์ฆ ์งํ
-
PG์ฌ ๊ฒฐ์ ์ฒ๋ฆฌ: ํฌํธ์์ ํตํด ์ค์ PG์ฌ(์ด๋์์ค)๋ก ๊ฒฐ์ ์น์ธ ์์ฒญ
- ํฌํธ์์ด ๋ด๋ถ์ ์ผ๋ก ์ด๋์์ค API ํธ์ถ
- ์นด๋์ฌ ์น์ธ ์ฒ๋ฆฌ
-
๊ฒฐ์ ๊ฒฐ๊ณผ ์ฒ๋ฆฌ:
- ์ฑ๊ณต ์:
paymentId๋ฅผ ๋ฐ์successUrl๋ก ๋ฆฌ๋ค์ด๋ ํธ - ์คํจ ์: ์๋ฌ ๋ฉ์์ง์ ํจ๊ป
failUrl๋ก ๋ฆฌ๋ค์ด๋ ํธ
- ์ฑ๊ณต ์:
-
์๋ฒ ์ฝ๋ฐฑ ์ฒ๋ฆฌ:
GET /pay/success๋๋GET /pay/fail์๋ํฌ์ธํธ ํธ์ถ- ์ฑ๊ณต:
PaymentBO.updatePaymentSuccess()ํธ์ถorderId๋ก Payment ์กฐํstatus๋ฅผpaid๋ก ๋ณ๊ฒฝpg_tid์paymentId์ ์ฅpg_response์ "SUCCESS" ์ ์ฅ
- ์คํจ:
PaymentBO.updatePaymentFail()ํธ์ถstatus๋ฅผfailed๋ก ๋ณ๊ฒฝpg_response์ ์คํจ ์ฌ์ ์ ์ฅ
- ์ฑ๊ณต:
-
๊ฒฐ์ ์๋ฃ ํ์ด์ง: ์ต์ข ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฉ์์๊ฒ ํ์
- ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๊ฒฐ์ : ์๋ฒ์์ ๋ณ๋์ ์น์ธ API ํธ์ถ ์์ด ํด๋ผ์ด์ธํธ์์ ์ง์ ๊ฒฐ์ ์ฒ๋ฆฌ
- ์๋ ๋ฆฌ๋ค์ด๋ ํธ: ๊ฒฐ์ ์๋ฃ ํ ์๋์ผ๋ก
successUrl๋๋failUrl๋ก ์ด๋ - ๊ฐํธํ ์ฐ๋: ๋ณต์กํ ์๋ฒ-์๋ฒ ํต์ ์์ด JavaScript SDK๋ง์ผ๋ก ๊ตฌํ ๊ฐ๋ฅ
- ํตํฉ PG ๊ด๋ฆฌ: ํ๋์ ์ฑ๋ ํค๋ก ์ฌ๋ฌ PG์ฌ๋ฅผ ์๋ ์ ํ
erDiagram
users ||--o{ payments : "has"
payments ||--o{ payment_logs : "logs"
payments ||--o{ webhook_callback : "receives"
payments ||--o{ refunds : "may have"
users {
BIGINT id PK
VARCHAR name
VARCHAR email
DATETIME created_at
}
payments {
BIGINT id PK
BIGINT user_id FK
VARCHAR order_id UK
INT amount
VARCHAR status
VARCHAR method
DATETIME created_at
DATETIME updated_at
}
payment_logs {
BIGINT id PK
BIGINT payment_id FK
VARCHAR log_type
TEXT message
DATETIME created_at
}
webhook_callback {
BIGINT id PK
BIGINT payment_id FK
TEXT pg_data
DATETIME created_at
}
refunds {
BIGINT id PK
BIGINT payment_id FK
INT refund_amount
VARCHAR reason
DATETIME created_at
}
| ์ปฌ๋ผ๋ช | ํ์ | ์ค๋ช |
|---|---|---|
id |
BIGINT | ๊ธฐ๋ณธํค(PK), AUTO_INCREMENT |
name |
VARCHAR(50) | ์ฌ์ฉ์ ์ด๋ฆ |
email |
VARCHAR(100) | ์ฌ์ฉ์ ์ด๋ฉ์ผ |
created_at |
DATETIME | ์์ฑ์ผ (๊ธฐ๋ณธ๊ฐ: CURRENT_TIMESTAMP) |
| ์ปฌ๋ผ๋ช | ํ์ | ์ค๋ช |
|---|---|---|
id |
BIGINT | ๊ธฐ๋ณธํค(PK), AUTO_INCREMENT |
user_id |
BIGINT | ์ฌ์ฉ์ ์๋ณ์ (FK โ users.id) |
order_id |
VARCHAR(100) | ์ฃผ๋ฌธ ์๋ณ์ (UNIQUE) - ORD-{timestamp} ํ์ |
amount |
INT | ๊ฒฐ์ ๊ธ์ก (NOT NULL) |
status |
VARCHAR(20) | ๊ฒฐ์ ์ํ (๊ธฐ๋ณธ๊ฐ: 'ready') |
method |
VARCHAR(20) | ๊ฒฐ์ ๋ฐฉ์ (CARD/VBANK ๋ฑ) |
created_at |
DATETIME | ์์ฑ์ผ |
updated_at |
DATETIME | ์์ ์ผ (ON UPDATE ์๋ ๊ฐฑ์ ) |
๊ฒฐ์ ์ํ(status) ๊ฐ:
| PayFlow | ์ค๋ช |
|---|---|
ready |
๊ฒฐ์ ๋๊ธฐ |
paid |
๊ฒฐ์ ์๋ฃ |
failed |
๊ฒฐ์ ์คํจ |
์ฐธ๊ณ : ์ํฐํฐ ํด๋์ค(
Payment.java)์๋pg_tid,pg_response์ปฌ๋ผ์ด ์ ์๋์ด ์์ผ๋, ์ด๊ธฐ DB ์คํค๋ง์๋ ํฌํจ๋์ง ์์๋ค. ์ด๋ JPA์ddl-auto: update์ค์ ์ ์ํด ์๋์ผ๋ก ์ถ๊ฐ๋๊ฑฐ๋, ์ถํ ๋ง์ด๊ทธ๋ ์ด์ ์ ํตํด ์ถ๊ฐ๋ ์์ ์ด๋ค.
| ์ปฌ๋ผ๋ช | ํ์ | ์ค๋ช |
|---|---|---|
id |
BIGINT | ๊ธฐ๋ณธํค(PK), AUTO_INCREMENT |
payment_id |
BIGINT | ๊ฒฐ์ ์๋ณ์ (FK โ payments.id) |
log_type |
VARCHAR(20) | ๋ก๊ทธ ์ ํ (REQUEST, RESPONSE, ERROR ๋ฑ) |
message |
TEXT | ๋ก๊ทธ ๋ฉ์์ง (PG ์๋ต ๋ฐ์ดํฐ ํฌํจ) |
created_at |
DATETIME | ์์ฑ์ผ |
๊ฒฐ์ ๊ณผ์ ์ ๋ชจ๋ ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ์ฌ ๋๋ฒ๊น ๋ฐ ๊ฐ์ฌ(Audit) ๋ชฉ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
| ์ปฌ๋ผ๋ช | ํ์ | ์ค๋ช |
|---|---|---|
id |
BIGINT | ๊ธฐ๋ณธํค(PK), AUTO_INCREMENT |
payment_id |
BIGINT | ๊ฒฐ์ ์๋ณ์ (FK โ payments.id) |
pg_data |
TEXT | PG์ฌ๋ก๋ถํฐ ๋ฐ์ Webhook ์ฝ๋ฐฑ ๋ฐ์ดํฐ (JSON) |
created_at |
DATETIME | ์์ฑ์ผ |
ํฌํธ์์ ๊ฒฝ์ฐ ๊ฐ์๊ณ์ข ์ ๊ธ ํ์ธ, ๊ฒฐ์ ์ทจ์ ๋ฑ์ ์ด๋ฒคํธ๋ฅผ Webhook์ผ๋ก ์ ๋ฌ๋ฐ์ ์ ์๋ค. ๋ณธ ํ๋ก์ ํธ์์๋ ์์ง ๊ตฌํํ์ง ์์์ผ๋, ํฅํ ํ์ฅ์ ์ํด ํ ์ด๋ธ์ ๋ฏธ๋ฆฌ ์ค๊ณํ์๋ค.
| ์ปฌ๋ผ๋ช | ํ์ | ์ค๋ช |
|---|---|---|
id |
BIGINT | ๊ธฐ๋ณธํค(PK), AUTO_INCREMENT |
payment_id |
BIGINT | ๊ฒฐ์ ์๋ณ์ (FK โ payments.id) |
refund_amount |
INT | ํ๋ถ ๊ธ์ก (NOT NULL) |
reason |
VARCHAR(255) | ํ๋ถ ์ฌ์ |
created_at |
DATETIME | ์์ฑ์ผ |
ํฌํธ์ ํ๋ถ API ํธ์ถ ํ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ค. ๋ณธ ํ๋ก์ ํธ์์๋ ์์ง ๊ตฌํํ์ง ์์์ผ๋, ํฅํ ํ์ฅ์ ์ํด ํ ์ด๋ธ์ ๋ฏธ๋ฆฌ ์ค๊ณํ์๋ค.
com.payflow
โโโ PayFlowApplication.java # Spring Boot ๋ฉ์ธ ํด๋์ค
โโโ controller
โ โโโ WelcomeController.java # ๋ฉ์ธ ํ์ด์ง ์ปจํธ๋กค๋ฌ
โโโ payment
โ โโโ PaymentController.java # ๊ฒฐ์ ํ์ด์ง ๋ ๋๋ง
โ โโโ PaymentRestController.java # ๊ฒฐ์ REST API
โ โโโ bo
โ โ โโโ PaymentBO.java # ๊ฒฐ์ ๋น์ฆ๋์ค ๋ก์ง
โ โโโ client
โ โ โโโ PgClient.java # PG ํด๋ผ์ด์ธํธ ์ธํฐํ์ด์ค
โ โ โโโ PortOneClient.java # ํฌํธ์ V2 ๊ตฌํ์ฒด
โ โ โโโ PgResponse.java # PG ์๋ต DTO
โ โโโ domain
โ โ โโโ Payment.java # ๊ฒฐ์ ์ํฐํฐ
โ โโโ repository
โ โโโ PaymentRepository.java # ๊ฒฐ์ JPA Repository
โโโ user
โโโ UserController.java # ์ฌ์ฉ์ ์ปจํธ๋กค๋ฌ
โโโ bo
โ โโโ UserBO.java # ์ฌ์ฉ์ ๋น์ฆ๋์ค ๋ก์ง
โโโ domain
โ โโโ User.java # ์ฌ์ฉ์ ์ํฐํฐ
โโโ repository
โโโ UserRepository.java # ์ฌ์ฉ์ JPA Repository
flowchart LR
subgraph Presentation["Presentation Layer"]
A[PaymentController]
B[PaymentRestController]
end
subgraph Business["Business Layer"]
C[PaymentBO]
D[PgClient Interface]
end
subgraph Data["Data Access Layer"]
E[PaymentRepository]
end
subgraph External["External - PG์ฌ"]
F[ํฌํธ์ V2 API]
end
A --> C
B --> C
C --> D
C --> E
D --> F
| ๊ตฌ์ฑ ์์ | ์ญํ |
|---|---|
| PaymentController | ๊ฒฐ์ ์์ฑ/์์ฒญ/๊ฒฐ๊ณผ ํ์ด์ง ๋ ๋๋ง, Thymeleaf ๋ทฐ ๋ฐํ |
| PaymentRestController | REST API ์๋ํฌ์ธํธ ์ ๊ณต, JSON ์๋ต (/api/pay/create) |
| PaymentBO | ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง ๋ด๋น, ๊ฒฐ์ ์์ฑ/์ํ ์ ๋ฐ์ดํธ/๋ชฉ๋ก ์กฐํ |
| PgClient | PG์ฌ ์ฐ๋ ์ธํฐํ์ด์ค (Strategy Pattern) |
| PortOneClient | ํฌํธ์ V2 ๊ตฌํ์ฒด (Checkout V2๋ ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์ ์ฒ๋ฆฌ๋๋ฏ๋ก ์ธํฐํ์ด์ค๋ง ๊ตฌํ) |
| PaymentRepository | JPA๋ฅผ ํตํ ๊ฒฐ์ ๋ฐ์ดํฐ CRUD |
# PG์ฌ ์ค์
pg:
type: portOneClient
portone:
store-id: store-xxx
channel-key: channel-key-xxx
api-url: https://api.portone.io
success-url: http://localhost/pay/success
fail-url: http://localhost/pay/failํ๋ก ํธ์๋์์ ํฌํธ์ V2 SDK๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฐ์ ์ฐฝ์ ํธ์ถํ๋ค:
<!-- PortOne Checkout V2 SDK -->
<script src="https://cdn.portone.io/v2/browser-sdk.js"></script>
<script>
const response = await PortOne.requestPayment({
storeId: "store-xxx",
channelKey: "channel-key-xxx",
paymentId: "PAY-" + Date.now(),
orderName: "PayFlow ๊ฒฐ์ ",
totalAmount: 10000,
payMethod: "CARD",
currency: "KRW",
customer: {
fullName: "ํ
์คํธ์ฌ์ฉ์",
phoneNumber: "01012345678",
email: "test@example.com"
},
redirectUrl: "http://localhost/pay/success"
});
</script>| PayFlow | ํฌํธ์ V2 |
|---|---|
card |
CARD |
vbank |
VBANK |
- ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๊ฒฐ์ : ์๋ฒ์์ ๋ณ๋์ ์น์ธ API ํธ์ถ ์์ด ํด๋ผ์ด์ธํธ์์ ์ง์ ๊ฒฐ์ ์ฒ๋ฆฌ
- ์๋ ๋ฆฌ๋ค์ด๋ ํธ: ๊ฒฐ์ ์๋ฃ ํ ์๋์ผ๋ก
successUrl๋๋failUrl๋ก ์ด๋ - ๊ฐํธํ ์ฐ๋: ๋ณต์กํ ์๋ฒ-์๋ฒ ํต์ ์์ด JavaScript SDK๋ง์ผ๋ก ๊ตฌํ ๊ฐ๋ฅ
- ํตํฉ PG ๊ด๋ฆฌ: ํ๋์ ์ฑ๋ ํค๋ก ์ฌ๋ฌ PG์ฌ๋ฅผ ์๋ ์ ํ
์ฅ์ :
- ๊ตฌํ์ด ๊ฐ๋จํ๊ณ ๋น ๋ฆ
- ์๋ฒ ๋ถํ ๊ฐ์ (ํด๋ผ์ด์ธํธ์์ ์ง์ ์ฒ๋ฆฌ)
- ์ฌ์ฉ์ ๊ฒฝํ ํฅ์ (๋น ๋ฅธ ์๋ต ์๋)
๋จ์ :
- ์๋ฒ์์ ๊ฒฐ์ ๊ฒ์ฆ์ด ์ด๋ ค์
- ํด๋ผ์ด์ธํธ ์กฐ์ ๊ฐ๋ฅ์ฑ (์ถ๊ฐ ๊ฒ์ฆ ๋ก์ง ํ์)
- Webhook์ ํตํ ์๋ฒ ๊ฒ์ฆ ๊ถ์ฅ
๋ณธ ํ๋ก์ ํธ ์กฐ์ฌ๋ฅผ ํตํด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋์ถํ์๋ค.
- ๊ฒฐ์ ํ๋ก์ธ์ค๊ฐ ๋จ์ผ ์์ฒญ์ด ์๋ ์ฌ๋ฌ ๋จ๊ณ์ ์ํ ์ ์ด๋ฅผ ํตํด ๋์ํจ์ ์ดํดํ์๋ค.
ready โ paid/failed์ ๊ฐ์ ์ํ ๋ชจ๋ธ๋ง์ด ๊ฒฐ์ ์์คํ ์ ํต์ฌ์ด๋ผ๋ ์ ์ ํ์ธํ์๋ค.- PgClient ์ธํฐํ์ด์ค๋ฅผ ํตํด ๋ค์ํ PG์ฌ๋ฅผ ์ถ์ํํ์ฌ ํ์ฅ ๊ฐ๋ฅํ ๊ตฌ์กฐ๋ก ์ค๊ณํ ์ ์์์ ํ์ธํ์๋ค.
- ํฌํธ์ V2 Checkout SDK๋ฅผ ํ์ฉํ์ฌ ์ค์ PG์ฌ(์ด๋์์ค) ์ฐ๋์ ์ฑ๊ณต์ ์ผ๋ก ๊ตฌํํ์๋ค.
- ๋ค์ํ PG์ฌ(ํ ์คํ์ด๋จผ์ธ , KCP, ์ด๋์์ค, ์นด์นด์คํ์ด ๋ฑ)์ ํน์ง๊ณผ ์ฐ๋ ๋ฐฉ์์ ์กฐ์ฌํ์ฌ ๋น๊ตํ ์ ์์๋ค.
- ํฌํธ์ V2์ ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๊ฒฐ์ ๋ฐฉ์์ ํตํด ์๋ฒ ๋ถํ๋ฅผ ์ค์ด๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ ์ ์์์ ํ์ธํ์๋ค.
PayFlow ํ๋ก์ ํธ ์กฐ์ฌ๋ฅผ ํตํด ๊ฒฐ์ ์์คํ ์ ๊ธฐ๋ณธ ์๋ฆฌ๋ฅผ ์ดํดํ๋ ๋ฐ ํฐ ๋์์ด ๋์์ผ๋ฉฐ, ํนํ ์ํ ๊ด๋ฆฌ ๊ธฐ๋ฐ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋ฐฉ์์ ๋ํ ์ดํด๊ฐ ๋์์ก๋ค.
์ด์ ์กธ์ ์ํ Ignis ํ๋ก์ ํธ์์๋ ๊ฒฐ์ ๊ธฐ๋ฅ์ ๋จ์ํ ์ฐ๋ํ๋ ์์ค์์ ๊ทธ์ณค๋ค๋ฉด, ์ด๋ฒ PayFlow ํ๋ก์ ํธ๋ฅผ ํตํด ๊ฒฐ์ ์์คํ ์ ์ ์ฒด ๊ตฌ์กฐ์ ํ๋ฆ์ ์ง์ ์ค๊ณํ๊ณ ๊ตฌํํด๋ด์ผ๋ก์จ ๋ ๊น์ ์ดํด๋ฅผ ์ป์ ์ ์์๋ค.
๋ํ Spring Boot์ ๊ณ์ธต ๊ตฌ์กฐ์ ์ญํ ๋ถ๋ด์ ์ฒดํํจ์ผ๋ก์จ, ์ค๋ฌด์ ์ธ ๊ฐ๋ฐ ๊ณผ์ ์์ ๋ฐฑ์๋ ๋ก์ง์ ์ค๊ณํ๊ณ ๊ตฌํํ๋ ๋ฅ๋ ฅ์ ๊ฐํํ ์ ์์๋ค.
ํฌํธ์ V2 Checkout SDK๋ฅผ ์ฌ์ฉํ๋ฉด์, ๊ธฐ์กด์ ์๋ฒ-์๋ฒ ํต์ ๋ฐฉ์๊ณผ ๋ฌ๋ฆฌ ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์ ์ง์ ๊ฒฐ์ ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ์ฅ๋จ์ ์ ๊ฒฝํํ ์ ์์๋ค. ์ด๋ ํฅํ ๋ค๋ฅธ PG์ฌ ์ฐ๋ ์์๋ ์ ์ฉํ ๊ฒฝํ์ด ๋ ๊ฒ์ด๋ค.
- ๊ฒฐ์ ์ทจ์(Refund) ๊ธฐ๋ฅ ๊ตฌํ
- Webhook ์ฝ๋ฐฑ ๊ฒ์ฆ ๋ก์ง ์ถ๊ฐ
- ๊ฒฐ์ ๋ก๊ทธ ํ ์ด๋ธ ํ์ฉ (payment_logs)
- ๋ค๋ฅธ PG์ฌ(ํ ์คํ์ด๋จผ์ธ ) ์ง์ ์ฐ๋ ๋น๊ต ๊ตฌํ
- ์์ธ ์ฒ๋ฆฌ ๊ฐํ ๋ฐ ์๋ฌ ํธ๋ค๋ง ๊ณ ๋ํ
pg_tid,pg_response์ปฌ๋ผ์ DB ์คํค๋ง์ ์ถ๊ฐ
- ํฌํธ์ V2 ๊ฐ๋ฐ์ ๋ฌธ์
- ํ ์คํ์ด๋จผ์ธ ๊ฐ๋ฐ์์ผํฐ
- Spring Boot ๊ณต์ ๋ฌธ์
- JPA/Hibernate ๊ณต์ ๋ฌธ์
๋ณธ ์กฐ์ฌ๋ ๊ฒฐ์ ์์คํ ๊ฐ๋ฐ์ ๊ธฐ์ด๋ฅผ ๋ค์ง๋ ์ข์ ์ถ๋ฐ์ ์ด ๋์์ผ๋ฉฐ, ์ด๋ฅผ ๋ฐํ์ผ๋ก ๋ ๋์ ์์ค์ ์น ์๋น์ค ๊ฐ๋ฐ ์ญ๋์ ๊ฐ์ถ๋ ๋ฐ ๊ธฐ๋ฐ์ด ๋ ๊ฒ์ด๋ค.