빠른 매뉴얼

결제위젯

쿠폰·적립금·프로모션으로 금액이 바뀌는 주문서에 위젯을 붙여봐요.

이 문서는 결제위젯을 주문서에 빠르게 붙이기 위한 매뉴얼이에요. 위젯 생성·디자인·운영 옵션은 위젯 생성단건결제위젯 연동에서 더 자세히 다뤄요.

아래 순서대로 진행하면 주문서 화면에서 결제위젯을 렌더링하고 서버에서 결제 결과를 확인하는 기본 흐름을 만들 수 있어요. 관리자는 위젯에 노출할 결제수단과 스타일을 설정하고, 사용자는 주문서에 렌더링된 위젯 안에서 결제수단을 선택해요. 백엔드는 주문 확인과 상태 저장을 담당해요.

데모 영상

결제창 vs 결제위젯

결제창 결제위젯
결제수단 선택 코드에서 pg, method 지정 사용자가 위젯에서 선택
UI PG사 결제창만 결제수단 선택 UI + 약관 동의 포함
결제수단 관리 코드 수정 필요 관리자에서 노출 수단 활성화/비활성화 및 스타일 설정
추천 결제수단이 1~2개로 고정 여러 결제수단을 유연하게 관리

완성 후 모습

위젯은 관리자가 활성화한 결제수단을 주문서 화면에 보여주고, 사용자는 그 안에서 결제수단 선택과 약관 동의를 진행해요. 결제 완료 후 receipt_id를 받은 뒤부터는 백엔드가 결제 정보를 검증하고 주문을 확정해야 해요.

서버 승인을 쓰는 경우

이 빠른 매뉴얼의 기본 예제는 클라이언트 자동 승인 후 done으로 receipt_id를 받는 흐름이에요. 운영에서 extra.separately_confirmed: true를 쓰면 done 대신 confirm이 호출되고, 클라이언트의 onDone은 호출되지 않아요. 이때는 confirm에서 receipt_id를 서버로 보내고 서버가 승인 API를 호출해야 해요.

사전 준비

결제 설정에서 사용할 PG사와 결제수단을 먼저 활성화해요. 그다음 위젯 설정에서 사용할 위젯을 만들고, 생성된 widget_key를 연동 코드에 넣어요.

구현 순서

SDK 설치 (프론트엔드)

플랫폼별 표준 패키지 매니저로 설치해요.

<!-- Bootpay JS SDK (CDN) -->
<script src="https://js.bootpay.co.kr/bootpay-5.3.0.min.js"></script>

<!-- 또는 npm -->
<!-- npm install @bootpay/client-js -->html

위젯 렌더링 (프론트엔드)

사용 중인 SDK 탭에서 widget_key를 어디에 넘기는지 확인해요.

<!-- 위젯이 렌더링될 영역 -->
<div id="widget"></div>
<button id="pay-btn" disabled onclick="requestPayment()">결제하기</button>

<script>
let widgetData = null
const payButton = document.getElementById('pay-btn')

function updateButtonState(data) {
    widgetData = data
    // completed + term_passed 가 모두 true 일 때만 결제 버튼을 활성화한다.
    payButton.disabled = !(data?.completed && data?.term_passed)
}

document.addEventListener('bootpay-widget-ready', () => {
    // 위젯 SDK가 준비된 시점이다.
    console.log('위젯 준비 완료')
})
document.addEventListener('bootpay-widget-change-payment', (e) => {
    // 결제수단 변경 이벤트. e.detail 안에 pg, method, easy_pay, card_quota 등이 들어온다.
    updateButtonState(e.detail)
})
document.addEventListener('bootpay-widget-change-terms', (e) => {
    // 약관 동의 변경 이벤트. e.detail.term_passed 로 필수 약관 통과 여부를 확인한다.
    updateButtonState(e.detail)
})
document.addEventListener('bootpay-widget-resize', (e) => {
    // 위젯 높이가 바뀌면 컨테이너 높이도 같이 맞춘다.
    document.getElementById('widget').style.height = `${e.detail.height}px`
})

// JavaScript는 BootpayWidget.render(...)에 widget_key를 넘겨요.
BootpayWidget.render('#widget', {
    client_key: 'YOUR_CLIENT_KEY',
    price: 1000,
    widget_key: 'default-widget',
    sandbox: true,
    use_terms: true
})
</script>html

상세 문서: 위젯 렌더링 옵션

WidgetData 예시

위젯의 결제수단이나 약관 상태가 바뀌면 아래와 비슷한 데이터가 이벤트로 전달돼요.

{
  "pg": "nicepay",
  "method": "card",
  "method_symbol": "card",
  "easy_pay": null,
  "card_quota": "0",
  "currency": "KRW",
  "completed": true,
  "term_passed": true,
  "select_terms": [
    {
      "pk": "terms-privacy",
      "title": "개인정보 수집 및 이용 동의",
      "agree": true,
      "term_type": 1
    }
  ],
  "extra": {
    "card_quota": 0
  }
}json

결제 요청 (프론트엔드)

사용자가 위젯에서 결제수단 선택과 약관 동의를 완료하면, 각 SDK의 위젯 결제 요청 API를 호출해야 해요. 완료 이벤트에서 받은 receipt_id는 백엔드로 보내 결제 조회를 진행해야 해요.

완료 이벤트 데이터는 상태별로 아래처럼 들어와요. 결제창과 결제위젯의 최종 조회 응답은 같으므로, 서버에서는 결제 조회 응답 예시의 필드를 기준으로 주문을 확정해요.

{
  "event": "done",
  "receipt_id": "6244f60c1fc19202e42e8c4e",
  "order_id": "order_20260428_001",
  "price": 1000,
  "status": 1,
  "method": "card",
  "method_symbol": "card"
}json
async function requestPayment() {
    const response = await BootpayWidget.requestPayment({
        order_name: '테스트 상품',
        order_id: 'order_' + Date.now(),
        user: { username: '홍길동', phone: '01012345678' },
        extra: { open_type: 'iframe' },  // iframe, popup, redirect
    })

    if (response.event === 'done') {
        // 결제 완료 이벤트. receipt_id를 백엔드로 보내 결제 조회를 진행한다.
        await verifyPayment(response.receipt_id)
    }

    if (response.event === 'issued') {
        // 가상계좌 발급 이벤트. 입금 완료는 웹훅으로 최종 확정한다.
    }

    if (response.event === 'cancel' || response.event === 'error') {
        // 구매자 취소 또는 결제 실패 이벤트. 주문은 미결제 상태로 유지한다.
    }
}

async function verifyPayment(receiptId) {
    const res = await fetch('/api/server/verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ receipt_id: receiptId }),
    })
    const { success } = await res.json()
    alert(success ? '결제 완료!' : '결제 조회 실패')
}javascript

위젯에서는 pg, method를 코드에 하드코딩하지 않아요. 관리자가 활성화한 결제수단이 위젯에 표시되고, 사용자가 선택한 결과를 그대로 결제 요청 페이로드에 합쳐요.

상세 문서: 결제위젯 가이드

서버 SDK 설치 (백엔드)

SDK 설치

PG 결제창 연동 → SDK 설치의 서버 SDK 단계를 먼저 완료해요.

결제 조회 (백엔드)

결제창과 동일한 SDK 호출이에요. 서버에서는 조회한 결제 정보의 status가 결제 완료 상태인지 확인하고, DB에 저장해 둔 price, 주문 상태 등 기준값이 Bootpay 응답값과 일치하는지도 확인해요. 응답 필드는 결제 조회 응답 예시의 JSON과 같아요.

app.post('/api/server/verify', async (req, res) => {
    const { receipt_id } = req.body
    const receipt = await Bootpay.receiptPayment(receipt_id)

    if (receipt.status === 1 && receipt.price === 1000) {
        await db.payments.create({
            receipt_id,
            price: receipt.price,
            method: receipt.method_symbol,
            paid_at: receipt.purchased_at,
        })
        res.json({ success: true })
    } else {
        await Bootpay.cancelPayment({
            receipt_id,
            cancel_price: receipt.price,
            cancel_message: '검증 실패',
        })
        res.json({ success: false })
    }
})javascript

상세 문서: 결제 조회

다음 단계