Skip to content

결제 테스트 가이드 ​

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

결제 시스템 구조 ​

결제 처리 흐름 ​

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

주의 사항 ​

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

1. PaymentKey ​

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

2. 카드 결제 승인 취소 ​

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

결제 API ​

결제 승인 및 취소 API는 여기서 확인할 수 있어요.

진행 중인 통합결제창 닫기 ​

파트너 POS에서 취소 요청을 받으면 프론트 플러그인에서 sdk.payment.cancelPayment()를 호출해 통합결제창을 닫을 수 있어요. 닫기 가능 여부를 미리 확인하려면 sdk.payment.canCancelPayment()를 사용해요.

프론트 앱 2.71 이상에서 아래 항목을 확인해주세요. 반환값과 화면 전환 시 주의사항은 각 API 레퍼런스를 참고해주세요.

테스트 상황확인할 결과
결제창이 열려 있고 VAN 승인이 시작되기 전에 닫기 요청canceled: true이고, 대기 중인 requestPayment에서 CANCELED 결과를 받아요.
VAN 승인 중 또는 승인 완료 화면에서 닫기 요청canceled: false이고, 결제창이 닫히지 않아요.
결제창이 없는 상태에서 닫기 요청canceled: false를 받아요.
결제창 닫기 후 화면 전환requestPayment의 결과 분기에서만 화면을 전환하는지 확인해요.

로컬 SDK 모의 환경에서는 두 메서드가 각각 canCancel: true, canceled: true를 반환해요. 실제 결제 상태에 따른 제한과 requestPayment 결과 처리는 프론트 단말기에서 확인해주세요.

결제 승인 기능 구현 예시 ​

결제 승인 요청 ​

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

  // 결제 완료 여부 확인
  const paymentResult = await sdk.payment.getPayment(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.getPayment(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. 백업: 중요 결제 데이터 백업 체계

문의사항 ​

개발·연동 중 궁금한 점은 개발자 포럼에 남겨주세요. 다른 개발자의 질문과 답변도 확인할 수 있어요.

특정 단말기나 결제 오류를 확인해야 한다면 오류 문의에 필요한 정보를 확인해 주세요.