ここではUnrealでアプリ内決済機能を使用するために必要な設定方法を説明します。 Gamebaseは、1つの統合された決済APIを提供し、ゲームから簡単に多くのストアのアプリ内決済を連携できるようにサポートします。
AndroidまたはiOSでアプリ内決済機能を設定する方法は、次の文書を参照してください。
[注意]
外部プラグインで決済関連処理がある場合、 Gamebase決済機能が正常に動作しない可能性があります。
Unrealでデフォルトで有効になっているOnline SubSystemプラグインを無効化またはストア機能を利用できないように変更する必要があります。
Online SubSystem GooglePlayプラグイン使用時 /Config/Android/AndroidEngine.iniファイルを編集します。
[OnlineSubsystemGooglePlay.Store]
bSupportsInAppPurchasing=False
bUseGooglePlayBillingApiV2=False
Online SubSystem iOSプラグイン使用時 /Config/IOS/IOSEngine.iniファイルを編集します。
[OnlineSubsystemIOS.Store]
bSupportsInAppPurchasing=False
アイテムの購入は大きく分けて決済フロー、消費フロー、再処理フローの3つがあります。 決済フローは、次のような順序で実装してください。
未消費決済履歴リストに値がある場合、次のような順序でConsume Flowを進行してください。
[注意]
アイテムの重複支給が発生しないように、ゲームサーバーで必ず重複支給の有無をチェックしてください。
購入したいアイテムのitemSeqを利用して次のAPIを呼び出し、購入をリクエストします。 ゲームユーザーが購入をキャンセルする場合、PURCHASE_USER_CANCELEDエラーが返されます。
API
Supported Platforms ■ UNREAL_ANDROID ■ UNREAL_IOS ■ UNREAL_WINDOWS
void RequestPurchase(const FString& GamebaseProductId, const FGamebasePurchasableReceiptDelegate& Callback);
void RequestPurchase(const FString& GamebaseProductId, const FString& payload, const FGamebasePurchasableReceiptDelegate& Callback);
Example
void USample::RequestPurchase(const FString& GamebaseProductId)
{
UGamebaseSubsystem* Subsystem = UGameInstance::GetSubsystem<UGamebaseSubsystem>(GetGameInstance());
Subsystem->GetPurchase()->RequestPurchase(GamebaseProductId, FGamebasePurchasableReceiptDelegate::CreateLambda(
[](const FGamebasePurchasableReceipt* PurchasableReceipt, const FGamebaseError* Error)
{
if (Gamebase::IsSuccess(Error))
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestPurchase succeeded. (GamebaseProductId= %s, price= %f, currency= %s, paymentSeq= %s, purchaseToken= %s)"),
*PurchasableReceipt->GamebaseProductId, PurchasableReceipt->Price, *PurchasableReceipt->Currency,
*PurchasableReceipt->PaymentSeq, *PurchasableReceipt->PurchaseToken);
}
else
{
if (Error->Code == GamebaseErrorCode::PURCHASE_USER_CANCELED)
{
UE_LOG(GamebaseTestResults, Display, TEXT("User canceled purchase."));
}
else
{
// Check the Error code and handle the Error appropriately.
UE_LOG(GamebaseTestResults, Display, TEXT("RequestPurchase failed. (Error: %d)"), Error->Code);
}
}
}));
}
void USample::RequestPurchaseWithPayload(const FString& GamebaseProductId)
{
FString UserPayload = TEXT("{\"description\":\"This is example\",\"channelId\":\"delta\",\"characterId\":\"abc\"}");
UGamebaseSubsystem* Subsystem = UGameInstance::GetSubsystem<UGamebaseSubsystem>(GetGameInstance());
Subsystem->GetPurchase()->RequestPurchase(GamebaseProductId, UserPayload, FGamebasePurchasableReceiptDelegate::CreateLambda(
[](const FGamebasePurchasableReceipt* PurchasableReceipt, const FGamebaseError* Error)
{
if (Gamebase::IsSuccess(Error))
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestPurchase succeeded. (GamebaseProductId= %s, price= %f, currency= %s, paymentSeq= %s, purchaseToken= %s)"),
*PurchasableReceipt->GamebaseProductId, PurchasableReceipt->price, *PurchasableReceipt->Currency,
*PurchasableReceipt->PaymentSeq, *PurchasableReceipt->PurchaseToken);
FString payload = PurchasableReceipt->payload;
}
else
{
if (Error->Code == GamebaseErrorCode::PURCHASE_USER_CANCELED)
{
UE_LOG(GamebaseTestResults, Display, TEXT("User canceled purchase."));
}
else
{
// Check the Error code and handle the Error appropriately.
UE_LOG(GamebaseTestResults, Display, TEXT("RequestPurchase failed. (Error: %d)"), Error->Code);
}
}
}));
}
VO
struct FGamebasePurchasableReceipt
{
// 購入したアイテムの商品IDです。
FString GamebaseProductId;
// itemSeqで商品を購入するLegacy API用の識別子です。
int64 ItemSeq;
// 購入した商品の価格です。
float Price;
// 通貨コードです。
FString Currency;
// 決済識別子です。
// purchaseTokenと一緒に「Consume」サーバーAPIを呼び出すのに使用する重要な情報です。
// Consume API : https://docs.toast.com/en/Game/Gamebase/en/api-guide/#purchase-iap
// 注意:Consume APIはゲームサーバーで呼び出してください!
FString PaymentSeq;
// 決済識別子です。
// paymentSeqと一緒に「Consume」サーバーAPIを呼び出すのに使用する重要な情報です。
// Consume APIでは「accessToken」という名前のパラメータで伝達する必要があります。
// Consume API : https://docs.toast.com/en/Game/Gamebase/en/api-guide/#purchase-iap
// 注意:Consume APIはゲームサーバーで呼び出してください!
FString PurchaseToken;
// Google、Appleなどのストアコンソールに登録された商品IDです。
FString MarketItemId;
// 次のような商品タイプがあります。
// * UNKNOWN:認識できないタイプ。 Gamebase SDKをアップデートするか、Gamebaseサポートへお問い合わせください。
// * CONSUMABLE:消費性商品。
// * AUTO_RENEWABLE:サブスクリプション型の商品。
// * CONSUMABLE_AUTO_RENEWABLE:サブスクリプション型の商品を購入したユーザーに、定期的に消費が可能な商品を支給したい場合に使用される「消費が可能なサブスクリプション商品」。
FString ProductType;
// 商品を購入したUser ID
// 商品を購入していないUser IDでログインした場合、購入したアイテムを獲得できません。
FString UserId;
// ストアの決済識別子です。
FString PaymentId;
// サブスクリプション商品は更新するごとにpaymentIdが変更されます。
// このフィールドは、初めてサブスクリプション商品を決済した時のpaymentIdを伝えます。
// ストアや、決済サーバーの状態によって値が存在しない場合があるため
// 常に有効な値を保障するわけではありません。
FString OriginalPaymentId;
// 商品を購入した時刻です。(epoch time)
int64 PurchaseTime;
// 購読が終了する時刻です。(epoch time)
int64 ExpiryTime;
// 決済したストアコードです。
// GamebaseStoreCodeクラスでストアコードリストを確認できます。
FString StoreCode;
// RequestPurchase API呼び出し時、payloadに渡された値です。
// ストアサーバー状態によって情報が流出する場合があるため、使用を推奨しません。
FString Payload;
// プロモーション決済かどうか
// - (Android) Gamebase決済サーバーで一時的に検証ロジックを切る場合は、常にfalseと出力されるため、有効な値が保障されません。
bool bIsPromotion;
// テスト決済かどうか
// - (Android) Gamebase決済サーバーで一時的に検証ロジックを切る場合は、常にfalseと出力されるため、有効な値が保障されません。
bool bIsTestPurchase;
};
アイテムリストを照会するには、次のAPIを呼び出します。 コールバックで返されるリスト内には、各アイテムの情報が含まれています。
API
Supported Platforms ■ UNREAL_ANDROID ■ UNREAL_IOS ■ UNREAL_WINDOWS
void RequestItemListPurchasable(const FGamebasePurchasableItemListDelegate& Callback);
Example
void USample::RequestItemListPurchasable()
{
UGamebaseSubsystem* Subsystem = UGameInstance::GetSubsystem<UGamebaseSubsystem>(GetGameInstance());
Subsystem->GetPurchase()->RequestItemListPurchasable(FGamebasePurchasableItemListDelegate::CreateLambda(
[](const TArray<FGamebasePurchasableItem>* PurchasableItemList, const FGamebaseError* Error)
{
if (Gamebase::IsSuccess(Error))
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestItemListPurchasable succeeded."));
for (const FGamebasePurchasableItem& PurchasableItem : *PurchasableItemList)
{
UE_LOG(GamebaseTestResults, Display, TEXT(" - GamebaseProductId= %s, price= %f, itemName= %s, itemName= %s, marketItemId= %s"),
*PurchasableItem.GamebaseProductId, PurchasableItem.Price, *PurchasableItem.Currency, *PurchasableItem.ItemName, *PurchasableItem.MarketItemId);
}
}
else
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestItemListPurchasable failed. (Error: %d)"), Error->Code);
}
}));
}
VO
struct FGamebasePurchasableItem
{
// Gamebaseコンソールに登録された商品IDです。
// Gamebase.Purchase.requestPurchase APIで商品を購入する時に使用されます。
FString GamebaseProductId;
// itemSeqで商品を購入するLegacy API用の識別子です。
int64 ItemSeq;
// 商品の価格です。
float Price;
// 通貨コードです。
FString Currency;
// Gamebaseコンソールに登録された商品名です。
FString ItemName;
// Google、Appleなどのストアコンソールに登録された商品IDです。
FString MarketItemId;
// 次のような商品タイプがあります。
// * UNKNOWN:認識できないタイプ。 Gamebase SDKをアップデートするか、Gamebaseサポートへお問い合わせください。
// * CONSUMABLE:消費性商品。
// * AUTORENEWABLE:サブスクリプション型の商品。
// * CONSUMABLE_AUTO_RENEWABLE:サブスクリプション型の商品を購入したユーザーに、定期的に消費が可能な商品を支給したい場合に使用される「消費が可能なサブスクリプション商品」。
FString ProductType;
// 通貨記号が含まれたローカライズされた価格情報です。
FString LocalizedPrice;
// ストアコンソールに登録されたローカライズされた商品名です。
FString LocalizedTitle;
// ストアコンソールに登録されたローカライズされた商品説明です。
FString LocalizedDescription;
// Gamebaseコンソールで該当商品の「使用状態」を表します。
bool bIsActive;
};
アイテムを購入したが、正常にアイテムが消費(配送、支給)されていない未消費決済内訳をリクエストします。 未決済内訳がある場合は、ゲームサーバー(アイテムサーバー)にリクエストして、アイテムを配送(支給)するように処理する必要があります。
FGamebasePurchasableConfiguration
API | Mandatory(M) / Optional(O) | Description |
---|---|---|
bAllStores | O | 同じUserIDにて他のストアで購入した未消費履歴も返します。 デフォルト値はfalseです。 |
API
Supported Platforms ■ UNREAL_ANDROID ■ UNREAL_IOS ■ UNREAL_WINDOWS
void RequestItemListOfNotConsumed(const FGamebasePurchasableConfiguration& Configuration, const FGamebasePurchasableReceiptListDelegate& Callback);
Example
void USample::RequestItemListOfNotConsumed(bool bAllStores)
{
FGamebasePurchasableConfiguration Configuration;
Configuration.bAllStores = bAllStores;
UGamebaseSubsystem* Subsystem = UGameInstance::GetSubsystem<UGamebaseSubsystem>(GetGameInstance());
Subsystem->GetPurchase()->RequestItemListOfNotConsumed(Configuration, FGamebasePurchasableReceiptListDelegate::CreateLambda(
[](const TArray<FGamebasePurchasableItem>* PurchasableItemList, const FGamebaseError* Error)
{
if (Gamebase::IsSuccess(Error))
{
// Should Deal With This non-consumed Items.
// Send this item list to the game(item) server for consuming item.
UE_LOG(GamebaseTestResults, Display, TEXT("RequestItemListOfNotConsumed succeeded."));
for (const FGamebasePurchasableReceipt& PurchasableReceipt : *purchasableReceiptList)
{
UE_LOG(GamebaseTestResults, Display, TEXT(" - GamebaseProductId= %s, price= %f, currency= %s, paymentSeq= %s, purchaseToken= %s"),
*PurchasableReceipt.GamebaseProductId, PurchasableReceipt.Price, *PurchasableReceipt.Currency, *PurchasableReceipt.PaymentSeq, *PurchasableReceipt.PurchaseToken);
}
}
else
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestItemListOfNotConsumed failed. (Error: %d)"), Error->Code);
}
}));
}
現在のユーザーID基準で有効になっている購読リストを照会します。 決済が完了した購読商品(自動更新型購読、自動更新型消費性購読商品)は、有効期限が切れる前まで照会できます。 ユーザーIDが同じ場合、AndroidとiOSで購入した購読商品が照会されます。
[注意]
Androidでは現在、Google Playストアでのみサブスクリプション商品をサポートしています。
FGamebasePurchasableConfiguration
API | Mandatory(M) / Optional(O) | Description |
---|---|---|
bAllStores | O | 同じUserIDにて他のストアで購入した未消費履歴も返します。 デフォルト値はfalseです。 |
API
Supported Platforms ■ UNREAL_ANDROID ■ UNREAL_IOS
void RequestActivatedPurchases(const FGamebasePurchasableConfiguration& Configuration, const FGamebasePurchasableReceiptListDelegate& Callback);
Example
void USample::RequestActivatedPurchases(bool bAllStores)
{
FGamebasePurchasableConfiguration Configuration;
Configuration.bAllStores = bAllStores;
UGamebaseSubsystem* Subsystem = UGameInstance::GetSubsystem<UGamebaseSubsystem>(GetGameInstance());
Subsystem->GetPurchase()->RequestActivatedPurchases(Configuration, FGamebasePurchasableReceiptListDelegate::CreateLambda(
[](const TArray<FGamebasePurchasableReceipt>* purchasableReceiptList, const FGamebaseError* Error)
{
if (Gamebase::IsSuccess(Error))
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestActivatedPurchases succeeded."));
for (const FGamebasePurchasableReceipt& PurchasableReceipt : *purchasableReceiptList)
{
UE_LOG(GamebaseTestResults, Display, TEXT(" - GamebaseProductId= %s, price= %f, currency= %s, paymentSeq= %s, purchaseToken= %s"),
*PurchasableReceipt.GamebaseProductId, PurchasableReceipt.Price, *PurchasableReceipt.Currency, *PurchasableReceipt.PaymentSeq, *PurchasableReceipt.PurchaseToken);
}
}
else
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestActivatedPurchases failed. (Error: %d)"), Error->Code);
}
}));
}
現在ユーザーID基準でサブスクリプション商品の状態を照会します。 コールバックで返されるリスト内にはサブスクリプション商品の情報が含まれています。
[注意]
- 以下のガイドに従って購読イベントを設定すると購読ステータスコードが正常に返されます。
- Game > Gamebase > ストアコンソールガイド > Googleコンソールガイド > Googleシステム内リアルタイム購読情報イベント配信設定
- イベント設定を行っていない状態で購入したサブスクリプション商品のステータスコードは常に0(PURCHASED)が返されます。
- 現在サブスクリプション商品はGoogle Playストアのみサポートします。
FGamebasePurchasableConfiguration
API | Mandatory(M) / Optional(O) | Description |
---|---|---|
bIncludeExpiredSubscriptions | O | 期限切れのサブスクリプション商品まで含めて照会します。 デフォルト値はfalseです。 |
API
Supported Platforms ■ UNREAL_ANDROID
void RequestSubscriptionsStatus(const FGamebasePurchasableConfiguration& Configuration, const FGamebasePurchasableSubscriptionStatusDelegate& Callback);
Example
void USample::RequestSubscriptionsStatus(bool bIncludeExpiredSubscriptions)
{
FGamebasePurchasableConfiguration Configuration;
Configuration.bAllStores = bAllStores;
UGamebaseSubsystem* Subsystem = UGameInstance::GetSubsystem<UGamebaseSubsystem>(GetGameInstance());
Subsystem->GetPurchase()->RequestSubscriptionsStatus(Configuration, FGamebasePurchasableSubscriptionStatusDelegate::CreateLambda(
[](const TArray<FGamebasePurchasableSubscriptionStatus>* purchasableReceiptList, const FGamebaseError* Error)
{
if (Gamebase::IsSuccess(Error))
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestSubscriptionsStatus succeeded."));
for (const FGamebasePurchasableSubscriptionStatus& PurchasableReceipt : *purchasableReceiptList)
{
UE_LOG(GamebaseTestResults, Display, TEXT(" - GamebaseProductId= %s, price= %f, currency= %s, paymentSeq= %s, purchaseToken= %s"),
*PurchasableReceipt.GamebaseProductId, PurchasableReceipt.Price, *PurchasableReceipt.Currency, *PurchasableReceipt.PaymentSeq, *PurchasableReceipt.PurchaseToken);
}
}
else
{
UE_LOG(GamebaseTestResults, Display, TEXT("RequestSubscriptionsStatus failed. (Error: %d)"), Error->Code);
}
}));
}
VO
struct FGamebasePurchasableSubscriptionStatus
{
// アプリがインストールされたストアについてGamebaseで内部的に定義したコードです。
FString StoreCode;
// ストアの決済識別子です。
FString PaymentId;
// サブスクリプション商品は更新さえる度にpaymentIdが変更されます。
// このフィールドはサブスクリプション商品を最初に決済した時のpaymentIdを示します。
// ストアおよび決済サーバーの状態によって値が存在しない場合があるため
// 常に有効な値を保障するわけではありません。
FString OriginalPaymentId;
// 決済識別子です。
// purchaseTokenと一緒にConsumeサーバーAPIを呼び出すために使用する重要な情報です。
//
// 注意:Consume APIはゲームサーバーで呼び出してください! (https://docs.toast.com/en/Game/Gamebase/en/api-guide/#purchase-iap)
FString PaymentSeq;
// 購入商品の商品IDです。
FString MarketItemId;
// IAP Webコンソールの項目固有識別子
int64 ItemSeq;
// 次のいずれかの値を持ちます。
// * UNKNOWN:不明なタイプです。Gamebase SDKをアップデートするか、Gamebaseサポートにお問い合わせください。
// * CONSUMABLE:消耗品です。
// * AUTO_RENEWABLE:サブスクリプション商品です。
FString ProductType;
// 商品を購入したユーザーIDです。
// 商品購入に使用していないユーザーIDでログインすると、購入した商品を受け取ることができません。
FString UserId;
// 商品の価格です。
float Price;
// 通貨情報です。
FString Currency;
// Payment識別子。
// paymentSeqで'Consume'サーバーAPIを呼び出すために使われる重要な情報です。
// Consume APIで引数名を'accessToken'に指定する必要があります。
// 参考: https://docs.toast.com/ko/Game/Gamebase/ko/api-guide/#purchase-iap
FString PurchaseToken;
// 商品を購入した時間。(epoch time)
int64 PurchaseTime;
// 購読の期限が切れる時間。(epoch time)
int64 ExpiryTime;
// RequestPurchase APIの呼び出し時にペイロードに渡される値です。
// ストアサーバー状態によって情報が流出する場合があるため、使用を推奨しません。
FString Payload;
// 購読状態
// 全体ステータスコードは次の文書を参照してください。
// - https://docs.nhncloud.com/en/TOAST/en/toast-sdk/iap-unity/#iapsubscriptionstatus
int32 StatusCode;
// 購読状態の説明です。
FString StatusDescription;
// Gamebaseコンソールに登録された商品IDです。
// RequestPurchase APIで商品を購入する時に使用されます。
FString GamebaseProductId;
// この値はGoogleで購入する際に使用され、次の値を持つことができます。
// ただし、GoogleサーバーのエラーによりGamebase決済サーバーで一時的に認証ロジックが無効になった場合
// nullのみを返すため、常に有効な値を保証するわけではありません。
// * null:正常決済
// * テスト:テスト決済
// * プロモーション:プロモーション決済
FString PurchaseType;
};
Error | Error Code | Description |
---|---|---|
PURCHASE_NOT_INITIALIZED | 4001 | Purchaseモジュールが初期化されませんでした。 gamebase-adapter-purchase-IAPモジュールをプロジェクトに追加したかを確認してください。 |
PURCHASE_USER_CANCELED | 4002 | ゲームユーザーがアイテムの購入をキャンセルしました。 |
PURCHASE_NOT_FINISHED_PREVIOUS_PURCHASING | 4003 | 購入ロジックがまだ完了していない状態でAPIが呼び出されました。 |
PURCHASE_NOT_ENOUGH_CASH | 4004 | 該当ストアのキャッシュが不足しているため決済できません。 |
PURCHASE_INACTIVE_PRODUCT_ID | 4005 | 該当商品が有効な状態ではありません。 |
PURCHASE_NOT_EXIST_PRODUCT_ID | 4006 | 存在しないGamebaseProductIDで決済をリクエストしました。 |
PURCHASE_LIMIT_EXCEEDED | 4007 | 月購入限度を超過しました。 |
PURCHASE_NOT_SUPPORTED_MARKET | 4010 | サポートしないストアです。 選択できるストアはAS(App Store)、GG(Google)、ONESTORE、GALAXYです。 |
PURCHASE_EXTERNAL_LIBRARY_ERROR | 4201 | NHN Cloud IAPライブラリエラーです。 詳細エラーを確認してください。 |
PURCHASE_UNKNOWN_ERROR | 4999 | 定義されていない購入エラーです。 全てのログをサポートへご送付ください。迅速に対応いたします。 |
* エラーコードの一覧は、次の文書を参照してください。 | ||
* エラーコード |
PURCHASE_EXTERNAL_LIBRARY_ERROR
GamebaseError* gamebaseError = Error; // GamebaseError object via callback
if (Gamebase::IsSuccess(Error))
{
// succeeded
}
else
{
UE_LOG(GamebaseTestResults, Display, TEXT("code: %d, message: %s"), Error->Code, *Error->Messsage);
GamebaseInnerError* moduleError = gamebaseError.Error; // GamebaseError.Error object from external module
if (moduleError.code != GamebaseErrorCode::SUCCESS)
{
UE_LOG(GamebaseTestResults, Display, TEXT("moduleErrorCode: %d, moduleErrorMessage: %s"), moduleerror->Code, *moduleerror->Messsage);
}
}