API 레퍼런스: webhook
webhook은 이벤트가 생기는 순간 내 서버로 밀어 보내 줘요. 더보기 → Merchant API에서 앱의 Webhook URL을 지정해 두면, 구독한 이벤트마다 서명된 JSON 본문을 POST해 드려요.
이벤트 형식
{
"update_id": 123,
"update_type": "invoice_paid",
"request_date": "2026-08-11T12:00:00Z",
"payload": { … }
}
update_id는 같은 이벤트를 다시 보낼 때도 그대로예요. 중복 제거는 이 값으로
하세요. request_date는 보낼 때마다 새로 찍혀요. payload에는 이벤트 종류에
맞는 객체가 통째로 들어가요. 청구서 이벤트면 청구서 객체, 송금 링크 객체, 구독
객체 같은 식이에요(각각의 모양은 해당 레퍼런스에
있어요).
이벤트 종류
update_type | 언제 오나요 | 페이로드 |
|---|---|---|
invoice_paid | 청구서가 결제됐을 때 — 항상 보내요 | 청구서 객체 |
invoice_expired | 결제되지 않은 채 기한이 지났을 때 | 청구서 객체 |
check_activated | 내 송금 링크를 누가 받아 갔을 때 | 송금 링크 객체 |
refund_completed | 환불이 나갔을 때 | refunded_* 필드가 채워진 청구서 객체 |
subscription_activated | 사용자가 요금제를 승인했을 때 | 구독 객체 |
subscription_charged | 주기가 청구됐을 때 — 어떤 청구인지는 charge.kind에 있어요: initial, renewal, resubscribe | 구독 객체 + charge: {period_no, kind, asset, amount, fee, paid_at} |
subscription_cancelled | 어느 쪽이든 구독을 취소했을 때 | 구독 객체 |
subscription_expired | 유예 기간이 결제 없이 끝났을 때 | 구독 객체 |
invoice_paid 말고는 전부 원할 때만 켜는 방식이에요(Crypto Bot에는 없는
확장 기능이라, Crypto Bot 규격만 아는 클라이언트는 직접 켜지 않는 한 모르는
update_type을 만날 일이 없어요). 앱마다 Merchant API 화면의 추가 webhook
이벤트에서 켜면 돼요. 이 스위치는 webhook 주소를 저장해야 나타나고, 위 표의
식별자가 그대로 이름으로 적혀 있어요.
서명 확인하기
전송마다 TgPayCrypto-API-Signature 헤더가 붙어요
(Crypto-Pay-API-Signature도 값이 같은 호환용 별칭이에요). 원본 요청 본문을
HMAC-SHA256으로 서명한 16진수 값이고, 키는 앱의 기본 API 토큰을 SHA-256으로 해시한
값이에요. Crypto Bot과 같은 방식이라 쓰던 검증 코드를 그대로 써도 돼요.
import hashlib, hmac
secret = hashlib.sha256(API_TOKEN.encode()).digest()
expected = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, headers["TgPayCrypto-API-Signature"])
받은 원본 바이트로 확인해야 해요. 다시 직렬화한 값은 바이트가 달라질 수 있어서 검증이 실패해요. 서명하는 건 기본 토큰뿐이고, 권한 제한 토큰은 서명하지 않아요. 기본 토큰을 새로 발급하면 webhook 서명 키도 그 순간 바뀌니까, 서버의 시크릿도 같이 바꿔 주세요.
전송과 재시도
- 10초 안에 2xx로 답하면 전송에 성공한 거예요.
- 그 밖에는 — 오류 상태, 시간 초과, 연결 실패 — 간격을 늘려 가며 다시 보내요. 첫 재시도는 약 10초 뒤이고, 간격은 8시간까지 두 배씩 늘어나요. 대략 3일에 걸쳐 최대 17번 보내요.
- 마지막 시도까지 실패하면 그 전송은 버려요. webhook 주소 자체가 자동으로 꺼지지는 않아요. 엔드포인트가 불안정하다고 해서 앱이 조용히 구독 해지되지는 않아요.
- 다시 보내는 일이 있으니 핸들러는 여러 번 불려도 괜찮아야 해요. 처리하기
전에
update_id로 중복을 걸러 주세요.
⚠️ 처리하기 전에 확인하세요
내 webhook 주소로는 누구나 POST를 보낼 수 있어요. 서명 확인이 통과하기
전까지는 본문을 믿지 마세요. 주문을 보내지도, 사용자 잔액에 반영하지도,
결제됨으로 표시하지도 마세요. 안전한 순서는 서명 확인 → update_id로 중복
제거 → 처리예요.
이 문서가 도움이 됐나요?
의견 고마워요.