영수증 확인
미지급된 아이템의 영수증 정보 요청¶
상품 구매 과정에서 여러 가지 상황으로 인하여 아이템 지급을 실패하거나 취소되는 경우가 발생할 수 있습니다. 이런 상황을 대비하기 위해서는 미지급된 아이템의 영수증 정보를 요청해야 합니다.
IAPV4 클래스의 restore()
메서드를 호출하여 미지급된 아이템의 정보를 요청하세요.
restore()
메서드를 호출한 결과값이SUCCESS
이고 ReceiptList가 전달되었다면, 복구할 아이템이 존재하는 상황입니다. 미지급된 아이템을 지급하고 완료로 처리하세요.restore()
메서드를 호출한 결과 값이SUCCESS
가 아닌 경우에는 게임에서 처리하지 않아도 됩니다.restore()
메서드 호출 후 에러가 발생하더라도 유저가 반드시 알아야 하는 에러가 아니라면 별도 알림 없이 유저가 게임을 계속 플레이할 수 있도록 구현하세요.- Google Play용 Windows 앱(Hive SDK v4 Unity Windows 23.0.0 이상)에서 `GOOGLE_PLAYSTORE` 마켓을 사용할 때,
restore()
메서드를 호출하는 시점에 결제용 Google 계정의 인증 정보가 만료되면, Hive SDK는 자동으로 재인증 과정을 실행합니다.
다음은 미지급된 아이템의 영수증 정보를 요청하는 예제 코드입니다.
API Reference: 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: 전달받은 IAPV4ReceiptList 로 영수증 검증 요청
} else if (Result.ErrorCode == FHiveResultAPI::EErrorCode::RESTORE_NOT_OWNED) {
// 미지급된 아이템 없음
}
}
}));
API Reference: 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: Request receipt verification using the received receiptList
} else if (result.errorCode == ResultAPI::ErrorCode::RESTORE_NOT_OWNED) {
// No unpaid items
}
}
});
API Reference: 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: Request receipt verification using the received iapv4ReceiptList
} else if (result.errorCode == ResultAPI.RESTORE_NOT_OWNED) {
// No unpaid items
}
}
}
})
API Reference: 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 Reference: IAPV4Interface .restore
API Reference: HIVEIAPV4::restore
#import <HIVEService/HIVEService-Swift.h>
[HIVEIAPV4 restore: ^(HIVEResultAPI *result, NSArray<HIVEIAPV4Receipt *> *receiptList) {
if ([result isSuccess]) {
if ([result getErrorCode] == HIVEResultAPITypeSuccess) {
// TODO: Request receipt verification using the received receiptList
} else if ([result getErrorCode] == HIVEResultAPITypeNotOwned){
// No unpaid items
}
}
}];
Note
운영체제/마켓별로 지원하는 기능에 차이가 있을 수 있습니다. 자세한 내용은 기능 지원 현황을 확인하세요.
구매 복구 에러코드¶
에러코드 | 설명 |
NEED_INITIALIZE | 초기화가 안됨 |
NETWORK | 네트워크 에러 |
NOT_SUPPORTED | restore 불가 상태(기기 앱 내 구입 차단 등),지원되지 않는 마켓 설정 시 |
INVALID_SESSION | 복구를 진행할 수 없는 사용자 세션 |
IN_PROGRESS | restore API가 이미 호출됨 |
RESTORE_NOT_OWNED | 복구할 아이템이 없음 |
NOT_OWNED | 복구할 아이템이 없음 |
RESPONSE_FAIL | 기타 오류. Hive IAP 서버 오류 |
구매 영수증 검증 요청¶
구매 성공 후 게임 서버에서 아이템을 지급하기 전에 Hive IAP의 영수증 검증 API를 이용하여 구매 영수증의 유효성 검증을 구현합니다. 영수증 검증 API 응답값의 hiveiap_transaction_id
는 영수증 별로 고유하게 발급되는 ID이기 때문에 이 값을 게임 서버에 저장 후 영수증의 중복 여부를 체크할 수 있습니다. Hive IAP 영수증 검증 API를 사용하여 검증하고 요청 파라미터의 모든 값을 전송하면 매출 정보 및 매출에 대한 게임 정보의 애널리틱스 전송을 Hive IAP 서버에서 대행합니다. 또한 Hive One 통합 조회의 결제 조회를 위한 API를 게임에서 따로 개발할 필요가 없습니다. 자세한 내용은 IAP v4 영수증 검증 가이드를 확인하세요.
구매/복구한 아이템의 지급 완료¶
상품 구매 후 아이템 지급을 완료했다면 IAPV4 클래스의 transactionFinish()
를 반드시 호출하여 구매를 완료하세요.
Warning
Unless you call transactionFinish()
method after sending an item, 미지급된 아이템으로 남아있게 되고 restore()
메서드를 호출해도 영수증 정보가 반환될 수 있습니다.
Note
Android 환경의 Google Play Store에서 Hive SDK v4.12.0 이후 버전 적용 시, 결제는 성공했으나 IAPV4 클래스의 TransactionFinish()
메서드 호출 결과가 3일 (테스트 환경에서는 5분) 이내에 성공하지 못하면 자동 환불 처리됩니다.
다음은 하나의 지급 요청을 완료하는 예제 코드입니다.
API Reference: hive .IAPV4.transactionFinish
API Reference: IAPV4 ::transactionFinish
API Reference: 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 Reference: com.hive.IAPV4.transactionFinish
API Reference: IAPV4Interface.transactionFinish
API Reference: HIVEIAPV4::transactionFinish:handler:
다음은 여러 개의 지급 요청을 완료하는 예제 코드입니다.
API Reference: 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 Reference: 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 Reference: 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 Reference: 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 Reference: 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 Reference: 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)를 대조하는 방법으로 특정 사용자 행태를 얻어 분석할 수 있습니다.
아래 메서드는 Hive SDK가 앱 스토어에 전달하는 UUID v3 형태의 PlayerId 해시값인 AccountUuid
를 반환합니다.
API Reference: IAPV4.getAccountUuid
API Reference: IAPV4 ::getAccountUuid
API Reference: IAPV4.getAccountUuid
API Reference: IAPV4.INSTANCE .getAccountUuid
API Reference: IAPV4Interface.getAccountUuid
API Reference: [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.purchase(productId) {
iapv4Receipt -> game.sendReceipt(iapv4Receipt.purchase_bypass_info)
}
영수증 검증 요청¶
게임 서버는 bypassInfo
데이터를 Hive IAP v4 서버에 전달하고 영수증 검증을 요청합니다. 영수증 검증 요청은 다음을 확인하세요. AccountUuid
는 응답 값에 있는 앱 마켓/스토어 영수증 원문인 hiveiap_receipt
안에 다음과 같이 포함되어 있습니다.
# Apple
# 구매 시간: purchase_date_ms
# 구매 상품: product_id
# AccountUuid: app_account_token
# Google
# 구매 시간: purchaseTime
# 구매 상품: productId
# AccountUuid: obfuscatedAccountId
구매 메타 정보의 AccountUuid
와 hiveiap_receipt
의 AccountUuid
를 대조해 분석¶
앞서 구매 메타 정보에 저장해두었던 AccountUuid
와 영수증 검증 응답 값에서 얻은 AccountUuid
를 대조해 게임 캐릭터 정보 등 분석에 필요한 유저 정보를 찾습니다. 구매 메타 정보와 영수증 정보를 매치시켜 해당 게임 계정의 구매 활동을 분석할 수 있습니다.
한 유저가 여러 아이템을 구매한 경우, 구매 메타 정보와 hiveiap_receipt
의 purchaseTime
을 대조하여 각 구매 건을 특정할 수 있습니다. hiveiap_receipt
에 관한 자세한 내용은 영수증 검증에서 마켓별 API 응답값을 확인하세요.
유저 구매 활동 정보 얻기: iapPayload
를 활용¶
Purchase()로 구매를 요청할 때 iapPayload
에 게임사에서 사전에 정의한 구매 활동 분석용 데이터를 넣으면, Hive 서버가 iapPayload
에 해당하는 구매 영수증을 매치시켜 게임 서버에 전달합니다. 게임 스튜디오는, 영수증에 있는 정보와 iadPayload
에 있는 정보를 함께 사용하여 사용자 구매 활동을 분석할 수 있습니다.
Note
이 방법을 사용하면 Hive 서버에서 각 구매 건의 구매 메타 정보를 영수증과 매치시켜 전달해주므로, 위 방법과 같이 AccountUuid
로 구매 메타 데이터와 영수증을 게임 스튜디오가 직접 매치할 필요가 없습니다.
iapPayload
를 정의해 Purchase()
실행¶
각 상품 구매 요청을 실행할 때 iapPayload
에 원하는 필드와 값을 정의하여 인자로 사용합니다. 이 과정은 구매 메타 데이터 정의와 유사합니다.
영수증 검증 요청하여 iapPayload
와 이에 해당하는 영수증을 수령¶
결제가 완료되고 영수증 검증 요청을 할 때, Hive 서버는 게임 서버에 구매 건별로 iapPayload
와 이에 해당하는 영수증을 매치시켜 전달합니다. 게임 스튜디오는 iapPayload
영수증에 있는 정보를 활용해 유저의 구매 활동을 분석할 수 있습니다.