Skip to content
Stove
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)

Step 1. Partners settings

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.


Step 2. Apply the module

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

Step 3. Implement the SDK

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.

  1. Call initialize() — SDK initialization
  2. Register setListener() — set up the payment-event listener
  3. 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).


Register mobile market info

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 IDSelect the registered game info
Market typeONE Store
Market game IDEnter the app's Package Name (don't use the same value as Google)
Package nameThe market game ID value is shown as-is
In-app billing keyUp to 1024 characters
App IDUp to 250 characters
App Icon500×500px PNG, up to 2MB
Google Push FCM KeyUp 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.


Per-market IAP info management

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 IDEnter the same value as the app's Package Name
ONE Store Client IDThe OAuth Client ID issued from the ONE Store console
ONE Store Client SecretThe OAuth Client Secret issued from the ONE Store console
ONE Store License KeyThe 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)


Market product management

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 typeGeneral product / OOAP product / Subscription product
Product IDThe in-game product identifier
Market product nameThe product name shown in the market
TierThe price tier
Payout exclusion / itemThe payout-exclusion policy or the item to grant
Purchase restrictionWhether a purchase restriction applies
Purchase-restriction infoDetails 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 initializesetListener. 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.

groovy
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

  1. Create the OneStore object: create a OneStore(activity) instance with the Activity context.
  2. 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
  3. Register setListener(): register the listener that receives payment results. Registering once while the game is active is enough.
  4. Branch on the payment result: branch on the result.errorCode received in the callback and handle each case. (see the table below)

Payment-result branching

ResultSituationHandling guide
isSuccessful()Purchase succeededProceed with granting the game item
isCanceled()User cancelledShow the payment screen again or return to the previous screen
isServerError()Billing-server errorNotify the user of a temporary error and recommend a retry
SuccessButUserMissMatchPayment 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
PurchasedPayment complete, awaiting billing-server verificationThe billing server automatically verifies and grants afterward, then notifies the user by push. Show activation guidance to users with push reception disabled
WaitingBilling-server verification complete, item grant pending due to a game-server/cache-system communication errorThe billing server auto-reprocesses, then notifies the user by push. Show activation guidance to users with push reception disabled
MarketErrorMarket (ONE Store) communication errorCheck 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

kotlin
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)
            }
        }
    }
}

Frequently Asked Questions



Q1. Why can't I use the same Package Name as Google Play?
A. Per-market payment handling uses the Package Name as an identifier, so if the same Package Name is registered in multiple markets,
an IAP failure can occur during payment verification and item grant. Also, if an app downloaded from ONE Store calls the Google Play payment system, or vice versa, payment isn't processed correctly.
You must issue and use a separate Package Name per market (e.g., com.smilegate.xxxx.onestoreqa).
Q2. Where do I check the ONE Store OAuth authentication info (Client ID, Secret, License Key)?
A. You can check it in the ONE Store developer console. The path is ONE Store console > APPS > Product Status > In-App Info > Managed Products > In-App API Management (info window).
In that info window, check and copy all three—Client ID, Client Secret, and License Key—and enter them in the Per-Market IAP Info Management menu of STOVE Partners.
If the OAuth info is missing, payment verification fails, so enter all 4 items (including the billing-integration ID) accurately.
Q3. Why does ONE Store's flush() always return true?
A. Because ONE Store provides an asynchronous unpaid-payment lookup function, the return value of flush() can't be determined synchronously.
So, for consistent handling, it always returns true.
Unpaid-item handling on call is entirely the same as existing IAP, so as long as you don't directly reference the return value of flush(), there's no impact on the game-side implementation.
If you have code that uses the return value as a branching condition, it needs review when applying ONE Store.
Q4. How do I handle the RESULT_NEED_UPDATE and RESULT_NEED_LOGIN errors?
A. These two error codes are representative ONE Store cases, and detailed error messages with multilingual translations are provided automatically.
RESULT_NEED_UPDATE means the ONE Store app needs to be installed or updated, and RESULT_NEED_LOGIN means the user isn't logged in to ONE Store.
Extract the multilingual message via result.userInfo!!.getValue(IAP.DebugMessageKey) and show it to the user as-is. You can check the detailed error-code list in the ONE Store SDK guide.
Q5. In what situation does the SuccessButUserMissMatch error occur?
A. It occurs when the payment and verification process succeeded normally, but the account at payment time differs from the currently logged-in account.
It mainly happens when the user changes accounts or moves worlds while payment verification isn't complete.
The handling depends on the deliveryMethod policy. In ALL(default) mode, the item is granted to the account at payment time; when using ONLY or NOTI, the game side needs separate account-matching handling.
Q6. How do the Purchased and Waiting states differ?
A. In both states, payment proceeded but the in-game item grant isn't complete.
Purchased is the state awaiting verification by the STOVE billing server after payment, where verification is temporarily delayed due to a market communication error, etc.
Waiting is the state where billing-server verification is complete but the game-item grant is delayed due to a communication error with the game server or cache system.
In both states, the billing server automatically proceeds with follow-up handling and notifies the user by push message. So on the game side, it's recommended to show a message guiding users with push reception disabled to enable push.
Q7. How do I map the market product ID?
A. Map it in the Partners Market Product Management menu.
Each product row has per-market product-ID columns (Google, Apple, ONE Store, MyCard, Facebook Cloud, Amazon, Huawei, Galaxy), and you enter the product ID registered in that market.
For example, for ONE Store integration, map it in the 'Market Product ID (ONE Store)' column. If the mapping is missing, payment proceeds but the item grant can fail.
Q8. I'm using an existing version below SDK 2.6.1—do I need to change the repository address?
A. The repository location changed as of SDK 2.6.1. The new address is https://externalnexus.iam0.com/repository/mvp.
To keep using an existing version (below 2.6.1) as-is, you can keep the old address (http://e-nexus.iam0.net/content/repositories/mvp/).
However, to receive new features and security patches, we recommend doing the SDK update and repository-address change together. When using Android Gradle Plugin 7.0 or higher, you need to add the allowInsecureProtocol = true option.
Q9. Do other SDK features (authentication, log, push, etc.) besides payment also need to be changed separately?
A. No. Third-party market integration swaps only the payment (IAP) module; other features (authentication, character, log, push, coupon, deep link, etc.) can use the standard Android modules as-is. If your game already applies the STOVE SDK for Google Play, you can apply it quickly by just swapping the IAP module for the ONE Store one and adding Partners settings.
A common pattern is to use per-market Build Variants to manage multiple market builds from a single codebase.
Q10. I registered the market game ID incorrectly. Can I fix it?
A. Unfortunately, the market game ID can't be edited after registration. So enter it carefully at registration.
If registered incorrectly, separate action through the STOVE business/technical contact may be needed, so inquire in advance.
Also, mis-entering other items such as the billing-integration ID can cause IAP failures, so always check the impact when editing.



Need to contact us directly? stove.developers@smilegate.com