결제위젯

단건결제위젯 연동

위젯 렌더링부터 인증 결과, 승인 요청, 결과 수신까지 이어봐요.

결제위젯은 주문서 페이지 안에 결제수단 선택 UI​​를 먼저 렌더링하고, 구매자가 선택을 마치면 주문하기 버튼으로 결제를 요청하는 방식이에요. 쿠폰, 적립금, 결제수단별 프로모션처럼 주문서 로직이 복잡할수록 PG 결제창만 띄우는 방식보다 결제위젯이 관리하기 쉬워요.

이 문서의 목표는 화면에 위젯을 띄우는 것에서 끝나지 않아요. 독자가 아래 흐름을 따라 주문서에서 결제 완료를 안전하게 확정하는 지점​​까지 도달하도록 구성했어요.

독자 상황 먼저 볼 내용 완성 기준
처음 붙이는 개발자 첫 결제까지 붙이기 위젯 렌더링 → 결제 버튼 활성화 → 결제 요청 → 인증 결과 수신 → 결과 확인까지 연결
운영 주문서를 만드는 개발자 주문서 전체 흐름으로 합치기 쿠폰·적립금 변경, 인증 결과 수신, 결제 승인 요청, 결과 페이지 이동까지 연결
특정 SDK 이슈를 확인하는 개발자 이벤트·금액 업데이트·플랫폼별 상세 SDK별 콜백, 높이 조정, 재로드/종료 처리를 확인
빠르게 시작하려면?

전체 구조보다 최소 E2E 코드가 먼저 필요하면 빠른 매뉴얼의 결제위젯을 먼저 봐요. 이 페이지는 전체 플랫폼(Android/iOS/Flutter/RN)과 운영 옵션까지 확인하는 상세 문서예요.

결제위젯 프로세스

0연동 전에 준비할 것

이 문서는 주문서 화면에 단건결제위젯을 렌더링하고, 결제 요청부터 서버 조회·주문 확정까지 연결하는 연동 가이드​​예요. 관리자에서 해야 하는 위젯 생성·결제수단·디자인 설정은 위젯 생성에서 먼저 끝내요.

여기서 “준비한다”는 말은 Bootpay 관리자에서 발급·설정할 값​​과 가맹점 백엔드에서 직접 구현할 API​​를 나눠 둔다는 뜻이에요.

구분 준비 항목 누가 준비하나 확인 위치
Bootpay 설정 PG·결제수단 활성화 Bootpay 관리자에서 설정 결제 설정, 위젯 생성
Bootpay 설정 client_key Bootpay 관리자에서 발급 연동키 발급
Bootpay 설정 widget_key Bootpay 관리자에서 위젯 생성 시 발급 위젯 생성
가맹점 서버 주문 준비 API 가맹점 백엔드에서 구현 아래 주문서 전체 흐름 예제의 /api/payment/prepare
가맹점 서버 결제 조회 API 가맹점 백엔드에서 Bootpay 조회 API를 호출하도록 구현 결제 조회
가맹점 서버 서버 승인 API 서버 승인 모드를 쓸 때 가맹점 백엔드에서 구현 분리 승인
관리자 설정과 연동 코드의 경계

결제수단 노출, PG 우선순위, 약관, 색상, 레이아웃은 관리자에서 조정해요. 이 문서에서는 SDK 렌더링, 이벤트 처리, 결제 요청, 인증 결과, 승인 요청, 결과 수신만 다뤄요.

1첫 결제까지 붙이기

이 구간은 결제위젯을 주문서에 붙이는 최소 여정이에요. 먼저 SDK를 준비하고, 위젯을 렌더링한 뒤, 결제수단 선택과 약관 동의가 끝났을 때만 결제 버튼을 활성화해요. 이후 흐름은 결제 요청 → 인증 결과 수신 → 결제 승인 요청 → 결제 결과 수신 순서로 나눠 봐요.

1-1. SDK 설치

SDK 설치

PG 결제창 연동 → SDK 설치를 먼저 완료해요. Client Key가 아직 없다면 연동키 발급도 함께 끝내요.

1-2. 위젯 렌더링

주문서 페이지에 결제위젯을 렌더링해요. 이 단계의 목적은 결제를 바로 요청하는 것이 아니라, 구매자가 결제수단을 선택하고 약관 동의 상태를 만들 수 있게 하는 거예요.

import { BootpayWidget } from '@bootpay/client-js'

function updateButtonState(data) {
    document.getElementById('pay-button').disabled =
        !(data?.completed && data?.term_passed)
}

document.addEventListener('bootpay-widget-ready', () => {
    console.log('위젯 렌더링 완료')
})
document.addEventListener('bootpay-widget-change-payment', (e) => {
    console.log('선택된 결제수단:', e.detail.method)
    updateButtonState(e.detail)
})
document.addEventListener('bootpay-widget-change-terms', (e) => {
    updateButtonState(e.detail)
})

BootpayWidget.render('#bootpay-widget', {
    client_key: '[ Client Key ]',       // Client Key
    widget_key: 'default-widget',       // 관리자에서 만든 단건결제 위젯 키
    price: 1000,                        // 결제 금액
    order_name: '테스트 상품',            // 주문명
    order_id: 'ORD-' + Date.now(),      // 주문번호
    sandbox: true,                      // 샌드박스 모드
    use_terms: true                     // 약관 동의 UI 표시
})javascript

1-3. 결제 요청

구매자가 결제수단 선택과 약관 동의를 마치면 결제를 요청해요. 이 단계는 구매자 인증을 시작하는 단계이지, 아직 결제 승인이나 주문 확정이 아니에요. 운영 코드에서는 버튼 클릭 직전에 서버에서 주문번호와 최종 금액을 확정한 뒤 요청하는 흐름을 권장해요.

document.getElementById('pay-button').onclick = async () => {
    try {
        const response = await BootpayWidget.requestPayment({
            order_name: '테스트 상품',
            order_id: 'order_' + Date.now(), // 테스트용. 실제로는 서버에서 생성한 주문번호 사용
            extra: {
                open_type: 'iframe'
            }
        })

        // 결제 결과 수신 — 서버에서 결제 조회 필요
        if (response.event === 'done') {
            console.log('결제 완료:', response.data.receipt_id)
        }
    } catch (error) {
        // 결제 실패 또는 취소
        console.error('결제 실패:', error)
    }
}javascript

1-4. 인증 결과 수신

운영 주문서는 서버 승인 모드를 우선 검토해요. extra.separately_confirmed: true로 설정하면 구매자 인증이 완료된 뒤 confirm 이벤트가 발생해요. 이 시점은 인증 결과를 받은 상태이지 결제 완료가 아니에요. receipt_id를 서버로 넘기고, 다음 단계에서 결제 승인 요청을 처리해야 해요. 서버 승인 모드에서는 클라이언트의 onDone이 호출되지 않아요.

document.getElementById('pay-button').onclick = async () => {
    try {
        const response = await BootpayWidget.requestPayment({
            order_name: '테스트 상품',
            order_id: 'order_' + Date.now(), // 테스트용. 실제로는 서버에서 생성한 주문번호 사용
            extra: {
                open_type: 'iframe',
                separately_confirmed: true  // 서버 승인 모드
            }
        })

        // 인증 결과 수신 — confirm 이벤트
        if (response.event === 'confirm') {
            const { receipt_id, order_id } = response

            // 서버로 receipt_id를 보내 승인 가능 여부와 승인 요청을 처리
            await fetch('/api/confirm', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ receipt_id, order_id })
            })

            // 결제 결과 수신은 결과 페이지에서 서버 DB 조회로 확인
            location.href = `/order/result?order_id=${order_id}`
        }
    } catch (error) {
        console.error('결제 실패:', error)
    }
}javascript
인증 결과와 결제 승인 요청 핵심 포인트
  • extra.separately_confirmed: true 설정 → 인증 완료 후 confirm 이벤트 수신
  • confirm은 결제 완료가 아니라 승인 직전 상태
  • onConfirm에서 receipt_id를 서버로 전달하고, 서버에서 승인 가능 여부를 검증
  • 서버 승인 가능 PG라면 서버가 Bootpay 승인 API를 호출
  • 서버 승인 방식에서는 onDone이 호출되지 않음 → 프론트엔드는 결과 페이지에서 서버 DB를 조회

1-5. 결제 승인 요청

인증 결과(confirm) 다음 단계는 결제 승인 요청이에요. 서버 승인 모드에서는 onConfirm에서 받은 receipt_id를 서버로 보내고, 서버가 주문 금액·상태·재고를 확인한 뒤 Bootpay 승인 API를 호출해야 해요. 서버 승인을 지원하지 않는 PG라면 서버에서 주문 금액·상태를 먼저 확인한 뒤 각 플랫폼의 클라이언트 승인 함수를 호출하는 방식으로 처리해요.

  • 서버 승인 지원 PGonConfirm에서 receipt_id를 서버로 전달하고, 서버에서 승인 API를 호출해요.
  • 서버 승인 미지원 PG — 서버에서 주문 금액·상태를 먼저 검증하고, 통과한 경우에만 각 플랫폼 탭의 클라이언트 승인 함수를 호출해요.

1-6. 결제 결과 수신

결제 승인 요청 이후에는 성공 또는 실패 결과를 수신해요. 프론트엔드 응답만으로 주문을 완료하면 안 돼요. 항상 서버에서 receipt_id로 결제 상태와 금액을 한 번 더 확인한 뒤 주문을 확정해야 해요.

  • 클라이언트 자동 승인 모드onDone 수신 후 서버에서 결제 조회 API를 호출해요.
  • 서버 승인 모드(separately_confirmed)onDone이 호출되지 않아요. 프론트엔드는 결과 페이지에서 서버 DB를 조회해 승인 결과를 표시해요.
  • 실패/취소onError 또는 onCancel을 받으면 주문은 미결제 상태로 유지하고, 위젯을 재로드해 재시도할 수 있게 해요.

결제위젯 결과 데이터 예시

결제위젯에는 두 종류의 데이터가 있어요. WidgetData는 결제수단 선택·약관 동의 같은 주문서 UI 상태​​이고, onConfirm·onDone·onIssued·onError에서 받는 데이터는 결제 진행 결과​​예요.

{
  "event": "done",
  "receipt_id": "6244f60c1fc19202e42e8c4e",
  "order_id": "order_20260428_001",
  "price": 1000,
  "status": 1,
  "method": "card",
  "method_symbol": "card",
  "pg": "kcp"
}json

confirm은 승인 전 인증 결과이고, done은 결제 완료 이벤트예요. 가상계좌 issued는 입금 완료가 아니라 계좌 발급 완료예요.

주문 확정에 필요한 전체 결제 JSON은 이벤트 데이터가 아니라 서버의 결제 조회 응답 예시를 기준으로 봐요. 결제창과 결제위젯 모두 같은 receipt_id 조회 응답을 사용해요.

2주문서 전체 흐름으로 합치기

앞에서는 렌더링·결제 요청·인증 결과·승인 요청·결과 수신을 조각별로 봤어요. 이제 실제 주문서처럼 적립금 적용, 금액 업데이트, 서버 준비 API, 결제 요청을 한 흐름으로 합쳐요. 주문서 코드를 승인 방식별로 두 벌 만들 필요는 없어요. 서버 승인 모드를 쓰면 아래 예제의 confirm 처리 위치에 앞에서 본 서버 승인 로직만 넣으면 돼요.

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="utf-8" />
  <title>주문서 - 내 쇼핑몰</title>
  <script src="https://js.bootpay.co.kr/bootpay-5.3.0.min.js"></script>
</head>
<body>
  <h2>주문 결제</h2>

  <!-- 적립금 사용 -->
  <div class="mileage-section">
    <label>
      <input type="checkbox" id="use-mileage" />
      보유 적립금 3,000원 사용
    </label>
  </div>

  <!-- 결제위젯 영역 -->
  <div id="payment-widget-area"></div>

  <!-- 결제 버튼 -->
  <button id="checkout-btn" disabled>39,000원 결제하기</button>

  <script>
    var orderInfo = {
      basePrice: 39000,
      mileageDiscount: 0
    }

    function updateButtonState(data) {
      document.getElementById('checkout-btn').disabled =
        !(data && data.completed && data.term_passed)
    }

    document.addEventListener('bootpay-widget-ready', function() {
      console.log('결제위젯 준비 완료')
    })
    document.addEventListener('bootpay-widget-change-payment', function(e) {
      console.log('결제수단 변경:', e.detail)
      updateButtonState(e.detail)
    })
    document.addEventListener('bootpay-widget-change-terms', function(e) {
      updateButtonState(e.detail)
    })

    // STEP 1. 위젯 렌더링
    BootpayWidget.render('#payment-widget-area', {
      client_key: '[ Client Key ]',
      price: orderInfo.basePrice,
      sandbox: true,
      use_terms: true
    })

    // STEP 2. 적립금 적용 시 금액 업데이트
    document.getElementById('use-mileage').addEventListener('change', function(e) {
      orderInfo.mileageDiscount = e.target.checked ? 3000 : 0
      var finalPrice = orderInfo.basePrice - orderInfo.mileageDiscount

      BootpayWidget.update({ price: finalPrice })
      document.getElementById('checkout-btn').textContent =
        finalPrice.toLocaleString() + '원 결제하기'
    })

    // STEP 3. 결제 요청
    document.getElementById('checkout-btn').addEventListener('click', async function() {
      try {
        var response = await fetch('/api/payment/prepare', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            order_name: '프리미엄 무선 이어폰',
            mileage_amount: orderInfo.mileageDiscount,
            payment_method: BootpayWidget.currentPaymentParameters()
          })
        })
        var orderData = await response.json()

        await BootpayWidget.requestPayment({
          client_key: '[ Client Key ]',
          pg: orderData.pg,
          method: orderData.method,
          order_id: orderData.order_id,
          order_name: orderData.order_name,
          price: orderData.price,
          redirect_url: window.location.origin + '/order/result'
        })
      } catch (err) {
        alert('결제 실패: ' + err.message)
      }
    })
  </script>
</body>
</html>html
서버 승인 모드를 쓰는 경우

위 주문서 코드를 한 벌 더 만들 필요는 없어요. 위 예제의 confirm 처리 위치에서 receipt_id를 서버로 보내고, 서버가 주문 금액·상태·재고를 확인한 뒤 Bootpay 승인 API를 호출하게 연결하면 돼요. 서버 승인 API 예제는 분리 승인 페이지를 참고해요.

3이벤트로 주문 상태 연결하기

주문서 UI는 이벤트를 기준으로 움직여요. 위젯이 준비되면 결제 버튼을 열고, 결제수단·약관 상태가 바뀌면 Payload를 갱신하며, 결제 완료·오류·취소 이벤트는 서버 조회 또는 재시도 흐름으로 연결해요.

const payButton = document.getElementById('pay-button')

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

document.addEventListener('bootpay-widget-ready', () => {
    // 위젯 SDK 준비 완료
    console.log('위젯이 준비되었다')
})
document.addEventListener('bootpay-widget-resize', (e) => {
    // 위젯 높이 변경 → 컨테이너 높이 갱신
    document.getElementById('bootpay-widget').style.height = `${e.detail.height}px`
})
document.addEventListener('bootpay-widget-change-payment', (e) => {
    // 구매자가 결제수단 변경
    updateButtonState(e.detail)
})
document.addEventListener('bootpay-widget-change-terms', (e) => {
    // 약관 동의 상태 변경
    updateButtonState(e.detail)
})

BootpayWidget.render('#bootpay-widget', {
    // ... 기본 옵션
    widget_key: 'default-widget'
})javascript

4쿠폰·적립금으로 금액이 바뀔 때

주문서에서 쿠폰이나 적립금을 적용하면 결제 금액과 할부 옵션이 바뀌어요. 이때 주문서 금액만 바꾸지 말고 위젯에도 같은 금액을 반영해야 해요.

// 적립금 적용 시 금액 업데이트
const finalPrice = basePrice - mileageDiscount
BootpayWidget.update({
    price: finalPrice,
    tax_free: 0,
    extra: {
        card_quota: [0, 2, 3]  // 할부 옵션
    }
})javascript

5플랫폼별 상세 확인

앞의 공통 흐름을 구현하다가 SDK별 세부 동작이 필요할 때만 이 섹션을 확인해요. 아래 플랫폼을 선택하면 해당 플랫폼의 상세 본문만 표시돼요. 다른 플랫폼 본문은 숨겨져 페이지 스크롤 길이에 영향을 주지 않아요.

플랫폼별 상세 코드는 공통적으로 위젯 삽입 → 콜백에서 결제수단·약관 상태 병합 → 결제 버튼 활성화 → 결제 후 재로드/종료 처리 순서로 봐요.

플랫폼

Web JS 상세 API

주문서 전체 구현은 위의 전체 연동 예제 selector를 기준으로 잡고, 아래는 Web에서 필요한 API만 가져다 써요.

const widgetEl = document.querySelector('#bootpay-widget')

// Web JS는 위젯 상태를 DOM 이벤트로 받아요.
widgetEl.addEventListener('bootpay-widget-ready', () => {
    console.log('위젯 준비 완료')
})

widgetEl.addEventListener('bootpay-widget-resize', (e) => {
    console.log('위젯 크기 변경:', e.detail.height)
})javascript
API 설명
BootpayWidget.render(selector, options) 위젯 렌더링
BootpayWidget.requestPayment(options) 결제 요청
BootpayWidget.update(options) 위젯 정보 업데이트 (금액 변경 등)
BootpayWidget.destroy() 위젯 제거
BootpayWidget.isRendered() 위젯 렌더링 상태 확인
BootpayWidget.currentPaymentParameters() 현재 선택된 결제 정보 조회
BootpayWidget.currentTermsCondition() 현재 약관 동의 상태 조회
BootpayWidget.selectPayment(options) 결제수단 프로그래밍 방식 선택

Android 위젯 상세

Android 위젯은 WebView 기반으로 결제 UI를 표시하며, 결제 시 전체화면으로 확장돼요. BootpayWidgetController로 위젯 상태를 관리해요.

콜백 설명 파라미터
setOnReady 위젯 준비 완료 없음
setOnResize 위젯 높이 변경 height: Double (dp 단위)
setOnChangePayment 결제수단 변경 data: WidgetData
setOnChangeAgreeTerm 약관동의 변경 data: WidgetData
setOnDone 결제 완료 data: String (JSON)
setOnError 결제 에러 data: String (JSON)
setOnCancel 결제 취소 data: String (JSON)
setOnConfirm 결제 확인 (검증) data: StringBoolean 반환
setOnIssued 가상계좌 발급 data: String (JSON)
setOnClose 위젯 닫기 없음
mergeWidgetData 패턴

onChangePaymentonChangeAgreeTerm 콜백에서 payload.mergeWidgetData(data)를 호출하면, 위젯에서 선택한 결제수단/약관동의 상태가 Payload에 자동 반영돼요. payload.widgetIsCompletedtrue이면 결제수단 선택과 약관동의가 모두 완료된 상태이에요.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <FrameLayout
            android:id="@+id/webViewContainer"
            android:layout_width="match_parent"
            android:layout_height="516dp" />
    </ScrollView>

    <Button
        android:id="@+id/payButton"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:enabled="false"
        android:text="결제하기"
        android:onClick="goPayment" />
</LinearLayout>xml
항목 핵심
WidgetCloseAction.NONE 위젯 닫기 이후 화면 이동을 앱 코드에서 직접 처리할 때 권장
collapseAndReload(activity) 취소·에러 후 축소하고 위젯을 재로드
collapseAndFinish(activity) 결제 완료 후 축소하고 Activity 종료
displaySuccessResult / displayErrorResult false가 기본값. 앱에서 결과 화면을 직접 구현할 때 권장
Android 주의사항
  1. 위젯 높이​: onResize 콜백의 높이 값은 dp 단위이에요. density를 곱하여 px로 변환 후 적용해요.
  2. 결제 버튼​: payload.widgetIsCompletedtrue일 때만 결제 버튼을 활성화해요.
  3. 앱 스킴​: extra.appScheme과 AndroidManifest.xml의 scheme이 일치해야 해요.
  4. 메모리 관리​: onDestroy()에서 BootpayWidget.destroy()를 호출해요.
  5. 뒤로가기​: onBackPressed()에서 전체화면 상태를 확인 후 적절히 처리해요.

iOS 위젯 상세

iOS 위젯은 BootpayWidgetView(UIView 서브클래스)를 화면에 삽입하고, BootpayWidgetController로 이벤트를 관리해요.

클래스 역할
BootpayWidgetView 결제위젯을 표시하는 UIView
BootpayWidgetController 위젯 상태 관리 및 이벤트 콜백 처리
Payload 결제 정보 데이터
콜백 설명 파라미터
onReady 위젯 준비 완료 없음
onResize 위젯 높이 변경 height: CGFloat
onChangePayment 결제수단 변경 data: WidgetData
onChangeAgreeTerm 약관동의 변경 data: WidgetData
onDone 클라이언트 자동 승인 시 결제 완료 data: [String: Any]
onError 결제 에러 data: [String: Any]
onCancel 결제 취소 data: [String: Any]
onConfirm 결제 확인 (검증) data: [String: Any]Bool 반환
onIssued 가상계좌 발급 data: [String: Any]
onClose 위젯 닫기 없음
// 1. WidgetView 생성 및 화면에 추가
widgetView = BootpayWidgetView()
view.addSubview(widgetView)

// 2. Controller 생성 및 콜백 설정
widgetController = BootpayWidgetController()
widgetController.onChangePayment = { data in ... }

// 3. Controller를 View에 연결
widgetView.controller = widgetController

// 4. Payload 설정 후 위젯 시작
widgetView.payload = payload
widgetView.startWidget()swift
mergeWidgetData 패턴

onChangePaymentonChangeAgreeTerm 콜백에서 payload.mergeWidgetData(data)를 호출하면, 위젯에서 선택한 결제수단/약관동의 상태가 Payload에 자동 반영돼요. payload.widgetIsCompletedtrue이면 결제수단 선택과 약관동의가 모두 완료된 상태이에요.

iOS 주의사항
  1. 위젯 높이​: onResize 콜백에서 widgetHeightConstraint를 업데이트해야 해요.
  2. 결제 버튼​: payload.widgetIsCompletedtrue일 때만 결제 버튼을 활성화해요.
  3. 앱 스킴​: payload.extra?.appScheme을 설정하고 Info.plist에 URL Scheme을 등록해요.
  4. 메모리 관리​: 클로저에서 [weak self]를 사용하여 순환 참조를 방지해요.

Flutter 위젯 상세

Flutter 위젯은 BootpayWidget StatefulWidget을 화면에 삽입하고, BootpayWidgetController로 이벤트와 결제를 관리해요. 위젯 전용 BootpayWidgetWebView를 내부적으로 사용해요.

클래스 역할
BootpayWidget 결제위젯을 표시하는 StatefulWidget
BootpayWidgetController 위젯 상태 관리, 결제 요청, 이벤트 콜백
Payload 결제 정보 데이터
콜백 설명 파라미터
onWidgetReady 위젯 준비 완료 없음
onWidgetResize 위젯 높이 변경 height: double
onWidgetChangePayment 결제수단 변경 data: WidgetData
onWidgetChangeAgreeTerm 약관동의 변경 data: WidgetData
onDone 클라이언트 자동 승인 시 결제 완료 data: String (JSON)
onError 결제 에러 data: String (JSON)
onCancel 결제 취소 data: String (JSON)
onConfirm 결제 확인 (검증) data: Stringbool 반환
onIssued 가상계좌 발급 data: String (JSON)
onClose 위젯 닫기 없음
double _widgetHeight = 516.0;

_controller.onWidgetResize = (height) {
    if (_widgetHeight == height) return;
    setState(() { _widgetHeight = height; });
};

SizedBox(
    height: _widgetHeight,
    child: BootpayWidget(
        payload: _payload,
        controller: _controller,
    ),
)dart
mergeWidgetData 패턴

onWidgetChangePaymentonWidgetChangeAgreeTerm 콜백에서 _payload.mergeWidgetData(widgetData)를 호출하면, 위젯에서 선택한 결제수단/약관동의 상태가 Payload에 자동 반영돼요. _payload.widgetIsCompletedtrue이면 결제수단 선택과 약관동의가 모두 완료된 상태이에요.

Flutter 주의사항
  1. 위젯 높이​: onWidgetResize 콜백에서 setStateSizedBox 높이를 업데이트해야 해요.
  2. 결제 버튼​: _payload.widgetIsCompletedtrue일 때만 결제 버튼을 활성화해요.
  3. ScrollView: 위젯 높이가 동적으로 변경되므로 SingleChildScrollView를 사용해요.
  4. dismiss: onClose 콜백에서 Bootpay().dismiss(context)를 호출해요. 웹에서는 kIsWeb 체크 후 skip해요.
  5. 앱 스킴​: Android AndroidManifest.xml과 iOS Info.plist에 모두 등록해야 해요.

React Native 위젯 상세

React Native 위젯은 BootpayWidget 컴포넌트와 ref를 통한 명령형 API로 동작해요. 다른 SDK와 달리 onWidgetReady 콜백에서 renderWidget()을 명시적으로 호출해야 해요.

메서드 설명 호출 시점
renderWidget(payload) 위젯 초기 렌더링 onWidgetReady 콜백 내에서
updateWidget(payload, refresh?) 위젯 데이터 업데이트 금액/상품 변경 시
requestPayment() 결제 요청 (전체화면 전환) 결제 버튼 클릭 시
transactionConfirm() 프론트엔드 승인 확정 onConfirm 콜백 내에서. 서버 승인에서는 호출하지 않음
reloadWidget() 위젯 재로드 결제 완료/에러/취소 후
콜백 설명 파라미터
onWidgetReady 위젯 준비 완료 없음
onWidgetResize 위젯 높이 변경 height: number
onWidgetChangePayment 결제수단 변경 data: WidgetData | null
onWidgetChangeTerms 약관동의 변경 data: WidgetData | null
onDone 클라이언트 자동 승인 시 결제 완료 data: unknown (JSON)
onError 결제 에러 data: unknown (JSON)
onCancel 결제 취소 data: unknown (JSON)
onConfirm 결제 승인 확인 data: unknownboolean 반환
onIssued 가상계좌 발급 data: unknown (JSON)
onClose 결제창 닫힘 없음

위젯 렌더링 흐름은 마운트 → onWidgetReady → renderWidget(payload) → 결제수단/약관 변경 → 버튼 활성화 → requestPayment() 순서이에요.

const widgetPlaceholderRef = useRef<View>(null);
const [widgetTop, setWidgetTop] = useState(0);

const measureWidgetPosition = useCallback(() => {
    widgetPlaceholderRef.current?.measureInWindow((x, y) => {
        if (y > 0) setWidgetTop(y);
    });
}, []);

<View ref={widgetPlaceholderRef}
    style={{ minHeight: widgetHeight }}
    onLayout={() => setTimeout(measureWidgetPosition, 100)} />

<BootpayWidget widgetTop={widgetTop} height={widgetHeight} ... />tsx
옵션 false (기본값, 권장) true
display_success_result 클라이언트 자동 승인 기준: 즉시 onDone 호출 → 앱에서 결과 처리 웹뷰 결과 화면 → 닫기 클릭 → onClose
display_error_result 즉시 onError 호출 → 위젯 재로드 가능 웹뷰 에러 화면 → 닫기 클릭 → onClose
React Native 주의사항
  1. onWidgetReady: 위젯 준비 후 renderWidget(payload)을 반드시 호출해야 해요.
  2. widgetTop: position: absolute 기반이므로 placeholder의 measureInWindow로 Y 좌표를 측정해요.
  3. 결제 버튼​: widgetData?.completed && widgetData?.term_passed가 모두 true일 때 활성화해요.
  4. 서버 승인​: extra.separately_confirmed: true 사용 시 onConfirm에서 receipt_id를 서버로 전달해요. 이 방식에서는 onDone이 호출되지 않으며, transactionConfirm()도 클라이언트에서 호출하지 않아요.
  5. 앱 스킴​: Android AndroidManifest.xml과 iOS Info.plist에 모두 등록해야 해요.
  6. iOS 스와이프 백​: 전체화면 결제 모드에서 좌측 스와이프 지원을 위해 react-native-gesture-handler가 필요해요.

6다음 단계

연동이 끝났다면 운영 설정과 확장 기능을 이어서 확인해요.