Node.js 로 Stripe Webhook 구축하기 (완전 가이드)
요약
본 가이드는 Node.js와 Express 프레임워크를 사용하여 Stripe 웹훅(Webhook)을 구축하는 방법을 상세히 다룹니다. 웹훅은 결제 성공, 실패, 구독 변경 등 비동기 이벤트를 처리하는 데 필수적이며, 본 예제는 단순한 수신을 넘어 보안 검증, 견고한 이벤트 처리 파이프라인, 그리고 중복 처리를 방지하는 멱등성(Idempotency) 메커니즘까지 포함하여 프로덕션 환경에 적합한 아키텍처를 제시합니다.
핵심 포인트
- Stripe 웹훅은 결제 시스템의 비동기 이벤트를 처리하기 위해 HTTP POST 요청을 사용한다.
- 웹훅 엔드포인트는 `stripe.webhooks.constructEvent`를 사용하여 Stripe 서명(signature)을 검증하는 것이 필수적이다 (보안).
- 이벤트 처리는 모듈화된 핸들러(`eventHandlers`)를 통해 구현하여 확장성과 가독성을 높인다.
- 멱등성(Idempotency) 로직을 적용하여 네트워크 오류나 재전송으로 인한 이벤트의 중복 처리를 방지해야 한다.
- 오류 처리 메커니즘을 포함하여, 비즈니스 로직 실패 시에도 적절한 HTTP 상태 코드를 반환하도록 설계한다.
How I Built a Stripe Webhook in Node.js (Full Technical Guide) 웹훅은 결제 처리 시스템에서 비동기 이벤트를 처리하는 데 필수적입니다. 여기서는 적절한 보안, 오류 처리 및 아키텍처를 갖춘 프로덕션급 Stripe 웹훅 핸들러를 Node.js 로 구축하는 내 심층 분석을 소개합니다.
웹훅 아키텍처 이해하기
Stripe 웹훅은 성공적인 결제 (payment_intent.succeeded), 실패한 청구 (charge.failed), 구독 변경 (customer.subscription.updated) 와 같은 이벤트를 알려주기 위해 HTTP POST 요청을 사용합니다. 우리가 구현할 핵심 구성 요소는 다음과 같습니다:
- Stripe 서명 (signature) 을 사용한 엔드포인트 검증
- 이벤트 처리 파이프라인
- 멱등성 (Idempotency) 처리
- 오류 복구 메커니즘
초기 설정
먼저 필요한 종속성을 설치합니다:
npm install stripe express body-parser crypto
Stripe 인스턴스를 구성합니다:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const app = express();
app.use(bodyParser.raw({ type: 'application/json' }));
웹훅 엔드포인트 구현
서명 (signature) 검증이 포함된 핵심 웹훅 핸들러입니다:
const WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET;
app.post('/webhook', async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, WEBHOOK_SECRET);
} catch (err) {
console.error(Webhook signature verification failed: ${err.message});
return res.status(400).send(Webhook Error: ${err.message});
}
// 이벤트 처리
try {
await handleStripeEvent(event);
res.json({ received: true });
} catch (err) {
console.error(Event processing failed: ${err.message});
res.status(500).send(Processing Error: ${err.message});
}
});
이벤트 처리 파이프라인 구현
적절한 오류 관리가 포함된 견고한 이벤트 핸들러를 구현합니다:
const eventHandlers = {
'payment_intent.succeeded': async (paymentIntent) => {
// 성공적인 결제에 대한 비즈니스 로직
console.log(Payment succeeded: ${paymentIntent.id});
await fulfillOrder(paymentIntent);
},
'payment_intent.payment_failed': async (paymentIntent) => {
console.log(Payment failed: ${paymentIntent.id});
await handleFailedPayment(paymentIntent);
},
// 필요한 경우 더 많은 이벤트 핸들러 추가
};
async function handleStripeEvent(event) {
const handler = eventHandlers[event.type];
if (!handler) {
console.log(Unhandled event type: ${event.type});
return;
}
try {
await handler(event.data.object);
} catch (err) {
console.error(Handler for ${event.type} failed:, err);
throw err; // 올바른 HTTP 응답을 위해 다시 던지기
}
}
멱등성 (Idempotency) 구현
중복 처리를 방지하는 데 필수적입니다:
const processedEvents = new Set();
async function handleStripeEvent(event) {
// 중복 이벤트 처리 확인
const eventId = event.id;
if (processedEvents.has(eventId)) {
console.log(Event already processed: ${eventId});
return;
}
try {
await processEventLogic(event);
processedEvents.add(eventId); // 처리된 이벤트를 저장
} catch (err) {
// 오류 시에도 멱등성 키를 제거하지 않도록 주의해야 함
console.error(Error processing event ${eventId}:, err);
throw err;
}
}
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기