본문으로 건너뛰기

충전 주문 생성

USD 정수 금액으로 충전 주문을 만듭니다, 원화 금액과 환율은 주문에 고정됩니다

POST /v1/organizations/{org_id}/topups

인증

방식허용조건
API 키 (Authorization: Bearer xci_live_…)불가콘솔 세션 전용, 키로 부르면 403 console_only
콘솔 세션 쿠키가능조직 역할 owner, admin, 같은 출처의 Origin 헤더 필요
  • 가격은 부가세 포함입니다, 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 기준)이 계약 초안과 다른 점

  • 핸들러는 server/ci/runner2-topup-routes.js에 있지만 아직 앱에 마운트되지 않았습니다, 지금 호출하면 404 not_found입니다
  • 마운트된 뒤에도 usd는 JSON 숫자여야 합니다, 문자열 "20"은 invalid_amount입니다

매개변수

이름위치타입필수설명
org_id경로string필수조직 ID, 형식 ^org_[A-Za-z0-9]+$
Idempotency-Key헤더string24시간 안에 같은 키로 다시 보내면 처음 응답을 그대로 돌려줍니다, 같은 키에 다른 본문이면 409 idempotency_conflict, 1자 이상, 255자 이하

요청 본문

필드타입필수설명
usdinteger필수USD 정수, 프리셋 5, 20, 50, 100 또는 5 이상 직접 입력, 문자열과 소수와 지수 표기는 invalid_amount, 최소 5

응답

201

필드타입항상설명
topupobject항상만들어진 주문
topup.order_idstring항상주문 ID, PortOne paymentId로 그대로 씁니다, 형식 ^[A-Za-z0-9_-]{6,64}$
topup.organization_idstring조직 ID
topup.statuspending | confirming | paid | failed | expired | cancelled항상주문 상태
topup.usdinteger항상충전 USD
topup.credit_micro_usdinteger항상적립될 크레딧(µUSD), 최소 0
topup.credit_usdstring항상같은 금액의 USD 문자열(소수 6자리), 형식 ^-?[0-9]+\.[0-9]{6}$
topup.amount_krwinteger항상결제 원화, ceil(usd × 고정 환율), 부가세 포함
topup.supply_krwinteger공급가액
topup.vat_krwinteger항상부가세
topup.fx_daydate항상환율 기준일
topup.fixed_krw_per_usdstring항상주문에 고정된 환율
topup.payment_idstringPortOne paymentId, null 가능
topup.methodstring결제 수단, null 가능
topup.paid_attimestamp 또는 null결제 시각, null 가능
topup.receipt_urlstring영수증 URL, null 가능
topup.cancelled_krwinteger취소된 원화(철회)
topup.withdrawable_untiltimestamp 또는 null청약철회 기한, 결제 시각 + 7일, null 가능
topup.failure_codestring실패 코드, null 가능
topup.created_attimestamp항상만든 시각
topup.expires_attimestamp대기 주문 만료 시각, 만든 뒤 60분
portoneobject항상PortOne 결제창에 넘길 값
portone.store_idstring항상PortOne 상점 ID
portone.channel_keystring항상PortOne 채널 키
portone.payment_idstring항상order_id와 같습니다
portone.order_namestring항상주문 이름
portone.currencyCURRENCY_KRW항상통화
portone.total_amountinteger항상결제 금액
portone.pay_methodCARD | EASY_PAY | TRANSFER항상결제 방식
portone.testboolean테스트 결제인지
portone.customerobject구매자 정보
portone.redirect_urlstring결제 뒤 돌아올 URL
{
  "topup": {
    "order_id": "topup_9f3KqZ2mP1",
    "organization_id": "org_7Kq2m9ZtP3",
    "status": "paid",
    "usd": 20,
    "credit_micro_usd": 20000000,
    "credit_usd": "20.000000",
    "amount_krw": 28775,
    "supply_krw": 26159,
    "vat_krw": 2616,
    "fx_day": "2026-10-12",
    "fixed_krw_per_usd": "1438.71",
    "payment_id": "topup_9f3KqZ2mP1",
    "method": "card",
    "paid_at": "2026-10-12T03:11:05Z",
    "receipt_url": "https://…",
    "cancelled_krw": 0,
    "withdrawable_until": "2026-10-19T03:11:05Z",
    "failure_code": null,
    "created_at": "2026-10-12T03:11:05Z",
    "expires_at": "2026-10-12T04:11:05Z"
  },
  "portone": {
    "store_id": "store-…",
    "channel_key": "channel-key-…",
    "payment_id": "topup_9f3KqZ2mP1",
    "order_name": "XenoCI 크레딧 US$20",
    "currency": "KRW",
    "total_amount": 28775,
    "pay_method": "CARD",
    "test": false,
    "customer": {
      "email": "owner@example.com"
    },
    "redirect_url": "https://xenoci.com/console/topup?order=topup_9f3KqZ2mP1"
  }
}

오류

HTTP코드다시 시도뜻
400invalid_amount아니오충전 usd는 정수여야 합니다, 프리셋 5, 20, 50, 100 또는 5 이상(직접 입력 최대 이하)
400invalid_request아니오본문이나 쿼리가 검증에 실패했습니다, error.details.fields에 문제 필드가 있습니다
401session_required아니오콘솔 엔드포인트를 유효한 콘솔 세션 쿠키 없이 불렀습니다
403console_only아니오콘솔 세션 전용 엔드포인트입니다, API 키로는 결제, 키, 멤버 관리를 할 수 없습니다
403insufficient_permission아니오역할이나 키 권한(run/read)이 이 요청을 허용하지 않습니다
404not_found아니오자원이 없거나 이 호출자에게 보이지 않습니다
409idempotency_conflict아니오같은 Idempotency-Key를 다른 본문으로 다시 썼습니다
429rate_limited가능이 키 또는 조직의 요청이 너무 많습니다, retryafters만큼 기다리세요
500internal_error가능예상하지 못한 오류입니다, 다시 시도하고 반복되면 request_id와 함께 알려 주세요
503fx_rate_unavailable가능오늘의 고정 원화 환율이 아직 없습니다, 환율이 올라올 때까지 충전이 닫힙니다
503payments_unavailable가능이 환경에서는 충전이 꺼져 있습니다(기능 플래그 또는 결제 키 없음)

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

예시

curl -X POST "https://xenoci.com/api/runner/v1/organizations/org_7Kq2m9ZtP3/topups" \
  --cookie "xci_console=$SESSION" \
  -H "Origin: https://xenoci.com" \
  -H "Content-Type: application/json" \
  -d '{"usd":20}'

다음 단계