SDK 레퍼런스
@saemol/sdk — Node.js 18+ 서버용 클라이언트. 플랫폼 API 래퍼와 웹훅 서명 검증, 새몰 계정 로그인(SSO/OAuth 2.0) 헬퍼를 제공합니다. 런타임 의존성 없음, TypeScript 타입 내장. GitHub에서 소스 보기 ↗
클라이언트 생성
import { Saemol } from '@saemol/sdk'
const saemol = new Saemol({
secret: process.env.SAEMOL_SECRET!, // 필수 — 연동 정보 카드의 연동 시크릿
})| 옵션 | 기본값 | 설명 |
|---|---|---|
secret | (필수) | 상품별 연동 시크릿 — X-Saemol-Secret 헤더로 전송 |
baseUrl | https://saemol.com | 플랫폼 주소 (개발 환경에서 변경) |
timeoutMs | 10000 | 요청당 타임아웃 (ms) |
maxRetries | 2 | 멱등 호출(health·verify)의 최대 재시도 — reportUsage는 재시도 안 함 |
retryDelayMs | 300 | 재시도 대기 (시도마다 2배 증가) |
fetch | 전역 fetch | 테스트·폴리필용 주입 |
메서드
| 메서드 | HTTP API | 용도 |
|---|---|---|
health() | GET /api/v1/health | 연동 테스트 |
verifyLicense({ licenseKey, deviceId?, version? }) | POST /api/v1/licenses/verify | 1회성 라이선스·인앱 토큰 검증 |
verifyEntitlement({ apiKey, version? }) | POST /api/v1/entitlements/verify | 구독/API/사용량/화이트라벨 권한 확인 |
reportUsage({ apiKey, quantity, metadata? }) | POST /api/v1/usage/report | 사용량 집계 |
checkUpdate({ currentVersion? }) | POST /api/v1/updates/check | 최신 버전·강제 업데이트 확인 |
currentUser({ licenseKey? | apiKey? }) | POST /api/v1/users/current | 키 소유 구매자 컨텍스트 조회 |
응답 필드는 HTTP API 응답과 1:1입니다. 대표 응답 형태:
verifyLicense 응답 (판별 유니언 — valid로 분기)
// valid: true
{ valid: true, status: 'active', kind: 'license', plan_name: 'Pro',
sku: 'com.myapp.pro' | null,
devices: { registered: 2, limit: 3 | null },
expires_at: '2026-08-05T…' | null, // 기간제·trial 만료 (null=무기한)
is_trial: false }
// valid: false
{ valid: false, reason: 'revoked' | 'expired' | 'suspended'
| 'device_limit_exceeded' | 'version_not_allowed' | …,
status?, devices?, allowed_versions?, expires_at?, is_trial? }verifyEntitlement 응답
// 키가 조회되면 상태와 무관하게 전체 응답 (valid만 false일 수 있음)
{ valid: boolean, status: 'active' | 'expired' | 'revoked' | …,
kind: 'subscription' | 'usage' | 'api' | …, plan_name: 'Pro' | null,
plan_features: ['core', 'advanced'] | null, rate_limit_per_min: 60 | null,
usage: { unit, used, limit } | null, // kind가 usage일 때만
current_period_end: '…' | null, expires_at: '…' | null, is_trial: boolean }
// 버전 미허용 등 거부 응답 (reason 존재 여부로 판별)
{ valid: false, reason: 'version_not_allowed', allowed_versions: ['1.0'], … }reportUsage 응답
{ accepted: true,
allowed: boolean, // false = 초과 정책 block → 사용 차단
usage: { unit, used, limit, remaining },
overage_policy: 'notify' | 'block' | …,
over_limit: boolean }checkUpdate 응답 — 상품 수정 페이지 '업데이트 정보' 카드에서 설정
{ product_id, latest_version: '1.4.0' | null,
update_available: boolean, // currentVersion < latest
update_required: boolean, // currentVersion < min_version → 강제 업데이트
min_version: '1.0.0' | null, download_url: '…' | null, release_notes: '…' | null }currentUser 응답 (판별 유니언 — found로 분기)
// found: true — 키 소유 구매자 컨텍스트
{ found: true, entitlement_id, kind, status: 'active' | 'expired' | …,
plan_name: 'Pro' | null, is_trial: boolean, expires_at: '…' | null,
user: { id, name, email } }
// found: false (키 미존재 — 던지지 않고 반환)
{ found: false, reason: '…' }새몰 계정 로그인 — SaemolOAuth
구독형 상품의 인증 연동 방식이 새몰 계정으로 로그인일 때 사용합니다. 구매자가 키를 복사·입력할 필요 없이 새몰 계정으로 로그인하고, 판매자는 같은 응답에서 구독 상태·플랜까지 받습니다. 자격증명은 상품 수정 페이지의 연동 정보 카드에서 확인합니다.
import { SaemolOAuth, isSubscriptionActive, hasPlanFeature } from '@saemol/sdk'
const oauth = new SaemolOAuth({
clientId: process.env.SAEMOL_OAUTH_CLIENT_ID!,
clientSecret: process.env.SAEMOL_OAUTH_CLIENT_SECRET!,
redirectUri: 'https://app.example.com/auth/callback',
})
// 1) 로그인 버튼 — state(CSRF)는 SDK가 생성해 반환
const { url, state } = oauth.createAuthorizationUrl()
session.oauthState = state
redirect(url)
// 2) 콜백 — 오류 확인·state 검증·토큰 교환·userinfo를 한 번에
const { user, tokens } = await oauth.handleCallback({
url: req.url,
expectedState: session.oauthState,
})
if (!isSubscriptionActive(user)) return showPricing()
if (hasPlanFeature(user, 'advanced')) enableAdvanced()| 메서드 | 용도 |
|---|---|
createAuthorizationUrl() | 동의 화면 URL + state 생성 |
handleCallback() | 콜백 한 번에 처리 (아래 3개를 묶은 것) |
exchangeCode() | 인가 코드 → 토큰 |
getUserInfo() | 액세스 토큰 → 구매자 정보·구독 상태 |
refresh() | 토큰 갱신 — 두 토큰 모두 회전 |
isSubscriptionActive() | 구독 개방 판정 (순수 함수) |
hasPlanFeature() | 플랜별 기능 게이팅 (순수 함수) |
redirectUri는 상품 설정의 OAuth Redirect URI와 정확히 일치해야 합니다 — 부분 일치·와일드카드는 허용되지 않습니다(오픈 리다이렉트 방지).scope로 무엇을 받을지 정합니다 — identity(로그인만) 또는 entitlement(로그인 + 구독 상태·플랜). 생략하면 entitlement가 기본이며, 같은 상품에서 페이지마다 다르게 요청할 수 있습니다.실패는 SaemolOAuthError로 던져지며 code로 구분합니다 — access_denied(구매자 거부) · state_mismatch(CSRF 차단) · invalid_client · invalid_grant(코드 재사용·만료) · invalid_token(갱신 필요) · missing_code.
에러 규약
| 상황 | SDK 동작 |
|---|---|
| 키가 유효하지 않음 (미존재·회수·만료·제한 초과) | 던지지 않음 — valid: false 응답 반환 |
| 연동 설정 문제 (시크릿 불일치 401 · 잘못된 요청 400) | SaemolApiError |
| 일시 서버 장애 (502·503·504) | 멱등 호출은 재시도 후 SaemolApiError |
| 네트워크 실패·타임아웃 | SaemolNetworkError (timedOut 플래그) |
| 웹훅 검증 실패 | SaemolWebhookError (code로 원인 구분) |
import { SaemolApiError, SaemolNetworkError } from '@saemol/sdk'
try {
const result = await saemol.verifyLicense({ licenseKey })
} catch (err) {
if (err instanceof SaemolApiError) log(err.status, err.reason) // 설정 점검
if (err instanceof SaemolNetworkError) retryLater(err.timedOut)
}시그니처 동결 — 플랫폼 API 응답 필드는 추가만 되며 제거·의미 변경되지 않습니다. SDK 마이너 업데이트는 항상 하위 호환입니다 (semver).
SDK 없이 직접 호출
모든 요청에 X-Saemol-Secret 헤더를 붙여 호출하면 됩니다.
curl
curl -X POST https://saemol.com/api/v1/licenses/verify \
-H 'Content-Type: application/json' \
-H 'X-Saemol-Secret: <연동 시크릿>' \
-d '{ "license_key": "MF-XXXX-XXXX-XXXX-XXXX", "device_id": "machine-abc" }'