API 레퍼런스: 청구서와 환불
청구서 메서드는 createInvoice, getInvoices, deleteInvoice,
refundInvoice예요. 공통 규칙(인증, 응답 형식, 금액, spend_id)은
Merchant API 레퍼런스에 있고, 흐름을 따라가는
설명은 청구서로 결제 받기에 있어요.
createInvoice
POST /pay/api/createInvoice — invoices 권한, 분당 60회.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
currency_type | string | 아니요 | crypto(기본값) 또는 fiat |
asset | string | crypto 모드 | 청구할 자산, 예: USDT. fiat과 함께 쓸 수 없어요 |
fiat | string | fiat 모드 | 가격을 매길 법정화폐(getCurrencies에서 is_fiat인 행) |
accepted_assets | string / array | 아니요 | fiat 모드 전용. 내는 사람이 결제에 쓸 수 있는 자산이에요 — 쉼표로 이은 문자열("USDT,GRAM")이나 JSON 배열. 비워 두면 지원하는 모든 자산 |
amount | string | open_amount를 안 쓰면 필수 | 양수 소수점 문자열. crypto 모드에서는 자산 단위, fiat 모드에서는 법정화폐 단위(소수점 2자리까지) |
open_amount | boolean | 아니요 | 확장 기능, crypto 모드 전용. 금액을 정해 두지 않고 내는 사람이 결제할 때 넣어요(후원, 팁). amount와 함께 쓸 수 없어요 |
description | string | 아니요 | 1024자까지, 내는 사람에게 보여요 |
hidden_message | string | 아니요 | 2048자까지, 결제한 뒤에만 보여요 |
payload | string | 아니요 | 내가 넣는 데이터 4096자까지. 청구서와 웹훅에 그대로 돌아와요 |
allow_comments | boolean | 아니요 | 내는 사람이 코멘트를 남길 수 있게 해요(기본값 true) |
allow_anonymous | boolean | 아니요 | 내는 사람이 자기를 숨길 수 있게 해요(기본값 true) |
paid_btn_name | string | 아니요 | 결제 후 버튼: viewItem, openChannel, openBot, callback |
paid_btn_url | string | 아니요 | 버튼이 열 http(s) 주소. paid_btn_name을 쓰면 꼭 넣어야 해요 |
swap_to | string | 아니요 | 받은 결제를 이 자산으로 자동 교환해요. 가능할 때만 바꿔 주는 방식이라, 결제 시점에 교환이 안 되면 교환 없이 결제만 끝나요 |
expires_in | integer | 아니요 | 청구서가 만료되기까지의 초, 2678400(31일)까지. 생략하거나 0이면 기본 유효 기간이 적용돼요. 결과의 expiration_date를 확인하세요 |
rate_lock_seconds | integer | 아니요 | 확장 기능, fiat 모드 전용. 만들 때의 환산 시세를 이 시간 동안 묶어 둬요(아래 참고) |
설명, 결제 후 콘텐츠, 버튼은 검토 중이거나 승인되지 않으면 결제자에게 숨겨질 수 있어요. 청구서 금액은 계속 결제할 수 있어요.
결과는 — 그리고 invoice_paid 웹훅의 페이로드도 — 청구서 객체예요.
주요 필드는 이래요.
- 식별자와 상태:
invoice_id,hash(pay_url안에 들어가는 공개 id),status(active/paid/expired),pay_url— 내는 사람에게 보내는t.me링크예요(bot_invoice_url,mini_app_invoice_url,web_app_invoice_url은 같은 값의 별칭이에요). - 금액:
amount(액면 금액 — fiat 청구서면 법정화폐 단위, 아니면 코인 단위예요. 금액을 정하지 않은 청구서가 아직 결제 전이면null),amount_minor, 그리고 결제된 fiat 청구서에는paid_asset/paid_amount/paid_fiat_rate— 실제로 청구한 코인과 쓰인 시세예요. - 수수료:
fee_asset/fee_amount. 결제할 때 찍히고, 장부에 쓸 값은 이거예요(fee와usd_rate는 이제 쓰지 않는 Crypto Bot 호환 별칭이에요). 수수료와 한도를 보세요. - 결제한 사람:
paid_by_user_id(익명을 골랐으면null),paid_anonymously,comment. - 환불(확장 기능): 누적으로 쌓이는
refunded_amount/refunded_minor, 그리고 전액 환불되면 찍히는refunded_at. - 시세 고정(확장 기능):
rate_lock_until과rate_lock_rates— 자산별로 찍어 둔 시세예요. 고정을 요청하지 않았으면null이에요. - 만들 때 넣은 값:
description,hidden_message,payload,paid_btn_name/paid_btn_url,expiration_date(expires_at은 별칭), 교환 관련 필드(swap_to,is_swapped,swapped_to,swapped_rate,swapped_output, …).
오류: 400 invalid_currency(asset과 fiat을 섞어 쓴 경우),
400 invalid_amount, 400 unknown_asset, 400 unsupported_fiat,
400 paid_btn_url_required. 시세 고정을 요청했다면
400 rate_lock_fiat_only, 409 ratelock_disabled,
409 rate_unavailable(받기로 한 자산의 최신 시세가 없을 때 — 다시
시도하세요)도 있어요.
청구서는 이렇게 결제돼요
결제자는 미니 앱에서 지갑 잔액으로 즉시 결제하며 네트워크 수수료는 없어요. 충분한 잔액과 결제 확인 조건을 충족해야 해요. 외부 지갑에서 충전할 수도 있어요. 해당 옵션이 제공되면 앱이 입금 주소와 결제 기한을 표시해요. 입금은 결제자 본인의 지갑에 반영돼요. 자동 결제에는 해당 주소로의 입금, 충분한 사용 가능 잔액, 나가는 결제 제한이 없는 상태, 만료되지 않은 결제 요청과 청구서가 필요해요. 늦게 도착한 입금은 결제자의 지갑에 남으며 청구서 결제를 증명하지는 않아요. 어느 경로든 일반적인 paid 청구서가 되고, 웹훅 URL을 설정했다면 invoice_paid 이벤트가 발생해요. 별도로 처리할 파라미터나 필드는 없어요.
fiat 청구서는 결제 시점에 코인 금액을 계산하고 선택한 자산의 최소 단위로 올림해요. 그 코인 금액에서 가맹점 수수료를 차감해요. 유효한 고정 시세나 최신 시세가 없으면 오래된 시세로 처리하지 않고 결제자 쪽에서 결제가 실패해요.
fiat 청구서의 시세 고정하기
rate_lock_seconds를 넘기면 받기로 한 모든 자산의 현재 시세를 만들 때
찍어 둬요. 환산 금액이 고정된 동안 내는 사람은 찍힌 금액 그대로 보고, 결제도 그
시세로 환산돼요. 그 시간 동안의 시세 변동은 내가 떠안아요. 서버는 요청한 시간을 지원 범위에 맞춰 조정해요. 실제 만료 시각은 결과의 rate_lock_until에서 확인하세요.
시세 고정이 끝나도 청구서가 만료되지 않았다면 결제 시점의 시세로 결제할 수 있어요. 두 기한을 맞추려면 지원되는 고정 시간 범위 안에서 expires_in을 설정하고 응답의 expiration_date와 rate_lock_until을 비교하세요.
getInvoices
GET /pay/api/getInvoices — read 권한. 필터: asset, fiat,
invoice_ids(쉼표로 구분), status(active / paid / expired —
expired는 확장 기능이고, active에는 기한이 지난 청구서가 빠져요), 그리고
offset / count. {"items": [invoice, …]}를 최신순으로 돌려줘요.
deleteInvoice
POST /pay/api/deleteInvoice — invoices 권한. 파라미터는 invoice_id
하나예요. 아직 결제되지 않은 청구서를 취소하고 true를 돌려줘요. 오류:
404 invoice_not_found, 409 invoice_already_paid — 이미 결제된 청구서는 돈이
움직인 뒤라 지울 수 없어요.
refundInvoice
POST /pay/api/refundInvoice — refunds 권한, 분당 30회. Crypto Bot에는 없는
확장 기능이에요. 실제로 결제한 코인 금액의 전부 또는 일부를 앱 잔액에서
결제한 사람에게 돌려줘요. 익명으로 결제한 사람에게도 보낼 수 있고, 누구인지는
드러나지 않아요. fiat 청구서에서는 paid_asset과 paid_amount를 사용해요. 환불할 때 법정화폐를 새로 환산하지 않아요.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
invoice_id | integer | 예 | 결제된 청구서 |
amount | string | 아니요 | 원래 결제 자산(fiat 청구서는 paid_asset)으로 돌려줄 코인 금액. 생략하면 아직 환불하지 않은 나머지 전액이에요. 부분 환불은 원래 결제한 코인 금액까지 누적돼요 |
spend_id | string | 아니요 | 멱등키 — 응답이 늦어 다시 보내도 두 번 환불되지 않게 하나 넣어 주세요 |
결과는 누적 refunded_amount / refunded_minor가 반영된 청구서 객체예요.
전액 환불되면 refunded_at이 찍혀요. 상태는 paid 그대로예요. 서비스
수수료는 돌려주지 않아요. swap_to로 다른 자산으로 교환했더라도 앱에 원래 결제 자산의 잔액이 충분해야 해요. 환불할 때마다 원할 때만 켜는 refund_completed
웹훅이 발생해요.
오류: 404 invoice_not_found, 409 invoice_not_paid,
409 already_refunded(더 환불할 게 없을 때), 409 amount_too_big(남은
금액보다 클 때), 409 insufficient_funds, 400 invalid_amount, 그리고
spend_id 관련 오류인 409 idempotency_conflict / 409 idempotency_in_progress.
이 문서가 도움이 됐나요?
의견 고마워요.