> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fairytech.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# v2.0.0

# Class

## `MomentSDK`

```kotlin theme={null}
class MomentSDK {
	@JvmStatic
	fun getInstance(context: Context): MomentSDK

	// Lifecycle
	fun start(config: Config, callback: ResultCallback)
	fun stop(callback: ResultCallback)
	fun init(config: Config, callback: RestartResultCallback)
	fun isRunning(): Boolean

	// UI
	@JvmOverloads
	fun launchUI(config: Config, callback: ResultCallback, finishCallback: UiFinishCallback? = null)
	@JvmOverloads
	fun launchUI(config: Config, redirectTo: String, callback: ResultCallback, finishCallback: UiFinishCallback? = null)
	fun dismissUI(callback: ResultCallback)

	// User
	fun setUserId(userId: String, callback: ResultCallback)
	fun getUserId(): String
	fun setAdId(adId: String, isAdTrackingEnabled: Boolean, callback: ResultCallback)
	fun setUserAttributes(userAttributes: UserAttributes, callback: ResultCallback)
	fun setSendBubble(sendBubble: Boolean)

	// Consent
	fun getConsent(callback: CallbackWithResult<UserConsent>)
	fun setConsent(request: SubmitUserConsentRequest, callback: CallbackWithResult<SubmitUserConsentResponse>)
	fun withdraw(callback: ResultCallback)

	// Cashback
	fun listCashback(callback: ListCashbackResultCallback)
	fun goToCashback(businessId: String, callback: ResultCallback)

	// Project / API key
	fun setProjectId(projectId: String)
	fun setApiKey(apiKey: String)
	fun getProjectId(appContext: Context): String
	fun getApiKey(appContext: Context): String

	// Permission utils
	@JvmStatic
	fun isAppUsagePermissionGranted(context: Context): Boolean
	@JvmStatic
	fun isVpnPermissionGranted(context: Context): Boolean
	@JvmStatic
	fun isNotificationPermissionGranted(context: Context): Boolean
}
```

* Full reference
  ```kotlin theme={null}
  @JvmStatic
  fun getInstance(context: Context): MomentSDK
  ```
  * *Description*
    * MomentSDK 싱글톤 인스턴스를 반환함. 최초 호출 시 인스턴스 생성, 이후 호출은 동일 인스턴스 재사용.w
    * `applicationContext` 를 추출해 내부적으로 저장하며 inspector를 enable시킴.
  * *Parameters*
    * `context`
      * `applicationContext` 추출에 사용되는 Context. activity context 전달해도 됨.
  * *Returns*
    * `MomentSDK` 싱글톤 인스턴스.
  ```kotlin theme={null}
  fun start(config: Config, callback: ResultCallback)
  ```
  * *Description*
    * 서비스를 시작시키는 method로, 이 method가 성공적으로 완료된 후부터 사용자가 접속한 비즈니스를 인식할 수 있음.
    * 필수 권한 체크 → recognition rules 캐시/서버 로드 → config 검증 → config 영속화 → API alarm 스케줄링 → external recognition service 시작 순으로 진행.
  * *Parameters*
    * `config`
      * 서비스를 시작할 때 필요한 설정값들. 알림 채널/ID/아이콘 등 (`Config` 참고).
    * `callback`
      * 서비스 시작 결과를 알려주는 callback.
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 start가 deactivate된 경우 (서버 kill-switch)
    * `ErrorCode.APP_USAGE_PERMISSION_REQUIRED` - x flavor에서 사용 통계 접근 권한이 없는 경우
    * `ErrorCode.VPN_PERMISSION_REQUIRED` - vpn flavor에서 VPN 권한이 없는 경우
    * `ErrorCode.SERVICE_ALREADY_STARTING` - start가 이미 진행 중이거나 서비스가 이미 실행 중인 경우
    * `ErrorCode.EMPTY_USER_ID` - standard campaign이 있는 프로젝트에서 userId가 설정되지 않은 경우
    * `ErrorCode.INVALID_CONFIG` - serviceNotificationChannelId / serviceNotificationId / serviceNotificationIconResId 등 필수 필드가 누락되었거나 잘못된 경우
    * `ErrorCode.SERVICE_START_FAILED` - foreground service 시작에 실패한 경우
    * `ErrorCode.NETWORK_CONNECTION_REQUIRED` - 인터넷 연결이 없거나 timeout 발생
    * `ErrorCode.AUTHENTICATION_FAILED` - API 인증 실패 (HTTP 403)
    * `ErrorCode.TOO_MANY_REQUESTS` - API를 과도하게 호출한 경우 (HTTP 429)
    * `ErrorCode.TEMPORARY_SERVER_ERROR` - 페어리 서버에 일시적으로 문제가 생긴 경우 (HTTP 502/503/504)
    * `ErrorCode.ERROR_OCCURRED` - 알 수 없는 에러가 발생한 경우
  ```kotlin theme={null}
  fun stop(callback: ResultCallback)
  ```
  * *Description*
    * service를 중지시키는 method로, stop이 불린 후부터는 사용자가 접속한 비즈니스를 인식할 수 없음.
    * 내부적으로 `SHOULD_SERVICE_BE_RUNNING` flag를 false로 설정 후 host app lifecycle tracker 해제, BubbleService 중지, foreground recognition service 중지, internal alarm cancel 순으로 진행.
  * *Parameters*
    * `callback`
      * 서비스 중지 결과를 알려주는 callback.
  * *onFailure ErrorCode*
    * `ErrorCode.SERVICE_STOP_FAILED` - foreground service 중지에 실패한 경우
    * `ErrorCode.ERROR_OCCURRED` - 예상치 못한 에러가 발생한 경우
  ```kotlin theme={null}
  fun init(config: Config, callback: RestartResultCallback)
  ```
  * *Description*
    * 이전에 start()를 호출한 적이 있으나 서비스가 꺼져 있는 경우 external 인식 서비스의 재시작을 시도함. 앱에서 적절한 시점(e.g. `Application.onCreate`)에 이 메소드를 호출하면 서비스 restart를 시도함.
    * 호출 컨텍스트(interactive caller + foreground 여부)에 따라 즉시 bootstrap 또는 jitter delay bootstrap을 선택. 동시 업데이트/대량 부팅 시 서버 herd 방지.
  * *Parameters*
    * `config`
      * 서비스 재시작 시 사용할 설정값. null이면 저장된 config 사용.
    * `callback`
      * 재시작 결과를 알려주는 callback. 성공 시 `RestartResultCode` 반환.
  * *onSuccess RestartResultCode*
    * `RestartResultCode.SERVICE_SHOULD_NOT_BE_RUNNING` - 서비스가 꺼져 있어야 하는 상태 (start 호출된 적 없음)
    * `RestartResultCode.SERVICE_ALREADY_RUNNING` - service가 이미 실행 중이라 별도 처리 안 한 경우
    * `RestartResultCode.SERVICE_RESTARTED` - service가 꺼져 있어서 start 시도 후 재시작 성공
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 deactivate된 경우 (서버 kill-switch)
    * `MomentSDK.start에서 발생할 수 있는 모든 ErrorCode` - 재시작 시도 시 start와 동일한 실패 사유
  ```kotlin theme={null}
  fun isRunning(): Boolean
  ```
  * *Description*
    * recognition service가 실행 중인지 확인. activity manager의 running service 리스트를 조회함.
    * `shouldDenyAll` 킬스위치가 켜진 상태면 항상 false 반환.
  * *Returns*
    * 서비스가 실행 중이면 true, 아니면 false. 예외 발생 시도 false 반환 (안전).
  ```kotlin theme={null}
  @JvmOverloads
  fun launchUI(config: Config, callback: ResultCallback, finishCallback: UiFinishCallback? = null)
  ```
  ```kotlin theme={null}
  @JvmOverloads
  fun launchUI(config: Config, redirectTo: String, callback: ResultCallback, finishCallback: UiFinishCallback? = null)
  ```
  * *Description*
    * SDK 내부에 구현된 Activity (`FullWebViewActivity`)를 실행하여 페어리 제공 웹뷰를 띄움.
    * 호출 시 config를 파일에 영속화 후 deviceConfig에서 받은 cashback main URL로 웹뷰 진입. `redirectTo` 가 주어지면 해당 path 로 redirect.
    * 사용자가 웹뷰를 닫으면 `finishCallback.onFinish()` 가 호출됨.
  * *Parameters*
    * `config`
      * 서비스를 시작할 때 필요한 설정값.
    * `redirectTo`
      * 웹뷰 진입 후 redirect할 path (예: `/basic-sdk/consent`). 비어 있으면 default page.
    * `callback`
      * 웹뷰 activity 실행 성공/실패 callback. webview 진입 시점까지의 결과만 통보.
    * `finishCallback`
      * 웹뷰 activity가 finish 되었을 때 호출되는 callback. nullable.
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 deactivate된 경우 (`shouldDenyAll`)
    * `ErrorCode.EMPTY_USER_ID` - userId가 설정되지 않은 경우
    * `ErrorCode.INVALID_CONFIG` - config의 필수 필드가 누락되었거나 잘못된 경우
    * `ErrorCode.NETWORK_CONNECTION_REQUIRED` - 인터넷 연결이 없는 경우
    * `ErrorCode.AUTHENTICATION_FAILED` - API 인증 실패
    * `ErrorCode.TOO_MANY_REQUESTS` - API를 과도하게 호출한 경우
    * `ErrorCode.TEMPORARY_SERVER_ERROR` - 페어리 서버에 일시적으로 문제가 생긴 경우
    * `ErrorCode.ERROR_OCCURRED` - 설정 파일 저장 실패 또는 알 수 없는 에러
  ```kotlin theme={null}
  fun dismissUI(callback: ResultCallback)
  ```
  * *Description*
    * 현재 활성 상태인 cashback 웹 UI(`FullWebViewActivity`)를 닫음.
    * UI가 active 상태가 아니어도 desired state (UI 닫힘)이 이미 달성된 것으로 보고 `onSuccess()` 호출.
  * *Parameters*
    * `callback`
      * 닫기 결과 callback. UI 닫기에 실패한 경우 onFailure.
  * *onFailure ErrorCode*
    * `ErrorCode.ERROR_OCCURRED` - 예상치 못한 에러로 UI 닫기 실패
  ```kotlin theme={null}
  fun setUserId(userId: String, callback: ResultCallback)
  ```
  * *Description*
    * 사용자 식별에 사용되는 userId를 SDK 로컬에 설정함. `MomentSDK.start` 를 호출하기 전/후 모두 호출 가능.
    * userId 설정 후 백그라운드로 adId 업로드 (변경 있을 때만).
    * userId를 빈 문자열로 입력하면 실패함.
  * *Parameters*
    * `userId`
      * 사용자 식별에 사용되는 unique id. 빈 문자열 불가.
    * `callback`
      * 결과 callback.
  * *onFailure ErrorCode*
    * `ErrorCode.EMPTY_USER_ID` - userId가 빈 문자열인 경우
    * `ErrorCode.ERROR_OCCURRED` - 예상치 못한 에러
  ```kotlin theme={null}
  fun getUserId(): String
  ```
  * *Description*
    * `setUserId` 를 통해 저장된 userId를 반환. `MomentSDK.start` 전/후 모두 호출 가능.
    * `shouldDenyAll` 킬스위치가 켜진 상태면 빈 문자열 반환. 예외 발생 시도 빈 문자열 반환.
  * *Returns*
    * 저장된 userId. 없으면 빈 문자열.
  ```kotlin theme={null}
  fun setAdId(adId: String, isAdTrackingEnabled: Boolean, callback: ResultCallback)
  ```
  * *Description*
    * 호스트 앱에서 전달한 device advertising ID (ADID)를 SDK에 설정함. `MomentSDK.start` 전/후 모두 호출 가능.
    * `isAdTrackingEnabled=false`, `adId`가 빈 값, 또는 zeroed-out 값(`"00000000-0000-0000-0000-000000000000"`)이면 저장/업로드 안 함 (사용자 추적 거부).
    * 값이 변경되었을 때만 백그라운드에서 서버로 업로드.
  * *Parameters*
    * `adId`
      * advertising ID 문자열.
    * `isAdTrackingEnabled`
      * 사용자가 ad tracking에 동의한 경우 true. false면 adId를 저장/업로드하지 않음.
    * `callback`
      * 결과 callback.
  * *onFailure ErrorCode*
    * `ErrorCode.ERROR_OCCURRED` - 예상치 못한 에러
  ```kotlin theme={null}
  fun setUserAttributes(userAttributes: UserAttributes, callback: ResultCallback)
  ```
  * *Description*
    * 사용자의 demographics 정보 (생년, 성별)를 서버에 업로드함.
    * 모든 필드가 null이면 no-op으로 `onSuccess()` 호출. birthYear가 1900\~현재년도 범위를 벗어나면 실패.
  * *Parameters*
    * `userAttributes`
      * `UserAttributes.Builder` 로 구성된 사용자 속성 객체.
    * `callback`
      * 업로드 결과 callback.
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 deactivate된 경우
    * `ErrorCode.EMPTY_USER_ID` - userId가 설정되지 않은 경우
    * `ErrorCode.INVALID_USER_ATTRIBUTES` - birthYear가 1900 \~ 현재년도 범위를 벗어난 경우
    * `ErrorCode.NETWORK_CONNECTION_REQUIRED` - 인터넷 연결이 없는 경우
    * `ErrorCode.AUTHENTICATION_FAILED` - API 인증 실패
    * `ErrorCode.TOO_MANY_REQUESTS` - API를 과도하게 호출한 경우
    * `ErrorCode.TEMPORARY_SERVER_ERROR` - 페어리 서버에 일시적으로 문제가 생긴 경우
    * `ErrorCode.ERROR_OCCURRED` - 알 수 없는 에러
  ```kotlin theme={null}
  fun setSendBubble(sendBubble: Boolean)
  ```
  * *Description*
    * 버블 알림을 보낼지 일반 알림을 보낼지 설정함. `MomentSDK.start` 전/후 모두 호출 가능. 콜백 없는 fire-and-forget.
    * `shouldDenyAll` 킬스위치 상태면 no-op.
  * *Parameters*
    * `sendBubble`
      * true면 버블 알림, false면 일반 알림.
  ```kotlin theme={null}
  fun listCashback(callback: ListCashbackResultCallback)
  ```
  * *Description*
    * 현재 project에서 지원하는 cashback program 리스트를 callback으로 전달.
    * 이미 종료된 cashback (첫 product의 endDate가 현재 시각 이전)은 필터링.
    * callback의 `onSuccess` 에서 `List<CashbackProgram>` 을 받을 수 있음.
  * *Parameters*
    * `callback`
      * cashback 리스트 또는 실패 사유를 받는 callback.
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 listCashback이 deactivate된 경우
    * `ErrorCode.NETWORK_CONNECTION_REQUIRED` - 인터넷 연결이 없는 경우
    * `ErrorCode.AUTHENTICATION_FAILED` - API 인증 실패
    * `ErrorCode.TOO_MANY_REQUESTS` - API를 과도하게 호출한 경우
    * `ErrorCode.TEMPORARY_SERVER_ERROR` - 페어리 서버에 일시적으로 문제가 생긴 경우
    * `ErrorCode.ERROR_OCCURRED` - 알 수 없는 에러
  ```kotlin theme={null}
  fun goToCashback(businessId: String, callback: ResultCallback)
  ```
  * *Description*
    * 사용자가 선택한 캐시백의 businessId를 받아 해당 캐시백을 받을 수 있는 웹사이트/앱을 `FullWebViewActivity` 로 띄움.
    * businessId 또는 userId가 비어 있으면 실패.
  * *Parameters*
    * `businessId`
      * 사용자가 클릭한 캐시백의 business id.
    * `callback`
      * 실패/성공 결과 callback.
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 deactivate된 경우
    * `ErrorCode.EMPTY_CASHBACK_BUSINESS_ID` - businessId가 빈 문자열인 경우
    * `ErrorCode.EMPTY_USER_ID` - userId가 설정되지 않은 경우
    * `ErrorCode.NETWORK_CONNECTION_REQUIRED` - 인터넷 연결이 없는 경우
    * `ErrorCode.AUTHENTICATION_FAILED` - API 인증 실패
    * `ErrorCode.TOO_MANY_REQUESTS` - API를 과도하게 호출한 경우
    * `ErrorCode.TEMPORARY_SERVER_ERROR` - 페어리 서버에 일시적으로 문제가 생긴 경우
    * `ErrorCode.ERROR_OCCURRED` - 알 수 없는 에러
  ```kotlin theme={null}
  fun setConsent(request: SubmitUserConsentRequest, callback: CallbackWithResult<SubmitUserConsentResponse>)
  ```
  * *Description*
    * 현재 userId의 사용자 동의 상태를 생성하거나 업데이트하는 method (upsert).
    * `request.items` 에 명시된 동의 항목들이 서버에 그대로 저장됨.
    * `withdraw_all=true` 로 설정하면 서버에서 모든 동의 항목 일괄 철회 (items는 무시됨).
    * 호출 성공 시 서버에 저장된 최종 동의 상태가 `onSuccess` callback으로 반환됨.
  * *Parameters*
    * `request`
      * 저장할 동의 정보를 담은 `SubmitUserConsentRequest` 객체.
    * `callback`
      * 결과 callback (`CallbackWithResult<SubmitUserConsentResponse>`).
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 deactivate된 경우
    * `ErrorCode.NETWORK_CONNECTION_REQUIRED` - 인터넷 연결이 없는 경우
    * `ErrorCode.AUTHENTICATION_FAILED` - API 인증 실패
    * `ErrorCode.TOO_MANY_REQUESTS` - API를 과도하게 호출한 경우
    * `ErrorCode.TEMPORARY_SERVER_ERROR` - 페어리 서버에 일시적으로 문제가 생긴 경우
    * `ErrorCode.ERROR_OCCURRED` - 알 수 없는 에러
  ```kotlin theme={null}
  fun getConsent(callback: CallbackWithResult<UserConsent>)
  ```
  * *Description*
    * 현재 userId에 저장된 사용자 동의 상태를 서버에서 조회.
    * `UserConsent.isGranted(code)` 로 특정 동의 항목의 동의 여부 조회 가능. 해당 code의 동의 항목이 없으면 null 반환.
  * *Parameters*
    * `callback`
      * 결과 callback. `onSuccess(result: UserConsent)` 로 현재 동의 상태 전달.
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 deactivate된 경우
    * `ErrorCode.NETWORK_CONNECTION_REQUIRED` - 인터넷 연결이 없는 경우
    * `ErrorCode.AUTHENTICATION_FAILED` - API 인증 실패
    * `ErrorCode.TOO_MANY_REQUESTS` - API를 과도하게 호출한 경우
    * `ErrorCode.TEMPORARY_SERVER_ERROR` - 페어리 서버에 일시적으로 문제가 생긴 경우
    * `ErrorCode.ERROR_OCCURRED` - 알 수 없는 에러
  ```kotlin theme={null}
  fun withdraw(callback: ResultCallback)
  ```
  * *Description*
    * 현재 userId의 **모든** 사용자 동의를 철회. 내부적으로 `setConsent` 와 동일 endpoint를 `withdraw_all=true` 로 호출함.
    * 부분 철회는 `setConsent` 의 items 에서 원하는 code들을 `is_granted=false` 로 보낼 것.
  * *Parameters*
    * `callback`
      * 결과 callback.
  * *onFailure ErrorCode*
    * `ErrorCode.API_NOT_ACTIVATED` - 현재 프로젝트에서 deactivate된 경우
    * `ErrorCode.NETWORK_CONNECTION_REQUIRED` - 인터넷 연결이 없는 경우
    * `ErrorCode.AUTHENTICATION_FAILED` - API 인증 실패
    * `ErrorCode.TOO_MANY_REQUESTS` - API를 과도하게 호출한 경우
    * `ErrorCode.TEMPORARY_SERVER_ERROR` - 페어리 서버에 일시적으로 문제가 생긴 경우
    * `ErrorCode.ERROR_OCCURRED` - 알 수 없는 에러
  ```kotlin theme={null}
  fun setProjectId(projectId: String)
  ```
  * *Description*
    * SDK의 project ID를 설정함. `AuthManager` 를 통해 SharedPreferences에 저장.
    * 일반적으로 `AndroidManifest.xml` 의 meta-data로 설정하지만 런타임 변경이 필요할 때 사용.
  * *Parameters*
    * `projectId`
      * 설정할 project ID 문자열.
  ```kotlin theme={null}
  fun setApiKey(apiKey: String)
  ```
  * *Description*
    * SDK의 API key를 설정함. `AuthManager` 를 통해 SharedPreferences에 저장.
  * *Parameters*
    * `apiKey`
      * 설정할 API key 문자열.
  ```kotlin theme={null}
  fun getProjectId(appContext: Context): String
  ```
  * *Description*
    * 현재 SDK에 설정된 project ID를 반환.
  * *Parameters*
    * `appContext`
      * application context.
  * *Returns*
    * 현재 project ID. 없으면 빈 문자열.
  ```kotlin theme={null}
  fun getApiKey(appContext: Context): String
  ```
  * *Description*
    * 현재 SDK에 설정된 API key를 반환.
  * *Parameters*
    * `appContext`
      * application context.
  * *Returns*
    * 현재 API key. 없으면 빈 문자열.
  ```kotlin theme={null}
  @JvmStatic
  fun isAppUsagePermissionGranted(context: Context): Boolean
  ```
  * *Description*
    * 호스트 앱이 App Usage 권한(`PACKAGE_USAGE_STATS`)을 갖고 있는지 알려주는 util method.
    * `shouldDenyAll` 킬스위치가 켜진 상태면 false 반환.
  * *Parameters*
    * `context`
      * 권한 확인에 사용되는 null이 아닌 context.
  * *Returns*
    * 권한이 있으면 true, 아니면 false.
  ```kotlin theme={null}
  @JvmStatic
  fun isVpnPermissionGranted(context: Context): Boolean
  ```
  * *Description*
    * VPN 권한 상태를 확인하는 util method (vpn flavor용).
    * `shouldDenyAll` 킬스위치가 켜진 상태면 false 반환.
  * *Parameters*
    * `context`
      * 권한 확인에 사용되는 null이 아닌 context.
  * *Returns*
    * 권한이 있으면 true, 아니면 false.
  ```kotlin theme={null}
  @JvmStatic
  fun isNotificationPermissionGranted(context: Context): Boolean
  ```
  * *Description*
    * 앱에서 사용자에게 알림을 보낼 수 있는지를 알려주는 util method.
    * runtime notification permission은 Android API level 33부터 도입된 개념이므로 API level에 따라 아래의 값을 리턴함.
      * API level ≥ 33: notification permission이 있는지
      * API level \< 33: notification을 보낼 수 있는지
  * *Parameters*
    * `context`
      * 권한 확인에 사용되는 null이 아닌 context.
  * *Returns*
    * 알림 표시 가능하면 true, 아니면 false.

## `MomentSDK.Config`

```kotlin theme={null}
class Config : Serializable {
	constructor()
	constructor(appContext: Context)

	// Notification config
	fun notificationChannelId(notificationChannelId: String): Config
	fun notificationId(notificationId: Int): Config
	fun notificationIconResId(notificationIconResId: Int): Config

	// Service notification config
	fun serviceNotificationChannelId(serviceNotificationChannelId: String): Config
	fun serviceNotificationId(serviceNotificationId: Int): Config
	fun serviceNotificationIconResId(serviceNotificationIconResId: Int): Config
	fun serviceNotificationIconColorInt(@ColorInt serviceNotificationIconColorInt: Int): Config
	fun serviceNotificationTitle(serviceNotificationTitle: String): Config
	fun serviceNotificationText(serviceNotificationText: String): Config
	fun serviceNotificationLinkUrl(serviceNotificationLinkUrl: String): Config

	// BETA
	fun hideServiceNotification(hideServiceNotification: Boolean): Config
}
```

Config 클래스는 builder pattern으로 인스턴스를 구성합니다. 각 setter 메서드는 `Config` 자기 자신을 반환하므로 chaining 가능합니다.

\<aside>\
❗

`notificationChannelId`로 넘겨주는 알림 채널을 생성할 때, importance를 `IMPORTANCE_HIGH` 이상으로 설정해주셔야 합니다. (그래야 사용자에게 heads-up 알림으로 알림이 나가게 됩니다.)

\</aside>

## `MomentException`

```kotlin theme={null}
class MomentException(val errorCode: ErrorCode, errorMessage: String) : Exception(errorMessage) {
	constructor(errorCode: ErrorCode, context: Context)

	val errorCode: ErrorCode
	override val message: String
}
```

## `UserAttributes`

```kotlin theme={null}
class UserAttributes private constructor(
	val gender: Gender?,
	val birthYear: Int?,
) {
	class Builder {
		fun gender(value: Gender): Builder
		fun birthYear(value: Int): Builder
		fun build(): UserAttributes
	}
}
```

객체는 다음과 같이 builder를 통해 생성합니다.

```kotlin theme={null}
val attrs = UserAttributes.Builder()
	.gender(Gender.FEMALE)
	.birthYear(1990)
	.build()
```

# Enum

## `ErrorCode`

```kotlin theme={null}
enum class ErrorCode {
	API_NOT_ACTIVATED,
	APP_USAGE_PERMISSION_REQUIRED,
	VPN_PERMISSION_REQUIRED,
	NETWORK_CONNECTION_REQUIRED,
	SERVICE_START_FAILED,
	SERVICE_STOP_FAILED,
	SERVICE_ALREADY_STARTING,
	PROJECT_ID_REQUIRED,
	API_KEY_REQUIRED,
	GET_SIGNING_KEY_FAILED,
	AUTHENTICATION_FAILED,
	EMPTY_USER_ID,
	EMPTY_CASHBACK_BUSINESS_ID,
	INVALID_CONFIG,
	INVALID_USER_ATTRIBUTES,
	CASHBACK_NOT_FOUND,
	TOO_MANY_REQUESTS,
	TEMPORARY_SERVER_ERROR,
	ERROR_OCCURRED,
	EMPTY_DEVICE_TOKEN,
	PUSH_NOT_SUPPORTED,
}
```

* Full reference
  * `API_NOT_ACTIVATED` — 현재 프로젝트가 서버 kill-switch 또는 device 상태(rooted)에 의해 deactivate된 경우.
  * `APP_USAGE_PERMISSION_REQUIRED` — x flavor에서 `PACKAGE_USAGE_STATS` 권한이 없는 경우.
  * `VPN_PERMISSION_REQUIRED` — vpn flavor에서 VPN 권한이 없는 경우.
  * `NETWORK_CONNECTION_REQUIRED` — 인터넷 연결 없음 또는 timeout (TimeoutException / IOException).
  * `SERVICE_START_FAILED` — foreground service 시작에 실패한 경우.
  * `SERVICE_STOP_FAILED` — foreground service 중지에 실패한 경우.
  * `SERVICE_ALREADY_STARTING` — start가 이미 진행 중이거나 서비스가 실행 중인 경우.
  * `PROJECT_ID_REQUIRED` — projectId가 설정되지 않은 경우.
  * `API_KEY_REQUIRED` — apiKey가 설정되지 않은 경우.
  * `GET_SIGNING_KEY_FAILED` — signing key 조회에 실패한 경우.
  * `AUTHENTICATION_FAILED` — API 인증 실패 (HTTP 403).
  * `EMPTY_USER_ID` — userId가 설정되지 않거나 빈 문자열인 경우.
  * `EMPTY_CASHBACK_BUSINESS_ID` — cashback businessId가 빈 문자열인 경우.
  * `INVALID_CONFIG` — `Config` 의 필수 필드가 누락되었거나 잘못된 경우.
  * `INVALID_USER_ATTRIBUTES` — `UserAttributes` 검증 실패 (birthYear가 1900\~현재년도 범위 밖).
  * `CASHBACK_NOT_FOUND` — 주어진 businessId의 cashback이 존재하지 않는 경우.
  * `TOO_MANY_REQUESTS` — API를 과도하게 호출한 경우 (HTTP 429).
  * `TEMPORARY_SERVER_ERROR` — 서버 일시 오류 (HTTP 502/503/504).
  * `ERROR_OCCURRED` — 알 수 없는 / 분류되지 않은 에러.
  * `EMPTY_DEVICE_TOKEN` — push device token이 비어 있는 경우.
  * `PUSH_NOT_SUPPORTED` — push 미지원 빌드 또는 환경.

## `Gender`

```kotlin theme={null}
enum class Gender {
	MALE,
	FEMALE,
}
```

## `MomentSDK.RestartResultCode`

```kotlin theme={null}
enum class RestartResultCode {
	SERVICE_SHOULD_NOT_BE_RUNNING,
	SERVICE_ALREADY_RUNNING,
	SERVICE_RESTARTED,
}
```

* Full reference
  * `SERVICE_SHOULD_NOT_BE_RUNNING` — 서비스가 꺼져 있어야 하는데 `init` 이 호출된 경우 (start가 호출된 적 없거나 명시적 stop 이후).
  * `SERVICE_ALREADY_RUNNING` — service가 켜져 있고 이미 실행 중인 경우 (재시작 불필요).
  * `SERVICE_RESTARTED` — service가 꺼져 있어서 재시작을 시도했고 성공한 경우.

# Callback

## `MomentSDK.CallbackWithResult<T>`

```kotlin theme={null}
interface CallbackWithResult<T> {
	fun onSuccess(result: T)
	fun onFailure(exception: MomentException)
}
```

## `MomentSDK.ResultCallback`

```kotlin theme={null}
interface ResultCallback {
	fun onSuccess()
	fun onFailure(exception: MomentException)
}
```

## `MomentSDK.UiFinishCallback`

```kotlin theme={null}
interface UiFinishCallback {
	fun onFinish()
}
```

`launchUI` 로 띄운 페어리 제공 웹뷰가 finish 되었을 때 트리거됩니다.

## `MomentSDK.RestartResultCallback`

```kotlin theme={null}
interface RestartResultCallback {
	fun onSuccess(resultCode: RestartResultCode)
	fun onFailure(exception: MomentException)
}
```

## `MomentSDK.ListCashbackResultCallback`

```kotlin theme={null}
interface ListCashbackResultCallback {
	fun onSuccess(cashbackPrograms: List<CashbackProgram>)
	fun onFailure(exception: MomentException)
}
```

# Data

## `UserConsent`

```kotlin theme={null}
class UserConsent(items: List<ConsentItem>) {
	val items: List<ConsentItem>
	fun isGranted(code: String): Boolean?
}

data class ConsentItem(
	val code: String,
	val isGranted: Boolean,
)
```

* `isGranted(code)` 는 해당 code의 동의 항목이 없으면 **null**, 있으면 그 항목의 동의 여부 (true/false)를 반환합니다. 호스트 앱은 null vs false를 구분해서 UI를 결정할 수 있습니다.

지원되는 consent item code:

* `TERMS_OF_SERVICE` — 서비스 이용 약관
* `PERSONAL_INFO` — 개인정보 수집/이용
* `MARKETING_PUSH` — 마케팅 정보 수신
* `MARKETING_PERSONAL_INFO` — 마케팅 활용을 위한 개인정보 처리
* `NIGHTTIME_PUSH` — 야간 알림 수신
* `PARTNER_THIRD_PARTY_PROVISION` — 제3자 제공 동의 (파트너)

## `SubmitUserConsentRequest`

```kotlin theme={null}
class SubmitUserConsentRequest {
	val items: List<ConsentItemValue>
	val withdrawAll: Boolean
}
```

객체는 다음과 같이 builder를 통해 생성합니다.

```kotlin theme={null}
val request = SubmitUserConsentRequest.newBuilder()
	.addItems(ConsentItemValue.newBuilder()
		.setItemCode("TERMS_OF_SERVICE")
		.setIsGranted(true)
		.build())
	.addItems(ConsentItemValue.newBuilder()
		.setItemCode("PERSONAL_INFO")
		.setIsGranted(true)
		.build())
	.build()
```

* `items` 에 명시한 동의 항목만 서버에 upsert됩니다 (항목별 부분 업데이트 가능).
* `withdrawAll=true` 로 설정하면 items 는 무시되고 모든 동의 항목이 일괄 철회됩니다. (`SDK.withdraw` 가 내부적으로 이 형태로 호출함)

## `SubmitUserConsentResponse`

```kotlin theme={null}
class SubmitUserConsentResponse {
	val items: List<ConsentItemValue>
}
```

서버에 저장된 최종 동의 상태가 `setConsent` 의 onSuccess callback으로 전달됩니다.

## `ConsentItemValue`

```kotlin theme={null}
class ConsentItemValue {
	val itemCode: String       // 서버 정의 consent item code
	val isGranted: Boolean     // true = 동의 / false = 비동의 (null 상태는 표현 안 함)
}
```

proto 응답에서는 false가 "비동의"를 의미합니다. "답변 안 함" 상태는 proto에 별도로 표현되지 않고, 응답의 items 리스트에 해당 code가 포함되지 않는 형태로 전달됩니다.

## `CashbackProgram`

```kotlin theme={null}
class CashbackProgram {
	val businessId: String
	val businessName: String
	val businessImageUrl: String
	val url: String
	val programName: String
	val productsList: List<CashbackProduct>
}
```

* `productsList` 는 비어 있을 수 없으며, 항상 `[0]` 번 index가 main product로 취급됨.
* 모든 항목이 동일한 프로그램을 공유하는 경우 `[0]` 번 index에 `target = ""` 로 데이터가 들어가 있음.
