This page describes how to set In-App Purchase (IAP).
Gamebase provides an integrated purchase API to easily link IAP of many stores in a game.
[Caution]
ONE Store v17, v19, and v21 are supported. For ONE Store, you can use only one of the following versions: v17, v19, v21, or external, but not at the same time.
String STORE_CODE = "GG"; // Google
GamebaseConfiguration configuration = GamebaseConfiguration.newBuilder(APP_ID, APP_VERSION, STORE_CODE)
.build();
Gamebase.initialize(activity, configuration, callback);
Purchase of an item can be divided into Purchase Flow, Consume Flow, and Reprocess Flow. It is recommended to implement the Purchase Flow in the following order:
If there's a value on the list of unconsumed purchases, proceed with the Consume Flow in the following order:
[Caution]
To prevent duplicate provision of an item, always check whether the item is being provided in duplicate in the game server.
Request purchase by calling the following API using the gamebaseProductId of the item to purchase.
The gamebaseProductId is generally the same as the ID of item registered at the store, but it can be changed in the Gamebase console.
When a game user cancels purchase, the GamebaseError.PURCHASE_USER_CANCELED error is returned. Please process cancellation.
API
+ (void)Gamebase.Purchase.requestPurchase(@NonNull final Activity activity,
@NonNull final String gamebaseProductId,
@NonNull final GamebaseDataCallback<PurchasableReceipt> callback);
Example
Gamebase.Purchase.requestPurchase(activity, gamebaseProductId, new GamebaseDataCallback<PurchasableReceipt>() {
@Override
public void onCallback(PurchasableReceipt receipt, GamebaseException exception) {
if (Gamebase.isSuccess(exception)) {
// Succeeded.
} else if(exception.getCode() == GamebaseError.PURCHASE_USER_CANCELED) {
// User canceled.
} else {
// To Purchase Item Failed cause of the error
}
}
});
VO
class PurchasableReceipt {
// The product ID of a purchased item.
@Nullable
String gamebaseProductId;
// It is the value passed to payload when calling Gamebase.Purchase.requestPurchase API.
// Not recommended to use the value due to the possible loss of information depending on the store server status.
@Nullable
String payload;
// Price of purchased product.
float price;
// Currency code.
@NonNull
String currency;
// Payment identifier.
// This is an important piece of information used to call 'Consume' Server API with purchaseToken.
//
// Consume API : https://docs.toast.com/en/Game/Gamebase/en/api-guide/#purchase-iap
// Caution: Call Consume API from game server!
@NonNull
String paymentSeq;
// Payment identifier.
// This is an important piece of information used to call 'Consume' server API with paymentSeq.
// In Consume API, the parameter must be named 'accessToken' to be passed.
//
// Consume API : https://docs.toast.com/en/Game/Gamebase/en/api-guide/#purchase-iap
// In Consume API, the parameter must be named 'accessToken' to be passed.
@Nullable
String purchaseToken;
// This product ID is registered to the console of stores such as Google and Apple.
@NonNull
String marketItemId;
// This is a product type that can have the following values:
// * UNKNOWN : An unknown type. Either update Gamebase SDK or contact Gamebase Customer Center.
// * CONSUMABLE : A consumable product.
// * AUTO_RENEWABLE : A subscription product.
// * CONSUMABLE_AUTO_RENEWABLE : This 'consumable subscription product' is used when providing a subscribed user a subscription product that can be consumed periodically.
@NonNull
String productType;
// This is a user ID with which a product is purchased.
// If a user logs in with a user ID that is not used to purchase a product, the user cannot obtain the product they purchased.
@NonNull
String userId;
// The payment identifier of a store.
@Nullable
String paymentId;
// The time when the product was purchased.(epoch time)
long purchaseTime;
// The time when the subscription expires.(epoch time)
long expiryTime;
// This value is used when making a purchase on Google, which can have the following values.
// However, if the verification logic is temporarily disabled by Gamebase payment server due to error on Google server,
// it returns only null, so please remember that it does not guarantee a valid return value at all times.
// * null : Normal payment
// * Test : Test payment
// * Promo : Promotion payment
@Nullable
String purchaseType;
// paymentId is changed whenever product subscription is renewed.
// This field shows the paymentId that was used when a subscription product was first purchased.
// This value does not guarantee to be always valid, as it can have no value
// depending on the store from which the user made a purchase and the status of the payment server.
@Nullable
String originalPaymentId;
// An identifier for Legacy API that purchases products with itemSeq.
long itemSeq;
// Store Code that purcahsed products.
@NonNull
public String storeCode;
}
Response Example
{
"gamebaseProductId": "my_product_001",
"price": 1000.0,
"currency": "KRW",
"paymentSeq": "2021032510000001",
"purchaseToken": "5U_NVCLKSDFKLJJ...",
"marketItemId": "my_product_001",
"productType": "CONSUMABLE",
"userId": "AS@123456ABCDEFGHIJ",
"paymentId": "GPA.1111-2222-3333-44444",
"purchaseTime": 1616649225531,
"expiryTime": 0,
"itemSeq": 1000001
}
{
"gamebaseProductId": "my_subcription_product_001",
"price": 1000.0,
"currency": "KRW",
"paymentSeq": "2021032510000001",
"purchaseToken": "5U_NVCLKKLJLSDG...",
"marketItemId": "my_subcription_product_001",
"productType": "CONSUMABLE_AUTO_RENEWABLE",
"userId": "AS@123456ABCDEFGHIJ",
"paymentId": "GPA.1111-2222-3333-56789",
"purchaseTime": 1617069916128,
"expiryTime": 1617070323784,
"purchaseType": "Test",
"originalPaymentId": "GPA.1111-2222-3333-56789",
"itemSeq": 1000002
}
To retrieve the list of items, call the following API. Information of each item is included in the array of callback return.
API
+ (void)Gamebase.Purchase.requestItemListPurchasable(Activity activity, GamebaseDataCallback<List<PurchasableItem>> callback);
Example
Gamebase.Purchase.requestItemListPurchasable(activity, new GamebaseDataCallback<List<PurchasableItem>>() {
@Override
public void onCallback(List<PurchasableItem> data, GamebaseException exception) {
if (Gamebase.isSuccess(exception)) {
// Succeeded.
} else {
// Failed.
Log.e(TAG, "Request item list failed- "
+ "errorCode: " + exception.getCode()
+ "errorMessage: " + exception.getMessage());
}
}
});
VO
class PurchasableItem {
// The product ID that is registered with the Gamebase console.
// This is used when a product is purchased using Gamebase.Purchase.requestPurchase API.
@Nullable
String gamebaseProductId;
// Product price.
float price;
// Currency code.
@Nullable
String currency;
// The name of a product registered with the Gamebase console.
@Nullable
String itemName;
// This product ID is registered to the console of stores such as Google and Apple.
@NonNull
String marketItemId;
// This is a product type that can have the following values:
// * UNKNOWN : An unknown type. Either update Gamebase SDK or contact Gamebase Customer Center.
// * CONSUMABLE : A consumable product.
// * AUTORENEWABLE : A subscription product.
// * CONSUMABLE_AUTO_RENEWABLE : This 'consumable subscription product' is used when providing a subscribed user a subscription product that can be consumed periodically.
@NonNull
String productType;
// Localized price information with currency symbol.
@Nullable
String localizedPrice;
// The name of a localized product registered with the store console.
@Nullable
String localizedTitle;
// The description of a localized product registered with the store console.
@Nullable
String localizedDescription;
// Shows whether the product is 'used or not' in the Gamebase console.
boolean isActive;
// An identifier for Legacy API that purchases products with itemSeq.
long itemSeq;
}
Response Example
{
"gamebaseProductId": "my_product_001",
"price": 1000.0,
"currency": "KRW",
"itemName": "Consumable product for test",
"marketItemId": "my_product_001",
"productType": "CONSUMABLE",
"localizedPrice": "₩1,000",
"localizedTitle": "TEST PRODUCT 001",
"localizedDescription": "Product for test 001",
"isActive": true,
"itemSeq": 1000001
}
PurchasableConfiguration
API | Mandatory(M) / Optional(O) | Description |
---|---|---|
newBuilder() | M | Create Builder for Configuration object creation. |
build() | M | Convert the Builder that has been set up into a Configuration object. |
setAllStores(boolean allStores) | O | Return unconsumed lists purchased from a different store with the same UserID. Default value is false. |
API
+ (void)Gamebase.Purchase.requestItemListOfNotConsumed(@NonNull final Activity activity,
@NonNull final PurchasableConfiguration configuration,
@NonNull final GamebaseDataCallback<List<PurchasableReceipt>> callback);
Example
final PurchasableConfiguration configuration = PurchasableConfiguration.newBuilder().build();
Gamebase.Purchase.requestItemListOfNotConsumed(activity, configuration, new GamebaseDataCallback<List<PurchasableReceipt>>() {
@Override
public void onCallback(List<PurchasableReceipt> data, GamebaseException exception) {
if (Gamebase.isSuccess(exception)) {
// Succeeded.
} else {
// Failed.
Log.e(TAG, "Request item list failed- "
+ "errorCode: " + exception.getCode()
+ "errorMessage: " + exception.getMessage());
}
}
});
List activated subscriptions for a current user ID. Paid subscriptions (auto-renewable subscription, or auto-renewed consumable subscription) can be queried before they're expired. For subscription life cycle, refer to the following document. NHN Cloud > SDK User Guide > IAP > Android > Google Play Store Subscription (Regular payment) feature > Subscription Lifecycle Handing
[Caution]
Current subscriptions for Android are supported by Google Play Store only.
PurchasableConfiguration
API | Mandatory(M) / Optional(O) | Description |
---|---|---|
newBuilder() | M | Create Builder for Configuration object creation. |
build() | M | Convert the Builder that has been set up into a Configuration object. |
setAllStores(boolean allStores) | O | Return the subscriptions purchased from a different store with the same UserID. Default value is false. |
API
+ (void)Gamebase.Purchase.requestActivatedPurchases(@NonNull final Activity activity,
@NonNull final PurchasableConfiguration configuration,
@NonNull final GamebaseDataCallback<List<PurchasableReceipt>> callback);
Example
final PurchasableConfiguration configuration = PurchasableConfiguration.newBuilder()
.setAllStores(true)
.build();
Gamebase.Purchase.requestActivatedPurchases(activity, configuration, new GamebaseDataCallback<List<PurchasableReceipt>>() {
@Override
public void onCallback(List<PurchasableReceipt> data, GamebaseException exception) {
if (Gamebase.isSuccess(exception)) {
// Succeeded.
} else {
// Failed.
Log.e(TAG, "Request subscription list failed- "
+ "errorCode: " + exception.getCode()
+ "errorMessage: " + exception.getMessage());
}
}
});
You can view the status of purchased subscription products based on your current user ID. Subscription products that have been paid for (auto-renewable subscriptions, auto-renewable consumable subscription products) can be viewed until they expire. You can retrieve the status of expired subscription products with the PurchasableConfiguration.setIncludeExpiredSubscriptions(true) API. For subscription status codes, see the following document. NHN Cloud > SDK User Guide > IAP > Android > NHN Cloud IAP Class Reference > IapSubscriptionStatus.StatusCode.
[Caution]
- The subscription status code is only returned correctly if you follow the guide below to set up the subscription event.
- Go to Game > Gamebase > Store Console Guide > Google Console Guide and set up event propagation of real-time subscription information within Google's system
- The status code for a subscription product purchased without setting up events always returns 0 (PURCHASED).
- Subscription products currently only supports Google Play Store.
PurchasableConfiguration
API | Mandatory(M) / Optional(O) | Description |
---|---|---|
newBuilder() | M | Creates a Builder to create Configuration objects. |
build() | M | Converts the configured builder into a Configuration object. |
setIncludeExpiredSubscriptions(boolean include) | O | Includes expired subscription products Default value is false. |
API
+ (void)Gamebase.Purchase.requestSubscriptionsStatus(@NonNull final Activity activity,
@NonNull final PurchasableConfiguration configuration,
@NonNull final GamebaseDataCallback<List<PurchasableSubscriptionStatus>> callback);
Example
final PurchasableConfiguration configuration = PurchasableConfiguration.newBuilder()
.setIncludeExpiredSubscriptions(true)
.build();
Gamebase.Purchase.requestSubscriptionsStatus(activity, configuration, new GamebaseDataCallback<List<PurchasableSubscriptionStatus>>() {
@Override
public void onCallback(List<PurchasableSubscriptionStatus> data, GamebaseException exception) {
if (Gamebase.isSuccess(exception)) {
// Succeeded.
} else {
// Failed.
Log.e(TAG, "Request status of subscription list failed- "
+ "errorCode: " + exception.getCode()
+ "errorMessage: " + exception.getMessage()
+ "errorDetail: " + exception.toString());
}
}
});
VO
class PurchasableSubscriptionStatus {
// Product ID of purchased item.
@Nullable
String gamebaseProductId;
// Subscription status code.
//
// IapSubscriptionStatus.StatusCode : https://docs.nhncloud.com/en/TOAST/en/toast-sdk/iap-android/#iapsubscriptionstatusstatuscode
public int statusCode;
// Description for subscription status code.
@NonNull
public String statusDescription;
// Price of purchased product.
float price;
// Currency code.
@NonNull
String currency;
// Payment identifier
// This is an important piece of information used to call 'Consume' Server API with purchaseToken.
//
// Consume API: https://docs.toast.com/en/Game/Gamebase/en/api-guide/#purchase-iap
// Caution: Call the Consume API from game server!
@NonNull
String paymentSeq;
// Payment identifier.
// This is an important piece of information used to call 'Consume' server API with paymentSeq.
// In Consume API, the parameter must be named 'accessToken' to be passed.
//
// Consume API: https://docs.toast.com/en/Game/Gamebase/en/api-guide/#purchase-iap
// Caution: Call Consume API from game server!
@NonNull
String purchaseToken;
// This is a product ID registered in store consoles such as Google, Apple.
@NonNull
String marketItemId;
// Product types are as follows.
// * UNKNOWN: An unknown type. Either update Gamebase SDK or contact Gamebase Customer Center.
// * CONSUMABLE: A consumable product.
// * AUTO_RENEWABLE: A subscription product.
// * CONSUMABLE_AUTO_RENEWABLE: This 'consumable subscription product' is used when providing a subscribed user a subscription product that can be consumed periodically.
@NonNull
String productType;
// This is a user ID that purchased a product.
// If a user logs in with a user ID that is not used to purchase a product, the user cannot obtain the product they purchased.
@NonNull
String userId;
// Store Code that purchased the product..
@NonNull
public String storeCode;
// Payment identifier for store.
@Nullable
String paymentId;
// Time when the product was purchased.(epoch time)
long purchaseTime;
// Time when subsciription ended.(epoch time)
long expiryTime;
// This value is used when making a purchase on Google, which can have the following values.
// However, if the verification logic is temporarily disabled by Gamebase payment server due to error on Google server,
// it returns only null, so please remember that it does not guarantee a valid return value at all times.
// * null: Normal payment
// * Test: Test payment
// * Promotion: Promotion payment
@Nullable
String purchaseType;
// PaymentId is changed whenever the subscription product is renewed.
// This field shows the paymentId used when the subscription product was first paid for.
// This value does not guarantee to be always valid, as it can have no value
// depending on the store from which the user made a purchase and the status of the payment server.
@Nullable
String originalPaymentId;
// Identifier for Legacy API for purchasing with itemSeq.
long itemSeq;
// Value sent to payload when calling the Gamebase.Purchase.requestPurchase API.
// Depending on the status of your store's server, information may be lost, so using it is not recommended.
@Nullable
String payload;
}
Response Example
{
"gamebaseProductId": "my_subcription_product_002",
"statusCode": 13,
"statusDescription": "EXPIRED",
"userId": "AS@123456ABCDEFGHIJ",
"storeCode": "GG",
"currency": "KRW",
"expiryTime": 1675012345678,
"itemSeq": 1000003,
"marketItemId": "my_subcription_product_002",
"originalPaymentId": "GPA.1111-2222-3333-56789",
"paymentId": "GPA.1111-2222-3333-56789",
"paymentSeq": "2021032510000002",
"price": 1000.0,
"productType": "CONSUMABLE_AUTO_RENEWABLE",
"purchaseTime": 1675001234567,
"purchaseToken": "kfetTfGk4...",
"purchaseType": "Test"
}
When a promotional purchase is completed, get an event from GamebaseEventHandler to be processed.
See the guide on how to process a promotional purchase event via GamebaseEventHandler.
Game > Gamebase > User Guide for Android SDK > ETC > Gamebase Event Handler
Error | Error Code | Description |
---|---|---|
PURCHASE_NOT_INITIALIZED | 4001 | The purchase module has not been initialized. Check if the gamebase-adapter-purchase-IAP module has been added to the project. |
PURCHASE_USER_CANCELED | 4002 | Purchase has been cancelled. |
PURCHASE_NOT_FINISHED_PREVIOUS_PURCHASING | 4003 | API has been called when a purchase logic is not completed. |
PURCHASE_INACTIVE_PRODUCT_ID | 4005 | Product is not activated. |
PURCHASE_NOT_EXIST_PRODUCT_ID | 4006 | Requested for purchase with invalid GamebaseProductID. |
PURCHASE_LIMIT_EXCEEDED | 4007 | You have exceeded your monthly purchase limit. |
PURCHASE_NOT_SUPPORTED_MARKET | 4010 | The store is not supported. You can choose either GG (Google), ONESTORE, GALAXY, AMAZON, HUAWEI, or MYCARD. |
PURCHASE_EXTERNAL_LIBRARY_ERROR | 4201 | Error in NHN Cloud IAP library. Check the error details. |
PURCHASE_UNKNOWN_ERROR | 4999 | Unknown error in purchase. Please upload the entire logs to Customer Center and we'll reply at the earliest possible moment. |
PURCHASE_EXTERNAL_LIBRARY_ERROR
Gamebase.Purchase.requestPurchase(activity, gamebaseProductId, new GamebaseDataCallback<PurchasableReceipt>() {
@Override
public void onCallback(PurchasableReceipt data, GamebaseException exception) {
if (Gamebase.isSuccess(exception)) {
Log.d(TAG, "Purchase successful");
...
} else {
Log.e(TAG, "Purchase failed");
// Gamebase Error Info
int errorCode = exception.getCode();
String errorMessage = exception.getMessage();
if (errorCode == GamebaseError.PURCHASE_EXTERNAL_LIBRARY_ERROR) {
// IAP Error Info
int moduleErrorCode = exception.getDetailCode();
String moduleErrorMessage = exception.getDetailMessage();
...
}
}
}
});