무료로 시작하기
한국어

개발자

직접 정산을 위해 만든 암호화폐 결제 API

인증된 요청 한 번으로 청구서를 만들면 Unheld가 결제할 주소와 정확한 금액, 그리고 기다릴 승인 깊이를 돌려줍니다. 결제는 판매자가 관리하는 지갑으로 곧바로 정산됩니다. 이 API는 자신이 보고하는 어떤 것도 결코 보관하지 않습니다.

청구서 만들기

POST 한 번입니다. 금액은 사람이 쓰는 방식 그대로 적고 서버가 해당 자산의 소수 자릿수로 환산하므로, 기본 단위로 손수 변환할 일이 없습니다.

curl -X POST https://api.unheld.io/api/v1/invoices \
  -H "Authorization: Bearer $UNHELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chainId": "11155111",
    "assetType": "native",
    "amountDecimal": "0.05",
    "expiresInSeconds": 900,
    "metadata": { "orderId": "A-1043" }
  }'

응답

{
  "id": "e2b0…",
  "status": "CREATED",
  "receiveAddress": "0x…",
  "expectedAmount": "50000000000000000",
  "minConfirmations": 3,
  "expiresAt": "2026-08-10T14:15:00.000Z",
  "instructions": {
    "chainId": "11155111",
    "to": "0x…",
    "amount": "50000000000000000",
    "assetType": "native"
  }
}

receiveAddress는 판매자의 지갑에서 파생되어 이 청구서에 묶입니다. expectedAmount는 기본 단위입니다. minConfirmations는 저희가 만들어 낸 설정이 아니라 그 체인 자체의 기준값입니다.

인증과 스코프

키는 베어러 토큰이며, 대시보드에서 만들어 한 번만 표시됩니다. 모든 키는 명시적인 스코프 목록을 지니고, 키에 해당 경로의 스코프가 없으면 요청이 거부됩니다.

사용 가능한 스코프

  • invoices:write
  • invoices:read
  • webhooks:write
  • webhooks:read
  • balances:read
  • customers:write
  • customers:read
  • subscriptions:write
  • subscriptions:read

동작하는 가장 좁은 조합만 부여하세요. 청구서만 만드는 키는 고객을 읽을 수 없고, 유출된 읽기 키는 아무것도 움직일 수 없습니다. 이 API에서는 애초에 그럴 수 있는 것이 없기 때문입니다.

검증할 수 있는 웹훅

모든 전달에는 서명이 붙습니다. 서명은 타임스탬프와 원본 본문을 점으로 이은 값을 판매자의 웹훅 비밀 키로 계산한 HMAC-SHA256이며, 16진수 다이제스트로 보내집니다.

전달 검증하기

const signature = crypto
  .createHmac("sha256", webhookSecret)
  .update(`${req.headers["x-timestamp"]}.${rawBody}`)
  .digest("hex");

crypto.timingSafeEqual(
  Buffer.from(signature),
  Buffer.from(req.headers["x-signature"])
);

다이제스트는 일정 시간 비교로 대조하고, 허용 범위를 벗어난 타임스탬프는 거부하며, X-Request-Id를 멱등 키로 다루세요. 재시도는 그 값을 그대로 쓰므로 같은 이벤트가 두 번 도착하는 것은 정상이며, 두 번 처리되어서는 안 됩니다.

청구서 이벤트

결제는 미납에서 완납으로 툭 뒤집히는 것이 아니라 여러 상태를 거쳐 움직이고, 모든 전이가 웹훅입니다. 부족 결제와 초과 결제는 오류가 아니라 그 자체로 하나의 이벤트입니다.

이벤트무슨 일이 있었는가
invoice.created청구서가 존재하고 수취 주소가 감시되고 있습니다.
invoice.receiving결제하는 거래가 체인에서 보였지만 아직 확정되지 않았습니다.
invoice.confirming결제가 승인을 쌓는 중이며 아직 체인의 기준값에 이르지 못했습니다.
invoice.paid체인이 요구하는 깊이까지 완전히 확정되었습니다. 이행해도 안전합니다.
invoice.underpaid예상보다 적게 도착했습니다. 부분 결제가 누적되므로 두 번째 송금으로 마무리할 수 있습니다.
invoice.overpaid예상보다 많이 도착했으며, 반올림해 없애지 않고 정확히 기록되었습니다.
invoice.expired완납되지 않은 채로 접수 기간이 닫혔습니다.

전체 흐름을 무료로 시험하세요

실제 자금을 쓰지 않고 청구서 생성, 감지, 승인, 웹훅을 시험할 수 있습니다. 테스트넷 토큰에는 금전적 가치가 없습니다. 테스트넷에서의 활동은 실제 결제가 아닙니다.

적용 범위

  • 자금을 결코 보관하지 않습니다. 출금할 잔액 엔드포인트가 없습니다. 결제가 판매자의 주소에 도착해 그대로 머물기 때문입니다.
  • 법정화폐로 환전하거나 정산하지 않습니다. 결제는 보내진 자산 그대로, 보내진 체인 위에 도착합니다.
  • 아직 공식 SDK가 없습니다. 여기 있는 것은 모두 평범한 HTTP이며, 저희가 형편없이 관리하게 될 패키지 대신 예제를 공개합니다.

개발자 질문

샌드박스가 있나요?

실제 자금을 쓰지 않고 청구서 생성, 감지, 승인, 웹훅을 시험할 수 있습니다. 테스트넷 토큰에는 금전적 가치가 없습니다. 테스트넷에서의 활동은 실제 결제가 아닙니다.

같은 웹훅을 두 번 처리하지 않으려면 어떻게 하나요?

X-Request-Id를 멱등 키로 쓰세요. 재시도는 같은 값을 그대로 쓰므로, 그것을 기록해 두고 반복은 무시하시면 됩니다. 재시도는 예정된 동작입니다. 실패한 전달은 버려지는 것이 아니라 정해진 일정에 따라 다시 시도됩니다.

금액을 기본 단위로 직접 변환해야 하나요?

아닙니다. amountDecimal을 사람이 쓰듯 보내면 API가 해당 자산의 소수 자릿수로 환산합니다. 응답은 expectedAmount를 기본 단위로 돌려주므로 두 표현이 모두 명시되어 서로 어긋날 수 없습니다.

예제를 가동 중인 API와 대조해 확인한 날짜 .

먼저 테스트넷을 상대로 만드세요

키를 만들어 테스트넷 체인을 가리키게 하고, 실제로 무언가 움직이기 전에 전체 흐름을 한 번 돌려 보세요.

무료로 시작하기

요금제 · 지원 네트워크와 자산. · 돈이 저희를 거쳐 가지 않습니다.