/** 구독 해제 함수. core(connect-kit) 의존을 피하려고 kyc 안에 자체 정의. */ type Unsubscribe = () => void; /** * 백엔드 KYC 진행 상태 (cross-auth `KYCResp.status`). * - none : 아직 시작 전 * - pending : 심사 진행 중 * - approved : 승인 완료 (kyc_verified=true) * - rejected_retry : 거절됐지만 재시도 가능 (reject_type=RETRY) * - rejected_final : 최종 거절 (reject_type=FINAL) * - wallet_required : 검증 전 지갑 링크가 필요 */ type KycStatus = 'none' | 'pending' | 'approved' | 'rejected_retry' | 'rejected_final' | 'wallet_required'; /** 로그인 방식 (cross-auth `KYCResp.login_type`). */ type KycLoginType = 'siwe' | 'social'; /** 거절 사유 종류 (rejected_* 상태에서만 존재, `KYCResp.reject_type`). */ type KycRejectType = 'RETRY' | 'FINAL'; /** * 호출자 신원 + KYC 상태. cross-auth `KYCResp`를 도메인 친화(camelCase)로 * 정규화한 형태. 외부 스키마(snake_case)는 어댑터가 이 타입으로 변환한다. */ interface KycIdentity { /** none|pending|approved|rejected_retry|rejected_final|wallet_required */ readonly status: KycStatus; /** identity-core-api 기준 승인 여부 (`kyc_verified`). */ readonly verified: boolean; /** "siwe" | "social". 미상이면 undefined. */ readonly loginType?: KycLoginType; /** 액세스 토큰에서 파생된 지갑 주소 (`wallet_address`). */ readonly walletAddress?: string; /** rejected_* 상태에서만 존재 (`reject_type`). */ readonly rejectType?: KycRejectType; /** KYC 공급자 식별자 (`provider`). 예: "sumsub". 멀티 벤더 대비. */ readonly provider?: string; /** * 호스티드 검증 URL (`verification_url`). 백엔드가 Sumsub websdkLink 등으로 * 생성해 내려주면, 프론트는 이 URL을 새 창으로 열어 검증을 진행한다(기본 경로). */ readonly verificationUrl?: string; /** Sumsub SDK 토큰 (`kyc_token`). POST /kyc의 InitKYC 발급 시에만 존재. */ readonly sdkToken?: string; /** Sumsub SDK 토큰 만료 시각 ISO 문자열 (`kyc_expires_at`). */ readonly sdkTokenExpiresAt?: string; /** social 로그인 전용 — 이메일. */ readonly email?: string; /** social 로그인 전용 — 닉네임. */ readonly nickname?: string; /** social 로그인 전용 — IdP subject (`sub`). */ readonly sub?: string; /** social 로그인 전용 — 내부 uuid. */ readonly uuid?: string; } type KycErrorCode = 'MISSING_PROJECT_ID' | 'UNAUTHORIZED' | 'STATUS_FAILED' | 'START_FAILED' | 'INVALID_RESPONSE' | 'NETWORK_ERROR' | 'LAUNCH_FAILED'; declare class KycError extends Error { readonly code: KycErrorCode; readonly details?: Record; constructor(code: KycErrorCode, message: string, details?: Record); } /** 알 수 없는 status 문자열은 'none'으로 폴백. */ declare function normalizeKycStatus(value: unknown): KycStatus; /** "siwe" | "social" 외 값은 undefined. */ declare function normalizeLoginType(value: unknown): KycLoginType | undefined; /** "RETRY" | "FINAL" 외 값은 undefined. 대소문자 무관. */ declare function normalizeRejectType(value: unknown): KycRejectType | undefined; /** * cross-auth KYC 추상화. 호출자 신원 + KYC 상태 조회와 검증 시작을 담당한다. * * 구현체는 어댑터 레이어가 담당 — 이 Port는 도메인 계약만 가진다. */ interface KycPort { /** * GET /kyc — 읽기 전용. 호출자의 신원(SIWE/social) + KYC 상태를 반환한다. * 부수효과 없음: 지갑 링크나 검증 시작을 하지 않는다 (그건 startVerification). */ getStatus(): Promise; /** * POST /kyc — 호출자의 지갑을 프로젝트에 링크(idempotent)하고, KYC가 아직 * 승인되지 않았다면 검증을 시작/재개(idempotent)하며 Sumsub SDK 토큰 * (`sdkToken`)을 surfacing 한다. 이미 승인된 경우 토큰 없이 상태만 반환. */ startVerification(): Promise; } /** * cross-auth 백엔드 엔드포인트 — 패키지 내부 상수. DApp 개발자는 이 파일을 * 보지도, 수정할 일도 없다. 환경별 base URL은 빌드 시 환경변수로 override * 가능하다 (env 처리 방식은 `@nexus-cross/onramp`의 endpoints와 동일). * * 우선순위 (override): * 1) `VITE_CROSSX_AUTH_BASE_URL` (Vite 빌드) * 2) `NEXT_PUBLIC_CROSSX_AUTH_BASE_URL` (Next.js) * 3) 환경 식별(`VITE_CROSSX_ENVIRONMENT` / `NEXT_PUBLIC_CROSSX_ENVIRONMENT`) * 에 따라 DEFAULT_BASE_URL의 dev/stage/production */ type KycEnvironment = 'dev' | 'stage' | 'production'; interface KycEndpointPaths { /** * 신원/KYC 상태 — GET은 읽기 전용 조회, POST는 지갑 링크 + 검증 시작. * cross-auth는 같은 경로(`/kyc`)에 두 메서드를 둔다. */ kyc: string; } declare const DEFAULT_KYC_PATHS: KycEndpointPaths; declare function getKycBaseUrl(): string; /** * 외부 통신을 추상화한 리포지토리. BrowserKycAdapter는 fetch를 직접 호출하지 * 않고 이 인터페이스를 통한다. * * 구현체: * - HttpKycRepository: cross-auth `/kyc` 호출 (GET 상태 / POST 시작) * * DApp이 직접 구현해서 주입할 수도 있다 (자체 게이트웨이가 다른 응답 스키마를 * 쓰는 경우, 테스트 mock 등). 식별자(projectId, accessToken getter 등)는 구현체 * 생성자에서 주입하므로 메서드 인자에 포함하지 않는다. */ interface KycRepository { /** GET /kyc — 읽기 전용 신원 + KYC 상태. */ fetchStatus(): Promise; /** POST /kyc — 지갑 링크 + 검증 시작/재개. */ initVerification(): Promise; } /** 매 호출마다 최신 access token을 읽어오는 getter. 동기/비동기 모두 허용. */ type AccessTokenGetter = () => string | undefined | null | Promise; interface HttpKycRepositoryOptions { /** * social 로그인 시 `X-Project-Id` 헤더로 전송 (embedded-wallet-gateway * whitelist). SIWE-only DApp은 생략 가능하지만, 두 흐름 모두 지원하려면 * embeddedProjectId(미설정 시 crossProjectId)를 넘기는 걸 권장. */ projectId?: string; /** * Bearer access token getter. 반환값이 있으면 `Authorization: Bearer ` * 헤더를 붙인다. 쿠키 기반 세션(HttpOnly)만 쓰는 경우 생략 — 요청은 항상 * `credentials: 'include'`로 전송되므로 쿠키가 자동 첨부된다. */ getAccessToken?: AccessTokenGetter; /** native SDK 흐름에서만 사용. `X-App-Id`로 전송. 웹은 생략. */ appId?: string; /** `X-App-Id`와 함께 전송. 'android' | 'ios' | 'windows'. 웹은 생략. */ appType?: string; /** override 안 하면 getKycBaseUrl() 사용. */ baseUrl?: string; paths?: Partial; /** GET/POST 공통 타임아웃. 기본 10초. */ timeoutMs?: number; } /** * cross-auth `/kyc` 호출 리포지토리. * * 엔드포인트 (cross-auth swagger 기준): * - GET /kyc → 읽기 전용. 신원(SIWE/social) + identity-core-api KYC 상태. * - POST /kyc → 지갑 링크(idempotent) + 검증 시작/재개(idempotent). 미승인 시 * Sumsub SDK 토큰(kyc_token) surfacing. * GET/POST 모두 요청 바디가 없다 (식별은 토큰/헤더로만). * 응답 envelope: { code, message, data: KYCResp }. * * 인증: * - BearerAuth(Authorization) 또는 HttpOnly 쿠키(credentials: include). * - social login은 추가로 `X-Project-Id` + client identifier가 필요하다: * web은 `Origin`(브라우저가 cross-origin 요청에 자동 첨부 — 수동 설정 불가), * native는 `X-App-Id` + `X-App-Type`. 이 조합이 없으면 백엔드가 401을 준다. * * 실패는 throw(KycError) — 상태 읽기는 호출자가 알아야 하므로 fail-closed로 * 숨기지 않는다. 401/403은 UNAUTHORIZED, 그 외는 STATUS_FAILED/START_FAILED. */ declare class HttpKycRepository implements KycRepository { private readonly projectId?; private readonly getAccessToken?; private readonly appId?; private readonly appType?; private readonly baseUrl; private readonly paths; private readonly timeoutMs; constructor(opts?: HttpKycRepositoryOptions); fetchStatus(): Promise; initVerification(): Promise; private request; private buildHeaders; private parseIdentity; } /** * 브라우저 환경용 KycPort 구현. * * 외부 통신은 KycRepository로 위임 — 기본은 HttpKycRepository(fetch), * 테스트/데모에서는 mock repository를 주입할 수 있다. KYC는 온램프와 달리 * popup/postMessage 같은 브라우저 부수효과가 없다 (Sumsub websdk 실행은 * DApp이 `sdkToken`으로 직접 처리). 따라서 이 어댑터는 얇은 위임 레이어로, * 추후 status 캐싱/이벤트 같은 정책의 확장 지점만 확보해 둔다. */ interface BrowserKycAdapterOptions { /** * social 로그인 시 `X-Project-Id`로 전달. kitConfig.embeddedProjectId * (미설정 시 crossProjectId)를 넘기는 걸 권장. `repository`를 직접 주입하는 * 경우 무시되어도 무방. */ projectId?: string; /** Bearer access token getter. 쿠키 세션만 쓰면 생략 가능. */ getAccessToken?: AccessTokenGetter; /** native SDK 흐름에서만 사용. 웹은 생략. */ appId?: string; /** `X-App-Id`와 함께 전송. 'android' | 'ios' | 'windows'. 웹은 생략. */ appType?: string; /** * 외부 통신 리포지토리. 미주입 시 옵션으로 HttpKycRepository를 자동 구성. * 테스트/데모에서는 mock 등을 주입. */ repository?: KycRepository; } declare class BrowserKycAdapter implements KycPort { private readonly repository; constructor(opts?: BrowserKycAdapterOptions); getStatus(): Promise; startVerification(): Promise; } /** * 프레임워크 무관 함수형 진입점. * * React/Vue/Svelte/vanilla 어디서든: * const kyc = createKyc({ projectId, getAccessToken: () => token }); * const me = await kyc.getStatus(); // GET /kyc * if (!me.verified) { * const started = await kyc.start(); // POST /kyc * // started.sdkToken 으로 Sumsub websdk 실행 (DApp 책임) * } * * 내부적으로 BrowserKycAdapter + GetKycStatusUseCase/StartKycUseCase 조립. */ interface CreateKycOptions extends BrowserKycAdapterOptions { } interface Kyc { /** 내부 어댑터. 고급 사용자가 직접 다뤄야 할 때 노출. */ readonly port: KycPort; /** GET /kyc — 읽기 전용 신원 + KYC 상태. */ getStatus(): Promise; /** POST /kyc — 지갑 링크 + 검증 시작/재개. Sumsub SDK 토큰 surfacing. */ start(): Promise; } declare function createKyc(options?: CreateKycOptions): Kyc; /** * KYC 검증 진입 — 응답 형태에 따라 자동 분기한다. * * 1) 기본: `identity.verificationUrl`(백엔드 hosted URL)이 있으면 새 탭으로 연다. * 벤더 중립 — CDN·CSP 불필요. (제안: docs/kyc/01-verification-link-proposal.md) * 2) fallback: URL이 없고 `identity.sdkToken`만 있으면 Sumsub WebSDK를 * 전체화면 모달에 launch한다 (벤더 결합은 여기서만). * * 완료 판정의 진실의 소스는 webhook이며, 호출자는 `GET /kyc` 폴링으로 최종 * 상태를 확인해야 한다 (두 경로 공통). */ type KycVerificationMode = 'url' | 'websdk'; /** * 검증 URL을 어디서 열지 (verificationUrl 모드에만 적용). * - 'newWindow' (기본): 새 탭/창 (`window.open`) * - 'currentWindow': 현재 창을 URL로 이동 (`location.assign`) — 검증 후 백엔드 * `redirect`로 앱에 복귀 * WebSDK fallback(sdkToken)은 창 개념이 없어 항상 현재 창의 모달로 렌더된다. */ type KycLaunchTarget = 'newWindow' | 'currentWindow'; interface KycVerificationHandle { /** 'url' = 새 탭으로 열림, 'websdk' = 인페이지 모달 launch. */ readonly mode: KycVerificationMode; /** websdk 모드: 모달을 닫는다. url 모드: no-op. */ close(): void; } interface LaunchKycVerificationOptions { /** * verificationUrl 모드에서 URL을 새 창/현재 창 중 어디서 열지. 기본 'newWindow'. * WebSDK fallback에는 적용되지 않는다(항상 현재 창 모달). */ target?: KycLaunchTarget; /** * websdk fallback에서 토큰 만료 시 새 SDK 토큰을 받아오는 콜백. * 보통 `POST /kyc`를 다시 호출(idempotent)해 새 kyc_token을 반환한다. * 미지정 시 최초 sdkToken을 재사용한다(세션이 짧으면 만료될 수 있음). */ getFreshToken?: () => Promise; /** websdk 언어. 기본 'ko'. */ lang?: string; /** url + newWindow 모드 window.open features. 기본 'noopener,noreferrer'. */ windowFeatures?: string; onError?: (payload: unknown) => void; onStatusChange?: (payload: unknown) => void; } declare function launchKycVerification(identity: KycIdentity, opts?: LaunchKycVerificationOptions): Promise; export { type AccessTokenGetter as A, BrowserKycAdapter as B, type CreateKycOptions as C, DEFAULT_KYC_PATHS as D, HttpKycRepository as H, type KycPort as K, type LaunchKycVerificationOptions as L, type Unsubscribe as U, type KycIdentity as a, type BrowserKycAdapterOptions as b, type HttpKycRepositoryOptions as c, type Kyc as d, type KycEndpointPaths as e, type KycEnvironment as f, KycError as g, type KycErrorCode as h, type KycLaunchTarget as i, type KycLoginType as j, type KycRejectType as k, type KycRepository as l, type KycStatus as m, type KycVerificationHandle as n, type KycVerificationMode as o, createKyc as p, getKycBaseUrl as q, launchKycVerification as r, normalizeKycStatus as s, normalizeLoginType as t, normalizeRejectType as u };