Merchant API 레퍼런스
모든 메서드가 함께 쓰는 규칙과, 읽기 전용 조회 메서드예요. 메서드별 문서는 청구서와 환불, 송금과 송금 링크, 구독, 웹훅에 있어요.
여기 설명한 메서드는 Crypto Bot 규칙을 따라요. 기본 URL과 토큰을 바꾼 뒤 연동에서 사용하는 메서드를 테스트넷에서 테스트하세요. 그 규격을 넘어서는 것은 아래에서 확장 기능이라고 표시해 뒀어요.
기계가 읽을 수 있는 OpenAPI 3.1 명세도 제공해요. crypto-pay-openapi.yaml · crypto-pay-openapi.json. 코드 생성기나 API 클라이언트, AI 도구에 사용할 수 있지만 메서드 문서도 확인하세요. 최신 확장 기능은 스키마에 아직 반영되지 않았을 수 있어요.
기본 URL과 인증
모든 메서드는 https://app.tgpaycrypto.com/pay/api/<methodName>에 있어요.
테스트넷에서는 기본 주소가 https://testnet.tgpaycrypto.com/pay/api예요.
앱과 토큰이 따로 있고, 테스트 코인에는 실제 가치가 없어요.
요청마다 TgPayCrypto-API-Token 헤더에 토큰을 담아 인증해요(TgCryptoPay-API-Token은 2026-09-07 전까지
쓰던 이름이고, Crypto-Pay-API-Token과 함께 호환용 별칭으로 받아 줘요.
여러 개를 보내면 정식 헤더를 우선해요). 토큰이 없거나 잘못됐거나 폐기됐을 때,
권한 제한 토큰이 만료됐거나 앱이 삭제됐거나 소유자가 차단된 경우에도 401 unauthorized가 돌아와요.
토큰은 <app_id>:<secret> 모양이고, 만들거나 새로 발급할 때 한 번만 보여
줘요. 서버에는 해시만 남아요.
개발자로 시작하기를 보세요.
토큰과 권한
인증하는 방식이 똑같은 두 가지 열쇠가 있어요.
- 기본 토큰 — 모든 메서드를 쓸 수 있고, 웹훅에 서명하는 유일한 열쇠예요.
- 권한 제한 토큰(확장 기능) — 앱마다 폐기하지 않은 토큰을 최대 10개까지 둘 수 있어요. 만료된 토큰도 포함해요. 더보기 → Merchant API에서 앱을 고르고 접근 권한과 토큰을 열어요. 권한 제한 토큰에서 이름과 권한을 정해 만들어요. 하나를 폐기해도 나머지는 그대로고, 기본 토큰을 새로 발급해도 이 토큰들은 건드리지 않아요. 만료일을 정할 수도 있으며 만료 후에는 인증에 쓸 수 없어요.
| 권한 | 쓸 수 있는 메서드 |
|---|---|
| (아무 유효한 토큰) | getMe, getCurrencies, getExchangeRates |
read | getBalance, getStats, getInvoices, getChecks, getTransfers, getSubscriptionPlans, getSubscriptions |
invoices | createInvoice, deleteInvoice |
refunds | refundInvoice |
payouts | transfer, transferBatch |
checks | createCheck, deleteCheck |
subscriptions | createSubscriptionPlan, archiveSubscriptionPlan, cancelSubscription |
webhooks | updateApp |
권한은 각각 독립적이고 필요한 것만 골라 줄 수 있어요. invoices가 있다고 read도 부여되지는 않아요. 청구서만 만드는 서버에는 잔액이나 결제 내역을 조회할 수 없는 토큰을 줄 수 있어요.
토큰에 없는 권한의 메서드를 부르면 403 scope_required가 돌아와요. getMe는
지금 쓰는 토큰의 scopes(기본 토큰이면 null)와 token_name을 알려 주니까,
현재 토큰의 권한을 언제든 확인할 수 있어요.
지급 모드
권한은 토큰이 어떤 메서드를 부를 수 있는지 정하고, 앱의 지급 모드는 기본
토큰까지 포함해 그 앱의 토큰이 누구에게 지급할 수 있는지 정해요. 새 앱은
만든 순간부터 누구에게나 지급할 수 있어요. 소유자는
더보기 → Merchant API에서 앱을 연 다음 지급 설정 → API로 지급에서 이 범위를 좁힐 수 있어요.
일일 지급 한도와 마찬가지로, 토큰이 새어 나갔을 때를 대비한 안전장치예요. 다시
넓히려면 PIN이나 생체 인증이 필요하고, API로는 읽을 수만
있어요(getMe → payout_mode).
payout_mode | transfer, transferBatch, createCheck가 할 수 있는 일 |
|---|---|
any | 아무 사용자에게나 지급하고, 아직 앱을 쓰지 않는 @username 앞으로는 지급을 에스크로에 맡겨 두고, 아무나 받을 수 있는 송금 링크를 만들 수 있어요. 기본값이고, Crypto Bot과 같은 동작이에요 |
allowlist | 목록에 있는 Telegram 사용자에게만 지급해요. 송금 링크의 지정 수신자나 명단의 모든 수신자가 허용 목록에 있어야 해요. 그 외 수신자, 알 수 없는 @username, 누구나 받을 수 있는 링크에는 403 recipient_not_allowed가 돌아와요 |
off | 아무것도 못 해요. 지급 호출은 전부 403 payouts_disabled가 돌아오고, 청구서, 구독, 환불, 웹훅은 평소처럼 돌아가요 |
지급이 거절되면 소유자에게 Telegram으로 알려 드리고, 소유자는 모드를 바꿀 수 있어요. 일일 지급 한도는 모드와 별개로 함께 걸려요. 지급을 무엇에 쓸 수 있는지는 Merchant API 이용약관에 정해져 있어요.
요청과 응답
- **읽기 메서드는
GET**이고 파라미터는 쿼리 문자열로 보내요. 돈이 움직이는 메서드는POST만 받아요. 파라미터는 JSON 본문, form-urlencoded, 쿼리 파라미터로 보낼 수 있고(겹치면 본문을 우선해요),multipart/form-data는 받지 않아요. - API 처리기가 보내는 응답은 같은 형식의 JSON이에요. 성공은
{"ok": true, "result": …}, 오류는{"ok": false, "error": {"code": <HTTP status>, "name": "<error_name>"}}. 분기는error.name으로 하세요. 기계가 읽으라고 고정해 둔 문자열이에요. - GET 메서드의 쿼리 값이 잘못되면(예:
offset=abc) API 처리기에 도달하기 전에 요청이 거절될 수 있어요. HTTP 상태를 확인하고, 모든 오류 응답이 위 형식이라고 가정하지 마세요. 쿼리 값은 타입에 맞게 보내 주세요.
금액
- 코인 금액은 코인 단위의 소수점 문자열(
"10.5")이에요. JSON 숫자로는 절대 오지 않아요. 응답에는amount_minor(확장 기능)도 같이 오는데, 최소 단위 정수를 문자열로 담은 값이에요. wei 단위의 큰 정수는 JavaScriptNumber로 정확하게 표현할 수 있는 범위를 넘을 수 있기 때문이에요. - 자산별
decimals는getCurrencies에서 가져와요. 금액 계산은 코드에 박지 말고 여기에 맞춰 주세요. - 법정화폐 금액은 소수점 2자리까지예요.
- 시세는 고정소수점 문자열이고, 숫자가 아니에요.
페이지 나누기
목록 메서드(getInvoices, getChecks, getTransfers,
getSubscriptions)는 offset(기본값 0)과 count(기본값 100, 최대 1000.
getSubscriptions는 최대 500)를 받고 {"items": […]}를 최신순으로 돌려줘요.
ID 필터(invoice_ids, check_ids, transfer_ids)는 쉼표로 이은 정수
목록이에요. 시각은 어디서나 ISO 8601 문자열이에요.
멱등키: spend_id
돈이 움직이는 메서드는 호출하는 쪽이 만든 spend_id(1~64자)를 받아요.
transfer와 transferBatch의 항목마다 꼭 있어야 하고, createCheck와
refundInvoice에서는 선택이에요(확장 기능이지만 넣는 게 좋아요). 같은 키에
같은 파라미터로 다시 보내면 돈이 두 번 나가는 대신 원래 결과가 그대로
돌아와요. 그래서 응답이 늦은 요청은 언제든 다시 보내도 안전해요. 같은 키에
다른 파라미터를 보내면 409 idempotency_conflict가, 원래 요청이 아직 도는
중에 다시 보내면 409 idempotency_in_progress가 돌아와요.
호출 한도
앱 단위로 걸리고, 그 앱의 모든 토큰이 함께 써요. 넘으면
429 rate_limited예요.
| 메서드 | 한도 |
|---|---|
createInvoice, createCheck | 분당 60회 |
refundInvoice | 분당 30회 |
transfer | 분당 30회 |
transferBatch | 분당 10회 |
updateApp | 분당 10회 |
createSubscriptionPlan | 분당 20회 |
추가 요청 한도가 적용될 수 있어요. 429 rate_limited가 반환되면 재시도 간격을 늘리세요. 플랫폼 점검 중에는 쓰기 메서드가
503 maintenance를 돌려주고, 읽기는 그대로 돌아가요.
조회와 계정 메서드
getMe
GET /pay/api/getMe — 파라미터 없음. 앱 정보를 돌려줘요. app_id, name, payment_processing_bot_username, webhook_url, webhook_events(앱이
켜 둔 확장 웹훅 종류), 위에서 설명한 토큰 확인용 필드 scopes / token_name, 그리고 payout_mode(off / allowlist / any), daily_payout_max_usd_cents(0이면 소유자가 정한 한도 없음), daily_payout_spent_usd_cents(최근 24시간 사용액)가 있어요.
updateApp
POST /pay/api/updateApp — webhooks 권한. 선택 파라미터는 name(1~64자), webhook_url(공개 HTTPS URL, 빈 문자열이면 삭제), webhook_events(지원하는 확장 이벤트 이름의 배열이나 쉼표로 구분한 목록)예요. 빈 이벤트 목록은 invoice_paid만 남겨요. getMe와 같은 앱 필드를 반환해요.
getBalance
GET /pay/api/getBalance — 파라미터 없음. 지원하는 자산마다 한 줄씩, 잔액이 없어도
빠짐없이 돌려줘요. currency_code, available(쓸 수 있는 잔액),
onhold(미수령 송금 링크와 대기 중인 @username 지급에 보류된 자산), amount_minor예요.
getCurrencies
GET /pay/api/getCurrencies — 파라미터 없음. API가 무엇을 지원하는지 알려
주는 기준 목록이에요. 코인 행(is_blockchain: true)과 청구서 가격을 매길 수
있는 법정화폐 행(is_fiat: true)이 있어요. 각 행에는 code, name,
decimals, 그리고 is_stablecoin 표시가 들어 있어요.
getExchangeRates
GET /pay/api/getExchangeRates — 파라미터 없음. 코인을 법정화폐로 환산한
시세예요. source, target, rate(고정소수점 문자열), 그리고 is_valid가
있어요. 시세는 참고용으로 보세요. is_valid: true라고 해서 최신 시세가 보장되지는 않아요. 시세를 가져올 수 없으면 빈 배열일 수 있어요. 실제 결제를 대조하고 정산할 때는 청구서에 기록된 금액과 시세를 사용하세요.
getStats
GET /pay/api/getStats — start_at / end_at은 선택이에요(ISO 8601. 기본
구간은 최근 24시간). volume(해당 기간에 결제된 청구서를 현재 시세로 환산한 USD 추정치. 시세가 없는 자산은 0으로 계산),
conversion(결제/생성 비율, 퍼센트), unique_users_count,
created_invoice_count, paid_invoice_count, 그리고 실제로 쓰인 구간의 시작과
끝을 돌려줘요. 날짜를 읽지 못하면 400 invalid_date가 돌아와요.
어떤 메서드에서나 나올 수 있는 오류
| HTTP | error.name | 언제 |
|---|---|---|
| 401 | unauthorized | 토큰이 없거나 잘못됐거나 폐기됨 |
| 403 | scope_required | 토큰에 그 메서드의 권한이 없음 |
| 403 | payouts_disabled | 앱의 지급 모드가 off임(지급 메서드만) |
| 403 | recipient_not_allowed | 받는 사람이 앱의 받는 사람 목록에 없음(지급 메서드만) |
| 400 | invalid_request | 파라미터를 읽지 못하거나 값이 잘못됨(POST) |
| 403 | daily_cap_exceeded | 앱의 일일 지급 한도 초과 |
| 403 | verification_required | 소유자의 나가는 거래 제한. 송금, 송금 링크, 환불에 적용 |
| 503 | merchant_payouts_paused | 앱 잔액 차감이 일시 중단됨 |
| 429 | rate_limited | 호출 한도를 넘음 |
| 503 | maintenance | 플랫폼 점검 중(쓰기 메서드) |
| 500 | internal_error | 예상 못 한 서버 오류 |
메서드마다 따로 나오는 오류는 각 메서드 문서에 적혀 있어요.
JSON 형식이 아닌 오류
오류 응답이 일반 텍스트나 HTML이라는 사실만으로는 어느 단계에서 발생했는지 알 수 없어요. HTTP 상태, 응답 본문, 응답 헤더를 확인해 주세요.
Cloudflare가 error code: 1010을 반환하면 요청이 API에 도달하기 전에 브라우저 서명을 기준으로 클라이언트가 거부된 거예요. 이 Cloudflare 오류의 원인은 토큰 권한이 아니에요. User-Agent가 없거나 일반적이지 않으면 네트워크 보호가 작동할 수 있어요. MyShop/1.0처럼 클라이언트를 식별할 수 있는 User-Agent 헤더를 보내고 다시 요청해 보세요. 다만 이 방법으로 해결된다고 보장할 수는 없어요. 오류가 계속되면 토큰과 쿠키를 제거한 뒤 HTTP 상태, 응답 본문, 응답 헤더를 고객센터로 보내 주세요.
이 문서가 도움이 됐나요?
의견 고마워요.