Fiatside

Developers

웹훅

웹훅은 주문 상태가 변경되었음을 알려줍니다. 주문 읽기를 대체하지 않습니다: 알림일 뿐이며 진실의 원천이 아닙니다.

게시된 계약, 아직 공개되지 않음

웹훅은 현재 발송되지 않습니다. 모양, 서명 및 재시도 정책은 여기에 확정되어 게시되므로 수신기를 사전에 작성하고 테스트할 수 있습니다.

01

이벤트

모든 이벤트는 동일한 봉투를 전달합니다: id, type, timestamp 및 data 객체. 처리하는 유형을 구독하고 나머지는 조용히 무시하십시오 — 새 유형이 추가될 것이며, 알 수 없는 유형에서 실패하는 수신기는 스스로를 망가뜨립니다.

이벤트
유형의미
order.created주문이 생성되고 환율이 고정되었습니다. 입금 주소가 할당되었습니다.
order.deposit_detected수신 거래가 네트워크에서 확인 전에 감지되었습니다. 이 이벤트를 사용하여 사용자를 안심시키고, 어떤 것도 전달하지 마십시오.
order.confirming확인 카운터가 진행 중입니다. 각 블록이 아닌 각 단계에서 발생합니다.
order.deposit_confirmed네트워크에서 요구하는 확인 횟수에 도달했습니다.
order.underpaid수신 금액이 허용 오차를 초과하여 예상 금액보다 적습니다. 주문은 손실되지 않습니다: 추가 입금, 수신 금액으로 계속 진행 또는 환불 중 선택을 기다립니다.
order.overpaid수신 금액이 예상 금액을 초과합니다. 초기 금액은 고정 환율로 유지됩니다. 초과분은 별도로 처리됩니다.
order.payout_sent송금이 결제 수단을 통해 출발했습니다. 주의: 보냄은 수신이 아닙니다 — 남은 지연은 수취인 은행에 따라 다릅니다.
order.completed결제 수단에 의해 결제가 확인되었습니다. 최종 상태입니다.
order.payout_failed결제 수단이 송금을 거부하거나 반환했으며, 그 이유가 있습니다. 은행 반환에 대해 자동 재시도는 이루어지지 않습니다: 원인은 거의 항상 세부 사항에 있습니다.
order.refunded자금이 반환되었으며, 네트워크 수수료를 제외하고 원래 주소로만 반환됩니다.
02

페이로드

유형에 관계없이 일정한 봉투입니다.

예시json
{
  "id": "evt_01J9ZQF3K8N2M4X7",
  "type": "order.payout_sent",
  "created": 1788356981,
  "data": {
    "reference": "K7Q4-M2XB",
    "state": "PAYOUT_SENT",
    "railId": "sepa_instant",
    "netMinor": 91228,
    "currency": "EUR",
    "sentAt": "2026-09-02T12:21:44Z"
  }
}
03

서명

모든 전달에는 서명 헤더가 포함됩니다: 타임스탬프와 해당 타임스탬프, 점, 원시 요청 본문에 대해 계산된 HMAC-SHA256.

Fiatside-Signaturehttp
Fiatside-Signature: t=<timestamp unix>,v1=<hex hmac-sha256>
검증, Node.jstypescript
import { createHmac, timingSafeEqual } from 'node:crypto'

// IMPORTANT : le corps doit etre le corps BRUT, avant tout parsing JSON.
// Re-serialiser l'objet change l'ordre des cles et invalide la signature.
export function verify(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=') as [string, string]))
  const timestamp = Number(parts['t'])
  const signature = parts['v1']
  if (!timestamp || !signature) return false

  // Rejeu : une signature valide capturee hier ne doit pas etre rejouable aujourd'hui.
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false

  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')
  const a = Buffer.from(expected, 'hex')
  const b = Buffer.from(signature, 'hex')
  // Comparaison a temps constant : un === laisse fuiter la signature octet par octet.
  return a.length === b.length && timingSafeEqual(a, b)
}
  • 파싱 전에 원시 본문에 서명하십시오. JSON 객체를 다시 직렬화하면 키 순서와 공백이 변경되어 서명이 더 이상 일치하지 않으며 오랫동안 찾게 될 것입니다.
  • 일정한 시간에 비교하십시오. === 비교는 첫 번째 다른 바이트에서 중지되므로 공격자가 예상 서명을 바이트 단위로 측정할 수 있습니다.
  • 초 허용 오차 창을 초과하면 거부하십시오. 이 검사가 없으면 어제 캡처된 유효한 요청이 오늘도 재생 가능합니다.300-초 허용 오차 창을 초과하면 거부하십시오. 이 검사가 없으면 어제 캡처된 유효한 요청이 오늘도 재생 가능합니다.
  • 처리 전에 200으로 응답하십시오. 이벤트를 대기열에 넣고 나중에 처리하십시오: 긴 처리는 시간 초과를 유발하여 이벤트를 수신했더라도 재시도를 초래합니다.
04

재시도 및 순서

2xx가 아닌 상태 또는 응답 없음은 아래 일정에 따라 재시도를 트리거합니다.

재시도 및 순서
시도지연
1즉시
230 s
32 min
410 min
51 h
66 h
  • 도착 순서는 보장되지 않습니다. 지급 이벤트가 논리적으로 앞서는 확인 이벤트보다 먼저 도착할 수 있습니다: 수신 순서가 아닌 주문 상태를 신뢰하십시오.
  • 같은 이벤트가 두 번 도착할 수 있습니다. 이벤트 ID를 저장하고 이미 처리된 ID는 무시하십시오. 수신 측의 멱등성은 귀하의 책임이며, 이를 구현하는 것은 쉽습니다.
  • 페이로드의 금액을 신뢰하여 귀하 측에서 어떤 것으로도 적립하지 마십시오. API를 통해 주문을 다시 읽으십시오. 페이로드는 무언가 발생했음을 말하고, API는 정확히 무엇이 발생했는지를 말합니다.
  • 마지막 시도 후 이벤트는 삭제되고 이벤트 로그에 계속 표시됩니다. 수동으로 다시 재생할 수 있습니다.