Skip to content

위젯(Widget) 연동 가이드

SoloPay 결제 위젯을 사용하면 결제 UI를 직접 개발하지 않고도 결제를 연동할 수 있습니다. SDK가 지갑 연결, 서명, 결제 처리를 모두 담당합니다.

프레임워크에 따라 적합한 패키지를 선택하세요.

React 프로젝트

@solo-pay/widget-react 패키지는 React 훅 방식으로 위젯을 연동합니다.

설치

bash
npm install @solo-pay/widget-react

사용법

typescript
import { useWidget } from '@solo-pay/widget-react';

function CheckoutButton({ orderId, amount }) {
  const { openWidget } = useWidget({
    publicKey: 'pk_xxxxx', // 발급받은 Public Key
    defaultPaymentRequest: {
      tokenAddress: '0xE4C687167705Abf55d709395f92e254bdF5825a2',
      successUrl: 'https://myshop.com/payment/success',
      failUrl: 'https://myshop.com/payment/fail',
      currency: 'USD',
    },
    onClose: () => console.log('위젯이 닫혔습니다.'),
    onError: (err) => console.error('결제 오류:', err),
  });

  return (
    <button onClick={() => openWidget({ orderId, amount: String(amount) })}>
      결제하기
    </button>
  );
}

useWidget은 컴포넌트 마운트 시 SDK 인스턴스를 초기화하고, 언마운트 시 자동으로 정리합니다.

Vanilla JS / 기타 프레임워크

@solo-pay/widget-js 패키지는 프레임워크 없이 사용할 수 있습니다.

설치

bash
npm install @solo-pay/widget-js

사용법

typescript
import { SoloPay } from '@solo-pay/widget-js';

const solopay = new SoloPay({
  publicKey: 'pk_xxxxx',
});

solopay.requestPayment(
  {
    orderId: 'order-2024-00001',
    amount: '25.5',
    tokenAddress: '0xE4C687167705Abf55d709395f92e254bdF5825a2',
    successUrl: 'https://myshop.com/payment/success',
    failUrl: 'https://myshop.com/payment/fail',
    currency: 'USD',
  },
  {
    onClose: () => {
      // 사용자가 위젯을 닫았을 때 처리
    },
  }
);

CDN

npm 없이 스크립트 태그로 바로 사용할 수 있습니다.

html
<script src="https://cdn.jsdelivr.net/npm/@solo-pay/widget-js/dist/widget.min.js"></script>
<script>
  const solopay = new SoloPay({ publicKey: 'pk_xxxxx' });
  solopay.requestPayment({
    orderId: 'order-2024-00001',
    amount: '25.5',
    tokenAddress: '0xE4C687167705Abf55d709395f92e254bdF5825a2',
    successUrl: 'https://myshop.com/payment/success',
    failUrl: 'https://myshop.com/payment/fail',
    currency: 'USD',
  });
</script>

amount와 currency 동작 방식

amount의 해석 방식은 currency 제공 여부에 따라 달라집니다.

currencyamount 해석
제공 (예: 'USD', 'KRW')법정화폐 금액 — 최근 1시간 평균 시세(TWAP)로 자동 환산
생략토큰 수량 직접 지정 — 변환 없이 그대로 사용

예시 1: USD 기준 결제 (currency 제공)

typescript
// amount: 25.5 USD → 최근 1시간 평균 USDT 시세로 환산
solopay.requestPayment({
  orderId: 'order-001',
  amount: '25.5',
  currency: 'USD',
  tokenAddress: '0xE4C687167705Abf55d709395f92e254bdF5825a2',
  successUrl: 'https://myshop.com/payment/success',
  failUrl: 'https://myshop.com/payment/fail',
});

예시 2: 토큰 수량 직접 지정 (currency 생략)

typescript
// amount: 25.5 USDT 직접 지정 (변환 없음)
solopay.requestPayment({
  orderId: 'order-001',
  amount: '25.5',
  // currency 생략 → amount가 토큰 수량으로 그대로 사용됨
  tokenAddress: '0xE4C687167705Abf55d709395f92e254bdF5825a2',
  successUrl: 'https://myshop.com/payment/success',
  failUrl: 'https://myshop.com/payment/fail',
});

currency를 생략하면?

currency를 생략하면 amount토큰 수량으로 직접 처리됩니다. 예를 들어 USDT 토큰에 amount: '25.5'를 전달하면 정확히 25.5 USDT를 요청합니다. 환율 변환이 필요 없는 경우 사용합니다.

동작 방식

  • PC 환경: 팝업 창으로 위젯이 열립니다.
  • 모바일 환경: 전체 화면 페이지로 리다이렉트됩니다.
  • 결제 완료 또는 실패 시 successUrl 또는 failUrl로 자동 리다이렉트합니다.

Callback URL 처리

결제 완료 후 SoloPay는 결제 생성 시 지정한 successUrl 또는 failUrl로 사용자를 리다이렉트합니다. 위젯이 자동으로 paymentId, orderId, status를 쿼리 파라미터로 추가합니다.

파라미터설명
paymentId고유 결제 식별자
orderId가맹점 주문 ID
status결제 결과: success, fail, closed
https://myshop.com/payment/success?paymentId=0xabc123...&orderId=order-001&status=success
https://myshop.com/payment/fail?paymentId=0xabc123...&orderId=order-001&status=fail
https://myshop.com/payment/fail?paymentId=0xabc123...&orderId=order-001&status=closed

프론트엔드 결과를 신뢰하지 마세요

URL 파라미터는 사용자가 조작할 수 있습니다. 반드시 API를 통해 결제 상태를 최종 확인하세요.

결제 결과 검증 (필수)

Callback URL에서 paymentId를 받은 즉시, 상태 조회 API를 호출하여 결제 상태를 검증합니다. GET /payments/:id 엔드포인트는 x-public-key 헤더를 사용하며 브라우저에서 직접 호출할 수 있습니다.

typescript
const response = await fetch(`https://gateway.dev.solonetwork.io/api/v1/payments/0xabc123...`, {
  headers: { 'x-public-key': 'pk_xxxxx' },
});
const result = await response.json();

공통 검증 체크리스트

  • [ ] status === 'PAID' 확인 (결제 성공)
  • [ ] tokenAddress가 기대한 토큰과 일치 확인
  • [ ] orderId가 기대한 orderId와 일치 확인
  • [ ] 동일 paymentId의 중복 완료 처리 방지

금액 검증 — 결제 생성 시 currency 사용 여부에 따라 비교 대상이 다릅니다.

위젯은 클라이언트에서 실행되므로 금액이 변조될 수 있습니다. 반드시 자사 주문 DB에 저장된 기대 금액과 비교하세요.

currency를 지정해 결제한 경우 (법정화폐 기준):

  • [ ] currency가 주문 시 저장한 통화 코드와 일치
  • [ ] fiatAmount가 주문 시 저장한 fiat 금액과 일치
  • [ ] (선택) tokenPrice가 허용 범위 내인지 확인 — 시세 급변 방어

currency를 생략해 결제한 경우 (토큰 수량 직접 지정):

  • [ ] amount(wei)가 기대 토큰 wei 금액과 일치 — 응답의 amount는 항상 wei 단위이므로, 비교할 때 parseUnits(expected, tokenDecimals)로 환산해서 비교하세요.

amount 필드는 항상 wei 단위입니다

응답의 amountcurrency 사용 여부와 무관하게 항상 wei 단위의 토큰 금액입니다. currency를 지정한 경우 사용자가 입력한 fiat 금액은 fiatAmount에 들어가고, amount에는 환산된 wei 값이 들어갑니다. fiat 금액으로 검증할 때 amount를 직접 비교하지 마세요.

Webhook 연동 권장

Callback은 브라우저 리다이렉트 기반이므로 네트워크 장애 등으로 유실될 수 있습니다. Webhook과 함께 사용하면 결제 완료를 안정적으로 수신할 수 있습니다. Webhook 설정 가이드 보기

승인 전용 모드 (Approve-Only)

위젯은 결제 없이 ERC-20 토큰 승인만 수행하는 승인 전용 모드를 지원합니다. 다음과 같은 경우에 유용합니다.

  • 실제 결제 전 토큰 사용 권한을 사전 승인
  • 빠른 결제 — 한 번 승인하면 이후 결제에서 승인 단계를 건너뜀
  • 반복 결제 — 높은 한도로 한 번 승인하고 여러 번 결제

React

typescript
import { useWidget } from '@solo-pay/widget-react';

function PreApproveButton() {
  const { openApproval } = useWidget({
    publicKey: 'pk_xxxxx',
    onClose: () => console.log('위젯이 닫혔습니다.'),
    onError: (err) => console.error('오류:', err),
  });

  return (
    <button onClick={() => openApproval({
      tokenAddress: '0xE4C687167705Abf55d709395f92e254bdF5825a2',
      successUrl: 'https://myshop.com/approve/success',
      failUrl: 'https://myshop.com/approve/fail',
    })}>
      토큰 사전 승인
    </button>
  );
}

Vanilla JS

typescript
const solopay = new SoloPay({ publicKey: 'pk_xxxxx' });

solopay.requestApproval({
  tokenAddress: '0xE4C687167705Abf55d709395f92e254bdF5825a2',
  successUrl: 'https://myshop.com/approve/success',
  failUrl: 'https://myshop.com/approve/fail',
});

Callback URL 파라미터 (승인 전용)

승인 후 위젯이 successUrl 또는 failUrl로 리다이렉트하며, 다음 쿼리 파라미터가 추가됩니다.

파라미터설명
status결과: approved 또는 closed
tokenAddress승인된 토큰 컨트랙트 주소
txHash승인 트랜잭션 해시 (성공 시에만)
https://myshop.com/approve/success?status=approved&tokenAddress=0x...&txHash=0x...
https://myshop.com/approve/fail?status=closed&tokenAddress=0x...

다음 단계

Non-custodial Web3 payment infrastructure for ERC-20 checkout, sponsored gas, and wallet-to-wallet settlement.