收据验证
未給予的項目收據¶
發送物品有時會在購買後失敗或被取消。為了防止這種情況,請呼叫 IAPV4 類別的 restore() 方法來請求未給予項目的資訊。
- 如果在呼叫
restore()後回傳SUCCESS且有傳送 ReceiptList,則代表存在需要恢復的項目。請發放未給予的項目並完成恢復流程 - 如果在呼叫
restore()後沒有回傳SUCCESS,則遊戲端不需要再做任何處理 - 如果在呼叫
restore()後發生錯誤,只要不是使用者必須知道的錯誤,就請讓使用者在沒有額外通知的情況下繼續遊戲
以下是請求未提供項目收據信息的示例代碼。
API 參考: hive .IAPV4.restore
#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
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
API 參考: IAPV4 ::transactionFinish
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
API 參考: IAPV4Interface.transactionFinish
API Reference: HIVEIAPV4::transactionFinish:handler:
以下是请求发送多个项目的示例代码。
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
API 參考: IAPV4 ::getAccountUuid
API 參考: IAPV4.getAccountUuid
API 參考: IAPV4.INSTANCE .getAccountUuid
API 參考: IAPV4Interface.getAccountUuid
API 參考: [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 資料傳遞給應用程式伺服器。以下程式碼為開發參考用的偽程式碼。
Warning
bypassInfo 僅可在呼叫 收據驗證伺服器 API 時,直接傳遞至 Request Body。請勿在遊戲用戶端或遊戲伺服器中直接解析 bypassInfo 的內部結構並用於其他用途。bypassInfo 的內部格式可能會依據 Hive SDK 與商店政策在未事先公告的情況下變更,直接解析可能導致遊戲發生非預期的問題。
Note
如需購買分析等額外資訊,請使用 AccountUuid 或 iapPayload。
收據驗證請求¶
遊戲伺服器會將 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
透過比對購買中繼資料中的 AccountUuid 與 hiveiap_receipt 的 AccountUuid 來分析¶
透過比對先前儲存在購買中繼資料中的 AccountUuid 與從收據驗證回應值取得的 AccountUuid,找出分析所需的使用者資訊,例如遊戲角色資訊。將購買中繼資料與收據資訊配對後,就能分析對應遊戲帳號的購買活動。
如果使用者購買了多個項目,您可以透過比對購買中繼資料與 hiveiap_receipt 的 purchaseTime 來識別各筆購買。關於 hiveiap_receipt 的詳細內容,請參閱 收據驗證 並查看各市場的 API 回應值。
取得使用者購買活動資訊:使用 iapPayload¶
當使用 Purchase 進行購買時,如果在 iapPayload 中放入遊戲公司事先定義的購買活動分析資料,Hive 伺服器就會將對應於 iapPayload 的購買收據配對後傳送給遊戲伺服器。遊戲工作室可以同時使用收據中的資訊與 iapPayload 中的資訊來分析使用者的購買活動。
Note
這樣一來,由於 Hive 伺服器會將各筆購買的中繼資料與收據配對後傳送給你的遊戲伺服器,因此你不需要像上面那樣自行使用 AccountUuid 將購買中繼資料與收據配對。
定義 iapPayload 並執行 Purchase()¶
當執行每個 商品購買請求 時,請在 iapPayload 中定義所需的欄位與值,並作為引數使用。此流程與定義 購買中繼資料 類似。
請求收據驗證並接收 iapPayload 與對應的收據¶
當付款完成並請求 收據驗證 時,Hive 伺服器會依照每筆購買將 iapPayload 與對應的收據配對後傳送給遊戲伺服器。遊戲工作室可以使用 iapPayload 與收據中的資訊來分析使用者的購買活動。
以下是使用 iapPayload 的情境範例。
- 市場帳號:A、Hive 平台 PlayerId:a、應用角色:1 登入
- purchase(marketPid, "playerId a - character 1")
- 將帳號切換為 Hive 平台 PlayerId:b、應用角色 2
- restore()
- 收到包含
iapPayload"playerId a - character 1" 的收據6. 驗證收據後,將商品發給應用角色 1,而不是應用角色 2