현금영수증은 두 가지로 발행해요. 이미 완료된 결제 건에 붙이는 결제 건 발행, 결제와 무관하게 단독으로 발행하는 별건 발행이에요. 모두 서버에서 Basic Auth로 호출해요.
발행 유형(소득공제/지출증빙)과 identity_no 규칙은 현금영수증 개요를 참고해요.
1결제 건에 발행
완료된 결제의 receipt_id에 현금영수증을 발행해요. 계좌이체·가상계좌처럼 현금성 결제를 한 뒤 영수증만 따로 발행할 때 사용해요.
https://api.bootpay.co.kr/v2/request/receipt/cash/publishBasic Auth| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
receipt_id |
String | 필수 | 현금영수증을 발행할 결제의 영수증 ID |
cash_receipt_type |
String | 선택 | 지출증빙이면 지출증빙, 그 외 모든 값은 소득공제로 처리. 생략하거나 빈 값이면 소득공제로 발행돼요 |
identity_no |
String | 필수 | 발행 식별번호 (휴대폰번호 / 주민번호 / 사업자번호) |
username |
String | 필수 | 구매자명. PG와 무관하게 비어 있으면 RC_CASH_RECEIPT_NEED_USERNAME 오류. 다만 기본 채널로 발행할 때 PG로 전달되는 구매자명은 이 값이 아니라 원 결제의 extra.username(없으면 구매자)이에요 |
phone |
String | 선택 | 구매자 전화번호. 이니시스·나이스페이먼츠는 그대로 PG로 전달돼요. 페이앱은 전화번호 자리에 identity_no를 넣어 보내므로 이 값을 쓰지 않고, 토스·기본 채널은 사용하지 않아요 |
email |
String | 선택 | 구매자 이메일. 이니시스·나이스페이먼츠·페이앱은 PG로 전달돼요. 토스·기본 채널은 사용하지 않아요 |
pg |
String | 선택 | 현금영수증을 발행할 PG를 직접 지정. 생략하면 부트페이 기본 발행 채널을 사용해요 |
test_production |
Boolean | 선택 | 샌드박스 프로젝트에서도 실제 발행을 강제할 때 true (기본 false) |
currency |
String | 선택 | 통화. 생략 시 WON |
pg 없이 부트페이 기본 발행 채널을 쓰면 발행 직전에 가맹점 정보를 검사해요. 관리자 콘솔에 사업자등록번호(하이픈 제외 10자리 숫자)·대표자명·상호가 채워져 있어야 하고, 비어 있거나 형식이 맞지 않으면 아래 오류로 거부돼요.
PROVIDER_RN_ONLY_NUMBER(389) — 사업자등록번호에 숫자가 아닌 문자가 섞임PROVIDER_RN_INVALID(390) — 하이픈을 뺀 사업자등록번호가 10자리가 아님PROVIDER_OWNER_BLANK(391) — 대표자명 미등록PROVIDER_NAME_INVALID(322) — 상호(가맹점명) 미등록
curl -X POST "https://api.bootpay.co.kr/v2/request/receipt/cash/publish" \
-H "Authorization: Basic $(printf '%s' 'CLIENT_KEY:SECRET_KEY' | base64)" \
-H "Content-Type: application/json" \
-d '{
"receipt_id": "RECEIPT_ID",
"cash_receipt_type": "소득공제",
"identity_no": "01012345678",
"username": "홍길동",
"phone": "01012345678"
}'bashimport { Bootpay } from '@bootpay/backend-js'
Bootpay.setConfiguration({
client_key: process.env.BOOTPAY_CLIENT_KEY,
secret_key: process.env.BOOTPAY_SECRET_KEY,
})
const response = await Bootpay.cashReceiptPublishOnReceipt({
receipt_id: '[ receipt_id ]',
cash_receipt_type: '소득공제', // 소득공제 | 지출증빙
identity_no: '01012345678', // 휴대폰번호 / 주민번호 / 사업자번호
username: '홍길동',
phone: '01012345678',
email: 'user@example.com',
})
console.log(response.bank_data.cash_receipt_no, response.bank_data.cash_receipt_url)javascript응답 예시
응답은 결제 건 전체 정보를 그대로 돌려주고, 현금영수증 정보는 그 안의 결제수단 블록에 담겨요.
원 결제가 계좌이체면 bank_data, 가상계좌면 vbank_data 안에 cash_receipt_* 필드가 들어가요.
{
"receipt_id": "62f356871fc192036f9f4ae2",
"order_id": "order_1700000000",
"price": 10000,
"tax_free": 0,
"cancelled_price": 0,
"cancelled_tax_free": 0,
"order_name": "테스트 결제",
"company_name": "테스트상점",
"gateway_url": "https://api.bootpay.co.kr",
"metadata": {},
"sandbox": false,
"pg": "나이스페이먼츠",
"method": "계좌이체",
"method_symbol": "bank",
"method_origin": "계좌이체",
"method_origin_symbol": "bank",
"purchased_at": "2026-04-10T15:56:08+09:00",
"requested_at": "2026-04-10T15:55:41+09:00",
"status_locale": "결제완료",
"currency": "KRW",
"receipt_url": "https://.../receipts/...",
"status": 1,
"bank_data": {
"tid": "5zJ4xY7m0kODnyRpQWGrNWja7bkR78Kwv1M9ENjbeoPaZdL6",
"bank_code": "088",
"bank_name": "신한",
"cash_receipt_tid": "AxMBvpmjnoD4yKeq5bgrp5QbZ7MqQ8GX0lzW6YOQJ1w9NLRZ",
"cash_receipt_type": 1,
"cash_receipt_no": "158190158",
"cash_receipt_url": "https://.../receipts/cash-receipt/..."
}
}json| 필드 | 설명 |
|---|---|
cash_receipt_tid |
현금영수증 발행 PG 거래 ID (취소 시 사용) |
cash_receipt_no |
발행된 현금영수증 번호 (국세청 조회용) |
cash_receipt_type |
1 소득공제 / 2 지출증빙 |
cash_receipt_url |
PG가 제공하는 현금영수증 확인 URL (일부 PG만 제공) |
2별건 발행
결제와 무관하게 현금영수증만 단독으로 발행해요. 오프라인에서 현금을 직접 수령한 경우 등에 사용해요.
https://api.bootpay.co.kr/v2/request/cash/receiptBasic Auth별건 발행은 해당 연동키에 cash_receipt_request 스코프가 부여돼 있어야 호출할 수 있어요. 없으면 API_SCOPE_INVALID (HTTP 401)로 거부돼요. 관리자 콘솔의 연동키 권한 설정에서 확인해요.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
pg |
String | 필수 | 현금영수증 발행 PG (이니시스 / 토스 / 페이앱 / 나이스페이먼츠) |
order_name |
String | 필수 | 현금영수증 주문명 |
order_id |
String | 필수 | 가맹점 고유 주문번호 |
price |
Integer | 필수 | 발행 금액 |
tax_free |
Integer | 선택 | 면세 금액 |
identity_no |
String | 필수 | 발행 식별번호 (휴대폰번호 / 주민번호 / 사업자번호) |
cash_receipt_type |
String | 선택 | 소득공제 또는 지출증빙. 생략하거나 빈 값이면 소득공제로 발행돼요 |
user.username |
String | 선택 | 구매자명. 이니시스·페이앱·나이스페이먼츠는 PG로 전달, 토스는 미사용 |
user.phone |
String | 선택 | 구매자 전화번호. 이니시스·페이앱·나이스페이먼츠는 PG로 전달, 토스는 미사용 |
user.email |
String | 선택 | 구매자 이메일. 이니시스·페이앱·나이스페이먼츠는 PG로 전달, 토스는 미사용 |
purchased_at |
String | 선택 | 현금을 수령한 시각(ISO8601 등). 생략하면 요청 시각으로 발행돼요. 파싱에 실패해도 오류 없이 요청 시각으로 대체되니 형식을 직접 검증해서 보내요. 페이앱은 이 값을 발행 거래일시로 사용해요 |
metadata |
Object | 선택 | 가맹점 커스텀 데이터. 응답의 metadata로 그대로 돌아와요 |
extra |
Object | 선택 | 부가 옵션. extra.service_price(봉사료), extra.minimum_price_limit(최소금액 검사 해제) 등 |
currency |
String | 선택 | 통화. 생략 시 WON |
curl -X POST "https://api.bootpay.co.kr/v2/request/cash/receipt" \
-H "Authorization: Basic $(printf '%s' 'CLIENT_KEY:SECRET_KEY' | base64)" \
-H "Content-Type: application/json" \
-d '{
"pg": "나이스페이먼츠",
"order_name": "현장 수령 현금결제",
"order_id": "cash_1700000000",
"price": 10000,
"tax_free": 0,
"identity_no": "01012345678",
"cash_receipt_type": "소득공제",
"user": { "username": "홍길동", "phone": "01012345678" }
}'bashconst response = await Bootpay.requestCashReceipt({
pg: '나이스페이먼츠',
order_name: '현장 수령 현금결제',
order_id: 'cash_' + Date.now(),
price: 10000,
tax_free: 0,
identity_no: '01012345678',
cash_receipt_type: '소득공제',
user: {
username: '홍길동',
phone: '01012345678',
email: 'user@example.com',
},
})
console.log(response.receipt_id, response.status) // status 60: 발행완료javascript응답 예시
{
"receipt_id": "62f356871fc192036f9f4ae2",
"order_id": "cash_1700000000",
"price": 10000,
"tax_free": 0,
"cancelled_price": 0,
"cancelled_tax_free": 0,
"order_name": "현장 수령 현금결제",
"company_name": "테스트상점",
"gateway_url": "https://api.bootpay.co.kr",
"metadata": {},
"sandbox": false,
"pg": "나이스페이먼츠",
"method": "현금영수증",
"method_symbol": "cash_receipt",
"method_origin": "현금영수증",
"method_origin_symbol": "cash_receipt",
"purchased_at": "2026-04-10T15:56:08+09:00",
"requested_at": "2026-04-10T15:56:02+09:00",
"status_locale": "현금영수증발행완료",
"currency": "KRW",
"status": 60,
"cash_receipt_data": {
"tid": "Ae75jWNka9lpP2YxJ4K87RXb62PYLrRGZwXLObgyB0vMDm1d",
"cash_receipt_type": 1,
"cash_receipt_no": "158190158",
"receipt_url": "https://.../receipts/cash-receipt/..."
}
}jsoncash_receipt_data의 필드는 다음과 같아요.
| 필드 | 설명 |
|---|---|
tid |
발행 PG 거래 ID |
cash_receipt_type |
1 소득공제 / 2 지출증빙 |
cash_receipt_no |
국세청 조회용 승인번호 |
receipt_url |
PG가 제공하는 현금영수증 확인 URL (일부 PG만 제공) |
cancel_tid |
취소 거래 ID 자리. 취소한 뒤에만 나타나고 값이 없으면 키 자체가 빠지는데, 현재 현금영수증 취소 흐름에서는 채워지지 않아요 |
토스로 별건 발행한 경우 응답의 cash_receipt_data.cash_receipt_type이 다른 PG와 달리 0(소득공제) / 1(지출증빙)로 내려와요.
게다가 이 값은 요청의 최상위 cash_receipt_type이 아니라 extra 안의 값을 기준으로 계산돼서, 최상위 파라메터로만 지출증빙을 보내면 실제 발행은 지출증빙인데 응답값은 0으로 표시돼요.
발행 유형은 응답값 대신 요청에 보낸 cash_receipt_type을 기준으로 관리해요.
별건 발행은 receipt_id를 새로 발급받아요. 이 receipt_id로 현금영수증 취소를 호출할 수 있으므로 저장해 두세요.
에러 코드
인증·권한 관련 에러는 에러 코드표를 참고해요.
| 코드 | 의미 | 대처 방법 |
|---|---|---|
RC_NOT_FOUND |
영수증 정보를 찾지 못했습니다 | receipt_id가 올바른지 확인해요 |
RC_NOT_CONFIRMED |
결제 완료 상태가 아닙니다 | 결제가 승인(status: 1)된 뒤에 발행해요 |
RC_CASH_RECEIPT_PUBLISHED (2809) |
이미 현금영수증이 발행된 건입니다 | 결제 건 상태를 먼저 조회해요 |
RC_CASH_RECEIPT_PUBLISH_UNABLE (2810) |
해당 결제의 PG로는 현금영수증을 발행할 수 없습니다 | 원 결제 PG가 이니시스·페이앱·토스·나이스페이먼츠인지 확인해요 |
RC_CASH_RECEIPT_NO_NEED_PAY_METHOD (2814) |
현금영수증 발행이 불가능한 결제수단입니다 | 계좌이체·가상계좌 결제 건에만 발행할 수 있어요. 원 결제 PG가 페이앱이면 카카오머니·네이버포인트 결제 건도 발행할 수 있어요 |
RC_CASH_RECEIPT_NEED_USERNAME (2812) |
구매자명(username)이 비어 있습니다 |
username을 함께 보내요 |
RC_CASH_RECEIPT_NEED_IDENTITY_NO (2813) |
식별번호(identity_no)가 비어 있습니다 |
identity_no를 함께 보내요 |
RC_CASH_RECEIPT_PUBLISH_FAILED (2815) |
현금영수증 발행에 실패했습니다 | identity_no 형식과 PG의 현금영수증 지원 여부를 확인해요 |
RC_CASH_RECEIPT_FAILED (2802) |
현금영수증 발행에 실패했습니다 | 별건 발행뿐 아니라 결제 건을 기본 채널(pg 생략)로 발행하다 실패한 경우에도 내려와요. message는 PG가 내려준 응답 메시지 그대로예요 |
RC_CASH_PURCHASED_AT_INVALID (2800) |
purchased_at 형식이 올바르지 않습니다 |
현재 미사용 — 파싱에 실패해도 이 오류 대신 요청 시각으로 대체 발행돼요. 원하는 발행 시각이 있다면 형식을 직접 검증해서 보내요 |
별건 발행에서 추가로 발생하는 에러
별건 발행은 영수증을 새로 만들기 때문에 결제 요청과 같은 기본 검증을 함께 거쳐요.
| 코드 | 의미 | 대처 방법 |
|---|---|---|
RC_NAME_BLANK (2003) |
상품명을 입력해주세요 | order_name을 채워 보내요 |
RC_O_ID_BLANK (2005) |
가맹점에서 식별할 수 있는 order_id 값을 입력해주세요 | order_id를 채워 보내요 |
RC_PRICE_LEAST_LT (2004) |
100원보다 큰 금액을 결제할 수 있습니다 | price를 100원 이상으로 보내요. 소액을 허용하려면 extra.minimum_price_limit: false를 함께 보내요 |
RC_PG_NOT_FOUND (2001) |
PG 정보를 찾지 못했습니다 | pg 값이 올바른 PG 이름인지 확인해요 (HTTP 404) |
RC_PM_NOT_FOUND (2002) |
결제수단 정보를 찾지 못했습니다 | 지정한 PG에 현금영수증 결제수단이 열려 있는지 확인해요 (HTTP 404) |
RC_CASH_RECEIPT_METHOD_NOT_FOUND (2803) |
요청한 PG에서는 현금영수증 발행 기능을 지원하지 않습니다 | 이니시스·토스·페이앱·나이스페이먼츠 중에서 골라요 (HTTP 404) |
RC_CASH_REQUEST_FAILED (2801) |
현금영수증 신청이 실패하였습니다 | 영수증 생성 단계에서 예기치 못한 오류가 난 경우예요. message의 실패 사유를 확인해요 |
에러 본문은 { "error_code": "...", "pg_error_code": "...", "message": "...", "payload": { ... } } 이고,
값이 없는 키는 응답에서 빠져요. error_code는 숫자가 아니라 위 표의 상수 이름 문자열이에요.
다음 단계
- 발행한 현금영수증을 취소하려면 현금영수증 취소을 확인해요.
