Appearance
결제 테스트 가이드
프론트 플러그인에서 결제 기능을 테스트하는 방법과 주의사항을 안내합니다.
결제 시스템 구조
결제 처리 흐름
- 서드 파티 서버
- 플러그인: 결제 요청 및 결과 처리
- 프론트 SDK: 플러그인과 결제앱 연결
- 결제앱: 실제 결제 처리 (카드 리더기 등)
- 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("결제 결과 서버 저장 실패");
}
}지원 결제 수단
카드 결제: IC카드, 마그네틱 카드, NFC 결제 (삼성페이, 애플페이 등)
간편결제: QR/바코드 (토스페이, 카카오페이, 네이버페이 등)
현금: 현금영수증 발행 가능
그 외: 알리페이, 위챗페이 등
승인 취소 기능 구현 예시
승인 취소 주의사항
- 전액 취소만 가능하고 부분 취소는 지원하지 않음
결제 승인 취소
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("취소 결과 서버 저장 실패");
}
}제품 출시 전 확인사항
필수 체크리스트
- [ ] 모든 결제수단 정상 동작 확인
- [ ] 부가세 계산 정확성 확인
- [ ] 취소 기능 정상 동작 확인
- [ ] 에러 상황별 적절한 메시지 표시
- [ ] 결제 데이터 정확한 저장/전송
- [ ] 네트워크 오류 시 안정적 처리
운영 고려사항
- 로그 관리: 결제 관련 모든 이벤트 로깅
- 보안: 결제 정보 암호화 전송
- 모니터링: 결제 성공률 실시간 모니터링
- 백업: 중요 결제 데이터 백업 체계
문의사항
결제 테스트 관련 문의는 developer-support@tossplace.com으로 연락주세요.