본문으로 건너뛰기

충전 주문 생성

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 string
    24시간 안에 같은 키로 다시 보내면 처음 응답을 그대로 돌려줍니다, 같은 키에 다른 본문이면 409 idempotency_conflict
    255자 이하

Path Parameters

  • org_id: string
    조직 ID
    형식 ^org_[A-Za-z0-9]+$

Body

  • usd: integer
    USD 정수, 프리셋 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 null
        PortOne 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: string
        PortOne 상점 ID
      • channel_key: string
        PortOne 채널 키
      • payment_id: string
        order_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와 함께 알려 주세요
  • fx_rate_unavailable: HTTP 503, 다시 시도 가능
    오늘의 고정 원화 환율이 아직 없습니다, 환율이 올라올 때까지 충전이 닫힙니다
  • payments_unavailable: HTTP 503, 다시 시도 가능
    이 환경에서는 충전이 꺼져 있습니다(기능 플래그 또는 결제 키 없음)

오류 봉투 모양과 전체 코드 표는 오류 코드에 있습니다

다음 단계