# 웹훅 연동 가이드

출처 https://www.groble.im/help/guides/webhook

전체 가이드 목록 https://www.groble.im/llms.txt

---

이 문서는 그로블 웹훅을 연결하는 분들을 위한 가이드예요.  
각 이벤트 응답의 모든 필드 정의가 필요하시다면 [웹훅 이벤트 페이지](https://www.groble.im/help/guides/webhook-events)를 확인해 주세요.

## 1\. 웹훅이 무엇인가요?

> 매번 새로운 소식이 있는지 확인할 필요 없어요. **그로블이 먼저 소식을 보내드리는 ‘자동 알림’** 기능이에요.  
> 결제 완료, 취소, 정기결제 해지 같은 중요한 일이 생기면,  
> 판매자님이 등록해둔 URL로 그로블이 즉시 상세 내용을 담아 안내(HTTPS POST 요청)를 보내드려요.

### 지원하는 이벤트

-   `payment.completed` \[일반결제 완료\]
    
-   `payment.cancel_requested` \[일반결제 취소 요청\]
    
-   `payment.refunded` \[일반결제 취소 완료\]
    
-   `subscription_payment.completed` \[정기결제 완료\]
    
-   `subscription_payment.refunded` \[정기결제 취소 완료 (회차 단위)\]
    
-   `subscription_payment.failed` \[정기결제 갱신 실패\]
    
-   `subscription.cancel_requested` \[정기결제 해지 요청\]
    
-   `subscription.terminated` \[정기결제 해지 완료\]
    

### 지원 예정 이벤트

-   \[정기결제 해지 요청 철회\]
    

> `subscription_payment.*`는 결제 회차 1건의 완료·취소·실패를, `subscription.*`는 정기결제 자체의 해지 요청·해지 완료를 뜻해요. 회차 하나가 취소돼도 정기결제는 계속 유지될 수 있으므로, 해지 완료는 `subscription.terminated`로 판단해 주세요.

## 2\. 5분 안에 첫 웹훅 받기

### 1단계. 그로블에 엔드포인트를 등록해 주세요

**내 스토어** \- **연동** 페이지에서 **HTTPS** 엔드포인트를 입력하고, 수신할 이벤트를 선택해 저장해요.

![webhook.png](https://image.dev.groble.im/article/2026/05/dc8e60d1-d8f7-4b19-98ba-c831e1caefd0_image.png)

*\* HTTPS 엔드포인트와 수신할 이벤트를 선택해 주세요*

첫 등록을 성공하면 **시크릿 키**가 발급돼요. 딱 한 번만 볼 수 있으니까  
**바로 복사해서 안전한 곳에 저장해 주세요!**

![webhook2.png](https://image.dev.groble.im/article/2026/05/08e92dce-edae-44e5-8569-671b3b13d529_image.png)

*\* 첫 등록으로 발급된 웹훅 시크릿 키를 안전한 곳에 저장해 주세요*

만약 **시크릿 키를 분실**했다면, 재발급할 수 있어요.

![image.png](https://image.groble.im/article/2026/05/02dded62-f93e-4df3-bc07-6eea8ded8f51_image.png)

*\* 재발급한 시크릿 키와 이전 시크릿 키는 24시간 동안 모두 사용할 수 있어요.*

시크릿 키 재발급은 **24시간마다 가능**해요.

### 2단계. 첫 이벤트 받기

실제 결제를 진행하거나 관리 페이지의 테스트 발송 기능으로 웹훅 작동을 확인해요.

![webhook3.png](https://image.dev.groble.im/article/2026/05/3d758cc4-d432-4dd4-9ce8-7f05af03ee92_image.png)

*\* 테스트 성공 여부를 확인할 수 있어요*

### 3단계. 서명 검증과 멱등 처리 추가

서명 검증이 없으면 누구나 “결제 완료” payload를 위조해서 보낼 수 있어요.  
아래 **4\. 서명 검증 방법**으로 검증을 붙이고, **5\. 처리 규칙**의 `X-Groble-Idempotency-Key`로 같은 이벤트를 두 번 처리하지 않도록 막아 주세요.

## 3\. 요청 형식

### 요청 헤더

| 헤더 | 설명 |
| --- | --- |
| `Content-Type` | 항상 `application/json` |
| `X-Groble-Signature` | 현재 시크릿 키로 만든 HMAC-SHA256 서명 |
| `X-Groble-Timestamp` | 서명 계산에 사용한 Unix timestamp(초) |
| `X-Groble-Idempotency-Key` | 이번 전송의 고유 키 |
| `X-Groble-Signature-Previous` | 시크릿 키 교체 후 24시간 동안만 전달되는 이전 시크릿 기준 서명 |

### 요청 본문

모든 웹훅은 아래 구조로 전달돼요.

```
{
     "id": "evt_xxxxxxxxxxxxxxxxxxxxxxxx",
     "type": "payment.completed",
     "version": "2026-04-30",
     "occurredAt": "2026-04-22T10:00:00+09:00",
     "data": {
        "object": {}
    }
}
```

-   `id`: 전송 고유 식별자
    
-   `type`: 이벤트 타입
    
-   `version`: payload 스키마 버전
    
-   `occurredAt`: 실제 이벤트 발생 시각
    
-   `data.object`: 이벤트별 상세 데이터
    

이벤트별 상세 필드는 [웹훅 이벤트 페이지](https://www.groble.im/help/guides/webhook-events)에서 확인해 주세요.

## 4\. 서명 검증

서명 검증은 아래 방식으로 수행해요.

```
signature = HEX(HMAC-SHA256(secret, "{timestamp}.{raw_body}"))
```

필수 규칙은 아래 4가지예요.

-   JSON 파싱 전의 원본 body bytes로 계산해야 해요.
    
-   `X-Groble-Signature`와 `X-Groble-Timestamp`를 반드시 확인해야 해요.
    
-   시크릿 키 교체 중에는 `X-Groble-Signature-Previous`도 함께 허용해야 해요.
    
-   `timestamp`는 현재 시각 기준 ±5분 이내인지 확인해야 해요.
    

검증 순서는 아래처럼 구현하면 돼요.

```
1. signature, signature_previous, timestamp 헤더를 읽습니다
2. raw body를 원본 bytes 그대로 읽습니다
3. "{timestamp}.{raw_body}"로 message를 만듭니다
4. secret으로 HMAC-SHA256을 계산합니다
5. 계산 결과가 signature 또는 signature_previous와 일치하는지 확인합니다
6. timestamp가 5분 이내인지 확인합니다
7. 통과한 경우에만 payload를 파싱합니다
```

## 5\. 처리 규칙

수신 서버는 아래 원칙으로 처리해 주세요.

-   서명 검증을 먼저 수행해 주세요.
    
-   가능한 한 빨리 `2xx`를 반환해 주세요.
    
-   응답은 10초 안에 끝내는 것을 기준으로 구현해 주세요.
    
-   `X-Groble-Idempotency-Key`로 같은 이벤트를 두 번 처리하지 않도록 막아 주세요.
    
-   이벤트 도착 순서는 보장되지 않으므로 `occurredAt` 기준으로 판단해 주세요.
    
-   리다이렉트는 따라가지 않아요. `3xx` 응답은 재시도 없이 최종 실패로 처리돼요.
    

상태 코드별 기본 동작은 아래와 같아요.

-   `2xx`: 성공
    
-   `408` · `429` · `500`~`504`: 재시도
    
-   `400` · `401` · `403` · `404`: 최종 실패
    
-   `410`: 엔드포인트 즉시 비활성화
    

재시도와 자동 비활성화는 아래 규칙을 따라요.

-   **최대 7회** 재시도해요. 간격은 1분 → 5분 → 30분 → 2시간 → 6시간 → 12시간 → 24시간으로 점점 늘어나고, 마지막 재시도까지 **약 44시간**이 걸려요.
    
-   `429` 응답에 `Retry-After` 헤더가 있으면 그 값을 우선해요 (최대 1시간).
    
-   **연속 실패 20건 이상**이면서 **마지막 성공 후 3일**이 지나면 엔드포인트가 자동으로 비활성화돼요.
    

## 6\. `sellerReference`로 결제를 내 회원·주문과 연결하기

결제창을 자사 SaaS나 회원제 서비스에 연결했다면, 결제 링크에 `?ref=값`을 붙여 **방금 결제한 사람이 내 서비스의 어떤 회원·주문인지** 자동으로 매칭할 수 있어요.

```
https://groble.im/payment/내-결제창-주소?ref=ord_9f1c2e7a4b6d
```

### 어떤 이벤트에 포함되나요?

-   `payment.completed` \[일반결제 완료\]
    
-   `subscription_payment.completed` \[정기결제 최초 결제와 이후 모든 갱신 회차\]
    
-   `subscription_payment.failed` \[정기결제 갱신 실패\]
    
-   `subscription.cancel_requested` \[정기결제 해지 요청\]
    
-   `subscription.terminated` \[정기결제 해지 완료\]
    

일반결제의 취소·환불 이벤트(`payment.cancel_requested` · `payment.refunded`)와 정기결제 회차 취소(`subscription_payment.refunded`)에는 포함되지 않아요. 이 이벤트들은 `merchantUid`가 원 결제와 같은 값이므로, 완료 이벤트를 받았을 때 `merchantUid ↔ sellerReference` 매핑을 내 DB에 저장해 두고 그 값으로 연결해 주세요.

> **정기결제는 회차마다** `**merchantUid**`**가 새로 발급돼요.** 그래서 정기결제 자체를 가리키는 값이 아니에요. 갱신 실패·해지 요청·해지 완료 이벤트를 내 서비스의 정기결제와 연결할 때는 회차와 무관하게 동일하게 실리는 `sellerReference`를 사용해 주세요.

### 값 규칙

-   허용 문자: 영문 대소문자·숫자와 `- _ . : = ~`
    
-   길이: 1~128자. 사전 검증 정규식은 `^[A-Za-z0-9\-_.:=~]{1,128}$`
    
-   앞뒤 공백은 제거되며, 값 안의 공백·한글·`@`·`+`·`/` 등 허용되지 않은 문자가 있거나 128자를 넘으면 값 전체가 폐기돼요.
    
-   형식이 잘못돼도 주문 생성은 `400`으로 실패하지 않아요. 결제는 정상 진행되고 `sellerReference`만 웹훅에서 빠져요.
    

> 표준 base64는 `+`·`/`가 섞일 수 있으므로 사용하지 말고 **base64url**을 사용해 주세요. URL에 노출되고 구매자가 바꿀 수 있는 값이므로 이메일·전화번호·순번 ID 대신 UUID나 추측하기 어려운 랜덤 토큰을 권장해요.

인코딩 방법, 값 보장, 이벤트별 포함 범위 등 자세한 규칙은 웹훅 이벤트의 `sellerReference` 절을 참고해 주세요.

### 연동 순서

1.  결제창 진입 링크에 `?ref=<value>`를 붙이고, 전송 전 정규식으로 검사해요.
    
2.  테스트 결제를 진행하고, 완료 웹훅의 `data.object.sellerReference`에 전송한 값이 그대로 담겨 오는지 확인해요. 키가 없으면 미전달이거나 형식 위반으로 폐기된 거예요.
    
3.  서명 검증을 통과한 웹훅의 참조값만 신뢰해 내 회원·주문과 매칭해요.
    
4.  `merchantUid`와 참조값의 매핑을 저장해 일반결제의 취소·환불 이벤트도 연결하고, 중복 처리는 `sellerReference`가 아닌 이벤트별 멱등 키로 막아요. 정기결제 관련 이벤트는 매핑 없이 `sellerReference`로 바로 연결하면 돼요.
    
5.  정기결제라면 갱신 회차에도 최초 결제와 같은 참조값이 오는지 확인해요.
    

## 7\. 최소 체크리스트

- [ ] HTTPS URL 등록
- [ ] 시크릿 키를 안전하게 저장
- [ ] 원본 body 기준으로 서명 검증
- [ ] `timestamp ±5분` 검증
- [ ] `X-Groble-Idempotency-Key` 멱등 처리
- [ ] 10초 안에 `2xx` 응답 반환
