충전 주문 생성
POST/v1/organizations/{org_id}/topups
USD 정수 금액으로 충전 주문을 만듭니다, 원화 금액과 환율은 주문에 고정됩니다
콘솔 세션 전용입니다, 조직 역할 owner, admin만 부를 수 있고 API 키로 부르면 403 console_only입니다
- 가격은 부가세 포함입니다, US$N 충전은 US$N을 결제하고 US$N 크레딧을 받습니다
amount_krw = ceil(usd × fixed_krw_per_usd), 그 안에서supply_krw = round(amount_krw / 1.1),vat_krw = amount_krw - supply_krw입니다, 고정 환율은 고시 매매기준율 × 1.02입니다- 응답의
portone객체를 그대로 브라우저 PortOne 결제창에 넘기고, 결제창에서 돌아오면 충전 결제 확인을 호출합니다 - 주문은 만든 뒤 60분 안에 결제하지 않으면 만료됩니다(
topup_expired)
참고 서버 구현(2026-10-12 dev 기준)이 계약 초안과 다른 점
- usd는 JSON 숫자여야 합니다, 문자열 "20"은 invalid_amount입니다
Headers
- Cookie: string콘솔 로그인 세션 쿠키, 콘솔 전용 엔드포인트라 API 키로 부르면 403 console_only입니다
- Origin: string콘솔 세션으로 상태를 바꿀 때 같은 출처(https://xenoci.com)여야 합니다, API 키 요청에는 필요 없습니다
- Idempotency-Key: optional string24시간 안에 같은 키로 다시 보내면 처음 응답을 그대로 돌려줍니다, 같은 키에 다른 본문이면 409 idempotency_conflict255자 이하
Path Parameters
- org_id: string조직 ID형식
^org_[A-Za-z0-9]+$
Body
- usd: integerUSD 정수, 프리셋 5, 20, 50, 100 또는 5 이상 직접 입력, 문자열과 소수와 지수 표기는 invalid_amount최소 5
Returns
201 application/json
TopupCreated: object
topup: Topup { order_id, organization_id, status, … }
만들어진 주문- order_id: string주문 ID, PortOne paymentId로 그대로 씁니다형식
^[A-Za-z0-9_-]{6,64}$ - organization_id: optional string조직 ID
- status: "pending" or "confirming" or "paid" or "failed" or "expired" or "cancelled"주문 상태
- usd: integer충전 USD
- credit_micro_usd: integer적립될 크레딧(µUSD)최소 0
- credit_usd: string같은 금액의 USD 문자열(센트, 소수 2자리)형식
^-?[0-9]+\.[0-9]{2}$ - amount_krw: integer결제 원화, ceil(usd × 고정 환율), 부가세 포함
- supply_krw: optional integer공급가액
- vat_krw: integer부가세
- fx_day: string환율 기준일
- fixed_krw_per_usd: string주문에 고정된 환율
- payment_id: optional string or nullPortOne paymentId
- method: optional string or null결제 수단
- paid_at: optional string or null결제 시각
- receipt_url: optional string or null영수증 URL
- cancelled_krw: optional integer취소된 원화(철회)
- withdrawable_until: optional string or null청약철회 기한, 결제 시각 + 7일
- failure_code: optional string or null실패 코드
- created_at: string만든 시각RFC 3339 UTC 시각
- expires_at: optional string대기 주문 만료 시각, 만든 뒤 60분RFC 3339 UTC 시각
portone: object { store_id, channel_key, payment_id, … }
PortOne 결제창에 넘길 값- store_id: stringPortOne 상점 ID
- channel_key: stringPortOne 채널 키
- payment_id: stringorder_id와 같습니다
- order_name: string주문 이름
- currency: string통화
- total_amount: integer결제 금액
- pay_method: "CARD" or "EASY_PAY" or "TRANSFER"결제 방식
- test: optional boolean테스트 결제인지
- customer: optional object구매자 정보
- redirect_url: optional string결제 뒤 돌아올 URL
오류
- invalid_amount: HTTP 400충전 usd는 정수여야 합니다, 프리셋 5, 20, 50, 100 또는 5 이상(직접 입력 최대 이하)
- invalid_request: HTTP 400본문이나 쿼리가 검증에 실패했습니다, error.details.fields에 문제 필드가 있습니다
- session_required: HTTP 401콘솔 엔드포인트를 유효한 콘솔 세션 쿠키 없이 불렀습니다
- console_only: HTTP 403콘솔 세션 전용 엔드포인트입니다, API 키로는 결제, 키, 멤버 관리를 할 수 없습니다
- insufficient_permission: HTTP 403역할이나 키 권한(run/read)이 이 요청을 허용하지 않습니다
- not_found: HTTP 404자원이 없거나 이 호출자에게 보이지 않습니다
- idempotency_conflict: HTTP 409같은 Idempotency-Key를 다른 본문으로 다시 썼습니다
- rate_limited: HTTP 429, 다시 시도 가능이 키 또는 조직의 요청이 너무 많습니다, retry_after_s만큼 기다리세요
- internal_error: HTTP 500, 다시 시도 가능예상하지 못한 오류입니다, 다시 시도하고 반복되면 request_id와 함께 알려 주세요
오류 봉투 모양과 전체 코드 표는 오류 코드에 있습니다