- Last Updated
Third-Party Market
Understanding
A payment-integration feature for servicing games on mobile markets (ONE Store) other than Google Play and the Apple App Store.
It supports unifying each market's payment system through the STOVE SDK's IAP interface for handling.
The basic payment flow is the same as Google (IAP) integration, and you apply it by declaring the per-market dedicated IAP module (iap-onestore) and adding Partners settings.
Other SDK features (authentication, log, push, etc.) can use the standard Android modules as-is.
Features other than payment (IAP) work with standard Android modules, without a market-dedicated module.
Third-party market integration can be applied just by swapping the IAP module and adding Partners settings.
Application Environment and Operating Scope
| Item | Details |
|---|---|
| Supported market | ONE Store. |
| Target platform | Android only. iOS isn't a target. |
| IAP module | ONE Store → use the dedicated iap-onestore module. |
| Package-name policy | Using the same Package Name as Google Play is prohibited. A separate Package Name must be issued per market. |
| Payment flow | Same as Google (IAP) integration work. The SDK abstracts and handles each market's payment system. |
| Other features | Authentication, character, log, push, coupon, deep link, etc. work with the standard Android modules as-is. |
Integration Flow
Third-party market integration proceeds in 3 steps: Partners settings → apply the module → implement the SDK.
| Step | Task | Task location | Key content |
|---|---|---|---|
| 1 | Partners settings | Partners console | Register market info, IAP info, and per-market product-ID mapping |
| 2 | Apply the module | build.gradle | Register the STOVE repository and add the per-market IAP module dependency |
| 3 | Implement the SDK | Game (app) code | Initialize → register listener → handle payment result (same as Google IAP) |
Register per-market info in Partners.
- Mobile market info: Package Name, billing key, App ID, etc.
- Per-market IAP info: the billing-integration ID and market authentication info
- Product-ID mapping: register the ONE Store product ID in the Market Product Management menu
Additional ONE Store registration items
ㆍ Besides the billing key, you must issue the OAuth Client ID, Client Secret, and License Key from the ONE Store console and enter them additionally.
ㆍ If the per-market product-ID mapping is missing, payment won't proceed.
Add the STOVE repository and the IAP module for the market you'll use to the game project's build.gradle.
When using ONE Store, also register the ONE Store SDK repository.
| Category | Repository | IAP module |
|---|---|---|
| STOVE common | https://externalnexus.iam0.com/repository/mvp | - |
| ONE Store | STOVE common repository + https://repo.onestore.net/repository/onestore-sdk-public |
iap-onestore |
It's the same flow as Google (IAP) integration. The module handles per-market differences internally, so you don't need to write separate branching code.
- Call
initialize()— SDK initialization - Register
setListener()— set up the payment-event listener - Handle the payment result — implement success/failure/cancellation branching
Integration Guide
Integration Preparation
Before third-party market integration, you must register the game in each market's developer console and issue the required authentication info.
| Target | Preparation items |
|---|---|
| Common | Decide the per-market Package Name (no duplication with Google), App ID, In-APP billing key, FCM Key, and app icon (500x500px PNG, up to 2MB). |
| ONE Store | After registering the app in the ONE Store Developer Center, issue the OAuth Client ID, Client Secret, and License Key. |
Be careful not to use the same Package Name as Google.
Using the same Package Name can cause IAP failures, and it can't be edited after registration, so enter it carefully in advance.
ONE Store Setup
ONE Store integration is done in 3 Partners menus.
For ONE Store, additionally register the OAuth authentication info (Client ID, Secret, License Key).
In the Partners Mobile Market Info menu, select ONE Store as the market type and register the following items.
| Item | Input value / format |
|---|---|
| Representative game name / game ID | Select the registered game info |
| Market type | ONE Store |
| Market game ID | Enter the app's Package Name (don't use the same value as Google) |
| Package name | The market game ID value is shown as-is |
| In-app billing key | Up to 1024 characters |
| App ID | Up to 250 characters |
| App Icon | 500×500px PNG, up to 2MB |
| Google Push FCM Key | Up to 500 characters |
Precautions when registering the market game ID
ㆍ The market game ID can't be edited after registration.
ㆍ You can't use the same Package Name as Google. Enter a separate value.
In the Partners Per-Market IAP Info Management menu, select ONE store as the market item and enter all 4 of the following items.
| Item | Description |
|---|---|
| Billing-integration ID | Enter the same value as the app's Package Name |
| ONE Store Client ID | The OAuth Client ID issued from the ONE Store console |
| ONE Store Client Secret | The OAuth Client Secret issued from the ONE Store console |
| ONE Store License Key | The License Key issued from the ONE Store console |
Issue the OAuth authentication info from the ONE Store console.
ㆍ Issuance path: ONE Store console > APPS > Product Status > In-App Info > Managed Products > In-App API Management (info window)
Enter product info in the Partners Market Product Management menu,
and map the product ID registered in ONE Store in the Market Product ID (ONE Store) column.
| Input item | Description |
|---|---|
| Product type | General product / OOAP product / Subscription product |
| Product ID | The in-game product identifier |
| Market product name | The product name shown in the market |
| Tier | The price tier |
| Payout exclusion / item | The payout-exclusion policy or the item to grant |
| Purchase restriction | Whether a purchase restriction applies |
| Purchase-restriction info | Details such as the restriction reason and period |
| Market product ID (ONE Store) | Map the product ID registered in ONE Store |
Development
ONE Store Implementation
For ONE Store payment, after applying the iap-onestore module, call in the order initialize → setListener. Payment results are delivered via the setListener callback, and for some error codes (RESULT_NEED_UPDATE / RESULT_NEED_LOGIN) the SDK automatically provides multilingual messages.
Third-party market payment integration currently provides only ONE Store.
Prerequisites
- The Partners settings (mobile market info, per-market IAP info, market product-ID mapping) must be complete in the 2. Integration Guide section.
- The OAuth Client ID / Client Secret / License Key issued from the ONE Store console must be registered in Partners.
- The module applies only to Android builds. (iOS isn't a target)
Apply the module (build.gradle)
Register both the STOVE repository and the ONE Store SDK repository in build.gradle, and add the iap-onestore module dependency. Check the latest version in SDK Download and Installation.
repositories {
google()
jcenter()
maven {
url "https://externalnexus.iam0.com/repository/mvp" // for Stove
// Add the option when using Android Gradle Plugin 7.0 or higher
allowInsecureProtocol = true
}
mavenCentral() // for Facebook
maven {
url "https://repo.onestore.net/repository/onestore-sdk-public" // for OneStore
allowInsecureProtocol = true
}
}
dependencies {
// Apply the latest version per the release notes.
implementation 'com.stove:iap-onestore:2.8.0'
}
Development Flow
- Create the
OneStoreobject: create aOneStore(activity)instance with the Activity context. - Call
initialize(): branch on the result in the initialization callback.- Success → call
setListener() Auth.UnauthorizedError(30002) → guide to the login screen- Other failures (InitializeError) → extract
IAP.ResponseCodeKey/IAP.DebugMessageKey, then notify the user
- Success → call
- Register
setListener(): register the listener that receives payment results. Registering once while the game is active is enough. - Branch on the payment result: branch on the
result.errorCodereceived in the callback and handle each case. (see the table below)
Payment-result branching
| Result | Situation | Handling guide |
|---|---|---|
isSuccessful() | Purchase succeeded | Proceed with granting the game item |
isCanceled() | User cancelled | Show the payment screen again or return to the previous screen |
isServerError() | Billing-server error | Notify the user of a temporary error and recommend a retry |
SuccessButUserMissMatch | Payment verification succeeded, but the current account differs from the account at payment time (can occur on account/world change) | With deliveryMethod = ALL(default), it's auto-granted to the account at payment time. When using ONLY/NOTI, the game decides whether to grant |
Purchased | Payment complete, awaiting billing-server verification | The billing server automatically verifies and grants afterward, then notifies the user by push. Show activation guidance to users with push reception disabled |
Waiting | Billing-server verification complete, item grant pending due to a game-server/cache-system communication error | The billing server auto-reprocesses, then notifies the user by push. Show activation guidance to users with push reception disabled |
MarketError | Market (ONE Store) communication error | Check the market response code via userInfo[IAP.ResponseCodeKey]. If RESULT_NEED_UPDATE / RESULT_NEED_LOGIN, show the multilingual message in userInfo[IAP.DebugMessageKey] as-is |
RESULT_NEED_UPDATE / RESULT_NEED_LOGIN automatically provide multilingual messages.
ㆍ RESULT_NEED_UPDATE: ONE Store app install/update needed
ㆍ RESULT_NEED_LOGIN: ONE Store login needed
ㆍ Show the result.userInfo[IAP.DebugMessageKey] value to the user as-is.
ㆍ For the full market response codes, refer to the ONE Store PurchaseClient.ResponseCode document and the SDK error codes.
Note: flush() behavior
Because ONE Store provides an asynchronous unpaid-payment lookup function, the return value of flush() is always true. Unpaid-item handling works the same as existing IAP, so as long as you don't directly reference the return value of flush() as a branching condition, there's no impact on the game implementation.
Sample Code
private val iap: OneStore by lazy {
OneStore(activity)
}
private fun initialize() {
iap.initialize { result ->
when {
result.isSuccessful() -> {
setListener() // call setListener
}
result.errorCode == Auth.UnauthorizedError -> {
// TODO: login
}
else -> {
// InitializeError
val responseCode = result.userInfo!!.getValue(IAP.ResponseCodeKey)
// RESULT_NEED_LOGIN / RESULT_NEED_UPDATE have multilingual messages applied
val debugMessage = result.userInfo!!.getValue(IAP.DebugMessageKey)
}
}
}
}
private fun setListener() {
iap.setListener { result, product, purchaseDetail ->
when {
result.isSuccessful() -> {
// Purchase succeeded
}
result.isCanceled() -> {
// Purchase cancelled → show the screen again
}
result.isServerError() -> {
// Billing-server error
}
result.errorCode == SuccessButUserMissMatch -> {
// Mismatch between the payment-verification-time account and the current account — handle per the deliveryMethod policy
}
result.errorCode == Purchased -> {
// Awaiting payment verification — guide to enable push
}
result.errorCode == Waiting -> {
// Awaiting grant — guide to enable push
}
result.errorCode == MarketError -> {
val responseCode = result.userInfo!!.getValue(IAP.ResponseCodeKey)
val debugMessage = result.userInfo!!.getValue(IAP.DebugMessageKey)
}
}
}
}