Stripe 웹훅(Webhooks)으로 이벤트 받기
Stripe 웹훅(Webhooks)으로 이벤트 받기
결제는 요청-응답만으로 끝나지 않아요. 고객의 은행이 결제를 확인하거나, 결제에 분쟁이 걸리거나, 반복 결제가 성공하는 일은 우리 앱이 아무것도 하지 않는 사이에 일어나죠. Stripe 웹훅은 이렇게 비동기로 일어나는 이벤트를 Stripe가 우리 엔드포인트로 알려주는 채널이에요. 이 페이지에서는 웹훅 엔드포인트를 등록하고, 이벤트를 받아 검증하는 방법을 공식 문서 기준으로 풀어요.
출처: Stripe 공식 문서 — Receive Stripe events in your webhook endpoint
엔드포인트 등록
Stripe가 이벤트를 보낼 곳을 알려면, 우리가 수신 가능한 엔드포인트 URL을 등록해야 해요. 등록은 API로 하거나 대시보드의 Webhooks 탭(Workbench)에서 할 수 있어요. Stripe에 등록할 수 있는 웹훅 엔드포인트는 최대 16개예요.
- 대시보드 — Webhooks 탭에서 '이벤트 대상(Event destination) 생성' → 계정 선택 → 사용할 API 버전·이벤트 타입 선택 → 수신 방식으로 '웹훅 엔드포인트' 선택 → 엔드포인트 URL과 설명 입력으로 등록해요.
- API —
/v2/core/event_destinations엔드포인트로 등록해요.
이벤트는 크게 두 가지 형태가 있어요. snapshot 이벤트(API v1 리소스)는 이벤트 객체에 리소스 전체 스냅샷을 담고, thin 이벤트(API v2)는 이벤트 자체를 담고 페이로드를 따로 가져옵니다. thin 이벤트를 받으려면 별도 웹훅 엔드포인트를 등록해야 하고, event_payload 값을 thin으로 설정해요.
핸들러 작성
웹훅을 받으면 우리가 처리해야 할 이벤트 타입을 골라 분기하는 핸들러 함수를 만들어요. 예를 들어 payment_intent.succeeded 이벤트가 오면 event.data.object에서 PaymentIntent를 꺼내 성공 처리를 하고, payment_method.attached이면 결제 수단 등록 처리를 해요. 이벤트 타입별 처리 로직을 명확히 나누는 게 핵심이에요.
이벤트 전달 동작
한 가지 기억할 점은 Stripe가 이벤트를 발생 순서대로 전달한다고 보장하지 않는다는 거예요. 구독을 만들면 다음 이벤트들이 서로 다른 순서로 도착할 수 있어요.
customer.subscription.createdinvoice.createdinvoice.paidcharge.created(결제가 있을 때)
또한 이벤트의 구조(API 버전)는 이벤트가 발생한 시점의 계정 API 버전 기준이에요.
검증 — 보안의 핵심
웹훅은 'Stripe가 보낸 게 진짜'인지 확인할 수 있어야 해요. 두 가지 방법을 함께 써요.
- IP 허용 목록 — Stripe가 웹훅을 보내는 IP 주소 목록(
docs.stripe.com/ips)을 기준으로 방화벽·서버가 이 IP에서 온 요청만 받도록 설정해요. - 서명 검증 — Stripe는 모든 웹훅 이벤트에
Stripe-Signature헤더를 넣어 서명해요. 이 서명을 검증하는 게 가장 확실한 방법이에요.
서명 검증 절차는 이렇게 진행돼요.
- 엔드포인트의 시크릿을 가져와요. 엔드포인트마다 고유하며, 테스트·라이브 API 키가 다르면 시크릿도 달라요.
Stripe-Signature헤더와 함께 검증을 수행해요. 검증에 실패하면 요청을 거부해야 해요.
공식 라이브러리로 검증하면 간단해요 — 이벤트 페이로드, Stripe-Signature 헤더, 엔드포인트 시크릿을 전달하면 검증이 끝나요. 수동으로 검증할 땐 signed_payload를 만들어야 해요. signed_payload는 타임스탬프(문자열) + . + 실제 JSON 페이로드(요청 본문) 를 이어 붙인 문자열이고, 이걸 메시지로, 엔드포인트 서명 시크릿을 키로 해 HMAC-SHA256 해시를 계산한 뒤 헤더의 기대 서명과 비교해요. Stripe-Signature 헤더는 실제로 한 줄이에요.
재생 공격(replay attack) 방지를 위해 서버 시계가 정확하고 Stripe 서버 시계와 동기화되도록 NTP(Network Time Protocol)를 써요.
중복 이벤트 처리
웹훅은 때로 같은 이벤트를 두 번 이상 받을 수 있어요. 이미 처리한 이벤트 ID(event.id)를 기록해 두고, 중복이 오면 처리하지 않도록 가드하는 게 좋아요. 아니라면 data.object의 ID와 event.type을 함께 보고 중복을 식별할 수 있어요. 또 우리 통합에 꼭 필요한 이벤트 타입만 수신하도록 설정을 좁혀 두는 편이 좋아요.
더 알아보기 (Learn more)
- Payment Intents API 이해하기 —
payment_intent.succeeded같은 이벤트의 주인 - 구독(Subscriptions) 이해하기 —
invoice.paid·invoice.payment_failed이벤트 - 공식 문서 — docs.stripe.com/webhooks
- Stripe IP 목록 — docs.stripe.com/ips