收据验证
未给出的项目收据¶
购买后,物品有时会失败或被取消。为防止这种情况,请调用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() 方法时可能会再次返回收据信息。
如果未调用 transactionFinish() 方法,物品会保持未发放状态。
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 参考: 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 哈希值。
API 参考: IAPV4.getAccountUuid
API 参考: IAPV4 ::getAccountUuid
API 参考: IAPV4.getAccountUuid
API 参考: IAPV4.INSTANCE .getAccountUuid
API 参考: IAPV4Interface.getAccountUuid
API 参考: [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 数据传递到游戏服务器。下面的代码是供开发参考的伪代码。
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 响应值。
获取用户购买活动信息: 使用 iadPayload¶
当通过Purchase发起购买请求时,如果在iapPayload中放入游戏公司事先定义的购买活动分析数据,Hive 服务器会将与iapPayload对应的购买收据匹配后传递给游戏服务器。游戏工作室可以结合收据中的信息和iapPayload中的信息来分析用户的购买活动。
Note
通过这种方式,Hive 服务器会将每笔购买的元信息与收据进行匹配并发送到您的游戏服务器,因此无需像上面那样由游戏公司直接通过 AccountUuid 匹配购买元信息和收据。
定义 iapPayload 并执行 purchase()¶
在执行各商品购买请求时,在 iapPayload 中定义所需的字段和值,并将其作为参数使用。该过程类似于定义 购买元数据。
请求收据验证,并接收 iapPayload 和相应的收据。¶
当支付完成并请求收据验证时,Hive 服务器会按购买将 iapPayload 与相应的收据匹配,并传递给游戏服务器。游戏工作室可以使用 iapPayload 和收据中的信息来分析用户购买活动。
以下是一个使用iapPayload的情况示例。
- 市场账户: A, Hive 玩家ID: a, 应用角色: 1 登录
- purchase(marketPid, "playerId a - character 1")
- 切换账户到 Hive 玩家ID: b, 应用角色 2
- restore()
- 收到包含
iapPayload"playerId a - character 1" 的收据6. 验证收据后,向应用角色 1 发放,而不是应用角色 2