충전금 운영 절차

충전 절차, 잔액 모니터링, 402 처리 패턴 등 운영 단계의 절차를 안내합니다.

충전금 결제 모델·차감/환불 규칙·거래 사유 코드는 충전금 모델을, 엔드포인트 명세는 Credits API를 참고하세요.

충전 절차

Sandbox와 Live 환경의 충전 방법은 다음과 같습니다.

환경충전 방법비고
Sandbox파트너 포털 > 충전금 > 충전 메뉴에서 직접 입력 (가상 충전금)실제 비용 발생 없음. 원하는 금액만큼 충전 가능
Live파트너 포털 > 충전금 > 충전 메뉴에서 PG 결제실제 결제·환불 처리
Sandbox API 충전: 통합 테스트 자동화 등 코드 흐름에서 충전이 필요한 경우 POST /credits/sandbox/charge API로도 충전할 수 있습니다 (Sandbox 전용). 상세는 Credits API를 참고하세요.
환경 분리: Sandbox와 Live 환경의 충전금은 완전히 분리되어 있습니다. Sandbox에서 사용한 충전금은 Live에 영향을 주지 않습니다. 상세는 충전금 모델 — 환경 분리를 참고하세요.

차감/환불 동작

충전금은 주문 생성 시 자동 차감되고 주문 취소 시 자동 환불됩니다. 별도 충전금 차감/환불 API 호출이 필요하지 않습니다. 차감 공식·환불 가능 상태·부분 취소 환불 규칙 등 상세 모델은 충전금 모델 — 차감·환불 규칙을 참고하세요.

모니터링 권장사항

  • 잔액 부족 알림 설정: GET /credits로 잔액을 정기 조회해 일정 기준 이하로 떨어지면 클라이언트가 알림을 발송하도록 설정하세요. 기준값은 평균 주문 금액·일 주문 빈도에 따라 결정하세요.
  • 거래 내역 모니터링: GET /credits/transactions로 정기적으로 거래 내역을 확인하세요. 정렬은 최근 거래 순(createdAt 내림차순)이며, 운영 회계와 대조 시 응답의 balanceAfter 필드로 시점별 잔액을 확인할 수 있습니다.

주문 직전 잔액 검증은 POST /orders/estimate로 한 번에 처리하는 것을 권장합니다. 아래 402 에러 처리 패턴 참조.

402 에러 처리 패턴

충전금이 부족한 상태에서 주문을 생성하면 402 Payment Required(ERR_INSUFFICIENT_CREDIT)가 반환됩니다. 다음 패턴으로 처리하는 것을 권장합니다.

  • 견적으로 사전 검증: POST /orders/estimate 응답의 data 객체에 paidCreditAmount·creditBalance·creditSufficient가 포함되어, 한 번의 호출로 차감 예정액과 충분 여부를 확인할 수 있습니다.
  • creditSufficient: false면 클라이언트가 충전 안내를 처리 (자동 충전 권장 안 함 — 의도하지 않은 결제 발생 방지)
  • 충전 후 동일 요청 본문으로 재시도 (Idempotency-Key를 활용해 중복 주문 방지)
  • 동시성 대비: 견적 통과 후에도 다른 클라이언트의 동시 차감으로 POST /orders가 402를 반환할 수 있으므로 분기를 함께 처리합니다.

402 응답 본문 shape(datarequired·balance·currency 동봉) 등 errorCode 분기는 에러 코드 & 트러블슈팅케이스 E를 참고하세요.

Node.js
async function createOrderWithEstimate(orderData, idempotencyKey) {
  const baseUrl = 'https://api-sandbox.sweetbook.com';
  const headers = {
    'Authorization': 'Bearer {YOUR_API_KEY}',
    'Content-Type': 'application/json; charset=utf-8',
  };

  // 1. 견적으로 잔액 사전 검증 (paidCreditAmount + creditBalance + creditSufficient)
  const est = await fetch(`${baseUrl}/v1/orders/estimate`, {
    method: 'POST',
    headers,
    body: JSON.stringify(orderData),
  }).then(res => res.json());

  if (!est.data.creditSufficient) {
    // 충전 안내 (자동 충전 금지 — 의도하지 않은 결제 방지)
    throw new Error(
      `충전금이 부족합니다. 필요: ${est.data.paidCreditAmount}, 잔액: ${est.data.creditBalance}`
    );
  }

  // 2. 주문 생성 (Idempotency-Key로 중복 방지)
  const response = await fetch(`${baseUrl}/v1/orders`, {
    method: 'POST',
    headers: { ...headers, 'Idempotency-Key': idempotencyKey },
    body: JSON.stringify(orderData),
  });

  // 3. 동시성 대비: 견적 통과 후에도 동시 차감으로 402 가능
  if (response.status === 402) {
    const body = await response.json();
    throw new Error(
      `충전금이 부족합니다. 필요: ${body.data.required}, 잔액: ${body.data.balance}`
    );
  }

  // 다른 에러(401/403/400 등)는 별도 처리 — /docs/operations/errors 참조
  return response.json();
}