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]
Only ONE Store v17 and v19 are supported. ONE Store v21 is under review and currently not supported.
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.
Additional information entered in the payload field is maintained at the PurchasableReceipt.payload field after a successful payment, so it can be used for many purposes.
[Caution]
The AMAZON store does not support the payload field.
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);
+ (void)Gamebase.Purchase.requestPurchase(@NonNull final Activity activity,
@NonNull final String gamebaseProductId,
@NonNull final String payload,
@NonNull final GamebaseDataCallback<PurchasableReceipt> callback);
Example
String userPayload = "{\"description\":\"This is example\",\"channelId\":\"delta\",\"characterId\":\"abc\"}";
Gamebase.Purchase.requestPurchase(activity, gamebaseProductId, userPayload, 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.
//
// It is the value passed to payload when calling Gamebase.Purchase.requestPurchase API.
// This field can be used to hold a variety of additional information.
// For example, this field can be used to separately handle purchase and provision
// of the products purchased using the same user ID and sort them by game channel or character.
@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;
}
Response Example
{
"gamebaseProductId": "my_product_001",
"payload": "UserPayload:!@#...",
"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",
"payload": "MyData:{\"1234\":\"5678\"}",
"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
}
API
+ (void)Gamebase.Purchase.requestItemListOfNotConsumed(Activity activity, GamebaseDataCallback<List<PurchasableReceipt>> callback);
Example
Gamebase.Purchase.requestItemListOfNotConsumed(activity, 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. Under a same user ID, both subscriptions for Android and iOS can be listed.
[Caution]
Current subscriptions for Android are supported by Google Play Store only.
API
+ (void)Gamebase.Purchase.requestActivatedPurchases(Activity activity, GamebaseDataCallback<List<PurchasableReceipt>> callback);
Example
Gamebase.Purchase.requestActivatedPurchases(activity, 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());
}
}
});
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, or HUAWEI. |
PURCHASE_EXTERNAL_LIBRARY_ERROR | 4201 | Error in IAP library. Check DetailCode. |
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();
...
}
}
}
});