토스페이먼츠 완전 연동 가이드: 결제부터 환불 자동화까지 - 코드픽 블로그
토스페이먼츠 완전 연동 가이드: 결제부터 환불 자동화까지
기술 가이드

토스페이먼츠 완전 연동 가이드: 결제부터 환불 자동화까지

2026년 2월 28일 57 views by 코드벤터

토스페이먼츠 완전 연동 가이드: 결제부터 환불 자동화까지

국내 개발자라면 결제 연동 시 가장 먼저 떠오르는 서비스가 바로 토스페이먼츠입니다. 깔끔한 문서, 풍부한 샘플 코드, 그리고 웹훅 자동화까지 갖춘 토스페이먼츠는 스타트업부터 대기업까지 폭넓게 사용됩니다. 이 글에서는 API 키 발급부터 결제 승인, 환불, 웹훅 자동화까지 전체 플로우를 실전 코드와 함께 정리합니다.


1. 시작 전 준비: API 키 발급

토스페이먼츠 개발자센터에 가입하면 두 가지 키를 발급받습니다.

키 종류사용 위치주의사항
클라이언트 키 (Client Key)프론트엔드 SDK 초기화노출되어도 무방하나 도메인 등록 필수
시크릿 키 (Secret Key)백엔드 API 호출절대 프론트엔드에 노출 금지

테스트 환경과 프로덕션 환경 키가 분리되어 있으므로, 개발 중에는 반드시 테스트 키를 사용하세요.


2. 프론트엔드: 결제 위젯 연동

토스페이먼츠 결제 위젯은 SDK를 통해 간단하게 렌더링할 수 있습니다.

javascript
// 토스페이먼츠 SDK 로드 (npm install @tosspayments/tosspayments-sdk)
import { loadTossPayments } from "@tosspayments/tosspayments-sdk";

const clientKey = "test_ck_your_client_key";
const tossPayments = await loadTossPayments(clientKey);

// 위젯 초기화
const widgets = tossPayments.widgets({ customerKey: "ANONYMOUS" });

// 결제 금액 설정
await widgets.setAmount({ currency: "KRW", value: 50000 });

// 위젯 렌더링
await Promise.all([
  widgets.renderPaymentMethods({
    selector: "#payment-method",
    variantKey: "DEFAULT",
  }),
  widgets.renderAgreement({ selector: "#agreement" }),
]);

// 결제 요청 버튼 클릭 시
document.getElementById("pay-btn").addEventListener("click", async () => {
  await widgets.requestPayment({
    orderId: "order-" + Date.now(),
    orderName: "상품명",
    successUrl: window.location.origin + "/success",
    failUrl: window.location.origin + "/fail",
    customerEmail: "customer@example.com",
    customerName: "홍길동",
  });
});

결제 요청 후 사용자는 토스페이먼츠 결제 UI로 이동하고, 완료되면 successUrl로 리다이렉트됩니다.


3. 백엔드: 결제 승인 처리

프론트엔드에서 넘어온 paymentKey, orderId, amount를 받아 최종 승인을 처리합니다. 금액 위변조 방지를 위해 반드시 백엔드에서 검증해야 합니다.

python
import httpx
import base64
from fastapi import APIRouter, HTTPException

router = APIRouter()
SECRET_KEY = "test_sk_your_secret_key"

def get_auth_header():
    encoded = base64.b64encode(f"{SECRET_KEY}:".encode()).decode()
    return {"Authorization": f"Basic {encoded}", "Content-Type": "application/json"}

@router.post("/confirm")
async def confirm_payment(payment_key: str, order_id: str, amount: int):
    # DB에서 주문 금액과 비교 검증
    order = await get_order_from_db(order_id)
    if order["amount"] != amount:
        raise HTTPException(status_code=400, detail="금액 불일치")

    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://api.tosspayments.com/v1/payments/confirm",
            headers=get_auth_header(),
            json={
                "paymentKey": payment_key,
                "orderId": order_id,
                "amount": amount,
            },
        )

    if response.status_code != 200:
        error = response.json()
        raise HTTPException(status_code=400, detail=error.get("message"))

    payment = response.json()
    await save_payment_to_db(payment)
    return payment

4. 환불(결제 취소) 자동화

환불은 POST /v1/payments/{paymentKey}/cancel 엔드포인트를 사용합니다. 전액 환불과 부분 환불 모두 지원됩니다.

python
@router.post("/cancel/{payment_key}")
async def cancel_payment(payment_key: str, cancel_amount: int = None, reason: str = "고객 요청"):
    body = {"cancelReason": reason}
    if cancel_amount:
        body["cancelAmount"] = cancel_amount  # 없으면 전액 환불

    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"https://api.tosspayments.com/v1/payments/{payment_key}/cancel",
            headers=get_auth_header(),
            json=body,
        )

    if response.status_code != 200:
        raise HTTPException(status_code=400, detail=response.json().get("message"))

    result = response.json()
    latest_cancel = result["cancels"][-1]
    return {"status": result["status"], "canceledAt": latest_cancel["canceledAt"]}

환불 처리 시간 비교

결제 수단환불 처리 시간비고
신용카드즉시 승인 취소카드사 정책에 따라 청구 취소까지 1~3일
가상계좌영업일 2일 이내환불 계좌 정보 필수
계좌이체영업일 2~3일은행 처리 시간 포함
간편결제즉시~1일PG사별 상이

5. 웹훅(Webhook)으로 실시간 이벤트 처리

가상계좌 입금 확인, 비동기 결제 완료 등 폴링 없이 실시간 처리가 필요할 때 웹훅을 사용합니다.

python
from fastapi import Request
import hmac, hashlib

WEBHOOK_SECRET = "your_webhook_secret"

@router.post("/webhook/tosspayments")
async def handle_webhook(request: Request):
    body = await request.body()
    signature = request.headers.get("tosspayments-webhook-signature", "")

    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        body,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        raise HTTPException(status_code=401, detail="Invalid signature")

    payload = await request.json()
    event_type = payload.get("eventType")
    data = payload.get("data", {})

    if event_type == "PAYMENT_STATUS_CHANGED":
        status = data.get("status")
        payment_key = data.get("paymentKey")

        if status == "DONE":
            await handle_payment_done(payment_key)
        elif status in ("CANCELED", "PARTIAL_CANCELED"):
            await handle_payment_canceled(payment_key, data)

    elif event_type == "DEPOSIT_CALLBACK":
        await handle_virtual_account_deposit(data)

    return {"ok": True}

웹훅 수신 후 200 응답을 반환해야 합니다. 7회 실패 시 토스페이먼츠가 이메일로 알림을 발송합니다.


6. 멱등성(Idempotency) 처리

네트워크 오류로 인한 중복 요청을 방지하려면 Idempotency-Key 헤더를 사용하세요.

python
import uuid

async def cancel_with_idempotency(payment_key: str, cancel_amount: int):
    idempotency_key = str(uuid.uuid4())

    headers = {
        **get_auth_header(),
        "Idempotency-Key": idempotency_key,
    }

    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"https://api.tosspayments.com/v1/payments/{payment_key}/cancel",
            headers=headers,
            json={"cancelReason": "중복 결제 환불", "cancelAmount": cancel_amount},
        )

    return response.json()

동일한 Idempotency-Key로 재요청하면 토스페이먼츠가 기존 응답을 그대로 반환합니다. 유효 기간은 15일입니다.


마무리: 실전 체크리스트

토스페이먼츠 연동 시 반드시 확인해야 할 포인트입니다.

  • 백엔드에서 금액 위변조 검증 구현
  • 시크릿 키 환경변수 관리 (절대 하드코딩 금지)
  • 웹훅 서명 검증 로직 구현
  • 멱등성 키로 중복 요청 방지
  • 가상계좌 환불 시 환불 계좌 정보 수집 UX 준비
  • 테스트 키와 프로덕션 키 분리 관리

코드벤터는 토스페이먼츠를 비롯한 다양한 결제 시스템 연동 경험을 바탕으로, 스타트업과 소규모 프로덕트의 결제 인프라 구축을 지원합니다. 결제 연동이 복잡하게 느껴진다면 코드벤터의 기술 블로그와 오픈소스 예제를 참고하세요. 빠르고 안전한 결제 시스템 구축을 함께 만들어가겠습니다.

개발 의뢰 상담

AI 서비스나 플랫폼 개발을
고민 중이신가요?

CodePick에서는
기획 → 개발 → 운영까지 함께합니다.
아이디어만 있어도 상담 가능합니다.

CodeVenter 개발팀이 직접 담당 · 1~2 영업일 내 회신

✓ 스타트업 MVP 개발✓ AI 서비스 개발✓ 웹 플랫폼 개발✓ 기업 시스템 구축✓ 모바일 앱 개발

AI Development Studio

코드픽 by 코드벤터

  • 대표: 윤승환 · 사업자등록번호: 121-57-64983
  • 대구광역시 중구 국채보상로 586, 16층 · info@codeventer.com

© 2025 코드벤터. All rights reserved.