diff --git a/docker-compose.yml b/docker-compose.yml index 234e3bb..6680965 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,8 +2,7 @@ services: app: # 서비스명 build: . container_name: myapp # 컨테이너명 - command: > - sh -c "pnpm start" + command: ["node", "dist/index.js"] env_file: # .env 가져오기 - .env environment: @@ -28,8 +27,7 @@ services: app-dev: # 서비스명 build: . container_name: myapp-dev # 컨테이너명 - command: > - sh -c "pnpm start" + command: ["node", "dist/index.js"] env_file: # .env 가져오기 - .env.dev environment: diff --git a/src/purchases/controller/purchase.webhook.controller.ts b/src/purchases/controller/purchase.webhook.controller.ts index b617679..d9e77b0 100644 --- a/src/purchases/controller/purchase.webhook.controller.ts +++ b/src/purchases/controller/purchase.webhook.controller.ts @@ -2,25 +2,59 @@ import { Request, Response, NextFunction } from 'express'; import { WebhookService } from '../services/purchase.webhook.service'; import { PayplePaymentResult } from '../utils/payple'; +type RedirectStatus = 'success' | 'fail' | 'error' | 'invalid'; + +const buildRedirectUrl = ( + status: RedirectStatus, + params: Record, +): string | null => { + const base = process.env.PURCHASE_RESULT_REDIRECT_URL; + if (!base) return null; + + try { + const url = new URL(base); + url.searchParams.set('status', status); + Object.entries(params).forEach(([key, value]) => { + if (value !== undefined && value !== '') url.searchParams.set(key, value); + }); + return url.toString(); + } catch { + return null; + } +}; + export const WebhookController = { - async handleWebhook(req: Request, res: Response, next: NextFunction) { - try { - const result = req.body as Partial; + async handleWebhook(req: Request, res: Response, _next: NextFunction) { + const result = req.body as Partial | undefined; + const oid = typeof result?.PCD_PAY_OID === 'string' ? result.PCD_PAY_OID : undefined; - if (!result || typeof result.PCD_PAY_RST !== 'string') { - return res.status(400).send('Invalid payload'); - } + const redirect = ( + status: RedirectStatus, + params: Record = {}, + ) => { + const url = buildRedirectUrl(status, { oid, ...params }); + if (url) return res.redirect(302, url); + return res.status(200).send('OK'); + }; - if (result.PCD_PAY_RST !== 'success') { - console.log('[Webhook] Non-success result:', result.PCD_PAY_CODE, result.PCD_PAY_MSG); - return res.status(200).send('OK'); - } + if (!result || typeof result.PCD_PAY_RST !== 'string') { + return redirect('invalid'); + } + + if (result.PCD_PAY_RST !== 'success') { + console.log('[Webhook] Non-success result:', result.PCD_PAY_CODE, result.PCD_PAY_MSG); + return redirect('fail', { + code: result.PCD_PAY_CODE, + message: result.PCD_PAY_MSG, + }); + } + try { await WebhookService.handlePaypleResult(result as PayplePaymentResult); - res.status(200).send('OK'); + return redirect('success'); } catch (err) { console.error('[Webhook] Error:', err); - res.status(500).send('Internal Server Error'); + return redirect('error'); } }, }; diff --git a/src/purchases/dtos/purchase.request.dto.ts b/src/purchases/dtos/purchase.request.dto.ts index e13c34b..8575345 100644 --- a/src/purchases/dtos/purchase.request.dto.ts +++ b/src/purchases/dtos/purchase.request.dto.ts @@ -19,4 +19,5 @@ export interface PurchaseRequestResponseDTO { PCD_PAY_GOODS: string; PCD_PAY_TOTAL: number; PCD_USER_DEFINE1: string; + PCD_RST_URL: string; } diff --git a/src/purchases/services/purchase.request.service.ts b/src/purchases/services/purchase.request.service.ts index 9eb508b..4a7fc1b 100644 --- a/src/purchases/services/purchase.request.service.ts +++ b/src/purchases/services/purchase.request.service.ts @@ -36,6 +36,7 @@ export const PurchaseRequestService = { prompt_id: dto.prompt_id, user_id: userId, }), + PCD_RST_URL: process.env.PAYPLE_RST_URL || '', }; }, };