tgpay cryptoAPI
crypto-payapiinvoicesrefunds

API 레퍼런스: 청구서와 환불

읽는 데 4분마지막 수정 2026년 8월 22일

청구서 메서드는 createInvoice, getInvoices, deleteInvoice, refundInvoice예요. 공통 규칙(인증, 응답 형식, 금액, spend_id)은 Merchant API 레퍼런스에 있고, 흐름을 따라가는 설명은 청구서로 결제 받기에 있어요.

createInvoice

POST /pay/api/createInvoiceinvoices 권한, 분당 60회.

파라미터타입필수설명
currency_typestring아니요crypto(기본값) 또는 fiat
assetstringcrypto 모드청구할 자산, 예: USDT. fiat과 함께 쓸 수 없어요
fiatstringfiat 모드가격을 매길 법정화폐(getCurrencies에서 is_fiat인 행)
accepted_assetsstring / array아니요fiat 모드 전용. 내는 사람이 결제에 쓸 수 있는 자산이에요 — 쉼표로 이은 문자열("USDT,GRAM")이나 JSON 배열. 비워 두면 지원하는 모든 자산
amountstringopen_amount를 안 쓰면 필수양수 소수점 문자열. crypto 모드에서는 자산 단위, fiat 모드에서는 법정화폐 단위(소수점 2자리까지)
open_amountboolean아니요확장 기능, crypto 모드 전용. 금액을 정해 두지 않고 내는 사람이 결제할 때 넣어요(후원, 팁). amount와 함께 쓸 수 없어요
descriptionstring아니요1024자까지, 내는 사람에게 보여요
hidden_messagestring아니요2048자까지, 결제한 뒤에만 보여요
payloadstring아니요내가 넣는 데이터 4096자까지. 청구서와 webhook에 그대로 돌아와요
allow_commentsboolean아니요내는 사람이 코멘트를 남길 수 있게 해요(기본값 true)
allow_anonymousboolean아니요내는 사람이 자기를 숨길 수 있게 해요(기본값 true)
paid_btn_namestring아니요결제 후 버튼: viewItem, openChannel, openBot, callback
paid_btn_urlstring아니요버튼이 열 http(s) 주소. paid_btn_name을 쓰면 꼭 넣어야 해요
swap_tostring아니요받은 결제를 이 자산으로 자동 교환해요. 가능할 때만 바꿔 주는 방식이라, 결제 시점에 교환이 안 되면 교환 없이 결제만 끝나요
expires_ininteger아니요청구서가 만료되기까지의 초, 2678400(31일)까지. 안 넣거나 0이면 만료 없음
rate_lock_secondsinteger아니요확장 기능, fiat 모드 전용. 만들 때의 환산 시세를 이 시간 동안 묶어 둬요(아래 참고)

결과는 — 그리고 invoice_paid webhook의 페이로드도 — 청구서 객체예요. 주요 필드는 이래요.

  • 식별자와 상태: 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. 결제할 때 찍히고, 장부에 쓸 값은 이거예요(feeusd_rate는 이제 쓰지 않는 Crypto Bot 호환 별칭이에요). 수수료와 한도를 보세요.
  • 결제한 사람: paid_by_user_id(익명을 골랐으면 null), paid_anonymously, comment.
  • 환불(확장 기능): 누적으로 쌓이는 refunded_amount / refunded_minor, 그리고 전액 환불되면 찍히는 refunded_at.
  • 시세 고정(확장 기능): rate_lock_untilrate_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, 404 unknown_asset, 400 unsupported_fiat, 400 paid_btn_url_required. 시세 고정을 요청했다면 400 rate_lock_fiat_only, 409 ratelock_disabled, 409 rate_unavailable(받기로 한 자산의 최신 시세가 없을 때 — 다시 시도하세요)도 있어요.

청구서는 이렇게 결제돼요

내는 사람은 미니 앱에서 지갑 잔액으로 결제해요. 바로 끝나고 네트워크 수수료도 없어요. 외부 지갑에서 입금해 결제할 수도 있어요. 앱이 입금 주소를 보여 주고, 그 사람 지갑으로 입금이 들어오면 청구서가 알아서 결제돼요. 어느 쪽이든 내 쪽에서 보이는 건 똑같아요. 평범한 paid 청구서와 invoice_paid webhook뿐이고, 따로 처리할 파라미터나 필드는 없어요.

fiat 청구서는 결제할 때 코인 금액을 계산하고, 받는 쪽에 유리하게 올림해요. 그래서 법정화폐 액면가보다 적게 받는 일은 없어요. 최신 시세를 가져오지 못하면 오래된 시세로 처리하는 대신, 내는 쪽에서 결제가 실패해요.

fiat 청구서의 시세 고정하기

rate_lock_seconds를 넘기면 받기로 한 모든 자산의 현재 시세를 만들 때 찍어 둬요. 고정이 살아 있는 동안 내는 사람은 찍힌 금액 그대로 보고, 결제도 그 시세로 환산돼요. 그 시간 동안의 시세 변동은 내가 떠안아요. 서버는 이 구간을 60초부터 플랫폼 최대치(지금은 15분)까지로 맞춰요.

고정이 풀려도 청구서는 그대로 결제할 수 있고, 조용히 결제 시점 환산으로 돌아가요. 견적과 함께 청구서도 끝나길 원한다면 expires_in에 같은 값을 넣어 주세요.

getInvoices

GET /pay/api/getInvoicesread 권한. 필터: asset, fiat, invoice_ids(쉼표로 구분), status(active / paid / expiredexpired는 확장 기능이고, active에는 기한이 지난 청구서가 빠져요), 그리고 offset / count. {"items": [invoice, …]}를 최신순으로 돌려줘요.

deleteInvoice

POST /pay/api/deleteInvoiceinvoices 권한. 파라미터는 invoice_id 하나예요. 아직 결제되지 않은 청구서를 취소하고 true를 돌려줘요. 오류: 404 invoice_not_found, 409 invoice_already_paid — 이미 결제된 청구서는 돈이 움직인 뒤라 지울 수 없어요.

refundInvoice

POST /pay/api/refundInvoicerefunds 권한, 분당 30회. Crypto Bot에는 없는 확장 기능이에요. 결제된 청구서의 액면 금액을, 또는 그중 일부를 앱 잔액에서 결제한 사람에게 돌려줘요. 익명으로 결제한 사람에게도 보낼 수 있고, 누구인지는 드러나지 않아요.

파라미터타입필수설명
invoice_idinteger결제된 청구서
amountstring아니요돌려줄 금액, 청구서의 자산 단위예요. 비워 두면 아직 환불하지 않은 나머지 전부. 부분 환불은 액면 금액까지 쌓여요
spend_idstring아니요멱등키 — 응답이 늦어 다시 보내도 두 번 환불되지 않게 하나 넣어 주세요

결과는 누적 refunded_amount / refunded_minor가 반영된 청구서 객체예요. 전액 환불되면 refunded_at이 찍혀요. 상태는 paid 그대로예요. 서비스 수수료는 돌려주지 않아요. 환불할 때마다 원할 때만 켜는 refund_completed webhook이 발생해요.

오류: 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.