- 마지막 업데이트
서드파티 마켓
이해하기
구글 플레이·애플 앱스토어 외의 모바일 마켓(원스토어)에서 게임을 서비스하기 위한 결제 연동 기능이에요.
각 마켓의 결제 시스템을 STOVE SDK의 IAP 인터페이스로 통일하여 처리할 수 있도록 지원해요.
기본 결제 흐름은 구글(IAP) 연동과 동일하며, 각 마켓별 전용 IAP 모듈(iap-onestore)을 선언하고 파트너스 설정을 추가하는 방식으로 적용해요.
이외의 SDK 기능(인증·로그·푸시 등)은 표준 Android 모듈을 그대로 적용하면 돼요.
결제(IAP) 외 기능은 마켓 전용 모듈 없이 표준 Android 모듈로 동작해요.
서드파티 마켓 연동은 IAP 모듈 교체와 파트너스 설정 추가만으로 적용할 수 있어요.
적용 환경 및 동작 범위
| 항목 | 내용 |
|---|---|
| 지원 마켓 | 원스토어(ONE Store). |
| 대상 플랫폼 | Android 전용. iOS는 적용 대상이 아님. |
| IAP 모듈 | 원스토어 → iap-onestore 전용 모듈 사용. |
| 패키지명 정책 | 구글 플레이와 동일한 Package Name 사용 금지. 마켓별 별도 Package Name 발급 필요. |
| 결제 흐름 | 구글(IAP) 연동 작업과 동일. SDK가 마켓별 결제 시스템을 추상화하여 처리. |
| 기타 기능 | 인증·캐릭터·로그·푸시·쿠폰·딥링크 등은 표준 Android 모듈 그대로 동작. |
연동 흐름
서드파티 마켓 연동은 파트너스 설정 → 모듈 적용 → SDK 구현 3단계로 진행돼요.
| 단계 | 작업 | 작업 위치 | 주요 내용 |
|---|---|---|---|
| 1 | 파트너스 설정 | 파트너스 콘솔 | 마켓 정보·IAP 정보·마켓별 상품 ID 매핑 등록 |
| 2 | 모듈 적용 | build.gradle | 스토브 저장소 등록 및 마켓별 IAP 모듈 의존성 추가 |
| 3 | SDK 구현 | 게임(앱) 코드 | 초기화 → 리스너 등록 → 결제 결과 처리 (구글 IAP와 동일) |
파트너스에서 마켓별 정보를 등록해요.
- 모바일 마켓 정보: Package Name, 빌링 키, App ID 등
- 마켓별 IAP 정보: 빌링 연동용 ID, 마켓 인증 정보
- 상품 ID 매핑: 마켓 상품 관리 메뉴에서 원스토어 상품 ID 등록
원스토어 추가 등록 사항
ㆍ 빌링 키 외에 OAuth Client ID·Client Secret·License Key를 원스토어 콘솔에서 발급받아 추가로 입력해야 해요.
ㆍ 마켓별 상품 ID 매핑이 누락되면 결제가 진행되지 않아요.
게임 프로젝트의 build.gradle에 스토브 저장소와 사용할 마켓의 IAP 모듈을 추가해요.
원스토어를 사용하는 경우 ONE Store SDK 저장소를 함께 등록해요.
| 구분 | 저장소 | IAP 모듈 |
|---|---|---|
| 스토브 공통 | https://externalnexus.iam0.com/repository/mvp | - |
| 원스토어 | 스토브 공통 저장소 + https://repo.onestore.net/repository/onestore-sdk-public |
iap-onestore |
구글(IAP) 연동과 동일한 흐름이에요. 마켓별 차이는 모듈이 내부적으로 처리하므로 별도 분기 코드를 작성할 필요는 없어요.
initialize()호출 — SDK 초기화setListener()등록 — 결제 이벤트 수신 리스너 설정- 결제 결과 처리 — 성공·실패·취소 분기 구현
연동 가이드
연동 준비
서드파티 마켓 연동 전에 마켓별 개발자 콘솔에 게임을 등록하고 필요한 인증 정보를 발급받아야 해요.
| 대상 | 준비 항목 |
|---|---|
| 공통 | 마켓별 Package Name 결정(구글과 중복 금지), App ID, In-APP 빌링 키, FCM Key, 앱 아이콘(500x500px PNG, 최대 2MB). |
| 원스토어 | 원스토어 개발자 센터에서 앱 등록 후 OAuth Client ID, Client Secret, License Key 발급. |
구글과 동일한 Package Name을 사용하지 않도록 주의해야 해요.
동일 Package Name 사용 시 IAP에 장애가 발생할 수 있으며, 등록 후 수정이 불가하므로 사전에 주의하여 입력해야 해요.
원스토어 설정
원스토어 연동은 파트너스의 3개 메뉴에서 진행해요.
원스토어는 OAuth 인증 정보(Client ID·Secret·License Key)를 추가로 등록해요.
파트너스 모바일 마켓 정보 메뉴에서 마켓 종류를 원스토어로 선택하고 다음 항목을 등록해요.
| 항목 | 입력 값 / 형식 |
|---|---|
| 대표 게임명·게임 ID | 등록한 게임 정보 선택 |
| 마켓 종류 | 원스토어 |
| 마켓 게임 ID | 앱의 Package Name 입력 (구글과 동일한 값 사용 금지) |
| 패키지명 | 마켓 게임 ID 값이 그대로 노출 |
| In-app 빌링 키 | 최대 1024자 |
| App ID | 최대 250자 |
| App Icon | 500×500px PNG, 최대 2MB |
| Google Push FCM Key | 최대 500자 |
마켓 게임 ID 등록 시 주의사항
ㆍ 마켓 게임 ID는 등록 후 수정이 불가해요.
ㆍ 구글과 동일한 Package Name은 사용할 수 없어요. 별도 값으로 입력해 주세요.
파트너스 마켓 별 IAP 정보 관리 메뉴에서 마켓 항목을 ONE store로 선택하고, 다음 4개 항목을 모두 입력해요.
| 항목 | 설명 |
|---|---|
| 빌링 연동용 ID | 앱의 Package Name과 동일한 값 입력 |
| ONE Store Client ID | 원스토어 콘솔에서 발급한 OAuth Client ID |
| ONE Store Client Secret | 원스토어 콘솔에서 발급한 OAuth Client Secret |
| ONE Store License Key | 원스토어 콘솔에서 발급한 License Key |
OAuth 인증 정보는 원스토어 콘솔에서 발급받아요.
ㆍ 발급 경로: 원스토어 콘솔 > APPS > 상품현황 > In-App정보 > 관리상품 > In-App API 관리(정보창)
파트너스 마켓 상품 관리 메뉴에서 상품 정보를 입력하고,
마켓 상품 ID(원스토어) 컬럼에 원스토어에 등록한 상품 ID를 매핑해요.
| 입력 항목 | 설명 |
|---|---|
| 상품 타입 | 일반상품 / OOAP 상품 / 구독상품 |
| 상품 ID | 게임 내 상품 식별자 |
| 마켓 상품명 | 마켓에 노출되는 상품명 |
| 티어 | 가격 티어 |
| 지급 제외 / 아이템 | 지급 제외 정책 또는 지급 아이템 |
| 구매 제한 여부 | 구매 제한 적용 여부 |
| 구매 제한 정보 | 제한 사유·기간 등 상세 |
| 마켓 상품 ID(원스토어) | 원스토어에 등록한 상품 ID 매핑 |
개발하기
원스토어 구현
원스토어 결제는 iap-onestore 모듈을 적용한 뒤 initialize → setListener 순으로 호출해요. 결제 결과는 setListener 콜백으로 전달되며, 일부 에러 코드(RESULT_NEED_UPDATE / RESULT_NEED_LOGIN)는 SDK가 다국어 메시지를 자동으로 제공해요.
서드파티 마켓 결제 연동은 현재 원스토어만 제공해요.
사전 준비
- 파트너스 설정(모바일 마켓 정보·마켓 별 IAP 정보·마켓 상품 ID 매핑)은 2. 연동 가이드 섹션에서 완료된 상태여야 해요.
- 원스토어 콘솔에서 발급한 OAuth Client ID / Client Secret / License Key가 파트너스에 등록돼 있어야 해요.
- 모듈은 Android 빌드에만 적용해요. (iOS는 적용 대상 아님)
모듈 적용 (build.gradle)
build.gradle에 스토브 저장소와 ONE Store SDK 저장소 두 개를 함께 등록하고, iap-onestore 모듈 의존성을 추가해요. 최신 버전은 SDK 다운로드 및 설치에서 확인하세요.
repositories {
google()
jcenter()
maven {
url "https://externalnexus.iam0.com/repository/mvp" // for Stove
// Android Gradle Plugin 7.0 이상 사용 시 옵션 추가
allowInsecureProtocol = true
}
mavenCentral() // for Facebook
maven {
url "https://repo.onestore.net/repository/onestore-sdk-public" // for OneStore
allowInsecureProtocol = true
}
}
dependencies {
// 릴리즈노트 기준 최신 버전으로 적용하세요.
implementation 'com.stove:iap-onestore:2.8.0'
}
개발 흐름
OneStore객체 생성: Activity 컨텍스트로OneStore(activity)인스턴스를 만들어요.initialize()호출: 초기화 콜백에서 결과를 분기 처리해요.- 성공 →
setListener()호출 Auth.UnauthorizedError(30002) → 로그인 화면으로 유도- 그 외 실패(InitializeError) →
IAP.ResponseCodeKey/IAP.DebugMessageKey추출 후 이용자 안내
- 성공 →
setListener()등록: 결제 결과를 받을 리스너를 등록해요. 게임이 활성 상태인 동안 1회 등록하면 돼요.- 결제 결과 분기 처리: 콜백으로 들어온
result.errorCode를 분기해 각 케이스를 처리해요. (아래 표 참고)
결제 결과 분기
| 결과 | 상황 | 처리 가이드 |
|---|---|---|
isSuccessful() | 구매 성공 | 게임 아이템 지급 진행 |
isCanceled() | 이용자 취소 | 결제 화면을 다시 노출하거나 직전 화면으로 복귀 |
isServerError() | 빌링 서버 오류 | 이용자에게 일시적 오류 안내, 재시도 권장 |
SuccessButUserMissMatch | 결제 검증은 성공했으나 현재 계정과 결제 시점 계정이 다름 (계정/월드 변경 시 발생 가능) | deliveryMethod = ALL(default)이면 결제 시점 계정에 자동 지급. ONLY·NOTI 사용 시 게임에서 지급 여부를 직접 판단 |
Purchased | 결제는 완료, 빌링 서버 검증 대기 중 | 빌링 서버가 자동으로 후속 검증·지급 후 푸시로 이용자에게 알림. 푸시 수신 비활성 이용자에게는 활성화 안내 노출 |
Waiting | 빌링 서버 검증은 완료, 게임 서버/캐시 시스템 통신 오류로 아이템 지급 대기 | 빌링 서버가 자동 재처리 후 푸시로 이용자에게 알림. 푸시 수신 비활성 이용자에게는 활성화 안내 노출 |
MarketError | 마켓(원스토어) 통신 오류 | userInfo[IAP.ResponseCodeKey]로 마켓 응답 코드 확인. RESULT_NEED_UPDATE / RESULT_NEED_LOGIN이면 userInfo[IAP.DebugMessageKey]의 다국어 메시지를 그대로 노출 |
RESULT_NEED_UPDATE / RESULT_NEED_LOGIN은 다국어 메시지가 자동 제공돼요.
ㆍ RESULT_NEED_UPDATE: 원스토어 앱 설치/업데이트 필요
ㆍ RESULT_NEED_LOGIN: 원스토어 로그인 필요
ㆍ result.userInfo[IAP.DebugMessageKey] 값을 그대로 이용자에게 노출하면 돼요.
ㆍ 마켓 응답 코드 전체는 원스토어 PurchaseClient.ResponseCode 문서와 SDK 에러 코드를 참고하세요.
참고: flush() 동작
원스토어는 비동기식 미지급결제 조회 함수를 제공하기 때문에 flush()의 반환값은 **항상 true**예요. 미지급건 처리는 기존 IAP와 동일하게 동작하므로, flush()의 리턴값을 분기 조건으로 직접 참조하지 않는다면 게임 구현에 영향이 없어요.
샘플 코드
private val iap: OneStore by lazy {
OneStore(activity)
}
private fun initialize() {
iap.initialize { result ->
when {
result.isSuccessful() -> {
setListener() // setListener 호출
}
result.errorCode == Auth.UnauthorizedError -> {
// TODO: login
}
else -> {
// InitializeError
val responseCode = result.userInfo!!.getValue(IAP.ResponseCodeKey)
// RESULT_NEED_LOGIN / RESULT_NEED_UPDATE는 다국어 메시지 적용됨
val debugMessage = result.userInfo!!.getValue(IAP.DebugMessageKey)
}
}
}
}
private fun setListener() {
iap.setListener { result, product, purchaseDetail ->
when {
result.isSuccessful() -> {
// 구매 성공
}
result.isCanceled() -> {
// 구매 취소 → 화면 다시 표시
}
result.isServerError() -> {
// 빌링 서버 오류
}
result.errorCode == SuccessButUserMissMatch -> {
// 결제 검증 시점과 현재 계정 불일치 — deliveryMethod 정책에 따라 처리
}
result.errorCode == Purchased -> {
// 결제 검증 대기 — 푸시 활성화 안내
}
result.errorCode == Waiting -> {
// 지급 대기 — 푸시 활성화 안내
}
result.errorCode == MarketError -> {
val responseCode = result.userInfo!!.getValue(IAP.ResponseCodeKey)
val debugMessage = result.userInfo!!.getValue(IAP.DebugMessageKey)
}
}
}
}