Skip to content

Commit 735487d

Browse files
authored
Merge pull request #408 from PromptPlace/develop
Develop
2 parents 65f53da + 9fe5718 commit 735487d

File tree

7 files changed

+110
-78
lines changed

7 files changed

+110
-78
lines changed

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import authRouter from "./auth/routes/auth.route"; // auth 라우터 경로 수
1414
import membersRouter from "./members/routes/member.route"; // members 라우터 import
1515
import promptRoutes from "./prompts/routes/prompt.route"; // 프롬프트 관련 라우터
1616
import ReviewRouter from "./reviews/routes/review.route";
17-
import purchaseRouter from "./purchases/routes/purchase.request.route";
17+
import purchaseRouter from "./purchases/routes/purchase.route";
1818
import settlementRouter from "./settlements/routes/settlement.route";
1919
import withdrawalRouter from "./withdrawals/routes/withdrawal.route";
2020
import accountRouter from "./accounts/routes/account.route";

src/purchases/controller/purchase.complete.controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { Request, Response, NextFunction } from 'express';
2-
import { PromptPurchaseCompleteRequestDTO } from '../dtos/purchase.complete.dto';
2+
import { PurchaseCompleteRequestDTO } from '../dtos/purchase.complete.dto';
33
import { PurchaseCompleteService } from '../services/purchase.complete.service';
44

55
export const PurchaseCompleteController = {
66
async completePurchase(req: Request, res: Response, next: NextFunction) {
77
try {
88
const userId = (req.user as any).user_id;
9-
const dto = req.body as Partial<PromptPurchaseCompleteRequestDTO>;
9+
const dto = req.body as Partial<PurchaseCompleteRequestDTO>;
1010

1111
if (!dto || typeof dto.paymentId !== 'string') {
1212
return res.status(400).json({
@@ -16,7 +16,7 @@ export const PurchaseCompleteController = {
1616
});
1717
}
1818

19-
const result = await PurchaseCompleteService.completePurchase(userId, dto as PromptPurchaseCompleteRequestDTO);
19+
const result = await PurchaseCompleteService.completePurchase(userId, dto as PurchaseCompleteRequestDTO);
2020
res.status(result.statusCode).json(result);
2121
} catch (err) {
2222
next(err);

src/purchases/dtos/purchase.complete.dto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
export interface PromptPurchaseCompleteRequestDTO {
1+
export interface PurchaseCompleteRequestDTO {
22
paymentId: string;
33
}
44

5-
export interface PromptPurchaseCompleteResponseDTO {
5+
export interface PurchaseCompleteResponseDTO {
66
message: string;
77
status: 'Succeed' | 'Failed' | 'Pending';
88
purchase_id?: number;
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1-
export interface PromptPurchaseRequestDTO {
1+
export interface PurchaseRequestDTO {
22
prompt_id: number;
3+
}
4+
5+
export interface PurchaseRequestResponseDTO {
6+
message: string;
7+
statusCode: number;
8+
storeId: string;
9+
paymentId: string;
10+
orderName: string;
11+
totalAmount: number;
12+
channelKey: string;
13+
customData: {
14+
prompt_id: number;
15+
user_id: number;
16+
};
317
}
Lines changed: 78 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Router } from 'express';
2-
import { PurchaseCompleteController } from '../controller/purchase.complete.controller';
32
import { authenticateJwt } from '../../config/passport';
43
import { PurchaseRequestController } from '../controller/purchase.request.controller';
54
import { PurchaseHistoryController } from '../controller/purchase.controller';
5+
import { PurchaseCompleteController } from '../controller/purchase.complete.controller';
66

77
const router = Router();
88

@@ -17,8 +17,8 @@ const router = Router();
1717
* @swagger
1818
* /api/prompts/purchases/requests:
1919
* post:
20-
* summary: 결제 요청 생성 (사전 검증)
21-
* description: 프론트엔드에서 결제창을 띄우기 전, 주문 번호 생성 및 사전 검증을 수행합니다.
20+
* summary: 결제 요청 생성 (주문서 발행)
21+
* description: 프론트엔드에서 포트원 V2 결제창을 띄우기 위해 필요한 주문 번호(paymentId)와 결제 정보를 생성합니다.
2222
* tags: [Purchase]
2323
* security:
2424
* - jwt: []
@@ -30,83 +30,57 @@ const router = Router();
3030
* type: object
3131
* required:
3232
* - prompt_id
33-
* - merchant_uid
34-
* - amount
35-
* - buyer_name
36-
* - redirect_url
3733
* properties:
3834
* prompt_id:
3935
* type: integer
40-
* merchant_uid:
41-
* type: string
42-
* description: 가맹점 주문 번호
43-
* amount:
44-
* type: integer
45-
* description: 결제 예정 금액
46-
* buyer_name:
47-
* type: string
48-
* redirect_url:
49-
* type: string
36+
* description: 구매하려는 프롬프트의 ID
37+
* example: 12
5038
* responses:
5139
* 200:
52-
* description: 요청 성공
40+
* description: 주문서 생성 성공 (PortOne V2 SDK 연동 데이터 반환)
5341
* content:
5442
* application/json:
5543
* schema:
5644
* type: object
5745
* properties:
5846
* message:
5947
* type: string
60-
* merchant_uid:
61-
* type: string
48+
* example: "주문서가 생성되었습니다."
6249
* statusCode:
6350
* type: integer
64-
*/
65-
router.post('/requests', authenticateJwt, PurchaseRequestController.requestPurchase);
66-
67-
/**
68-
* @swagger
69-
* /api/prompts/purchases/complete:
70-
* post:
71-
* summary: 결제 완료 처리 (검증 및 저장)
72-
* description: 포트원 결제 완료 후, paymentId를 서버로 보내 검증하고 구매를 확정합니다.
73-
* tags: [Purchase]
74-
* security:
75-
* - jwt: []
76-
* requestBody:
77-
* required: true
78-
* content:
79-
* application/json:
80-
* schema:
81-
* type: object
82-
* required:
83-
* - paymentId
84-
* properties:
85-
* paymentId:
86-
* type: string
87-
* description: 포트원 V2 결제 ID
88-
* merchant_uid:
89-
* type: string
90-
* description: 가맹점 주문 번호
91-
* responses:
92-
* 200:
93-
* description: 결제 성공 및 저장 완료
94-
* content:
95-
* application/json:
96-
* schema:
97-
* type: object
98-
* properties:
99-
* message:
51+
* example: 200
52+
* storeId:
10053
* type: string
101-
* status:
54+
* description: 포트원 상점 ID (SDK 설정용)
55+
* example: "store-abc12345..."
56+
* paymentId:
10257
* type: string
103-
* enum: [Succeed, Failed, Pending]
104-
* purchase_id:
105-
* type: integer
106-
* statusCode:
107-
* type: integer
58+
* description: 서버에서 생성한 고유 주문 번호 (구 merchant_uid)
59+
* example: "payment-550e8400-e29b-41d4-a716-446655440000"
60+
* orderName:
61+
* type: string
62+
* description: 주문명 (프롬프트 제목)
63+
* example: "감성적인 AI 풍경화 프롬프트"
64+
* totalAmount:
65+
* type: number
66+
* description: 결제 금액 (DB 기준)
67+
* example: 5000
68+
* channelKey:
69+
* type: string
70+
* description: 포트원 채널 키 (PG사 구분용)
71+
* example: "channel-key-uuid..."
72+
* customData:
73+
* type: object
74+
* description: 결제 검증 및 웹훅 처리를 위한 메타 데이터
75+
* properties:
76+
* prompt_id:
77+
* type: integer
78+
* example: 12
79+
* user_id:
80+
* type: integer
81+
* example: 5
10882
*/
109-
router.post('/complete', authenticateJwt, PurchaseCompleteController.completePurchase);
83+
router.post('/requests', authenticateJwt, PurchaseRequestController.requestPurchase);
11084

11185
/**
11286
* @swagger
@@ -153,4 +127,45 @@ router.post('/complete', authenticateJwt, PurchaseCompleteController.completePur
153127
*/
154128
router.get('/', authenticateJwt, PurchaseHistoryController.list);
155129

130+
/**
131+
* @swagger
132+
* /api/prompts/purchases/complete:
133+
* post:
134+
* summary: 결제 완료 처리 (검증 및 저장)
135+
* description: 포트원 결제 완료 후, paymentId를 서버로 보내 검증하고 구매를 확정합니다.
136+
* tags: [Purchase]
137+
* security:
138+
* - jwt: []
139+
* requestBody:
140+
* required: true
141+
* content:
142+
* application/json:
143+
* schema:
144+
* type: object
145+
* required:
146+
* - paymentId
147+
* properties:
148+
* paymentId:
149+
* type: string
150+
* description: 포트원 V2 결제 ID
151+
* responses:
152+
* 200:
153+
* description: 결제 성공 및 저장 완료
154+
* content:
155+
* application/json:
156+
* schema:
157+
* type: object
158+
* properties:
159+
* message:
160+
* type: string
161+
* status:
162+
* type: string
163+
* enum: [Succeed, Failed, Pending]
164+
* purchase_id:
165+
* type: integer
166+
* statusCode:
167+
* type: integer
168+
*/
169+
router.post('/complete', authenticateJwt, PurchaseCompleteController.completePurchase);
170+
156171
export default router;

src/purchases/services/purchase.complete.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { PromptPurchaseCompleteRequestDTO, PromptPurchaseCompleteResponseDTO } from '../dtos/purchase.complete.dto';
1+
import { PurchaseCompleteRequestDTO, PurchaseCompleteResponseDTO } from '../dtos/purchase.complete.dto';
22
import { PurchaseRequestRepository } from '../repositories/purchase.request.repository';
33
import { PurchaseCompleteRepository } from '../repositories/purchase.complete.repository';
44
import { AppError } from '../../errors/AppError';
55
import prisma from '../../config/prisma';
66
import { fetchAndVerifyPortonePayment } from '../utils/portone';
77

88
export const PurchaseCompleteService = {
9-
async completePurchase(userId: number, dto: PromptPurchaseCompleteRequestDTO): Promise<PromptPurchaseCompleteResponseDTO> {
9+
async completePurchase(userId: number, dto: PurchaseCompleteRequestDTO): Promise<PurchaseCompleteResponseDTO> {
1010
const { paymentId } = dto;
1111

1212
// 1. 포트원 조회 (검증 전 단계)

src/purchases/services/purchase.request.service.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { v4 as uuidv4 } from 'uuid';
2-
import { PromptPurchaseRequestDTO } from '../dtos/purchase.request.dto';
2+
import { PurchaseRequestDTO } from '../dtos/purchase.request.dto';
3+
import { PurchaseRequestResponseDTO } from '../dtos/purchase.request.dto';
34
import { PurchaseRequestRepository } from '../repositories/purchase.request.repository';
45
import { AppError } from '../../errors/AppError';
56

67
export const PurchaseRequestService = {
7-
async createPurchaseRequest(userId: number, dto: PromptPurchaseRequestDTO) {
8+
async createPurchaseRequest(userId: number, dto: PurchaseRequestDTO): Promise<PurchaseRequestResponseDTO> {
89
const prompt = await PurchaseRequestRepository.findPromptWithSeller(dto.prompt_id);
910
if (!prompt) throw new AppError('프롬프트를 찾을 수 없습니다.', 404, 'NotFound');
1011

@@ -18,10 +19,12 @@ export const PurchaseRequestService = {
1819
return {
1920
message: '주문서가 생성되었습니다.',
2021
statusCode: 200,
21-
merchant_uid: paymentId,
22-
amount: prompt.price,
23-
prompt_title: prompt.title,
24-
custom_data: {
22+
storeId: process.env.PORTONE_STORE_ID || '',
23+
paymentId: paymentId,
24+
orderName: prompt.title,
25+
totalAmount: prompt.price,
26+
channelKey: process.env.PORTONE_CHANNEL_KEY || '',
27+
customData: {
2528
prompt_id: dto.prompt_id,
2629
user_id: userId,
2730
},

0 commit comments

Comments
 (0)