<script>document.body.className="dc-bleed"</script>
<div class="dc-doc">
<header class="dc-doc__bar">
<a class="dc-doc__logo" href="#dc-doc-home">결산kr 개발 문서</a>
<form class="dc-doc__search" action="#dc-doc-home" method="get" role="search">
<label class="dc-sr" for="dc-doc-q">문서 검색</label>
<input class="dc-doc__search-input" id="dc-doc-q" type="search" name="q" placeholder="문서 검색">
</form>
<a class="dc-doc__key" href="#dc-doc-keys">API 키 보기</a>
</header>
<div class="dc-doc__body">
<article class="dc-doc__article">
<nav class="dc-doc__crumb" aria-label="현재 위치">
<a class="dc-doc__crumb-link" href="#dc-doc-home">문서</a>
<span class="dc-doc__crumb-sep" aria-hidden="true">/</span>
<a class="dc-doc__crumb-link" href="#dc-doc-pay">결제 연동</a>
<span class="dc-doc__crumb-sep" aria-hidden="true">/</span>
<span class="dc-doc__crumb-now" aria-current="page">웹훅 받기</span>
</nav>
<h1 class="dc-doc__h1">웹훅 받기</h1>
<p class="dc-doc__meta">2026년 8월 21일 고침 · 읽는 데 6분 · 한지우</p>
<p class="dc-doc__lead">결제가 끝나거나 환불이 일어나면 결산kr 서버가 정해 둔 주소로 POST 요청을 보냅니다. 주소를 등록하고, 서명을 검증하고, 같은 이벤트가 두 번 왔을 때를 처리하는 순서를 차례로 설명합니다.</p>
<h2 class="dc-doc__h2" id="dc-doc-register">1. 받을 주소 등록</h2>
<p class="dc-doc__p">콘솔의 개발자 · 웹훅 화면에서 주소를 넣습니다. https 주소만 받고, 포트는 443 만 씁니다. 주소 하나에 여러 이벤트를 묶어도 되고, 이벤트별로 다른 주소를 써도 됩니다.</p>
<ol class="dc-doc__ol">
<li class="dc-doc__li">콘솔에서 <b>개발자 · 웹훅 · 주소 추가</b> 를 누릅니다.</li>
<li class="dc-doc__li">받을 이벤트를 고릅니다. 처음에는 결제 완료와 환불 완료 두 개면 충분합니다.</li>
<li class="dc-doc__li">저장하면 비밀키가 한 번만 보입니다. 그 자리에서 환경 변수에 옮겨 두세요.</li>
</ol>
<div class="dc-doc__note">
<p class="dc-doc__note-title">테스트 키로 먼저 해 보세요</p>
<p class="dc-doc__note-text">테스트 환경과 운영 환경은 비밀키가 다릅니다. 테스트 주소에서 이벤트 스무 건을 흘려 본 뒤 운영 주소를 등록하는 편이 빠릅니다.</p>
</div>
<h2 class="dc-doc__h2" id="dc-doc-verify">2. 서명 검증</h2>
<p class="dc-doc__p">본문을 받은 그대로(파싱 전 원본 바이트) 비밀키로 HMAC-SHA256 해서 Fs-Signature 헤더와 견줍니다. 다르면 400 을 돌려주고 아무것도 처리하지 않습니다.</p>
<pre class="dc-doc__code"><code class="dc-doc__code-text">const crypto = require("crypto");
app.post("/hooks/fs", (req, res) => {
const mine = crypto
.createHmac("sha256", process.env.FS_WEBHOOK_SECRET)
.update(req.rawBody)
.digest("hex");
if (mine !== req.header("Fs-Signature")) {
return res.status(400).send("signature mismatch");
}
enqueue(req.body); // 처리는 큐에 맡기고
res.status(200).send("ok"); // 응답은 바로 돌려준다
});</code></pre>
<h3 class="dc-doc__h3" id="dc-doc-header">보내는 헤더</h3>
<dl class="dc-doc__dl">
<dt class="dc-doc__dt">Fs-Signature</dt>
<dd class="dc-doc__dd">본문을 비밀키로 서명한 값. 16진수 소문자입니다.</dd>
<dt class="dc-doc__dt">Fs-Timestamp</dt>
<dd class="dc-doc__dd">보낸 시각(초). 5분이 지난 요청은 받지 말고 버리세요.</dd>
<dt class="dc-doc__dt">Fs-Event-Id</dt>
<dd class="dc-doc__dd">이벤트 하나의 고유값. 같은 값이 다시 오면 이미 처리한 건입니다.</dd>
</dl>
<h2 class="dc-doc__h2" id="dc-doc-retry">3. 재시도와 중복</h2>
<p class="dc-doc__p">20초 안에 200 이 돌아오지 않으면 실패로 보고 다시 보냅니다. 간격은 1분, 5분, 30분, 2시간, 6시간이고 그 뒤에는 멈춥니다.</p>
<ul class="dc-doc__ul">
<li class="dc-doc__li">응답을 먼저 돌려주고, 무거운 일은 큐에 넘기세요. 재고 차감을 응답 안에서 하면 재시도가 쌓입니다.</li>
<li class="dc-doc__li">Fs-Event-Id 를 테이블에 유일 키로 저장해 두 번째 요청은 그냥 200 으로 받아 주세요.</li>
<li class="dc-doc__li">실패가 이어지면 등록한 메일로 알립니다. 콘솔의 전송 기록에서 본문을 그대로 다시 보낼 수 있습니다.</li>
</ul>
<footer class="dc-doc__foot">
<div class="dc-doc__helpful">
<p class="dc-doc__helpful-q">이 문서가 도움이 되었나요?</p>
<div class="dc-doc__helpful-act">
<button class="dc-doc__vote" type="button">도움이 됐어요</button>
<button class="dc-doc__vote" type="button">부족해요</button>
</div>
</div>
<nav class="dc-doc__around" aria-label="앞뒤 문서">
<a class="dc-doc__around-link" href="#dc-doc-prev"><span class="dc-doc__around-dir">이전 문서</span><span class="dc-doc__around-name">결제 요청 만들기</span></a>
<a class="dc-doc__around-link dc-doc__around-link--next" href="#dc-doc-next"><span class="dc-doc__around-dir">다음 문서</span><span class="dc-doc__around-name">정산 내역 조회 API</span></a>
</nav>
</footer>
</article>
<aside class="dc-doc__side">
<nav class="dc-doc__toc" aria-label="이 문서의 목차">
<p class="dc-doc__toc-title">이 문서 안에서</p>
<ul class="dc-doc__toc-list">
<li><a class="dc-doc__toc-link dc-doc__toc-link--on" href="#dc-doc-register" aria-current="true">1. 받을 주소 등록</a></li>
<li><a class="dc-doc__toc-link" href="#dc-doc-verify">2. 서명 검증</a></li>
<li><a class="dc-doc__toc-link dc-doc__toc-link--sub" href="#dc-doc-header">보내는 헤더</a></li>
<li><a class="dc-doc__toc-link" href="#dc-doc-retry">3. 재시도와 중복</a></li>
</ul>
<a class="dc-doc__edit" href="#dc-doc-edit">이 문서 고치기 제안</a>
</nav>
</aside>
</div>
</div>