Webhooks
Webhook을 설정하면 결제 완료, 정산 완료 이벤트를 실시간으로 받을 수 있습니다.
Setup
- Volr Dashboard 로그인
- 프로젝트 선택 → Settings → Payments
- Webhook URL 입력 (예:
https://api.yourapp.com/webhooks/volr) - 저장
팁
Webhook Secret은 자동으로 생성됩니다. 서명 검증에 사용하세요.
Events
| Event | Description |
|---|---|
checkout.paid | 결제 완료 (정상 결제) |
checkout.late_paid | 만료 후 결제됨 |
checkout.settled | 정산 완료 |
Payload Format
{
"event": "checkout.paid",
"data": {
"checkoutId": "cm5xyz123...",
"projectId": "project_id",
"status": "PAID",
"referenceId": "ORDER-12345",
"chainId": 8453,
"tokenAddress": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"amount": "3703703",
"depositAddress": "0xabc123...",
"fiatAmount": "5000",
"fiatCurrency": "KRW",
"exchangeRateUsed": "1350.50",
"exchangeRateAt": "2026-01-30T10:30:00.000Z",
"itemName": "아메리카노",
"itemDescription": null,
"customerEmail": null,
"customerName": null,
"paymentTxHash": "0xdef456...",
"paidAmount": "3703703",
"paidAt": "2026-01-30T10:35:00.000Z",
"createdAt": "2026-01-30T10:30:00.000Z",
"expiresAt": "2026-01-30T11:00:00.000Z",
"metadata": {
"customField": "value"
}
},
"timestamp": "2026-01-30T10:35:01.000Z"
}
Signature Verification
Webhook 요청은 X-Volr-Signature 헤더로 서명됩니다. 반드시 검증하세요.
Headers
| Header | Description |
|---|---|
X-Volr-Signature | HMAC-SHA256 서명 |
X-Volr-Event | 이벤트 타입 |
Content-Type | application/json |
Node.js Example
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhooks/volr', express.json(), (req, res) => {
const signature = req.headers['x-volr-signature'];
const webhookSecret = process.env.VOLR_WEBHOOK_SECRET;
if (!verifyWebhookSignature(req.body, signature, webhookSecret)) {
console.error('Invalid webhook signature');
return res.status(401).send('Invalid signature');
}
const { event, data } = req.body;
switch (event) {
case 'checkout.paid':
handlePaymentSuccess(data);
break;
case 'checkout.late_paid':
handleLatePayment(data);
break;
case 'checkout.settled':
handleSettlementComplete(data);
break;
}
res.status(200).send('OK');
});
Python Example
import hmac
import hashlib
import json
from flask import Flask, request
app = Flask(__name__)
def verify_signature(payload: dict, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
json.dumps(payload, separators=(',', ':')).encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.route('/webhooks/volr', methods=['POST'])
def webhook():
signature = request.headers.get('X-Volr-Signature')
webhook_secret = os.environ['VOLR_WEBHOOK_SECRET']
if not verify_signature(request.json, signature, webhook_secret):
return 'Invalid signature', 401
event = request.json['event']
data = request.json['data']
if event == 'checkout.paid':
handle_payment_success(data)
return 'OK', 200
Retry Policy
Webhook 전송 실패 시 재시도:
- 즉시 재시도: 1회
- 1분 후: 1회
- 5분 후: 1회
- 15분 후: 1회
총 4회 재시도 후 포기합니다.
Best Practices
1. Idempotency
동일한 이벤트가 여러 번 전송될 수 있으므로, checkoutId로 중복 처리를 방지하세요.
const processedCheckouts = new Set();
function handlePaymentSuccess(data) {
if (processedCheckouts.has(data.checkoutId)) {
console.log('Already processed:', data.checkoutId);
return;
}
processedCheckouts.add(data.checkoutId);
// ... 주 문 처리
}
2. Quick Response
Webhook 핸들러는 5초 이내에 200 응답을 반환해야 합니다. 무거운 작업은 백그라운드로 처리하세요.
app.post('/webhooks/volr', (req, res) => {
// 즉시 응답
res.status(200).send('OK');
// 백그라운드 처리
processWebhookAsync(req.body);
});
3. Fallback: Polling
Webhook이 실패할 수 있으므로, 폴링으로 백업하세요.
// 30초마다 PENDING 상태 checkout 확인
setInterval(async () => {
const pendingOrders = await getPendingOrders();
for (const order of pendingOrders) {
const checkout = await fetchCheckoutStatus(order.checkoutId);
if (checkout.status === 'PAID') {
handlePaymentSuccess(checkout);
}
}
}, 30000);
Testing Webhooks
개발 환경에서 webhook을 테스트하려면 ngrok을 사용하세요.
# Local server: http://localhost:3000
ngrok http 3000
# ngrok URL을 Dashboard Webhook URL로 설정
# https://abc123.ngrok.io/webhooks/volr