@bolta-io/cli 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CLAUDE.md +67 -5
  3. package/README.md +59 -8
  4. package/dist/bin/bolta.js +5 -1
  5. package/dist/bin/bolta.js.map +1 -1
  6. package/dist/src/client/bolta-client.d.ts +7 -1
  7. package/dist/src/client/bolta-client.d.ts.map +1 -1
  8. package/dist/src/client/bolta-client.js +110 -6
  9. package/dist/src/client/bolta-client.js.map +1 -1
  10. package/dist/src/commands/bank-account-holder/check-bulk.d.ts +4 -0
  11. package/dist/src/commands/bank-account-holder/check-bulk.d.ts.map +1 -0
  12. package/dist/src/commands/bank-account-holder/check-bulk.js +26 -0
  13. package/dist/src/commands/bank-account-holder/check-bulk.js.map +1 -0
  14. package/dist/src/commands/bank-account-holder/check.d.ts +7 -0
  15. package/dist/src/commands/bank-account-holder/check.d.ts.map +1 -0
  16. package/dist/src/commands/bank-account-holder/check.js +25 -0
  17. package/dist/src/commands/bank-account-holder/check.js.map +1 -0
  18. package/dist/src/commands/bank-account-holder/index.d.ts +3 -0
  19. package/dist/src/commands/bank-account-holder/index.d.ts.map +1 -0
  20. package/dist/src/commands/bank-account-holder/index.js +49 -0
  21. package/dist/src/commands/bank-account-holder/index.js.map +1 -0
  22. package/dist/src/commands/document/get.d.ts +3 -0
  23. package/dist/src/commands/document/get.d.ts.map +1 -0
  24. package/dist/src/commands/document/get.js +24 -0
  25. package/dist/src/commands/document/get.js.map +1 -0
  26. package/dist/src/commands/document/index.d.ts +3 -0
  27. package/dist/src/commands/document/index.d.ts.map +1 -0
  28. package/dist/src/commands/document/index.js +40 -0
  29. package/dist/src/commands/document/index.js.map +1 -0
  30. package/dist/src/commands/document/issue.d.ts +8 -0
  31. package/dist/src/commands/document/issue.d.ts.map +1 -0
  32. package/dist/src/commands/document/issue.js +28 -0
  33. package/dist/src/commands/document/issue.js.map +1 -0
  34. package/dist/src/commands/examples/index.d.ts.map +1 -1
  35. package/dist/src/commands/examples/index.js +50 -0
  36. package/dist/src/commands/examples/index.js.map +1 -1
  37. package/dist/src/commands/guide/index.d.ts.map +1 -1
  38. package/dist/src/commands/guide/index.js +174 -3
  39. package/dist/src/commands/guide/index.js.map +1 -1
  40. package/dist/src/commands/invoice/reference-id.js +2 -2
  41. package/dist/src/commands/invoice/reference-id.js.map +1 -1
  42. package/dist/src/commands/invoice/status.js +2 -2
  43. package/dist/src/commands/invoice/status.js.map +1 -1
  44. package/dist/src/commands/schema/index.d.ts.map +1 -1
  45. package/dist/src/commands/schema/index.js +2 -0
  46. package/dist/src/commands/schema/index.js.map +1 -1
  47. package/dist/src/types/api.d.ts +75 -0
  48. package/dist/src/types/api.d.ts.map +1 -1
  49. package/dist/src/utils/input.js +33 -8
  50. package/dist/src/utils/input.js.map +1 -1
  51. package/dist/src/validators/parse.d.ts +2 -2
  52. package/dist/src/validators/parse.d.ts.map +1 -1
  53. package/dist/src/validators/parse.js.map +1 -1
  54. package/dist/src/validators/schemas.d.ts +207 -21
  55. package/dist/src/validators/schemas.d.ts.map +1 -1
  56. package/dist/src/validators/schemas.js +230 -3
  57. package/dist/src/validators/schemas.js.map +1 -1
  58. package/llms.txt +57 -7
  59. package/package.json +4 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @bolta-io/cli
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ab8702c: 예금주 조회 명령 `bolta bank-account-holder check`와 `check-bulk`를 추가했습니다. 은행코드와 계좌번호로 예금주명을 확인하고, 계좌를 한 번에 100건까지 조회합니다. 지원 은행코드와 테스트 키 고정 계좌는 `bolta guide bank-account-holder`로 확인하세요.
8
+
9
+ 같은 에러 코드를 API마다 다른 뜻으로 쓰는 경우를 고려해 요청별 안내를 우선하도록 바꿨습니다. `LOOKUP_UNAVAILABLE`과 `POINT_RESERVED_BY_IN_FLIGHT_REQUESTS`가 예금주 조회에서 사업자등록 상태 조회나 서류 발급 문구로 나오지 않습니다.
10
+
11
+ 예금주 조회는 서버가 한 건에 최대 45초를 기다리므로 응답 대기 한도를 단건 60초, 일괄 120초로 늘렸습니다. 다른 명령은 기존 30초 그대로입니다.
12
+
13
+ ## 0.6.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 905ee6e: 홈택스 국세 증명 서류 5종을 발급하고 결과와 원본 PDF 주소를 조회하는 `document issue`, `document get` 명령을 추가합니다.
18
+ `TOO_MANY_IN_FLIGHT`는 재시도 없이 바로 안내하고, `--stdin` 입력을 모두 읽은 뒤 대기 없이 종료합니다.
19
+
3
20
  ## 0.5.1
4
21
 
5
22
  ### Patch Changes
package/CLAUDE.md CHANGED
@@ -25,7 +25,7 @@ bolta config set api-key test_your_key
25
25
  ## 요금 안내
26
26
 
27
27
  Bolta API는 **사전 충전 포인트 차감 방식** 유료 서비스입니다.
28
- - 정발행: 90원/건, 역발행: 200원/건 (VAT 별도)
28
+ - 정발행: 90원/건, 역발행: 200원/건 (VAT 별도), 서류 발급: 500포인트/건, 예금주 조회: 50포인트/계좌
29
29
  - `test_` API 키: 포인트 차감 없이 무료 테스트
30
30
  - `live_` API 키: 사전 포인트 충전 필요. 잔액 부족 시 API 호출 실패 (HTTP 402)
31
31
  - 포인트 충전: https://developers.bolta.io 개발자센터
@@ -150,6 +150,51 @@ bolta bank-account sync
150
150
  - `sync`는 사업자마다 30분 1회, 하루 12회입니다. CLI는 자동 재시도하지 않습니다. `202` 뒤에는 `list`로 `syncStatus`가 `IN_PROGRESS`인 계좌가 없어질 때까지 확인하고, 5분쯤 지나 조회하세요.
151
151
  - 테스트 키는 고정 계좌 2개와 거래 6건을 돌려줍니다(`bolta guide bank-account`).
152
152
 
153
+ ### 예금주 조회
154
+
155
+ 은행코드와 계좌번호로 예금주명을 조회합니다. 송금 전에 거래처 계좌를 확인할 때 씁니다. 발급자 등록, 공동인증서, 요청자 관리번호가 필요 없고 구독 플랜 제한도 없습니다.
156
+
157
+ ```bash
158
+ bolta bank-account-holder check 088 1000000001
159
+ bolta bank-account-holder check 089 1000000004 --amount 20000
160
+ bolta bank-account-holder check-bulk --data '{"accounts":[{"bankCode":"088","accountNumber":"1000000001"},{"bankCode":"089","accountNumber":"1000000004","amount":20000}]}'
161
+ ```
162
+
163
+ - 예금주를 돌려준 계좌마다 50포인트를 차감합니다. 찾지 못한 계좌는 차감하지 않습니다.
164
+ - 멱등 키가 없어 요청을 다시 보내면 다시 차감합니다. CLI는 자동으로 재요청하지 않습니다.
165
+ - `bankCode`는 금융결제원 3자리 코드입니다. 국내 26곳과 외국계 11곳을 지원하고 나머지는 `UNSUPPORTED_BANK`입니다. 목록은 `bolta guide bank-account-holder`로 확인하세요.
166
+ - `accountNumber`는 하이픈을 빼고 6~20자리 숫자입니다. 응답은 하이픈을 뗀 값입니다.
167
+ - `AMOUNT_REQUIRED`이면 입금 금액이 정해진 가상계좌입니다. `--amount`에 총 지급 금액을 넣어 다시 조회하세요.
168
+ - 일괄 조회는 1~100건입니다. `results`는 보낸 순서와 개수를 그대로 따르고 중복도 줄이지 않습니다.
169
+ - 일괄 조회가 `200`이어도 항목마다 `error`가 `null`인지 확인하세요. 한 계좌도 판정하지 못하면 `503 BANK_UNAVAILABLE`입니다.
170
+ - 하루 10,000계좌 한도이며 요청 수가 아니라 계좌 수로 셉니다.
171
+ - 테스트 키는 고정 계좌 5개만 조회합니다(`bolta guide bank-account-holder`).
172
+
173
+ ### 홈택스 국세 증명 서류 발급
174
+
175
+ 사업자등록증명, 사업자등록증 재발급, 납세증명서, 부가가치세 과세표준증명, 표준재무제표증명을 비동기로 발급합니다. API 키가 속한 사업자만 요청할 수 있으며 볼타 대시보드에 공동인증서를 등록해야 합니다.
176
+
177
+ ```bash
178
+ bolta document issue --reference-id order-20260919-001 --data '{
179
+ "businessRegistrationNumber": "1234567890",
180
+ "document": { "type": "BUSINESS_REGISTRATION_PROOF", "language": "KO" }
181
+ }'
182
+
183
+ bolta document get <issuanceKey>
184
+ ```
185
+
186
+ - `BUSINESS_REGISTRATION_PROOF`: `language`는 `KO` 또는 `EN`
187
+ - `BUSINESS_REGISTRATION_CERTIFICATE`: `reason`은 앞뒤 공백을 제외하고 1~10자이며 제어 문자를 쓸 수 없음
188
+ - `TAX_PAYMENT_CERTIFICATE`: `purpose`는 `PAYMENT_RECEIPT` 또는 `OTHER`
189
+ - `VAT_TAX_BASE_PROOF`: `from`은 01월 또는 07월, `to`는 06월 또는 12월이며 최대 5개 연도
190
+ - `STANDARD_FINANCIAL_STATEMENT_PROOF`: `fiscalYearEnd`는 `YYYY-MM`
191
+ - `ACCEPTED`와 `SUBMITTED`는 처리 중입니다. `COMPLETED`의 `downloadUrl`은 5분 동안 유효합니다. 만료되면 다시 조회하세요.
192
+ - `ACTION_REQUIRED`이면 같은 서류를 다시 요청하지 마세요.
193
+ - 서류 한 건에 500포인트를 차감하며 실패하면 차감하지 않습니다.
194
+ - reference-id는 앞뒤 공백 없는 출력 가능한 ASCII 1~255자입니다.
195
+ - 같은 reference-id와 같은 본문은 기존 요청을 반환합니다. 다른 본문에는 새 reference-id를 사용하세요.
196
+ - 테스트 키는 `1000000014`를 바로 완료하고 `1000000071`을 바로 실패 처리합니다.
197
+
153
198
  ## 커맨드 빠른 참조
154
199
 
155
200
  | 커맨드 | 설명 | 데이터 필요 |
@@ -175,6 +220,10 @@ bolta bank-account sync
175
220
  | `bolta bank-account list` | 연결된 계좌 목록 | - |
176
221
  | `bolta bank-account transactions [--from] [--to] [--cursor] [--all]` | 거래 변경분 조회 | - |
177
222
  | `bolta bank-account sync` | 입출금내역 동기화 요청 | - |
223
+ | `bolta bank-account-holder check <은행코드> <계좌번호> [--amount <원>]` | 예금주 단건 조회 | - |
224
+ | `bolta bank-account-holder check-bulk` | 예금주 일괄 조회 (최대 100건) | --data |
225
+ | `bolta document issue --reference-id <id>` | 홈택스 국세 증명 서류 발급 요청 | --data |
226
+ | `bolta document get <issuanceKey>` | 서류 발급 결과와 PDF 주소 조회 | - |
178
227
  | `bolta config set <key> <value>` | 설정 | - |
179
228
  | `bolta config show` | 설정 확인 | - |
180
229
  | `bolta config test` | 연결 테스트 | - |
@@ -204,14 +253,21 @@ bolta bank-account sync
204
253
  | `AUTH_ERROR` | API 키 없음/잘못됨 | `bolta config set api-key <key>` |
205
254
  | `VALIDATION_ERROR` | 입력 데이터 오류 | `bolta schema <command>`로 스키마 확인 |
206
255
  | `INPUT_ERROR` | 명령어·옵션·입력 방식 오류 | `bolta --help` 또는 하위 명령의 `--help`로 사용법 확인 |
207
- | `INVALID_REQUEST` | 세금계산서 요청 검증 실패, 발행 마감일 경과 또는 요청자 관리번호 중복 | `error.message` 확인. 중복이면 `invoice status`로 기존 요청 확인. 마감일은 `invoice due-date`로 확인 |
256
+ | `INVALID_REQUEST` | 요청 검증 실패, 세금계산서 발행 마감일 경과 또는 요청자 관리번호 중복 | `error.message`와 해당 명령의 `schema` 확인. 세금계산서 중복이면 `invoice status`로 기존 요청 확인 |
208
257
  | `HTTP_402` | 포인트 잔액 부족 | https://developers.bolta.io 에서 포인트 충전 |
209
258
  | `HTTP_400` | 잘못된 요청 | error.message 확인, 필수 필드 누락 체크 |
210
259
  | `HTTP_404` | 리소스 없음 | issuanceKey/issuerId 확인 |
211
260
  | `HTTP_429` | 요청 제한 초과 | 일반 요청은 자동 재시도(Retry-After 30초 이하). 관리번호가 있는 세금계산서 요청은 `invoice status`로 접수 여부 확인 |
212
- | `RATE_LIMITED` | 사업자등록 상태 조회 24시간 한도 또는 서비스 전체 일일 한도 초과 | `error.details.retryAfterSeconds`초 뒤 다시 실행 |
261
+ | `RATE_LIMITED` | 사업자등록 상태 조회 24시간 한도, 예금주 조회 하루 10,000계좌 한도 또는 서비스 전체 일일 한도 초과 | `error.details.retryAfterSeconds`초 뒤 다시 실행 |
213
262
  | `INVALID_BUSINESS_REGISTRATION_NUMBER` | 사업자등록번호 체크섬 불일치 | 번호 확인 |
214
- | `LOOKUP_UNAVAILABLE` | 사업자등록 상태 확인 불가 (포인트 미차감) | 잠시 후 다시 실행 |
263
+ | `LOOKUP_UNAVAILABLE` | 사업자등록 상태 확인 불가, 또는 예금주 조회의 보호 한도 판정 불가 (포인트 미차감) | 잠시 후 다시 실행 |
264
+ | `ACCOUNT_NOT_VERIFIED` | 예금주 조회에서 계좌를 확인하지 못함 (포인트 미차감) | 은행코드와 계좌번호 확인 |
265
+ | `ACCOUNT_NOT_AVAILABLE` | 입금이 정지된 가상계좌 | 계좌를 발급한 기관에 문의 |
266
+ | `AMOUNT_REQUIRED` | 입금 금액이 정해진 가상계좌인데 금액이 없음 | `--amount`에 총 지급 금액을 넣어 다시 조회 |
267
+ | `AMOUNT_MISMATCH` | 입력한 금액이 가상계좌에 정해진 금액과 다름 | 총 지급 금액 확인 |
268
+ | `AMOUNT_VERIFICATION_UNAVAILABLE` | 금액 확인형 가상계좌를 지금 조회할 수 없음 | 계좌를 발급한 곳에서 예금주 확인 |
269
+ | `UNSUPPORTED_BANK` | 예금주 조회를 지원하지 않는 은행코드 | `bolta guide bank-account-holder`로 지원 은행 확인 |
270
+ | `BANK_UNAVAILABLE` | 은행 점검이나 연결 오류 (포인트 미차감) | 잠시 후 해당 계좌만 다시 조회 |
215
271
  | `PLAN_UPGRADE_REQUIRED` | 입출금내역 API를 쓰려면 스탠다드 플랜 이상 필요 | 볼타 대시보드 결제 메뉴에서 플랜 업그레이드 |
216
272
  | `INVALID_CURSOR` | 입출금내역 cursor가 손상됐거나, 받을 때와 조회 조건 또는 API 키가 다름 | 같은 조건으로 이전 응답의 `nextCursor`를 그대로 사용. 조건이나 키를 바꿨다면 cursor 없이 다시 조회 |
217
273
  | `BANK_ACCOUNT_NOT_FOUND` | 없거나 내 계좌가 아닌 계좌 ID | `bank-account list`의 `id` 확인 |
@@ -219,7 +275,13 @@ bolta bank-account sync
219
275
  | `SYNC_IN_PROGRESS` | 입출금내역 동기화가 이미 진행 중 (한도 미차감) | `bank-account list`로 `syncStatus` 확인 |
220
276
  | `SYNC_RATE_LIMITED` | 동기화 한도(30분 1회, 하루 12회) 초과 | `error.details.retryAfterSeconds`초 뒤 다시 요청 |
221
277
  | `SYNC_UNAVAILABLE` | 동기화 한도를 확인할 수 없어 요청 거절 | 잠시 후 다시 요청 |
222
- | `NETWORK_ERROR` | 네트워크 오류 또는 응답 본문 읽기 실패 | 인터넷 연결 확인. 세금계산서 관리번호를 사용했다면 재전송하지 말고 `invoice status`로 접수 여부 확인 |
278
+ | `CERTIFICATE_REQUIRED` | 서류 발급용 공동인증서 미등록 또는 만료 | 볼타 대시보드에서 인증서 등록 |
279
+ | `TARGET_NOT_ALLOWED` | API 키가 속한 사업자가 아닌 번호 | 사업자등록번호 확인 |
280
+ | `IDEMPOTENCY_CONFLICT` | 같은 서류 발급 reference-id에 다른 본문 사용 | 기존 본문 또는 새 reference-id 사용 |
281
+ | `POINT_RESERVED_BY_IN_FLIGHT_REQUESTS` | 처리 중인 서류 요청이나 예금주 조회까지 합치면 포인트 부족 | 충전하거나 요청 완료 대기 |
282
+ | `TOO_MANY_IN_FLIGHT` | 처리 중인 서류 요청이 5건 | 기존 요청 완료 후 재요청 |
283
+ | `DOCUMENT_ISSUANCE_NOT_FOUND` | 서류 발급 요청 없음 | issuanceKey와 API 키 확인 |
284
+ | `NETWORK_ERROR` | 네트워크 오류 또는 응답 본문 읽기 실패 | 세금계산서는 `invoice status`로 확인. 서류 발급은 같은 reference-id와 같은 본문으로 다시 요청 |
223
285
 
224
286
  ## 개발 명령어
225
287
 
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # bolta
2
2
 
3
- Bolta API CLI - 한국 전자세금계산서와 현금영수증 발행/조회/관리 도구
3
+ Bolta API CLI - 한국 전자세금계산서, 현금영수증, 홈택스 국세 증명 서류 발급/조회 도구
4
4
 
5
- AI 에이전트(Claude Code 등)가 세금계산서와 현금영수증 업무를 자동화할 때 사용할 수 있도록 설계되었습니다.
5
+ AI 에이전트(Claude Code 등)가 세금계산서, 현금영수증, 국세 증명 서류 업무를 자동화할 때 사용할 수 있도록 설계되었습니다.
6
6
 
7
7
  ## 설치
8
8
 
@@ -261,6 +261,44 @@ bolta bank-account sync # 즉시 동
261
261
 
262
262
  `sync`는 사업자마다 30분에 한 번, 하루 12번까지 요청할 수 있고 CLI는 자동으로 다시 요청하지 않습니다. `202`는 접수만 뜻합니다. `bolta bank-account list`로 `syncStatus`가 `IN_PROGRESS`인 계좌가 없어질 때까지 확인한 뒤, 5분쯤 지나서 `transactions`를 조회하세요. 테스트 키가 돌려주는 고정 계좌와 거래는 `bolta guide bank-account`로 확인하세요.
263
263
 
264
+ #### 예금주 조회
265
+
266
+ 은행코드와 계좌번호로 예금주명을 조회합니다. 송금하기 전에 거래처 계좌가 맞는지 확인할 때 씁니다. 발급자 등록, 공동인증서, 요청자 관리번호가 모두 필요 없고 구독 플랜 제한도 없습니다.
267
+
268
+ ```bash
269
+ bolta bank-account-holder check 088 1000000001 # 예금주 단건 조회
270
+ bolta bank-account-holder check 088 100-0000-001 # 하이픈을 포함해도 됩니다
271
+ bolta bank-account-holder check 089 1000000004 --amount 20000 # 금액 확인형 가상계좌
272
+ bolta bank-account-holder check-bulk --data '{"accounts":[{"bankCode":"088","accountNumber":"1000000001"}]}'
273
+ ```
274
+
275
+ 예금주를 돌려준 계좌마다 50포인트를 차감합니다. 찾지 못했거나 조회에 실패한 계좌는 차감하지 않습니다. 일반 계좌는 한 요청 안에서 같은 은행코드와 계좌번호를 여러 번 넣어도 한 번만 차감합니다. 금액 확인형 가상계좌는 고유한 `amount`마다 차감합니다. 멱등 키가 없어 요청을 다시 보내면 다시 차감하며, CLI는 이 조회를 자동으로 재요청하지 않습니다.
276
+
277
+ `bankCode`는 금융결제원 3자리 은행코드입니다. 국내 26곳과 외국계 11곳을 지원하며 그 밖의 코드는 `400 UNSUPPORTED_BANK`입니다. 전체 목록은 `bolta guide bank-account-holder`로 확인하세요. `accountNumber`는 하이픈을 빼고 6~20자리 숫자여야 하고, 응답의 `accountNumber`는 하이픈을 뗀 값입니다.
278
+
279
+ 입금 금액이 정해진 가상계좌는 `AMOUNT_REQUIRED`를 돌려줍니다. 그 계좌로 보낼 총 지급 금액을 `--amount`에 넣어 다시 조회하세요. 금액이 다르면 `AMOUNT_MISMATCH`입니다.
280
+
281
+ 일괄 조회는 계좌를 1개 이상 100개 이하로 넣습니다. 항목 하나라도 형식이 틀리면 요청 전체를 `400 INVALID_REQUEST`로 거절합니다. `results`는 보낸 `accounts`와 같은 순서, 같은 개수이며 중복 계좌를 넣어도 줄이지 않습니다. **HTTP 200으로 와도 일부 계좌는 실패할 수 있으니 항목마다 `error`가 `null`인지 확인하세요.** `error.code`가 `BANK_UNAVAILABLE`인 항목은 판정하지 못한 계좌이니 잠시 후 그 계좌만 다시 조회하세요. 한 계좌도 판정하지 못하면 이 응답 대신 `503 BANK_UNAVAILABLE`입니다.
282
+
283
+ 하루에 조회할 수 있는 계좌는 10,000개이고 요청 수가 아니라 계좌 수로 셉니다. 테스트 키와 라이브 키는 한도를 따로 셉니다. 테스트 키로 조회할 수 있는 고정 계좌는 `bolta guide bank-account-holder`로 확인하세요.
284
+
285
+ #### 홈택스 국세 증명 서류 발급
286
+
287
+ API 키가 속한 사업자의 사업자등록증명, 사업자등록증 재발급, 납세증명서, 부가가치세 과세표준증명, 표준재무제표증명을 발급합니다. 볼타 대시보드에 해당 사업자의 공동인증서를 등록해야 합니다.
288
+
289
+ ```bash
290
+ bolta document issue --reference-id order-20260919-001 --data '{
291
+ "businessRegistrationNumber": "1234567890",
292
+ "document": { "type": "BUSINESS_REGISTRATION_PROOF", "language": "KO" }
293
+ }'
294
+
295
+ bolta document get <issuanceKey>
296
+ ```
297
+
298
+ 발급은 비동기입니다. `ACCEPTED`와 `SUBMITTED`는 처리 중이고, `COMPLETED`가 되면 5분 동안 유효한 `downloadUrl`을 반환합니다. 주소가 만료되면 `document get`으로 새 주소를 받으세요. `ACTION_REQUIRED`이면 같은 서류를 다시 요청하지 마세요.
299
+
300
+ 서류 한 건에 500포인트를 차감하며 실패하면 차감하지 않습니다. reference-id는 앞뒤 공백 없는 출력 가능한 ASCII 1~255자로 입력하세요. 같은 reference-id와 같은 본문은 안전하게 다시 보낼 수 있지만, 다른 본문을 보내면 `409 IDEMPOTENCY_CONFLICT`입니다. 테스트 키는 `1000000014`를 바로 완료하고 `1000000071`을 바로 실패 처리합니다. 자세한 입력은 `bolta schema document-issue`, 서류별 샘플은 `bolta examples document-issue-business-registration-proof` 등으로 확인하세요.
301
+
264
302
  #### 설정
265
303
 
266
304
  ```bash
@@ -272,7 +310,7 @@ bolta config test # API 연결 테스트
272
310
  #### 도움말
273
311
 
274
312
  ```bash
275
- # 주요 개념 안내 (issuer, payment, workflow, cash-receipt, business-registration-status, bank-account, glossary)
313
+ # 주요 개념 안내 (issuer, payment, workflow, cash-receipt, business-registration-status, bank-account, bank-account-holder, document, glossary)
276
314
  bolta guide [topic]
277
315
 
278
316
  # 바로 쓸 수 있는 샘플 JSON
@@ -284,7 +322,7 @@ bolta schema [command]
284
322
 
285
323
  `invoice issue`, `invoice issue-request`, `amend change-supply-cost` 입력은 국세청 XML 전송 제한에 맞춰 로컬에서 먼저 검증됩니다. 필드별 길이 제한은 `bolta schema invoice-issue`로 확인할 수 있습니다.
286
324
  `taxType`은 `TAXABLE`(과세), `ZERO_RATE`(영세율), `TAX_FREE`(면세) 중 하나이며 필수입니다. `ZERO_RATE`은 모든 품목의 `tax`를 `0`으로, `TAX_FREE`는 모든 품목의 `tax`를 `null`로 전달합니다.
287
- 사업자등록 상태 일괄 조회 입력은 `bolta schema business-registration-status-check-bulk`로 확인할 수 있습니다. 현금영수증 입력 규칙은 `bolta schema cash-receipt-issue`로 확인할 수 있습니다. 수취인 유형별 예제는 `bolta examples cash-receipt-issue`, `cash-receipt-issue-business`, `cash-receipt-issue-self`를 사용하세요.
325
+ 사업자등록 상태 일괄 조회 입력은 `bolta schema business-registration-status-check-bulk`로 확인할 수 있습니다. 현금영수증 입력 규칙은 `bolta schema cash-receipt-issue`로 확인할 수 있습니다. 수취인 유형별 예제는 `bolta examples cash-receipt-issue`, `cash-receipt-issue-business`, `cash-receipt-issue-self`를 사용하세요. 서류 발급 입력은 `bolta schema document-issue`로 확인하세요.
288
326
 
289
327
  #### 업데이트
290
328
 
@@ -338,14 +376,21 @@ cat invoice.json | bolta invoice issue --stdin
338
376
  | `AUTH_ERROR` | API 키 없음/잘못됨 | `bolta config set api-key <key>` |
339
377
  | `VALIDATION_ERROR` | 입력 데이터 오류 | `bolta schema <command>`로 스키마 확인 |
340
378
  | `INPUT_ERROR` | 명령어·옵션·입력 방식 오류 | `bolta --help` 또는 하위 명령의 `--help`로 사용법 확인 |
341
- | `INVALID_REQUEST` | 세금계산서 요청 검증 실패, 발행 마감일 경과 또는 요청자 관리번호 중복 | `error.message` 확인. 중복이면 `invoice status`로 기존 요청 확인. 마감일은 `invoice due-date`로 확인 |
379
+ | `INVALID_REQUEST` | 요청 검증 실패, 세금계산서 발행 마감일 경과 또는 요청자 관리번호 중복 | `error.message`와 해당 명령의 `schema` 확인. 세금계산서 중복이면 `invoice status`로 기존 요청 확인 |
342
380
  | `HTTP_402` | 포인트 잔액 부족 | https://developers.bolta.io 에서 포인트 충전 |
343
381
  | `HTTP_400` | 잘못된 요청 | error.message 확인 |
344
382
  | `HTTP_404` | 리소스 없음 | issuanceKey/issuerId 확인 |
345
383
  | `HTTP_429` | 요청 제한 초과 | 일반 요청은 자동 재시도(Retry-After 30초 이하). 관리번호가 있는 세금계산서 요청은 `invoice status`로 접수 여부 확인 |
346
- | `RATE_LIMITED` | 사업자등록 상태 조회 24시간 한도 또는 서비스 전체 일일 한도 초과 | `error.details.retryAfterSeconds`초 뒤 다시 실행 |
384
+ | `RATE_LIMITED` | 사업자등록 상태 조회 24시간 한도, 예금주 조회 하루 10,000계좌 한도 또는 서비스 전체 일일 한도 초과 | `error.details.retryAfterSeconds`초 뒤 다시 실행 |
347
385
  | `INVALID_BUSINESS_REGISTRATION_NUMBER` | 사업자등록번호 체크섬 불일치 | 번호 확인 |
348
- | `LOOKUP_UNAVAILABLE` | 사업자등록 상태 확인 불가 (포인트 미차감) | 잠시 후 다시 실행 |
386
+ | `LOOKUP_UNAVAILABLE` | 사업자등록 상태 확인 불가, 또는 예금주 조회의 보호 한도 판정 불가 (포인트 미차감) | 잠시 후 다시 실행 |
387
+ | `ACCOUNT_NOT_VERIFIED` | 예금주 조회에서 계좌를 확인하지 못함 (포인트 미차감) | 은행코드와 계좌번호 확인 |
388
+ | `ACCOUNT_NOT_AVAILABLE` | 입금이 정지된 가상계좌 | 계좌를 발급한 기관에 문의 |
389
+ | `AMOUNT_REQUIRED` | 입금 금액이 정해진 가상계좌인데 금액이 없음 | `--amount`에 총 지급 금액을 넣어 다시 조회 |
390
+ | `AMOUNT_MISMATCH` | 입력한 금액이 가상계좌에 정해진 금액과 다름 | 총 지급 금액 확인 |
391
+ | `AMOUNT_VERIFICATION_UNAVAILABLE` | 금액 확인형 가상계좌를 지금 조회할 수 없음 | 계좌를 발급한 곳에서 예금주 확인 |
392
+ | `UNSUPPORTED_BANK` | 예금주 조회를 지원하지 않는 은행코드 | `bolta guide bank-account-holder`로 지원 은행 확인 |
393
+ | `BANK_UNAVAILABLE` | 은행 점검이나 연결 오류 (포인트 미차감) | 잠시 후 해당 계좌만 다시 조회 |
349
394
  | `PLAN_UPGRADE_REQUIRED` | 입출금내역 API를 쓰려면 스탠다드 플랜 이상 필요 | 볼타 대시보드 결제 메뉴에서 플랜 업그레이드 |
350
395
  | `INVALID_CURSOR` | 입출금내역 cursor가 손상됐거나, 받을 때와 조회 조건 또는 API 키가 다름 | 같은 조건으로 이전 응답의 `nextCursor`를 그대로 사용. 조건이나 키를 바꿨다면 cursor 없이 다시 조회 |
351
396
  | `BANK_ACCOUNT_NOT_FOUND` | 없거나 내 계좌가 아닌 계좌 ID | `bank-account list`의 `id` 확인 |
@@ -353,7 +398,13 @@ cat invoice.json | bolta invoice issue --stdin
353
398
  | `SYNC_IN_PROGRESS` | 입출금내역 동기화가 이미 진행 중 (한도 미차감) | `bank-account list`로 `syncStatus` 확인 |
354
399
  | `SYNC_RATE_LIMITED` | 동기화 한도(30분 1회, 하루 12회) 초과 | `error.details.retryAfterSeconds`초 뒤 다시 요청 |
355
400
  | `SYNC_UNAVAILABLE` | 동기화 한도를 확인할 수 없어 요청 거절 | 잠시 후 다시 요청 |
356
- | `NETWORK_ERROR` | 네트워크 오류 또는 응답 본문 읽기 실패 | 인터넷 연결 확인. 세금계산서 관리번호를 사용했다면 재전송하지 말고 `invoice status`로 접수 여부 확인 |
401
+ | `CERTIFICATE_REQUIRED` | 서류 발급에 공동인증서가 없거나 만료됨 | 볼타 대시보드에서 API 키가 속한 사업자의 인증서 등록 |
402
+ | `TARGET_NOT_ALLOWED` | API 키가 속한 사업자가 아닌 번호로 서류 발급 요청 | 사업자등록번호 확인 |
403
+ | `IDEMPOTENCY_CONFLICT` | 같은 서류 발급 reference-id에 다른 본문 사용 | 기존 본문을 다시 보내거나 새 reference-id 사용 |
404
+ | `POINT_RESERVED_BY_IN_FLIGHT_REQUESTS` | 처리 중인 서류 요청이나 예금주 조회까지 합치면 포인트 부족 | 충전하거나 기존 요청이 끝난 뒤 다시 요청 |
405
+ | `TOO_MANY_IN_FLIGHT` | 처리 중인 서류 요청이 5건 | 기존 요청이 끝난 뒤 다시 요청 |
406
+ | `DOCUMENT_ISSUANCE_NOT_FOUND` | 없거나 다른 API 키의 서류 발급 요청 | issuanceKey와 API 키 확인 |
407
+ | `NETWORK_ERROR` | 네트워크 오류 또는 응답 본문 읽기 실패 | 세금계산서는 `invoice status`로 확인. 서류 발급은 같은 reference-id와 같은 본문으로 다시 요청 |
357
408
 
358
409
  ---
359
410
 
package/dist/bin/bolta.js CHANGED
@@ -13,6 +13,8 @@ import { registerGuideCommand } from '../src/commands/guide/index.js';
13
13
  import { registerCashReceiptCommands } from '../src/commands/cash-receipt/index.js';
14
14
  import { registerBusinessRegistrationStatusCommands } from '../src/commands/business-registration-status/index.js';
15
15
  import { registerBankAccountCommands } from '../src/commands/bank-account/index.js';
16
+ import { registerBankAccountHolderCommands } from '../src/commands/bank-account-holder/index.js';
17
+ import { registerDocumentCommands } from '../src/commands/document/index.js';
16
18
  import { legacyIssuerWarning, outputError } from '../src/utils/output.js';
17
19
  import { BoltaInputError } from '../src/utils/errors.js';
18
20
  import { getCurrentVersion } from '../src/utils/version.js';
@@ -25,7 +27,7 @@ program
25
27
  .exitOverride();
26
28
  program
27
29
  .name('bolta')
28
- .description('Bolta API CLI - 전자세금계산서·현금영수증 발행/조회/관리 도구 (AI 에이전트 최적화)')
30
+ .description('Bolta API CLI - 전자세금계산서·현금영수증·국세 증명 서류 발급/조회 도구 (AI 에이전트 최적화)')
29
31
  .version(getCurrentVersion())
30
32
  .option('--api-key <key>', 'Bolta API 키 (환경변수 BOLTA_API_KEY 대체)')
31
33
  .addOption(new Option('--supplier-key <key>').hideHelp())
@@ -57,6 +59,8 @@ registerInvoiceCommands(program);
57
59
  registerCashReceiptCommands(program);
58
60
  registerBusinessRegistrationStatusCommands(program);
59
61
  registerBankAccountCommands(program);
62
+ registerBankAccountHolderCommands(program);
63
+ registerDocumentCommands(program);
60
64
  registerConfigCommands(program);
61
65
  registerExamplesCommand(program);
62
66
  registerSchemaCommand(program);
@@ -1 +1 @@
1
- {"version":3,"file":"bolta.js","sourceRoot":"","sources":["../../bin/bolta.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bolta.js","sourceRoot":"","sources":["../../bin/bolta.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { CreateTaxInvoiceRequest, IssuanceResponse, TaxInvoiceDetail, StatusResponse, AmendTerminationRequest, AmendChangeSupplyCostRequest, CreateIssuerRequest, IssuerResponse, CreateSupplierRequest, LegacySupplierResponse, CreateCustomerRequest, LegacyCustomerResponse, CertificateUrlResponse, CertificateStatusResponse, CreateCashReceiptRequest, CashReceiptCancellationResponse, CashReceiptStatusResponse, BusinessRegistrationStatusCheckRequest, BusinessRegistrationStatusResponse, BusinessRegistrationStatusBulkCheckRequest, BusinessRegistrationStatusBulkResponse, TaxInvoiceIssueDueDateResponse, BankAccountListResponse, BankAccountTransactionQuery, BankAccountTransactionListResponse, BankAccountSyncResponse } from '../types/api.js';
1
+ import type { CreateTaxInvoiceRequest, IssuanceResponse, TaxInvoiceDetail, StatusResponse, AmendTerminationRequest, AmendChangeSupplyCostRequest, CreateIssuerRequest, IssuerResponse, CreateSupplierRequest, LegacySupplierResponse, CreateCustomerRequest, LegacyCustomerResponse, CertificateUrlResponse, CertificateStatusResponse, CreateCashReceiptRequest, CashReceiptCancellationResponse, CashReceiptStatusResponse, BusinessRegistrationStatusCheckRequest, BusinessRegistrationStatusResponse, BusinessRegistrationStatusBulkCheckRequest, BusinessRegistrationStatusBulkResponse, TaxInvoiceIssueDueDateResponse, BankAccountListResponse, BankAccountTransactionQuery, BankAccountTransactionListResponse, BankAccountSyncResponse, BankAccountHolderInquiry, BankAccountHolderResponse, BankAccountHolderBulkInquiryRequest, BankAccountHolderBulkResponse, CreateDocumentIssuanceRequest, DocumentIssuanceResponse } from '../types/api.js';
2
2
  import { type LegacyIssuerHeader } from '../utils/legacy-issuer.js';
3
3
  interface ClientConfig {
4
4
  apiKey: string;
@@ -60,6 +60,12 @@ export declare class BoltaClient {
60
60
  * 한 번만 시도하고 실패를 바로 돌려준다.
61
61
  */
62
62
  requestBankAccountSync(): Promise<BankAccountSyncResponse>;
63
+ inquireBankAccountHolder(data: BankAccountHolderInquiry): Promise<BankAccountHolderResponse>;
64
+ inquireBankAccountHoldersBulk(data: BankAccountHolderBulkInquiryRequest): Promise<BankAccountHolderBulkResponse>;
65
+ issueDocument(data: CreateDocumentIssuanceRequest, options: {
66
+ referenceId: string;
67
+ }): Promise<DocumentIssuanceResponse>;
68
+ getDocumentIssuance(issuanceKey: string): Promise<DocumentIssuanceResponse>;
63
69
  private request;
64
70
  }
65
71
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"bolta-client.d.ts","sourceRoot":"","sources":["../../../src/client/bolta-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bolta-client.d.ts","sourceRoot":"","sources":["../../../src/client/bolta-client.ts"],"names":[],"mappings":"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"}
@@ -214,6 +214,46 @@ export class BoltaClient {
214
214
  });
215
215
  }
216
216
  // ========================
217
+ // Bank Account Holder Methods
218
+ // ========================
219
+ async inquireBankAccountHolder(data) {
220
+ return this.request({
221
+ method: 'POST',
222
+ // AIP 스타일 커스텀 메서드라 콜론을 그대로 보낸다. 인코딩하면 %3A가 되어 라우팅이 깨진다.
223
+ path: '/bankAccountHolders:inquire',
224
+ body: data,
225
+ ...bankAccountHolderRequestPolicy(BANK_ACCOUNT_HOLDER_TIMEOUT_MS),
226
+ });
227
+ }
228
+ async inquireBankAccountHoldersBulk(data) {
229
+ return this.request({
230
+ method: 'POST',
231
+ path: '/bankAccountHolders:bulkInquire',
232
+ body: data,
233
+ ...bankAccountHolderRequestPolicy(BANK_ACCOUNT_HOLDER_BULK_TIMEOUT_MS),
234
+ });
235
+ }
236
+ // ========================
237
+ // Document Issuance Methods
238
+ // ========================
239
+ async issueDocument(data, options) {
240
+ return this.request({
241
+ method: 'POST',
242
+ path: '/documentIssuances',
243
+ body: data,
244
+ referenceId: options.referenceId,
245
+ retry: 'network-and-server',
246
+ failureSuggestion: '응답을 확인하지 못했다면 같은 reference-id와 같은 본문으로 다시 요청하세요. API가 기존 요청을 반환합니다.',
247
+ invalidRequestSuggestion: "error.message와 'bolta schema document-issue'를 확인하세요. 테스트 키는 1000000014와 1000000071만 지원합니다.",
248
+ });
249
+ }
250
+ async getDocumentIssuance(issuanceKey) {
251
+ return this.request({
252
+ method: 'GET',
253
+ path: `/documentIssuances/${encodeURIComponent(issuanceKey)}`,
254
+ });
255
+ }
256
+ // ========================
217
257
  // Internal Request Method
218
258
  // ========================
219
259
  async request(options) {
@@ -235,8 +275,10 @@ export class BoltaClient {
235
275
  if (options.referenceId !== undefined) {
236
276
  headers['Bolta-Client-Reference-Id'] = options.referenceId;
237
277
  }
238
- const maxAttempts = options.retry === 'none' ? 1 : this.maxRetries;
278
+ const retryPolicy = options.retry ?? 'default';
279
+ const maxAttempts = retryPolicy === 'none' ? 1 : this.maxRetries;
239
280
  const statusSuggestion = options.failureSuggestion;
281
+ const timeout = options.timeout ?? this.timeout;
240
282
  const legacyIssuerDetails = legacyIssuer
241
283
  ? { legacyIssuerKey: maskedLegacyIssuerKey, source: legacyIssuer.source, legacyHeader: true }
242
284
  : undefined;
@@ -244,33 +286,34 @@ export class BoltaClient {
244
286
  legacyIssuer,
245
287
  statusSuggestion,
246
288
  invalidRequestSuggestion: options.invalidRequestSuggestion,
289
+ codeSuggestions: options.codeSuggestions,
247
290
  retryAfterSeconds,
248
291
  }), status === 403
249
292
  ? legacyIssuerDetails
250
293
  : retryAfterSeconds !== undefined ? { retryAfterSeconds } : undefined);
251
294
  let lastError = null;
252
295
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
296
+ const controller = new AbortController();
297
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
253
298
  try {
254
299
  verbose(`${options.method} ${url}`, { attempt, ...(options.body ? { body: '...' } : {}) });
255
- const controller = new AbortController();
256
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
257
300
  const response = await fetch(url, {
258
301
  method: options.method,
259
302
  headers,
260
303
  body: options.body ? JSON.stringify(options.body) : undefined,
261
304
  signal: controller.signal,
262
305
  });
263
- clearTimeout(timeoutId);
264
306
  verbose(`Response ${response.status}`, { attempt });
265
307
  // 재시도 지연과 오류 details에 같은 값을 쓴다.
266
308
  const retryAfterSeconds = response.status === 429
267
309
  ? parseRetryAfterSeconds(response.headers.get('Retry-After'))
268
310
  : undefined;
269
311
  // 재시도할 응답은 본문이 손상됐더라도 먼저 재시도한다.
270
- if (response.status === 429 && attempt < maxAttempts) {
312
+ if (response.status === 429 && retryPolicy === 'default' && attempt < maxAttempts) {
271
313
  const delay = retryAfterSeconds !== undefined ? retryAfterSeconds * 1000 : backoff(attempt);
272
314
  if (delay <= MAX_RETRY_AFTER_MS) {
273
315
  await response.body?.cancel().catch(() => undefined);
316
+ clearTimeout(timeoutId);
274
317
  verbose(`Rate limited, retrying after ${delay}ms`);
275
318
  await sleep(delay);
276
319
  continue;
@@ -280,6 +323,7 @@ export class BoltaClient {
280
323
  if (response.status >= 500 && attempt < maxAttempts) {
281
324
  const delay = backoff(attempt);
282
325
  await response.body?.cancel().catch(() => undefined);
326
+ clearTimeout(timeoutId);
283
327
  verbose(`Server error ${response.status}, retrying after ${delay}ms`);
284
328
  await sleep(delay);
285
329
  continue;
@@ -300,6 +344,8 @@ export class BoltaClient {
300
344
  }
301
345
  }
302
346
  catch (error) {
347
+ if (error instanceof Error && error.name === 'AbortError')
348
+ throw error;
303
349
  const reason = error instanceof Error ? `: ${error.message}` : '';
304
350
  throw new BoltaNetworkError(`응답 본문을 읽지 못했습니다${reason}`, statusSuggestion);
305
351
  }
@@ -320,12 +366,22 @@ export class BoltaClient {
320
366
  catch (error) {
321
367
  if (error instanceof BoltaApiError)
322
368
  throw error;
369
+ if (error instanceof BoltaNetworkError) {
370
+ lastError = error;
371
+ if (retryPolicy === 'network-and-server' && attempt < maxAttempts) {
372
+ clearTimeout(timeoutId);
373
+ await sleep(backoff(attempt));
374
+ continue;
375
+ }
376
+ throw error;
377
+ }
323
378
  if ((error instanceof Error && error.name === 'AbortError') || error instanceof TypeError) {
324
379
  const msg = error instanceof Error && error.name === 'AbortError'
325
- ? `요청 시간 초과 (${this.timeout}ms)`
380
+ ? `요청 시간 초과 (${timeout}ms)`
326
381
  : `네트워크 오류: ${error.message}`;
327
382
  lastError = new BoltaNetworkError(msg, statusSuggestion);
328
383
  if (attempt < maxAttempts) {
384
+ clearTimeout(timeoutId);
329
385
  await sleep(backoff(attempt));
330
386
  continue;
331
387
  }
@@ -333,6 +389,9 @@ export class BoltaClient {
333
389
  }
334
390
  throw error;
335
391
  }
392
+ finally {
393
+ clearTimeout(timeoutId);
394
+ }
336
395
  }
337
396
  throw lastError || new BoltaNetworkError('모든 재시도 실패');
338
397
  }
@@ -371,8 +430,18 @@ const ERROR_CODE_SUGGESTIONS = {
371
430
  SYNC_IN_PROGRESS: () => "이미 동기화가 진행 중입니다. 한도는 차감되지 않았습니다. 'bolta bank-account list'로 syncStatus가 IN_PROGRESS에서 바뀌었는지 확인하세요.",
372
431
  SYNC_RATE_LIMITED: (ctx) => `동기화는 30분에 한 번, 하루 12번까지 요청할 수 있습니다. ${describeRetryAfter(ctx)} 다시 요청하세요. 볼타가 주기적으로 거래를 가져오므로 기다리지 않고 조회해도 됩니다.`,
373
432
  SYNC_UNAVAILABLE: () => '지금은 동기화 한도를 확인할 수 없어 요청을 받지 않았습니다. 잠시 후 다시 시도하세요.',
433
+ CERTIFICATE_REQUIRED: () => '공동인증서가 등록되지 않았거나 만료됐습니다. 볼타 대시보드에서 API 키가 속한 사업자의 인증서를 등록하세요.',
434
+ TARGET_NOT_ALLOWED: () => 'API 키가 속한 사업자의 서류만 발급할 수 있습니다. 요청한 사업자등록번호를 확인하세요.',
435
+ IDEMPOTENCY_CONFLICT: () => '같은 reference-id에 다른 요청 본문을 사용할 수 없습니다. 기존 요청과 같은 본문을 보내거나 새 reference-id를 사용하세요.',
436
+ POINT_RESERVED_BY_IN_FLIGHT_REQUESTS: () => '처리 중인 서류 요청이 포인트를 예약해 잔액이 부족합니다. 포인트를 충전하거나 기존 요청이 끝난 뒤 다시 요청하세요.',
437
+ TOO_MANY_IN_FLIGHT: () => "처리 중인 서류 요청은 최대 5건입니다. 기존 요청이 끝난 뒤 'bolta document issue'를 다시 실행하세요.",
438
+ DOCUMENT_ISSUANCE_NOT_FOUND: () => "서류 발급 요청을 찾을 수 없습니다. issuanceKey와 API 키를 확인한 뒤 'bolta document get <issuanceKey>'을 다시 실행하세요.",
374
439
  };
375
440
  function getSuggestion(status, code, ctx) {
441
+ // 같은 코드라도 요청한 API마다 뜻이 다르다. 요청별 안내가 있으면 전역 맵보다 우선한다.
442
+ const requestSuggestion = ctx?.codeSuggestions?.[code];
443
+ if (requestSuggestion)
444
+ return requestSuggestion(ctx);
376
445
  const codeSuggestion = ERROR_CODE_SUGGESTIONS[code];
377
446
  if (codeSuggestion)
378
447
  return codeSuggestion(ctx);
@@ -426,6 +495,41 @@ const BUSINESS_REGISTRATION_STATUS_REQUEST_POLICY = {
426
495
  retry: 'none',
427
496
  invalidRequestSuggestion: "error.message를 확인하세요. 테스트 키는 고정 번호만 조회할 수 있고, 일괄 조회는 목록 밖 번호가 하나라도 섞이면 요청 전체를 거절합니다. 'bolta guide business-registration-status'로 고정 번호를 확인하세요.",
428
497
  };
498
+ /**
499
+ * 서버는 조회 한 건에 최대 45초를 기다린 뒤 503 BANK_UNAVAILABLE로 끝낸다.
500
+ * 기본 30초를 그대로 쓰면 CLI가 먼저 끊어 서버의 판정을 받아보지 못한다.
501
+ */
502
+ const BANK_ACCOUNT_HOLDER_TIMEOUT_MS = 60_000;
503
+ /** 계좌 100건은 단건보다 오래 걸린다. */
504
+ const BANK_ACCOUNT_HOLDER_BULK_TIMEOUT_MS = 120_000;
505
+ /** 응답을 확인하지 못한 상태. 성공했다면 이미 차감됐고 멱등 키가 없어 다시 부르면 또 차감된다. */
506
+ const BANK_ACCOUNT_HOLDER_UNKNOWN_RESULT_SUGGESTION = '응답을 확인하지 못했습니다. 조회에 성공했다면 포인트가 이미 차감됐을 수 있습니다. 같은 계좌를 곧바로 다시 조회하지 말고 잠시 후 필요한 계좌만 다시 조회하세요.';
507
+ const BANK_ACCOUNT_HOLDER_CODE_SUGGESTIONS = {
508
+ ACCOUNT_NOT_VERIFIED: () => '계좌를 확인하지 못했습니다. 포인트는 차감되지 않았습니다. 은행코드와 계좌번호를 다시 확인하세요.',
509
+ ACCOUNT_NOT_AVAILABLE: () => '입금이 정지된 가상계좌입니다. 포인트는 차감되지 않았습니다. 계좌를 발급한 기관에 문의하세요.',
510
+ AMOUNT_REQUIRED: () => "입금 금액이 정해진 가상계좌입니다. 그 계좌로 보낼 총 지급 금액을 '--amount'에 넣어 다시 조회하세요. 일괄 조회는 해당 항목에 amount를 넣으세요.",
511
+ AMOUNT_MISMATCH: () => '입력한 금액이 가상계좌에 정해진 금액과 다릅니다. 총 지급 금액을 확인한 뒤 다시 조회하세요.',
512
+ AMOUNT_VERIFICATION_UNAVAILABLE: () => '금액 확인형 가상계좌를 지금은 조회할 수 없습니다. 계좌를 발급한 곳에서 예금주를 확인하세요.',
513
+ UNSUPPORTED_BANK: () => "예금주 조회를 지원하지 않는 은행코드입니다. 'bolta guide bank-account-holder'로 지원 은행을 확인하세요.",
514
+ BANK_UNAVAILABLE: () => '지금은 예금주를 판정하지 못했습니다. 포인트는 차감되지 않았습니다. 잠시 후 해당 계좌만 다시 조회하세요.',
515
+ LOOKUP_UNAVAILABLE: () => '조회 보호 한도를 판정할 수 없어 요청을 받지 않았습니다. 포인트는 차감되지 않았습니다. 잠시 후 다시 시도하세요.',
516
+ SERVICE_UNAVAILABLE: () => '일시적인 내부 API 통신 오류입니다. 포인트는 차감되지 않았습니다. 잠시 후 다시 시도하세요.',
517
+ RATE_LIMITED: (ctx) => `하루에 조회할 수 있는 계좌는 10,000개입니다. 요청 수가 아니라 조회한 계좌 수로 셉니다. ${describeRetryAfter(ctx)} 다시 시도하세요.`,
518
+ POINT_RESERVED_BY_IN_FLIGHT_REQUESTS: () => '처리 중인 요청이 잡아 둔 포인트까지 합치면 잔액이 모자랍니다. https://developers.bolta.io 개발자센터에서 충전하거나 처리 중인 요청이 끝난 뒤 다시 요청하세요.',
519
+ };
520
+ /**
521
+ * 예금주 조회는 멱등 키가 없고, 요청을 다시 보내면 예금주를 돌려준 계좌마다 50포인트를 다시 차감한다.
522
+ * 응답을 놓쳤을 때 CLI가 자동으로 재전송하면 중복 과금이 나므로 한 번만 시도한다.
523
+ */
524
+ function bankAccountHolderRequestPolicy(timeout) {
525
+ return {
526
+ retry: 'none',
527
+ timeout,
528
+ codeSuggestions: BANK_ACCOUNT_HOLDER_CODE_SUGGESTIONS,
529
+ failureSuggestion: BANK_ACCOUNT_HOLDER_UNKNOWN_RESULT_SUGGESTION,
530
+ invalidRequestSuggestion: "error.message를 확인하세요. 테스트 키는 계좌번호 1000000001부터 1000000005까지만 조회할 수 있습니다. 'bolta guide bank-account-holder'로 고정 계좌를, 'bolta schema bank-account-holder-check-bulk'로 일괄 조회 입력 형식을 확인하세요.",
531
+ };
532
+ }
429
533
  function getTaxInvoiceStatusSuggestion() {
430
534
  return "같은 요청을 다시 보내지 마세요. 'bolta invoice status --reference-id <id>'에 요청에 사용한 관리번호를 입력해 접수 여부를 확인하세요. 새 발행 요청에는 사용하지 않은 reference-id를 사용하세요.";
431
535
  }