1 개요

Webhook 이벤트 시스템에 대한 소개입니다.

CRYPTOMENTS는 입금 확정, 출금 상태 변경 등 주요 이벤트가 발생할 때 파트너에게 설정된 Webhook URL로 알림을 전송합니다. 이를 통해 파트너는 자신의 시스템과 CRYPTOMENTS를 동기화할 수 있습니다.

핵심 특징

  • 평면(flat) JSON: 모든 필드는 최상위에 위치합니다. data 래퍼가 없습니다.
  • HMAC-SHA256 서명: 페이로드 안의 signature 필드(hex 소문자 64자)로 검증합니다.
  • 자동 재시도: 전송 실패 시 최대 5회까지 자동 재시도됩니다. (5장 참조)
  • 필드 표기: 모든 키는 camelCase 입니다. (snake_case 아님)

요청 모델

CRYPTOMENTS의 Webhook 요청은 다음과 같은 구조를 따릅니다:

  • 방법: HTTP POST
  • Content-Type: application/json
  • 본문: 평면 JSON 오브젝트 1개
  • 커스텀 헤더: 없음 — 서명·타임스탬프·이벤트 타입은 모두 본문 필드로 전달됩니다.
  • 성공 응답: HTTP 2xx (본문은 무시됩니다)
⚠️
구버전 문서와의 차이: 이전 문서에 있던 event_id / event_type / data 중첩 봉투와 X-Signature·X-Timestamp·X-Event-Id 헤더는 실제 구현에 존재하지 않습니다. 현재 구현은 평면 JSON + 본문 signature 필드입니다.
ℹ️
팁: 서버는 Webhook 요청을 즉시 응답(HTTP 200)하고, 이벤트 처리는 백그라운드 작업으로 수행하는 것을 권장합니다. 응답이 지연되면 타임아웃으로 처리되어 재시도 대상이 됩니다.

2 설정

Webhook URL 설정 및 관리 방법입니다.

Webhook URL 설정

파트너 콘솔에서 Webhook 설정을 할 수 있습니다.

1
파트너 콘솔 접속

파트너 콘솔에 로그인합니다.

2
설정 메뉴 이동

설정 연동 설정로 이동합니다.

3
Webhook 탭 선택

Webhook 탭에서 콜백 URL을 입력합니다.

4
테스트 전송

"테스트 전송" 버튼을 클릭하여 정상 수신 여부를 확인합니다. 테스트 페이로드는 {"event":"TEST","message":"Webhook test from Cryptoments"} 이며 서명이 포함되지 않습니다 — 연결성 확인 전용입니다.

⚠️
중요: Webhook URL은 반드시 HTTPS이어야 합니다. HTTP는 보안상 지원되지 않습니다.

IP 화이트리스트 (선택사항)

추가 보안을 위해 CRYPTOMENTS의 Webhook 발신 서버 IP를 방화벽 화이트리스트에 등록할 수 있습니다. 발신 IP 목록은 지원팀에 문의하세요.

3 인증

Webhook 요청 서명 검증 방법입니다.

서명 검증 (HMAC-SHA256)

CRYPTOMENTS는 모든 Webhook 페이로드에 HMAC-SHA256 서명을 포함합니다. 서명은 HTTP 헤더가 아니라 본문(JSON)의 signature 필드로 전달됩니다. 수신 서버는 반드시 서명을 검증하여 요청의 출처와 무결성을 확인해야 합니다.

검증 알고리즘

signatureData = partnerId + "|" + transactionHash + "|" + amount + "|" + timestamp
signature     = HMAC-SHA256(apiSecret, signatureData)  → hex 소문자 64자
구성 요소
서명 대상 문자열 partnerId|transactionHash|amount|timestamp (파이프 | 구분자, 4개 값 모두 수신한 페이로드의 값 그대로)
파트너 API Secret (파트너 콘솔에서 발급받은 값)
알고리즘 / 인코딩 HMAC-SHA256 / hex (소문자, 64자) — Base64 아님
전달 위치 페이로드 최상위 signature 필드
⚠️
transactionHash 가 없을 때: 온체인 TX가 아직 없는 이벤트(출금 요청/승인, 정산 입금 등)에서는 transactionHash빈 문자열 "" 로 실립니다. 서명 계산에도 동일하게 "" 가 사용되므로, 파트너는 수신한 필드를 그대로 이어붙이면 항상 서명을 재현할 수 있습니다. (null 로 바꾸거나 생략하면 안 됩니다.)

서명 관련 페이로드 필드

필드 설명 예시
partnerId 파트너 ID (문자열) "7"
transactionHash 온체인 TX 해시. 없으면 빈 문자열 "0xabc..." / ""
amount 거래 수량 (문자열, plain 표기) "1000.000000"
timestamp 발송 시각 Unix 타임스탬프(초, 문자열) "1754800000"
signature 위 4개로 계산한 HMAC-SHA256 hex "9f2c...e41a"

검증 구현 (JavaScript)

JavaScript
const crypto = require('crypto');

app.post('/webhook', (req, res) => {
  const p = req.body;                       // 평면 JSON (data 래퍼 없음)
  const secret = process.env.CRYPTOMENTS_API_SECRET;

  // 서명 대상: partnerId|transactionHash|amount|timestamp
  const data = [p.partnerId, p.transactionHash, p.amount, p.timestamp].join('|');
  const computed = crypto
    .createHmac('sha256', secret)
    .update(data)
    .digest('hex');              // ← hex (Base64 아님)

  // 타이밍 공격 방지 비교
  const ok = computed.length === (p.signature || '').length &&
    crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(p.signature));

  if (!ok) {
    return res.status(401).json({ error: 'Signature mismatch' });
  }

  // eventType 으로 분기 (DEPOSIT_CONFIRMED, WITHDRAWAL_* ...)
  enqueue(p);
  res.status(200).json({ success: true });
});

검증 구현 (Python)

Python
import hmac
import hashlib
from flask import Flask, request

app = Flask(__name__)
API_SECRET = 'your_partner_api_secret'

@app.route('/webhook', methods=['POST'])
def webhook():
    p = request.get_json()          # 평면 JSON

    # transactionHash 는 없을 때 "" 로 오므로 그대로 사용한다
    data = '|'.join([
        p['partnerId'], p['transactionHash'],
        p['amount'], p['timestamp']
    ])
    computed = hmac.new(
        API_SECRET.encode(),
        data.encode(),
        hashlib.sha256
    ).hexdigest()          # ← hex

    if not hmac.compare_digest(computed, p.get('signature', '')):
        return {'error': 'Signature mismatch'}, 401

    return {'success': True}, 200

검증 구현 (Java)

Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Map;

@PostMapping("/webhook")
public ResponseEntity<?> receiveWebhook(@RequestBody Map<String, Object> p) {
  try {
    String secret = System.getenv("CRYPTOMENTS_API_SECRET");

    String data = p.get("partnerId") + "|" + p.get("transactionHash")
                + "|" + p.get("amount") + "|" + p.get("timestamp");

    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(
      secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));

    // hex 소문자 인코딩 — Base64 아님
    StringBuilder hex = new StringBuilder();
    for (byte b : mac.doFinal(data.getBytes(StandardCharsets.UTF_8))) {
      hex.append(String.format("%02x", b));
    }

    if (!hex.toString().equals(p.get("signature"))) {
      return ResponseEntity.status(401).build();
    }

    return ResponseEntity.ok().build();
  } catch (Exception e) {
    return ResponseEntity.status(500).build();
  }
}
🔒
보안: API Secret은 환경 변수에 저장하고 절대 소스 코드에 하드코딩하지 마세요. Secret이 노출되면 즉시 재발급하세요.

4 이벤트 타입

Webhook으로 전송되는 이벤트는 입금 1종 + 출금 9종입니다.

이벤트 목록

eventType 분류 발생 시점
DEPOSIT_CONFIRMED 입금 입금이 확정되었을 때 (HD/외부지갑/소수점 매칭, 원화결제 정산 입금 포함)
WITHDRAWAL_REQUESTED 출금 출금 요청이 접수되었을 때
WITHDRAWAL_APPROVED 출금 출금이 승인되었을 때 (자동/수동)
WITHDRAWAL_REJECTED 출금 관리자가 출금을 거부했을 때
WITHDRAWAL_CANCELLED 출금 출금이 취소되었을 때 (파트너 취소 / 관리자 취소)
WITHDRAWAL_P2P_PENDING 출금 출금이 P2P(원화 매칭) 경로로 전환되어 매칭 대기에 들어갔을 때
WITHDRAWAL_CONFIRMED 출금 출금 TX가 블록체인에서 확인되었을 때
WITHDRAWAL_FAILED 출금 출금 처리가 실패했을 때
WITHDRAWAL_EXHAUSTED 출금 재시도 상한을 초과하여 더 이상 진행하지 않고 종결됐을 때
WITHDRAWAL_COMPLETED 출금 P2P 전환 출금의 잔여 처리까지 끝나 최종 종결됐을 때
⚠️
주의: WITHDRAWAL_CONFIRM(끝에 D 없음)은 내부 분류기용 타입이며 파트너 이벤트가 아닙니다. 파트너가 수신하는 출금 이벤트는 위 9종뿐입니다.

페이로드 샘플

아래 이벤트 카드를 클릭하여 JSON 페이로드를 확인하세요. 모든 페이로드는 평면 구조이며 키는 camelCase 입니다.

입금 DEPOSIT_CONFIRMED
입금이 블록체인에서 확인되었습니다.
JSON 페이로드:
{{ JSON.stringify(events.deposit, null, 2) }}
필드 설명:
  • transactionId — CRYPTOMENTS 내부 입금 ID (숫자)
  • partnerId — 파트너 ID (문자열)
  • userId — 파트너 유저 ID
  • orderId — 파트너 주문 ID. 입금 예약/원화결제/Axim/결제 세션에서 파트너가 넘긴 값. 없으면 null
  • orderCode — CRYPTOMENTS 내부 주문 코드. 없으면 null
  • transactionHash — 온체인 TX 해시. 온체인 TX가 없는 정산 입금은 빈 문자열 ""
  • reservedAmount / reservedAmountKrw — 입금 예약 금액(예상). 실제 amount와 비교해 부분/초과 입금을 판별할 수 있습니다. 예약이 없으면 null
  • depositMethod — 입금 방식 (아래 표 참조)
  • tokenKrwPrice / tokenUsdPrice — 적용 단가. 거래 성립 시점의 확정 환율이 우선 사용됩니다
  • krwAmount / usdAmount — 입금액의 KRW/USD 환산 금액
  • timestamp — 발송 시각 Unix 타임스탬프(초, 문자열) — 서명 대상
  • signature — HMAC-SHA256 서명 hex (3장 참조)
depositMethod 값:
  • HD_WALLET — HD 파생 지갑 주소로 입금
  • EXTERNAL_WALLET — Axim 등 외부 연결 지갑에서 입금
  • DECIMAL_MATCH — 소수점 매칭 방식 입금
  • DIRECT — 파트너 직접 충전
  • MANUAL — 관리자 수동 처리
입금 페이로드에는 event, withdrawalId, metadata, feeAmount 키가 없습니다.
출금 WITHDRAWAL_CONFIRMED
출금 TX가 블록체인에서 확인되었습니다.
JSON 페이로드:
{{ JSON.stringify(events.withdrawal, null, 2) }}
필드 설명:
  • eventType / event — 같은 값입니다. event 는 하위 호환용 중복 키
  • transactionId / withdrawalId — 같은 값(내부 출금 ID)입니다. withdrawalId 는 하위 호환용 중복 키
  • fromAddress — 출금 원천인 파트너 MASTER 지갑 주소. MASTER 가 없는 파트너는 null
  • feeAmount현재 항상 "0" (실제 수수료가 아직 반영되지 않음)
  • status — 출금 상태값 (예: CONFIRMED)
출금 WITHDRAWAL_REQUESTED
출금 요청이 접수되었습니다. (온체인 TX 이전 — transactionHash 는 빈 문자열)
JSON 페이로드:
{{ JSON.stringify(events.requested, null, 2) }}
이 샘플의 특징:
  • transactionHash"" — 서명 계산에도 동일하게 "" 가 쓰입니다
  • confirmedAt 은 아직 null
  • orderId / metadata 는 출금 요청 시 파트너가 보냈을 때만 실립니다. 보내지 않으면 값이 null 입니다
이 페이로드 형태는 WITHDRAWAL_APPROVED·WITHDRAWAL_REJECTED·WITHDRAWAL_CANCELLED·WITHDRAWAL_P2P_PENDING·WITHDRAWAL_EXHAUSTED·WITHDRAWAL_COMPLETED 에도 동일하게 적용됩니다 — 키 구성은 같고 eventType / event / status 값만 달라집니다.
실패 WITHDRAWAL_FAILED
출금 처리가 실패했습니다.
JSON 페이로드:
{{ JSON.stringify(events.failed, null, 2) }}
주의:
  • 페이로드 키 구성은 다른 출금 이벤트와 동일합니다. eventType/eventWITHDRAWAL_FAILED, statusFAILED 로 옵니다
  • 실패 사유(reason) 필드는 페이로드에 포함되지 않습니다. 상세 사유는 파트너 콘솔에서 확인하세요
  • 실패 후 재시도로 상태가 다시 진행될 수 있으며, 재시도 상한을 넘기면 WITHDRAWAL_EXHAUSTED 가 발송됩니다

출금 페이로드 필드 (공통)

모든 출금 이벤트는 아래 키를 이 순서로 담은 평면 JSON 입니다.

필드 타입 설명
eventTypestring이벤트 타입 (9종)
eventstring하위호환 eventType 과 같은 값
withdrawalIdnumber하위호환 transactionId 와 같은 값
transactionIdnumber내부 출금 ID
partnerIdstring파트너 ID
userIdstring파트너 유저 ID
orderIdstring | null출금 요청 시 파트너가 보낸 주문 ID. 보내지 않았으면 null
metadatastring | null파트너가 보낸 JSON 문자열을 그대로 반환(pass-through). 서명 대상 아님
transactionHashstring온체인 TX 해시. 없으면 ""
fromAddressstring | null파트너 MASTER 지갑 주소. MASTER 가 없으면 null
toAddressstring | null수신 주소
amountstring출금 수량 — 서명 대상
currencyTypestringUSDT, USDC 등
chainTypestringBSC, ETHEREUM, POLYGON, TRON 등
statusstring출금 상태값
confirmedAtstring | nullyyyy-MM-dd'T'HH:mm:ss. 미확정이면 null
feeAmountstring현재 항상 "0"
timestampstring발송 시각 Unix 타임스탬프(초) — 서명 대상
signaturestringHMAC-SHA256 hex (소문자 64자)
tokenKrwPrice / tokenUsdPricestring적용 단가
krwAmount / usdAmountstringKRW/USD 환산 금액
⚠️
페이로드에 없는 필드: withdrawalCode, withdrawal_id, withdrawal_code, chain_type, failure_reasonsnake_case 필드는 존재하지 않습니다. 출금 건 식별은 transactionId(내부 ID) 또는 파트너가 보낸 orderId 로 하세요.

5 재시도 정책

Webhook 전송 실패 시 자동 재시도 방식입니다.

이벤트가 발생하면 전송 큐에 적재되고, 전송 배치가 10초 주기로 이를 발송합니다. 2xx 응답을 받지 못하면(타임아웃·연결 실패·4xx/5xx) 최대 5회까지 재시도하며, 재시도 간격은 아래와 같이 증가합니다.

재시도 일정

1
최초 전송
2
30초 후
3
1분 후
4
5분 후
5
15분 후
6
1시간 후

재시도 대기 건은 1분 주기 배치가 전송 대상으로 되돌리므로, 실제 발송 시각은 위 간격 + 최대 1분 정도 늦어질 수 있습니다.

🔁
재시도 본문은 최초 전송과 완전히 동일합니다. 저장된 페이로드를 그대로 다시 보내므로 timestampsignature 도 바뀌지 않습니다. 즉 같은 본문이 여러 번 도착할 수 있습니다 — 6장 멱등 처리를 반드시 구현하세요.

재시도 후 실패 처리

재시도를 모두 소진하면 해당 전송 건은 FAILED 상태로 기록됩니다. 파트너 콘솔에서 실패 건을 확인하고 재전송을 요청할 수 있습니다.

💡
권장: 서버 점검이나 배포 시간을 고려하여 Webhook 처리 시간을 설정하세요. 정기적인 인프라 점검 일정을 CRYPTOMENTS 지원팀에 공지하면 그 기간에 재시도를 일시 중단할 수 있습니다.

6 멱등성

중복 처리 방지 방법입니다.

재시도나 네트워크 지연으로 동일한 이벤트가 여러 번 전달될 수 있습니다. 페이로드에는 event_id 같은 전용 이벤트 식별자가 없으므로, 수신 서버는 아래 조합을 멱등 키로 사용하세요.

권장 멱등 키
  • 기본: eventType + transactionId — 같은 거래의 같은 이벤트는 1회만 처리
  • 더 엄격하게: eventType + transactionId + timestamp — 재시도 본문은 timestamp·signature까지 동일하므로 완전 중복을 잡아냅니다
  • 파트너 주문 기준 대사: orderId (파트너가 요청 시 보낸 경우에만 존재)

멱등성 구현

수신 서버는 다음과 같이 멱등성을 보장해야 합니다:

1
멱등 키 저장

처리한 모든 이벤트의 (eventType, transactionId)를 UNIQUE 제약과 함께 저장합니다.

2
중복 확인

Webhook 수신 시 먼저 해당 키가 이미 처리된 것인지 확인합니다.

3
중복 시 응답

이미 처리된 이벤트라면 즉시 HTTP 200으로 응답합니다. (에러가 아님 — 에러로 응답하면 재시도가 계속됩니다)

멱등성 구현 예시 (JavaScript)

JavaScript
app.post('/webhook', async (req, res) => {
  const p = req.body;                        // 평면 JSON
  const key = `${p.eventType}:${p.transactionId}`;

  try {
    // 1. 서명 검증 (3장 참조)
    if (!verifySignature(p)) {
      return res.status(401).json({ error: 'Signature mismatch' });
    }

    // 2. 멱등 키 중복 확인
    const existing = await db.webhookEvents.findOne({ event_key: key });
    if (existing) {
      return res.status(200).json({
        success: true,
        message: 'Event already processed'
      });
    }

    // 3. 이벤트 처리 — 페이로드 최상위 필드를 그대로 사용
    await processEvent(p);

    // 4. 멱등 키 저장
    await db.webhookEvents.insertOne({
      event_key: key,
      event_type: p.eventType,
      transaction_id: p.transactionId,
      processed_at: new Date()
    });

    res.status(200).json({ success: true });
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: 'Internal error' });
  }
});
데이터베이스 추천 인덱스:
SQL
CREATE UNIQUE INDEX idx_event_key ON webhook_events(event_type, transaction_id);

7 수신 서버 구현

Webhook 수신 서버 구현 가이드입니다.

모범 사례

  • 빠른 응답: Webhook 요청을 받으면 즉시 HTTP 200으로 응답하고, 실제 처리는 백그라운드 작업(큐, 메시지 브로커)으로 수행합니다.
  • 서명 검증: 모든 요청의 서명을 검증하여 출처를 확인합니다.
  • 멱등성: event_id를 사용하여 중복 처리를 방지합니다.
  • 로깅: 모든 Webhook 수신, 처리 결과를 기록합니다.
  • 에러 처리: 처리 실패 시 재시도 가능하도록 설계합니다.

Node.js (Express) 예시

JavaScript
const express = require('express');
const crypto = require('crypto');
const Bull = require('bull');

const app = express();
const webhookQueue = new Bull('webhooks');

app.use(express.json());

// Webhook 핸들러
app.post('/webhook', async (req, res) => {
  const signature = req.headers['x-signature'];
  const timestamp = req.headers['x-timestamp'];
  const eventId = req.headers['x-event-id'];
  const body = JSON.stringify(req.body);

  // 1. 서명 검증
  const secret = process.env.WEBHOOK_SECRET;
  const data = timestamp + '.' + body;
  const computed = crypto
    .createHmac('sha256', secret)
    .update(data)
    .digest('hex');

  if (computed !== signature) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  // 2. 중복 확인
  const exists = await db.webhookEvents.findOne({ event_id: eventId });
  if (exists) {
    return res.status(200).json({ success: true });
  }

  // 3. 큐에 추가
  await webhookQueue.add(req.body, {
    attempts: 3,
    backoff: { type: 'exponential', delay: 2000 }
  });

  // 4. 즉시 응답
  res.status(200).json({ success: true });
});

// 백그라운드 작업
webhookQueue.process(async (job) => {
  const { data } = job;
  const eventId = data.event_id;

  try {
    // 이벤트 타입별 처리
    switch (data.event_type) {
      case 'DEPOSIT_CONFIRMED':
        await handleDepositConfirmed(data.data);
        break;
      case 'WITHDRAWAL_CONFIRMED':
        await handleWithdrawalConfirmed(data.data);
        break;
      // ... 다른 이벤트 타입
    }

    // 이벤트 ID 저장
    await db.webhookEvents.insertOne({
      event_id: eventId,
      event_type: data.event_type,
      processed_at: new Date()
    });
  } catch (error) {
    console.error('Webhook processing error:', error);
    throw error; // 재시도 트리거
  }
});

app.listen(3000);

Python (Flask) 예시

Python
from flask import Flask, request, jsonify
from celery import Celery
import hmac
import hashlib
import json

app = Flask(__name__)
celery = Celery(app.name, broker='redis://localhost:6379')

WEBHOOK_SECRET = 'your_webhook_secret'

@app.route('/webhook', methods=['POST'])
def receive_webhook():
    signature = request.headers.get('X-Signature')
    timestamp = request.headers.get('X-Timestamp')
    event_id = request.headers.get('X-Event-Id')
    body = request.get_data(as_text=True)

    # 1. 서명 검증
    data = timestamp + '.' + body
    computed = hmac.new(
        WEBHOOK_SECRET.encode(),
        data.encode(),
        hashlib.sha256
    ).hexdigest()

    if computed != signature:
        return jsonify({'error': 'Unauthorized'}), 401

    # 2. 중복 확인
    if db.webhook_events.find_one({'event_id': event_id}):
        return jsonify({'success': True}), 200

    # 3. 비동기 처리
    webhook_data = json.loads(body)
    process_webhook.delay(webhook_data)

    # 4. 즉시 응답
    return jsonify({'success': True}), 200

@celery.task
def process_webhook(webhook_data):
    event_type = webhook_data['event_type']
    event_id = webhook_data['event_id']

    try:
        if event_type == 'DEPOSIT_CONFIRMED':
            handle_deposit_confirmed(webhook_data['data'])
        elif event_type == 'WITHDRAWAL_CONFIRMED':
            handle_withdrawal_confirmed(webhook_data['data'])

        # 이벤트 ID 저장
        db.webhook_events.insert_one({
            'event_id': event_id,
            'event_type': event_type,
            'processed_at': datetime.utcnow()
        })
    except Exception as e:
        print(f'Error processing webhook: {e}')
        raise

if __name__ == '__main__':
    app.run()

8 트러블슈팅

일반적인 문제와 해결 방법입니다.

자주 발생하는 문제

문제 원인 해결 방법
Webhook 수신 안 됨 URL 설정 오류, 네트워크 방화벽 1. URL이 HTTPS인지 확인
2. 콘솔의 "테스트 전송"으로 연결성 확인
3. 방화벽 설정 검토
서명 검증 실패 잘못된 Secret, 바뀐 타임스탬프 1. Webhook Secret 재확인
2. 서명 생성 시 정확한 timestamp 사용
3. request body 인코딩 확인
타임아웃 (5초 초과) 서버 응답 지연, 무거운 처리 1. 즉시 HTTP 200 응답 후 비동기 처리
2. 동기 처리 로직 제거
3. 데이터베이스 쿼리 최적화
SSL/TLS 오류 인증서 만료, 자체 서명 인증서 1. SSL 인증서 유효성 확인
2. 인증서 갱신
3. 신뢰할 수 있는 CA 사용
중복 처리 멱등성 미구현 1. event_id 저장 로직 추가
2. 중복 이벤트 ID 확인 로직 구현
3. 데이터베이스 트랜잭션 사용
콘솔에 실패 표시 6회 재시도 모두 실패 1. 서버 상태 확인
2. 콘솔에서 "수동 재전송" 클릭
3. 로그 분석

디버깅 팁

🔍
서명 검증 디버깅: 수신한 timestamp, signature, body를 로그에 기록하고 로컬 환경에서 서명을 재생성하여 비교해보세요.

모니터링

Webhook 안정성을 위해 다음을 모니터링하세요:

  • 응답 시간: 평균 응답 시간이 5초 이상인 경우 조사
  • 에러율: HTTP 5xx 에러 증가 추적
  • 재시도 횟수: 재시도 비율이 높으면 서버 점검
  • 이벤트 지연: 이벤트 생성 시간과 처리 시간의 차이 모니터링
⚠️
중요: 정기적인 서버 점검이나 배포가 필요하면 사전에 CRYPTOMENTS 지원팀에 알려주세요. 재시도 일정을 조정할 수 있습니다.

문제가 있으신가요? 지원팀에 문의하세요.

CRYPTOMENTS Webhook v2.0 © 2026