跳轉至

收据验证

未給予的項目收據

發送物品有時會在購買後失敗或被取消。為了防止這種情況,請呼叫 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: 使用接收到的 receiptList 请求收据验证    
            } else if (result.errorCode = ResultAPI.ErrorCode.NOT_OWNED) {    
            // 没有未支付的项目    
            }    
        }    
}
#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: Request receipt verification using the received iapv4ReceiptList    
             } else if (result.getErrorCode() == ResultAPI.Companion.getRESTORE_NOT_OWNED()) {    
                 // No unpaid items    
             }    
         }    
});

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 伺服器將轉發遊戲數據以分析銷售狀態。只需利用 API 在 Hive One > 搜索所有 中搜索付款。 有關更多詳細信息,請參閱 IAP 收據驗證 API

完成以提供已購買或未提供的項目

如果遊戲伺服器在購買後已發放物品,請務必呼叫 IAPV4 類別的 transactionFinish() 方法,以完成購買

Warning

如果在送出物品後沒有呼叫 transactionFinish(),該購買可能會維持為未給予的項目,而在之後呼叫 restore() 時,收據資訊可能會再次傳回

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 Reference: 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 呼叫成功
                }
        }
}));

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

可能有使用者濫用付款系統,例如讓同一個遊戲帳號使用多個應用商店帳號付款。遊戲公司可透過比對付款使用者資訊(AccountUuid)來分析特定使用者行為。

下列方法會回傳 AccountUuid,它是 Hive SDK 傳送給應用商店的 UUID v3 格式 PlayerId 雜湊值。 使用 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];

遊戲公司可以將這個雜湊值與購買收據資訊一起使用,來分析哪些使用者的購買是正常的。

以下範例說明當一個具有特定 PlayerId 的遊戲帳號中有多個遊戲角色時,如何使用 AccountUuid 分析該遊戲帳號的購買活動。此範例僅供開發參考,並不保證實際運作。

事先在遊戲伺服器儲存購買中繼資料

在發出購買請求之前,先將購買中繼資料(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 與商店政策在未事先公告的情況下變更,直接解析可能導致遊戲發生非預期的問題。

Note

如需購買分析等額外資訊,請使用 AccountUuidiapPayload

收據驗證請求

遊戲伺服器會將 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,找出分析所需的使用者資訊,例如遊戲角色資訊。將購買中繼資料與收據資訊配對後,就能分析對應遊戲帳號的購買活動。

如果使用者購買了多個項目,您可以透過比對購買中繼資料與 hiveiap_receiptpurchaseTime 來識別各筆購買。關於 hiveiap_receipt 的詳細內容,請參閱 收據驗證 並查看各市場的 API 回應值。

取得使用者購買活動資訊:使用 iapPayload

當使用 Purchase 進行購買時,如果在 iapPayload 中放入遊戲公司事先定義的購買活動分析資料,Hive 伺服器就會將對應於 iapPayload 的購買收據配對後傳送給遊戲伺服器。遊戲工作室可以同時使用收據中的資訊與 iapPayload 中的資訊來分析使用者的購買活動。

Note

這樣一來,由於 Hive 伺服器會將各筆購買的中繼資料與收據配對後傳送給你的遊戲伺服器,因此你不需要像上面那樣自行使用 AccountUuid 將購買中繼資料與收據配對。

定義 iapPayload 並執行 Purchase()

當執行每個 商品購買請求 時,請在 iapPayload 中定義所需的欄位與值,並作為引數使用。此流程與定義 購買中繼資料 類似。

請求收據驗證並接收 iapPayload 與對應的收據

當付款完成並請求 收據驗證 時,Hive 伺服器會依照每筆購買將 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. 驗證收據後,將商品發給應用角色 1,而不是應用角色 2