tgpay cryptoAPI
crypto-payapiwebhookssignature

API 레퍼런스: webhook

읽는 데 2분마지막 수정 2026년 8월 11일

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 주소를 저장해야 나타나고, 위 표의 식별자가 그대로 이름으로 적혀 있어요.

서명 확인하기

전송마다 TgCryptoPay-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["TgCryptoPay-API-Signature"])

받은 원본 바이트로 확인해야 해요. 다시 직렬화한 값은 바이트가 달라질 수 있어서 검증이 실패해요. 서명하는 건 기본 토큰뿐이고, 권한 제한 토큰은 서명하지 않아요. 기본 토큰을 새로 발급하면 webhook 서명 키도 그 순간 바뀌니까, 서버의 시크릿도 같이 바꿔 주세요.

전송과 재시도

  • 10초 안에 2xx로 답하면 전송에 성공한 거예요.
  • 그 밖에는 — 오류 상태, 시간 초과, 연결 실패 — 간격을 늘려 가며 다시 보내요. 첫 재시도는 약 10초 뒤이고, 간격은 8시간까지 두 배씩 늘어나요. 대략 3일에 걸쳐 최대 17번 보내요.
  • 마지막 시도까지 실패하면 그 전송은 버려요. webhook 주소 자체가 자동으로 꺼지지는 않아요. 엔드포인트가 불안정하다고 해서 앱이 조용히 구독 해지되지는 않아요.
  • 다시 보내는 일이 있으니 핸들러는 여러 번 불려도 괜찮아야 해요. 처리하기 전에 update_id로 중복을 걸러 주세요.

⚠️ 처리하기 전에 확인하세요

내 webhook 주소로는 누구나 POST를 보낼 수 있어요. 서명 확인이 통과하기 전까지는 본문을 믿지 마세요. 주문을 보내지도, 사용자 잔액에 반영하지도, 결제됨으로 표시하지도 마세요. 안전한 순서는 서명 확인 → update_id로 중복 제거 → 처리예요.