Skip to content

결제 테스트 가이드

프론트 플러그인에서 결제 기능을 테스트하는 방법과 주의사항을 안내합니다.

결제 시스템 구조

결제 처리 흐름

  1. 서드 파티 서버
  2. 플러그인: 결제 요청 및 결과 처리
  3. 프론트 SDK: 플러그인과 결제앱 연결
  4. 결제앱: 실제 결제 처리 (카드 리더기 등)
  5. VAN/카드사: 결제 중계 및 최종 승인 처리

주의 사항

프론트에서 카드 결제 테스트 시 실제 승인이 발생하여 주의가 필요합니다.

1. PaymentKey

  • PaymentKey는 파트너사와 토스플레이스 간에 중복 결제를 검사할 수 있는 값이므로, 동일 청구서(결제건)에는 동일 PaymentKey 발번이 필요합니다.

2. 카드 결제 승인 취소

  • 테스트 후 반드시 즉시 취소 처리 필요하므로, 결제 응답의 paymentKey, approvalNumber, timestamp 등 승인 취소를 위한 값을 저장하여 취소에 활용합니다.
  • 동작 확인 목적이라면 현금 결제 > 현금영수증 미발급으로 테스트하는 것을 권장합니다.

결제 API

결제 승인 및 취소 API는 여기서 확인하실 수 있습니다.

결제 승인 기능 구현 예시

결제 승인 요청

javascript
/**
 * 앱 시작 시 미완료 결제 복구
 * 결제 중 페이지 이탈로 응답을 받지 못한 경우를 처리합니다.
 */
async function recoverPendingPayment() {
  const pendingPaymentKey = sdk.payment.getBackupPaymentKey();
  if (!pendingPaymentKey) {
    return; // 미완료 결제 없음
  }

  // 결제 완료 여부 확인
  const paymentResult = await sdk.payment.getPaymentByKey(pendingPaymentKey);
  if (paymentResult) {
    // 결제가 완료되었지만 서버 업로드 전 이탈한 경우
    console.log("미완료 결제 발견, 서버로 업로드:", pendingPaymentKey);
    await uploadPaymentResult(pendingPaymentKey, paymentResult);
  }

  // 복구 완료 후 제거
  sdk.payment.resetBackupPaymentKey();
}

/**
 * 결제 처리 메인 함수
 */
async function processPayment() {
  // 1. 서버로부터 결제 요청 수신
  const paymentRequest = await fetchPaymentRequest();
  const { paymentKey, tax, supplyValue, tip } = paymentRequest;

  // 2. 중복 결제 요청 방지
  if (await isAlreadyPaid(paymentKey)) {
    console.log("이미 결제된 paymentKey입니다:", paymentKey);
    return;
  }

  try {
    // 4. 결제 요청
    const result = await sdk.payment.requestPayment({
      paymentKey,
      tax,
      supplyValue,
      tip: tip || 0,
      timeoutMs: 60000,
    });

    // 5. 결과 처리
    if (result.type === "SUCCESS") {
      await onPaymentSuccess(paymentKey, result.response);
    } else {
      await onPaymentFailure(result.type);
    }
  } catch (error) {
    await onPaymentError(error);
    throw error;
  }
}

/**
 * 서버로부터 결제 요청을 받는 함수
 * 아래 3가지 방식 중 하나를 선택하여 구현하세요:
 *   (1) WebSocket - 실시간 양방향 통신
 *   (2) SSE (Server-Sent Events) - 서버에서 클라이언트로 단방향 실시간 이벤트 전송
 *   (3) Polling - 주기적으로 서버에 결제 요청 여부 확인
 */
async function fetchPaymentRequest() {
  // TODO: 선택한 방식으로 서버로부터 결제 요청을 수신하는 로직 구현
  // 반환값: { paymentKey, tax, supplyValue, tip }
}

/**
 * 이미 결제된 건인지 확인
 */
async function isAlreadyPaid(paymentKey) {
  const existingPayment = await sdk.payment.getPaymentByKey(paymentKey);
  return !!existingPayment;
}

/**
 * 결제 성공 처리
 */
async function onPaymentSuccess(paymentKey, paymentResponse) {
  // 서버에 결제 결과 저장 (필수)
  await uploadPaymentResult(paymentKey, paymentResponse);

  sdk.payment.resetBackupPaymentKey();

  console.log("결제 성공:", paymentResponse);
}

async function onPaymentFailure(failureType) {
  sdk.payment.resetBackupPaymentKey();
  console.error("결제 실패:", failureType);
}

async function onPaymentError(error) {
  sdk.payment.resetBackupPaymentKey();
  console.error("결제 오류:", error);
}

/**
 * 결제 결과를 서버에 업로드
 */
async function uploadPaymentResult(paymentKey, paymentResponse) {
  const response = await fetch("https://your-server.com/api/payments", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      paymentKey,
      approvalNumber: paymentResponse.approvalNumber,
      timestamp: paymentResponse.timestamp,
      paymentMethod: paymentResponse.paymentMethod,
      amount: paymentResponse.amount,
      installment: paymentResponse.installment,
      ...등,
    }),
  });

  if (!response.ok) {
    throw new Error("결제 결과 서버 저장 실패");
  }
}

지원 결제 수단

  1. 카드 결제: IC카드, 마그네틱 카드, NFC 결제 (삼성페이, 애플페이 등)

  2. 간편결제: QR/바코드 (토스페이, 카카오페이, 네이버페이 등)

  3. 현금: 현금영수증 발행 가능

  4. 그 외: 알리페이, 위챗페이 등

승인 취소 기능 구현 예시

승인 취소 주의사항

  • 전액 취소만 가능하고 부분 취소는 지원하지 않음

결제 승인 취소

javascript
/**
 * 취소 처리 메인 함수
 */
async function processPaymentCancel() {
  // 1. 서버로부터 취소 요청 수신
  const cancelRequest = await fetchCancelRequest();
  const {
    paymentKey,
    paymentMethod,
    tax,
    supplyValue,
    timestamp,
    approvalNumber,
    installment,
    extraData,
  } = cancelRequest;

  try {
    // 2. 취소 요청
    const result = await sdk.payment.requestPaymentCancel({
      paymentKey,
      paymentMethod,
      tax,
      supplyValue,
      tip: 0,
      timestamp,
      approvalNumber,
      installment,
      timeoutMs: 60000,
      extraData,
    });

    // 3. 결과 처리
    if (result.type === "SUCCESS") {
      await onCancelSuccess(paymentKey, result.response);
    } else {
      onCancelFailure(paymentKey, result.type);
    }
  } catch (error) {
    onCancelError(paymentKey, error);
    throw error;
  }
}

/**
 * 서버로부터 취소 요청을 받는 함수
 * 결제 요청과 동일한 방식으로 구현하세요:
 *   (1) WebSocket, (2) SSE, (3) Polling
 */
async function fetchCancelRequest() {
  // TODO: 서버로부터 취소 요청 수신 로직 구현
  // 반환값: { paymentKey, paymentMethod, tax, supplyValue, timestamp, approvalNumber, installment, extraData }
}

/**
 * 취소 성공 처리
 */
async function onCancelSuccess(paymentKey, cancelResponse) {
  // 서버에 취소 결과 저장 (필수)
  await uploadCancelResult(paymentKey, cancelResponse);
  console.log("취소 성공:", cancelResponse);
}

function onCancelFailure(paymentKey, failureType) {
  console.error("취소 실패:", failureType);
}

function onCancelError(paymentKey, error) {
  console.error("취소 오류:", error);
}

/**
 * 취소 결과를 서버에 업로드
 */
async function uploadCancelResult(paymentKey, cancelResponse) {
  const response = await fetch("https://your-server.com/api/payments/cancel", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      paymentKey,
      cancelApprovalNumber: cancelResponse.approvalNumber,
      cancelTimestamp: cancelResponse.timestamp,
    }),
  });

  if (!response.ok) {
    throw new Error("취소 결과 서버 저장 실패");
  }
}

제품 출시 전 확인사항

필수 체크리스트

  • [ ] 모든 결제수단 정상 동작 확인
  • [ ] 부가세 계산 정확성 확인
  • [ ] 취소 기능 정상 동작 확인
  • [ ] 에러 상황별 적절한 메시지 표시
  • [ ] 결제 데이터 정확한 저장/전송
  • [ ] 네트워크 오류 시 안정적 처리

운영 고려사항

  1. 로그 관리: 결제 관련 모든 이벤트 로깅
  2. 보안: 결제 정보 암호화 전송
  3. 모니터링: 결제 성공률 실시간 모니터링
  4. 백업: 중요 결제 데이터 백업 체계

문의사항

결제 테스트 관련 문의는 developer-support@tossplace.com으로 연락주세요.