コンテンツにスキップ

レシートの検証

未提供のアイテムの領収書

アイテムの送信は、購入後に失敗したりキャンセルされたりすることがあります。このような状況を防ぐために、IAPV4 クラスの restore() メソッドを呼び出して、未提供のアイテムの領収書情報を取得してください。

  • restore() メソッドを呼び出した結果が SUCCESS で、ReceiptList が渡された場合は、復元対象のアイテムが存在します。未提供のアイテムを配布し、完了として処理してください。
  • restore() メソッドを呼び出した結果が SUCCESS でない場合、ゲーム側での処理は不要です。
  • restore() メソッド呼び出し後にエラーが発生しても、ユーザーが必ず把握すべきエラー でない限り、別途通知せずにユーザーがゲームを続けられるように実装してください。

以下は、与えられていないアイテムの領収書情報をリクエストするためのサンプルコードです。

APIリファレンス: hive .IAPV4.restore

using hive;    
    IAPV4.restore((ResultAPI result, List receiptList) => {    
         if (result.isSuccess()) {    
            if (result.errorCode = ResultAPI.ErrorCode.SUCCESS) {    
            // TODO: Request receipt verification using the received receiptList    
            } else if (result.errorCode = ResultAPI.ErrorCode.NOT_OWNED) {    
            // No unpaid items    
            }    
        }    
}
#include "HiveIAPV4.h"

FHiveIAPV4::Restore(FHiveIAPV4OnRestoreDelegate::CreateLambda([this](const FHiveResultAPI& Result, const TArray<FHiveIAPV4Receipt>& IAPV4ReceiptList) {
        if (Result.IsSuccess()) {
                if (Result.ErrorCode == FHiveResultAPI::EErrorCode::SUCCESS) {
                        // TODO: 受け取ったreceiptListを使用してレシートの検証をリクエストする
                } else if (Result.ErrorCode == FHiveResultAPI::EErrorCode::RESTORE_NOT_OWNED) {
                        //  未払いのアイテムはありません
                }
        }
}));

APIリファレンス: IAPV4 ::restore

#include <HIVE_SDK_Plugin/HIVE_CPP.h>    
    using namespace std;    
    using namespace hive;    
    IAPV4::restore([=](ResultAPI const & result, vector<reference_wrapper<IAPV4Receipt>> receiptList) {    
         if (result.isSuccess()) {    
            if (result.errorCode == ResultAPI::ErrorCode::SUCCESS) {    
            // TODO: 受け取ったreceiptListを使用してレシートの検証をリクエストする    
            } else if (result.errorCode == ResultAPI::ErrorCode::RESTORE_NOT_OWNED) {    
            // 未払いのアイテムはありません    
            }    
         }    
});

APIリファレンス: IAPV4.restore

import com.hive.IAPV4    
    import com.hive.ResultAPI    
    IAPV4.restore(object : IAPV4.IAPV4RestoreListener {    
         override fun onIAPV4Restore(result: ResultAPI, iapv4ReceiptList: ArrayList<IAPV4.IAPV4Receipt>?) {    
             if (result.isSuccess) {    
                 if (result.errorCode == ResultAPI.SUCCESS) {    
                     // TODO: 受け取ったiapv4ReceiptListを使用してレシート検証をリクエスト    
                 } else if (result.errorCode == ResultAPI.RESTORE_NOT_OWNED) {    
                     // 未払いのアイテムはありません    
                 }    
             }    
         }    
})

APIリファレンス: com.hive.IAPV4.restore

import com.hive.IAPV4;    
    import com.hive.ResultAPI;    
    IAPV4.INSTANCE.restore((result, iapv4ReceiptList) -> {    
         if (result.isSuccess()) {    
             if (result.getErrorCode() == ResultAPI.Companion.getSUCCESS()) {    
                 // TODO: 受け取ったiapv4ReceiptListを使用してレシートの検証をリクエスト    
             } else if (result.getErrorCode() == ResultAPI.Companion.getRESTORE_NOT_OWNED()) {    
                 // 未払いのアイテムはありません    
             }    
         }    
});

APIリファレンス: IAPV4Interface .restore

import HIVEService    
    IAPV4Interface.restore() { result, receiptList in    
        if result.isSuccess() {    
            if result.getErrorCode() == .success {    
            // TODO: Request receipt verification using the received receiptList    
            } else if result.getErrorCode() == .notOwned {    
            // No unpaid items    
            }    
        }    
}

APIリファレンス: HIVEIAPV4::restore

#import <HIVEService/HIVEService-Swift.h>    
    [HIVEIAPV4 restore: ^(HIVEResultAPI *result, NSArray<HIVEIAPV4Receipt *> *receiptList) {    
         if ([result isSuccess]) {    
            if ([result getErrorCode] == HIVEResultAPITypeSuccess) {    
            // TODO: 受け取ったreceiptListを使用してレシートの検証をリクエストする    
            } else if ([result getErrorCode] == HIVEResultAPITypeNotOwned){    
        // 未払いのアイテムはありません    
            }    
         }    
}];
Note

may differ by operating system/market. For more details, see the status of supported features.

購入復元エラーコードの一覧

エラーコード 説明
NEED_INITIALIZE 初期化できません
NETWORK ネットワークエラー
NOT_SUPPORTED 復元できません(デバイスでのアプリ内購入が拒否されたなど)。ユーザーが利用できないストアを設定
INVALID_SESSION 復元するための無効なセッション
IN_PROGRESS 復元APIが進行中
RESTORE_NOT_OWNED 復元するアイテムがありません
NOT_OWNED 復元するアイテムがありません
RESPONSE_FAIL IAPサーバーエラー

購入レシートの確認

ゲームサーバーからアイテムを送信する前に、IAPの検証APIがレシートが有効かどうかを確認する必要があります。 検証APIのhiveiap_transaction_idは、レシートによって発行されるユニークIDです。したがって、重複したレシートを確認するために、ゲームサーバーにIDを保存してください。 検証APIを使用してから、リクエストパラメータからすべてのデータを送信すると、IAPサーバーはゲームデータを転送して販売状況を分析します。Hive One > Search Allでの支払いを検索するためにAPIを利用してください。 詳細については、IAPレシート検証APIを参照してください。

購入済みまたは未購入のアイテムを提供する

商品購入後にアイテムの配布を完了したら、IAPV4 クラスの transactionFinish() を必ず呼び出して購入を完了してください。

Warning

アイテムを配布した後に transactionFinish() メソッドを呼び出さないと、その購入は未提供のアイテムとして残る可能性があり、後で restore() メソッドを呼び出したときに再びレシート情報が返されることがあります。

この原因は、restore()メソッドを呼び出す際にレシートを返すことです。すべてのプロセスが完了した後にフィニッシュAPIを呼び出すことを確認してください。

Note

Android 環境の Google Play ストアで Hive SDK 4.12.0 以降を適用した場合、決済は成功していても IAPV4 クラスの transactionFinish() メソッドの呼び出し結果が 3 日以内(テスト環境では 5 分以内)に成功しなければ、自動返金されます。

以下は、アイテム送信のリクエストを完了するサンプルコードです。

APIリファレンス: hive .IAPV4.transactionFinish

using hive;    
    String marketPid = "{YOUR_PRODUCT_MARKET_PID}";    
    IAPV4.transactionFinish(marketPid, (ResultAPI result, String marketPid) => {    
         if (result.isSuccess()) {    
             // call successful    
         }    
});
#include "HiveIAPV4.h"

FString MarketPid = TEXT("YOUR_PRODUCT_MARKET_PID");
FHiveIAPV4::TransactionFinish(MarketPid, FHiveIAPV4OnTransactionFinishDelegate::CreateLambda([this, idx](const FHiveResultAPI& Result, const FString& MarketPid) {
        if (Result.IsSuccess()) {
                // call successful 
        }
}));

APIリファレンス: IAPV4 ::transactionFinish

#include <HIVE_SDK_Plugin/HIVE_CPP.h>    
using namespace std;    
using namespace hive;    
string marketPid = "{YOUR_PRODUCT_MARKET_PID}";    

IAPV4::transactionFinish(marketPid, [=](ResultAPI const & result, string marketPid) {    
if (result.isSuccess()) {    
// call successful    
}    
});

APIリファレンス: IAPV4.transactionFinish

import com.hive.IAPV4    
    import com.hive.ResultAPI    
    val marketPid = "{YOUR_PRODUCT_MARKET_PID}"    
    IAPV4.transactionFinish(marketPid, object : IAPV4.IAPV4TransactionFinishListener {    
         override fun onIAPV4TransactionFinish(result: ResultAPI, marketPid: String) {    
             if (result.isSuccess) {    
                 // call successful    
             }    
         }    
})

APIリファレンス: com.hive.IAPV4.transactionFinish

import com.hive.IAPV4;    
    import com.hive.ResultAPI;    
    String pid = "{YOUR_PRODUCT_MARKET_PID}";    
    IAPV4.INSTANCE.transactionFinish(pid, (result, marketPid) -> {    
         if (result.isSuccess()) {    
             // call successful    
         }    
});

APIリファレンス: IAPV4Interface.transactionFinish

import HIVEService    
    let marketPid = "{YOUR_PRODUCT_MARKET_PID}"    
    IAPV4Interface.transactionFinish() { result, marketPid in    
    if result.isSuccess() {    
        // call successful    
        }    
}

APIリファレンス: HIVEIAPV4::transactionFinish:handler:

#import <HIVEService/HIVEService-Swift.h>    
    NSString *marketPid = @"{YOUR_PRODUCT_MARKET_PID}";    
    [HIVEIAPV4 transactionFinish: marketPid handler: ^(HIVEResultAPI *result, NSString *marketPid) {    
         if ([result isSuccess]) {    
             // call successful    
         }    
}];

以下は、いくつかのアイテムを送信するリクエストのサンプルコードです。

APIリファレンス: hive .IAPV4.transactionMultiFinish

using hive;    
    List marketPidList = new List();    
    marketPidList.Add("{YOUR_PRODUCT_MARKET_PID_01}");    
    marketPidList.Add("{YOUR_PRODUCT_MARKET_PID_02}");    
    marketPidList.Add("{YOUR_PRODUCT_MARKET_PID_03}");    
    IAPV4.transactionMultiFinish((List<ResultAPI> resultList, List<String> marketPidList) => {    
        for (ResultAPI result in resultList) {    
            if (result.isSuccess()) {    
            // call successful    
            }    
        }    
});
#include "HiveIAPV4.h"

TArray<FString> MarketPidList;
MarketPidList.Add(TEXT("YOUR_PRODUCT_MARKET_PID_01"));
MarketPidList.Add(TEXT("YOUR_PRODUCT_MARKET_PID_02"));
MarketPidList.Add(TEXT("YOUR_PRODUCT_MARKET_PID_03"));

FHiveIAPV4::TransactionMultiFinish(MarketPidList, FHiveIAPV4OnTransactionMultiFinishDelegate::CreateLambda([this](const TArray<FHiveResultAPI>& ResultList, const TArray<FString>& MarketPidList) {
        for (const auto& Result : ResultList) {
                if (Result.IsSuccess()) {
                        // API call successful
                }
        }
}));

APIリファレンス: IAPV4 ::transactionMultiFinish

#include <HIVE_SDK_Plugin/HIVE_CPP.h>    
    using namespace std;    
    using namespace hive;    
    vector<string> marketPidList;    
    marketPidList.push_back("{YOUR_PRODUCT_MARKET_PID_01}");    
    marketPidList.push_back("{YOUR_PRODUCT_MARKET_PID_02}");    
    marketPidList.push_back("{YOUR_PRODUCT_MARKET_PID_03}");    

    IAPV4::transactionMultiFinish(marketPidList, [=](vector<ResultAPI> const & resultList, vector<string> const & marketPidList) {    
        for (ResultAPI result : resultList) {    
            if (result.isSuccess()) {    
                        // call successful    
                    }    
        }    
});

APIリファレンス: IAPV4.transactionMultiFinish

import com.hive.IAPV4    
    import com.hive.ResultAPI    
    val marketPidList = arrayListOf(    
         "{YOUR_PRODUCT_MARKET_PID_01}",    
         "{YOUR_PRODUCT_MARKET_PID_02}",    
         "{YOUR_PRODUCT_MARKET_PID_03}"    
    )    
    IAPV4.transactionMultiFinish(marketPidList, object : IAPV4.IAPV4TransactionMultiFinishListener {    
         override fun onIAPV4TransactionMultiFinish(resultList: ArrayList<ResultAPI>, marketPidList: ArrayList<String>) {    
             for (i in 0 until resultList.size) {    
                 if (resultList[i].isSuccess) {    
                     // call successful    
                 }    
             }    
         }    
})

APIリファレンス: com.hive.IAPV4.transactionMultiFinish

import com.hive.IAPV4;    
    import com.hive.ResultAPI;    
    ArrayList<String> marketPidList = new ArrayList<>(Arrays.asList(    
             "{YOUR_PRODUCT_MARKET_PID_01}",    
             "{YOUR_PRODUCT_MARKET_PID_02}",    
             "{YOUR_PRODUCT_MARKET_PID_03}"    
    ));    
    IAPV4.INSTANCE.transactionMultiFinish(marketPidList, (resultList, marketPidList1) -> {    
         for (ResultAPI result : resultList) {    
             if (result.isSuccess()) {    
                 // call successful    
             }    
         }    
});

APIリファレンス: IAPV4Interface.transactionMultiFinish

import HIVEService    
    var marketPidList = [String]()    
    marketPidList.append("{YOUR_PRODUCT_MARKET_PID_01}")    
    marketPidList.append("{YOUR_PRODUCT_MARKET_PID_02}")    
    marketPidList.append("{YOUR_PRODUCT_MARKET_PID_03}")    
    IAPV4Interface.transactionMultiFinish(marketPidList) { resultList, marketPidList in    
        for result in resultList {    
            if result.isSuccess() {    
            // call successful    
            }    
        }    
}

APIリファレンス: HIVEIAPV4::transactionMultiFinish

#import <HIVEService/HIVEService-Swift.h>    
    NSMutableArray<NSString *> *maketPidList = [NSMutableArray array];    
    [maketPidList addObject:@"{YOUR_PRODUCT_MARKET_PID_01}"];    
    [maketPidList addObject:@"{YOUR_PRODUCT_MARKET_PID_02}"];    
    [maketPidList addObject:@"{YOUR_PRODUCT_MARKET_PID_03}"];    

    [HIVEIAPV4 transactionMultiFinish: maketPidList handler: ^(NSArray<HIVEResultAPI *> *resultList, NSArray<NSString *> *marketPidList) {    
         for (HIVEResultAPI *result in resultList) {    
             if ([result isSuccess]) {    
                 // call successful    
             }    
         }    
}];

ユーザーの購入活動情報を取得: AccountUuid を活用

1 つのゲームアカウントが複数のアプリストアアカウントを使って支払いを行うなど、支払いシステムを悪用するユーザーがいる場合があります。ゲーム会社側では、支払いユーザー情報(AccountUuid)を照合することで、特定ユーザーの行動を分析できます。

以下のメソッドは、Hive SDK がアプリストアに渡す UUID v3 形式の PlayerId ハッシュ値である AccountUuid を返します。 AccountUuid を活用したユーザー購入活動情報の取得機能は、Hive SDK Windows では提供されません。

APIリファレンス: IAPV4.getAccountUuid

using hive;    
String accountUuid = IAPV4.getAccountUuid();
#include "HiveIAPV4.h"

FString AccountUuid = FHiveIAPV4::GetAccountUuid();

APIリファレンス: IAPV4 ::getAccountUuid

#include <HIVE_SDK_Plugin/HIVE_CPP.h>    
    using namespace std;    
    using namespace hive;    
string accountUuid = IAPV4::getAccountUuid();

APIリファレンス: IAPV4.getAccountUuid

import com.hive.IAPV4    
val accountUuid = IAPV4.getAccountUuid()

APIリファレンス: IAPV4.INSTANCE .getAccountUuid

import com.hive.IAPV4;    
String accountUuid = IAPV4.INSTANCE.getAccountUuid();

APIリファレンス: IAPV4Interface.getAccountUuid

import HIVEService    
let accountUuid = IAPV4Interface.getAccountUuid()

API リファレンス: [HIVEIAPV4 getAccountUuid ]

#import <HIVEService/HIVEService-Swift.h>    
NSString accountUuid = [HIVEIAPV4 getAccountUuid];

ゲーム会社はこのハッシュ値と購入領収書情報を併用することで、どのユーザーの購入が正常だったかを分析できます。

以下の例は、AccountUuid を活用して特定の PlayerId を持つゲームアカウントに複数のゲームキャラクターがいる場合の購入活動を分析する方法を示しています。この例は開発の参考用であり、実際の動作を保証するものではありません。

ゲームサーバーに事前に購入メタ情報を保存する

購入リクエストが発生する前に、購入メタ情報(AccountUuid とキャラクター情報)をゲームサーバーに保存します。

/* The game client pass the info to the game server before the purchase occurs.
*
* @param purchaseTime: purchase time. The receipts are usually based on UTC. since the epoch (Jan 1, 1970).
* @param productId: purchase product ID.
* @param accountUuId: hashed PlayerId. It is included in the receipt and can be obtained with IAPV4.getAccountUuid().
* @param characterId: the character identifier within PlayerId.
*/
game.sendPurchaseGameData(purchaseTime, productId, accountUuid, characterId)   

bypassInfo の伝達

購入リクエストが発生した場合は、bypassInfo データをゲームクライアントからゲームサーバーに渡します。以下のコードは開発の参考用の擬似コードです。

IAPV4.purchaseproductId) {
    iapv4Receipt -> game.sendReceipt(iapv4Receipt.purchase_bypass_info)
}
Warning

bypassInfoレシート検証サーバー API 呼び出し時に Request Body にそのまま渡す用途のみ で使用してください。ゲームクライアントやゲームサーバーで bypassInfo の内部構造を直接解析して他の用途に使わないでください。bypassInfo の内部フォーマットは Hive SDK およびストアポリシーに応じて事前告知なく変更される場合があり、直接解析するとゲームに予期しない障害が発生する可能性があります。

ゲームサーバーは bypassInfo データを IAP v4 サーバーに渡し、領収書の検証をリクエストします。領収書検証のリクエストについては、 を確認してください。 AccountUuid は、レスポンス値にあるアプリマーケット/ストアの原始領収書 hiveiap_receipt に次のように含まれています。

# Apple
# Purchase time: purchase_date_ms
# Purchased products: product_id
# AccountUuid: app_account_token   
# Google
# Purchase time: purchaseTime
# Purchased products: productId
# AccountUuid: obfuscatedAccountId

購入メタ情報の AccountUuidhiveiap_receiptAccountUuid を照合して分析

前もって購入メタ情報に保存しておいた AccountUuid と、領収書検証レスポンス値から取得した AccountUuid を照合して、ゲームキャラクター情報など分析に必要なユーザー情報を取得します。購入メタ情報と領収書情報をマッチさせることで、該当ゲームアカウントの購入活動を分析できます。

分析に必要なユーザー情報を見つけます。購入メタ情報に以前保存されたAccountUuidと、領収書検証の結果から得られたAccountUuidを照合することで、ゲームキャラクター情報などを取得します。購入メタ情報と領収書情報を照合することで、該当するゲームアカウントの購入活動を分析できます。 1 人のユーザーが複数のアイテムを購入した場合は、購入メタ情報と hiveiap_receiptpurchaseTime を照合して各購入を特定できます。hiveiap_receipt の詳細については、レシート検証 でマーケット別の API レスポンスを確認してください。

ユーザーが複数のアイテムを購入した場合、各購入はhiveiap_receiptpurchaseTimeと購入メタ情報を照合することで特定できます。hiveiap_receiptの詳細については、レシートの検証を参照し、マーケットごとのAPIレスポンスを確認してください。

ユーザーの購入活動情報を取得: iapPayload を活用

Purchase で購入をリクエストする際に、ゲーム会社が事前に定義した購入活動分析用データを iapPayload に含めると、Hive サーバーは iapPayload に対応する購入領収書を照合してゲームサーバーに渡します。ゲーム会社は、領収書に含まれる情報と iapPayload に含まれる情報を併用して、ユーザーの購入活動を分析できます。

購入をPurchaseでリクエストする際に、ゲーム会社が事前に定義した購入活動分析データをiapPayloadに含めると、HiveサーバーはiapPayloadに対応する購入レシートを照合し、それをゲームサーバーに配信します。ゲームスタジオは、レシートの情報とiadPayloadの情報を使用して、ユーザーの購入活動を分析できます。 この方法では、Hive サーバーが各購入のメタ情報を各領収書と照合してゲームサーバーに送信するため、上記のように AccountUuid で購入メタ情報と領収書をゲーム会社が直接照合する必要はありません。

Note

iapPayload を定義して purchase() を実行

商品購入リクエスト を実行する際に、iapPayload に必要なフィールドと値を定義して引数として使用します。この流れは 購入メタデータ を定義するのと似ています。

iapPayloadを定義し、Purchase()を実行する

領収書検証をリクエストし、iapPayload と対応する領収書を受け取る

決済が完了して 領収書検証 をリクエストすると、Hive サーバーは購入ごとに iapPayload と対応する領収書を照合してゲームサーバーに渡します。ゲーム会社は iapPayload と領収書の情報を利用して、ユーザーの購入活動を分析できます。

レシートの検証をリクエストし、iapPayloadと対応するレシートを受け取ります。

以下は、iapPayload を使用した例です。

以下は、iapPayloadを使用した状況の例です。

  1. マーケットアカウント: A、Hive プラットフォーム PlayerId: a、アプリキャラクター: 1 でログイン
  2. purchase(marketPid, "playerId a - character 1")
  3. Hive プラットフォーム PlayerId: b、アプリキャラクター 2 にアカウントを切り替え
  4. restore()
  5. iapPayload "playerId a - character 1" を含む領収書を受け取る6. 領収書を検証した後、アプリキャラクター 2 ではなくアプリキャラクター 1 に付与する