본문으로 건너뛰기

충전 결제 확인

결제창에서 돌아온 뒤 호출합니다, 서버가 PortOne에서 결제를 다시 읽어 금액을 대조하고 크레딧을 적립합니다

POST /v1/organizations/{org_id}/topups/{order_id}/confirm

인증

방식허용조건
API 키 (Authorization: Bearer xci_live_…)불가콘솔 세션 전용, 키로 부르면 403 console_only
콘솔 세션 쿠키가능조직 역할 owner, admin, 같은 출처의 Origin 헤더 필요
  • 멱등입니다, 이미 결제된 주문을 다시 확인하면 같은 결과를 돌려주고 크레딧을 다시 적립하지 않습니다, PortOne 웹훅도 같은 경로로 정산됩니다

참고 서버 구현(2026-10-12 dev 기준)이 계약 초안과 다른 점

  • 핸들러는 server/ci/runner2-topup-routes.js에 있지만 아직 앱에 마운트되지 않았습니다, 지금 호출하면 404 not_found입니다, PortOne 웹훅 경로(server/payment-webhook.js)는 연결되어 있어 결제 정산 자체는 됩니다

매개변수

이름위치타입필수설명
org_id경로string필수조직 ID, 형식 ^org_[A-Za-z0-9]+$
order_id경로string필수충전 주문 ID, 형식 ^[A-Za-z0-9_-]{6,64}$

요청 본문

필드타입필수설명
payment_idstring필수결제창이 돌려준 paymentId, 200자 이하

응답

200

필드타입항상설명
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분
balanceobject항상적립 뒤 잔액
balance.organization_idstring항상조직 ID
balance.balance_micro_usdinteger항상원장 합계 잔액(µUSD), 최소 0
balance.balance_usdstring항상같은 금액의 USD 문자열(소수 6자리), 형식 ^-?[0-9]+\.[0-9]{6}$
balance.available_micro_usdinteger항상만료되지 않은 로트 합에서 실행 중인 잡이 이미 쓴 금액을 뺀 값, 최소 0
balance.available_usdstring항상같은 금액의 USD 문자열(소수 6자리), 형식 ^-?[0-9]+\.[0-9]{6}$
balance.reserved_micro_usdinteger항상실행 중인 잡이 이미 쓴 금액, 최소 0
balance.reserved_usdstring항상같은 금액의 USD 문자열(소수 6자리), 형식 ^-?[0-9]+\.[0-9]{6}$
balance.rate_micro_usd_per_mininteger항상지금 적용되는 분당 요율(µUSD)
balance.lowboolean항상사용 가능 잔액이 낮은 잔액 기준 아래인지
balance.low_threshold_micro_usdinteger낮은 잔액 기준, 최소 0
balance.low_threshold_usdstring낮은 잔액 기준(USD), 형식 ^-?[0-9]+\.[0-9]{6}$
balance.next_expiryobject다음에 만료될 로트의 시각과 남은 금액, 없으면 null, null 가능
balance.next_expiry.expires_attimestamp만료 시각
balance.next_expiry.remaining_micro_usdinteger금액(µUSD 정수), 최소 0
balance.next_expiry.remaining_usdstring같은 금액의 USD 문자열(소수 6자리), 형식 ^-?[0-9]+\.[0-9]{6}$
balance.topup_urlstring항상콘솔 충전 페이지, 결제 토큰은 없고 로그인이 필요합니다
tierobject적립 뒤 티어
tier.organization_idstring항상조직 ID
tier.tierinteger항상티어
tier.cumulative_topup_micro_usdinteger항상누적 충전액, 최소 0
tier.cumulative_topup_usdstring항상같은 금액의 USD 문자열(소수 6자리), 형식 ^-?[0-9]+\.[0-9]{6}$
tier.max_concurrent_jobsinteger항상동시 실행 잡 수
tier.running_jobsinteger항상지금 실행 중인 잡
tier.queued_jobsinteger대기 중인 잡
tier.monthly_limit_micro_usdinteger월 한도(µUSD), null 가능
tier.monthly_limit_usdstring월 한도(USD 정수), null 가능
tier.month_usage_micro_usdinteger항상이번 달 사용액, 최소 0
tier.month_usage_usdstring항상같은 금액의 USD 문자열(소수 6자리), 형식 ^-?[0-9]+\.[0-9]{6}$
tier.month_resets_attimestamp월 한도가 풀리는 시각(다음 달 1일 00:00 UTC)
tier.next_tierobject 또는 null다음 티어와 조건, 최고 티어면 null, null 가능
tier.upgraded_attimestamp 또는 null마지막 상향 시각, null 가능
{
  "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"
  },
  "balance": {
    "organization_id": "org_7Kq2m9ZtP3",
    "balance_micro_usd": 20000000,
    "balance_usd": "20.000000",
    "available_micro_usd": 19881200,
    "available_usd": "19.881200",
    "reserved_micro_usd": 118800,
    "reserved_usd": "0.118800",
    "rate_micro_usd_per_min": 39600,
    "low": false,
    "low_threshold_micro_usd": 1188000,
    "low_threshold_usd": "1.188000",
    "next_expiry": {
      "expires_at": "2027-10-12T03:11:05Z",
      "remaining_micro_usd": 19881200,
      "remaining_usd": "19.881200"
    },
    "topup_url": "https://xenoci.com/console/topup?org=org_7Kq2m9ZtP3"
  },
  "tier": {
    "organization_id": "org_7Kq2m9ZtP3",
    "tier": 1,
    "cumulative_topup_micro_usd": 20000000,
    "cumulative_topup_usd": "20.000000",
    "max_concurrent_jobs": 2,
    "running_jobs": 1,
    "queued_jobs": 0,
    "monthly_limit_micro_usd": 100000000,
    "monthly_limit_usd": "100.000000",
    "month_usage_micro_usd": 475200,
    "month_usage_usd": "0.475200",
    "month_resets_at": "2026-11-01T00:00:00Z",
    "next_tier": {
      "tier": 1,
      "name": "Tier 1",
      "min_cumulative_topup_micro_usd": 5000000,
      "min_cumulative_topup_usd": "5.000000",
      "max_concurrent_jobs": 2,
      "max_queued_jobs": 10,
      "monthly_limit_micro_usd": 100000000,
      "monthly_limit_usd": "100.000000"
    },
    "upgraded_at": "2026-10-12T03:11:05Z"
  }
}

오류

HTTP코드다시 시도뜻
400invalid_request아니오본문이나 쿼리가 검증에 실패했습니다, error.details.fields에 문제 필드가 있습니다
401session_required아니오콘솔 엔드포인트를 유효한 콘솔 세션 쿠키 없이 불렀습니다
403console_only아니오콘솔 세션 전용 엔드포인트입니다, API 키로는 결제, 키, 멤버 관리를 할 수 없습니다
403insufficient_permission아니오역할이나 키 권한(run/read)이 이 요청을 허용하지 않습니다
404not_found아니오자원이 없거나 이 호출자에게 보이지 않습니다
409payment_amount_mismatch아니오PortOne 결제의 금액, 통화, 상점이 주문과 다릅니다, 아무것도 적립되지 않았습니다
409payment_not_paid가능PortOne이 아직 결제 완료(PAID)로 보고하지 않습니다, 주문 상태를 다시 조회하세요
409payment_reference_reused아니오이 PortOne paymentId는 이미 다른 주문에 묶여 있습니다
409topup_expired아니오주문이 60분 대기 기간을 넘겼습니다, 새 주문을 만드세요
429rate_limited가능이 키 또는 조직의 요청이 너무 많습니다, retryafters만큼 기다리세요
500internal_error가능예상하지 못한 오류입니다, 다시 시도하고 반복되면 request_id와 함께 알려 주세요
503payments_unavailable가능이 환경에서는 충전이 꺼져 있습니다(기능 플래그 또는 결제 키 없음)

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

예시

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

다음 단계