메인 콘텐츠로 건너뛰기
웹훅은 일이 생긴 순간 바로 나가지 않고 발송 대기에 쌓였다가 발송 작업이 보냅니다. 발송 작업은 30초 간격으로 돌며 한 번에 최대 100건을 보냅니다. 그래서 일이 생긴 뒤 수신 서버에 닿기까지 수십 초가 걸릴 수 있고, 대기 중인 전송이 많으면 더 늦어집니다.

성공과 실패의 기준

응답 본문과 응답 헤더는 읽지 않습니다. 성공인지 실패인지는 상태 코드만으로 정합니다. 서명 검증에 실패한 요청에 400을 돌려주면 그 전송도 실패로 기록되어 다시 옵니다.

시간 제한

수신 서버는 서명을 확인하고 중복을 거른 다음 바로 2xx로 응답하고, 실제 처리는 큐나 작업 테이블에 넘겨 나중에 하세요. 처리를 다 끝낸 뒤 응답하면 시간 제한에 걸려 같은 웹훅을 다시 받게 됩니다.

자동 재전송

실패하면 아래 간격을 두고 다시 보냅니다. 처음 시도를 포함해 모두 6번 시도합니다. 간격이 지난 뒤 다음 발송 작업이 보내므로 실제 간격은 표보다 조금 깁니다. 6번째 시도까지 실패하면 그 전송은 ‘발송 실패’(failed)로 끝나고 더는 자동으로 보내지 않습니다. 처음 시도에서 마지막 시도까지 10시간 남짓 걸리므로, 수신 서버를 그보다 오래 멈출 일이 있으면 다시 켠 뒤 실패한 전송을 수동으로 다시 보내야 합니다. 다음 두 경우는 횟수가 남아 있어도 바로 ‘발송 실패’로 끝납니다.
  • 보낼 때 수신 주소가 공개되지 않은 주소로 풀린 경우(blocked_address)
  • 수신 주소의 발송을 일시 중지했거나 보관한 경우(endpoint_disabled)
다시 보낼 때마다 webhook-timestamp와 webhook-signature는 새로 만들고, webhook-id와 본문은 처음과 같습니다.

실패 사유

전송 기록의 failure_reason과 콘솔 ‘실패 사유’ 열의 값입니다. secret_unavailable 실패가 계속되면 MeterEngine 소개의 문의 절에 적힌 방법으로 알려 주세요.

전송 기록 보기

콘솔에서는 ‘API 키와 통보’ 화면에서 수신 주소의 ‘발송 내역과 설정’을 엽니다. ‘통보 발송 내역’ 화면에 최근 것부터 종류, 상태, 시도 수, 수신 응답, 실패 사유, 다음 발송이 나옵니다. API로는 read 권한 키로 GET /v1/webhook-endpoints/{id}/deliveries를 부릅니다.
curl
200 OK
예시의 attempts는 줄였습니다. 실제로는 시도마다 한 항목이 오래된 것부터 실립니다. webhook_event_id는 수신 서버가 받은 webhook-id 헤더 값과 같으므로, 수신 서버 로그와 맞춰 볼 때 이 값을 씁니다. 전송 기록과 시도 기록을 지우는 API는 없고, 수신 주소를 보관해도 기록은 남습니다.

실패한 전송 다시 보내기

‘발송 실패’로 끝난 전송만 다시 보낼 수 있습니다. 다시 보내면 시도 횟수가 처음부터 다시 세어져 위의 자동 재전송 일정이 처음부터 적용되고, 이전 시도 기록은 그대로 남습니다.
1

실패 원인을 고친다

‘실패 사유’와 ‘수신 응답’을 보고 수신 서버나 비밀값, DNS를 먼저 고칩니다. 고치지 않고 다시 보내면 같은 이유로 실패합니다.
2

수신 주소가 '발송 중'인지 확인한다

일시 중지했거나 보관한 수신 주소의 전송은 다시 보낼 수 없습니다. 보관했다면 복원하고, ‘발송 상태’를 ‘발송 사용’으로 바꿉니다.
3

다시 보낸다

콘솔에서는 ‘통보 발송 내역’의 해당 행에서 ‘다시 보내기’를 누릅니다. API로는 admin 권한 키로 POST /v1/webhook-endpoints/{id}/deliveries/{delivery_id}/retries를 부릅니다(본문 없음). 성공하면 202 Accepted와 빈 본문이 옵니다.
curl
4

결과를 확인한다

202는 발송 대기에 다시 넣었다는 뜻입니다. 잠시 뒤 전송 기록에서 ‘발송 완료’로 바뀌었는지 봅니다.
다시 보낸 웹훅은 처음과 같은 webhook-id와 본문으로 갑니다. 수신 서버는 이미 처리한 웹훅을 한 번 더 받을 수 있다고 보고 아래처럼 중복을 걸러야 합니다.

일시 중지와 보관의 영향

수신 주소의 발송을 일시 중지하거나 보관하면, 그동안 생긴 일은 그 수신 주소의 발송 대기에 아예 쌓이지 않습니다. 나중에 ‘발송 사용’으로 되돌리거나 복원해도 그 사이의 웹훅은 보내지 않고, 다시 보낼 전송 기록도 없습니다. 그 사이의 결제 결과는 API로 조회해 맞추세요.
  • 일시 중지나 보관 전에 이미 대기 중이던 전송은 다음 시도 때 endpoint_disabled로 ‘발송 실패’가 됩니다. 이 전송은 ‘발송 사용’으로 되돌린 뒤 수동으로 다시 보낼 수 있습니다.
  • 잠깐 수신 서버를 내려야 한다면 일시 중지하지 말고 그대로 두세요. 자동 재전송이 10시간 남짓 동안 다시 보내고, 그 뒤에 실패한 것은 수동으로 다시 보내면 됩니다.

중복 거르기

같은 웹훅이 두 번 이상 올 수 있습니다.
  • 수신 서버는 처리했지만 응답이 시간 제한을 넘겨 MeterEngine이 실패로 보고 다시 보낸 경우
  • 수신 서버의 응답이 네트워크에서 사라진 경우
  • 수동으로 다시 보낸 경우
  • 같은 URL을 수신 주소 둘로 등록한 경우(비밀값을 바꾸는 동안 등). 수신 주소가 여럿이어도 한 웹훅의 webhook-id는 같습니다
webhook-id는 웹훅 하나에 하나이고 다시 보내도 바뀌지 않습니다. 처리한 webhook-id를 저장하고, 이미 있는 값이면 처리하지 않고 2xx로 응답하세요.
1

처리 기록 테이블을 둔다

webhook-id에 유일 제약을 건 테이블을 만듭니다.
2

처리와 기록을 한 트랜잭션에 묶는다

webhook-id를 넣는 일과 도입사 시스템을 바꾸는 일을 같은 트랜잭션에서 합니다. 유일 제약 위반이 나면 이미 처리한 웹훅이니 2xx로 응답하고 끝냅니다.
unbilled_usage_detected는 매일 새 webhook-id로 오므로 이 방법으로는 반복이 걸러지지 않습니다. 웹훅 이벤트 유형과 data를 보세요.

순서

웹훅이 일이 생긴 순서대로 도착한다고 기대하지 마세요. 앞선 웹훅이 실패해 재전송을 기다리는 동안 뒤의 웹훅이 먼저 도착할 수 있습니다. 예를 들어 같은 인보이스의 payment_failed보다 다음 시도의 payment_succeeded가 먼저 올 수 있습니다.
  • 본문의 timestamp는 웹훅을 만든 시각이라 순서를 짐작하는 데는 쓸 수 있지만, 최종 상태를 정하는 데 쓰지 마세요.
  • 결제 웹훅을 받으면 GET /v1/invoice-payments?month=YYYY-MM나 GET /v1/invoices/{id}/payment-attempts로 지금 상태를 다시 읽고 그 값으로 갱신하세요. 결과 확인 방법은 첫 결제 요청과 결과 확인을 보세요.