본문으로 건너뛰기

Unreal Engine 빌링 튜토리얼

이 문서는 UnrealStarter 샘플 프로젝트를 기준으로, Android 환경에서 Google Play 인앱 결제(PlayStore) 를 이용해 Hybe Game Platform Service(이하 플랫폼)의 빌링(결제) 기능을 연동하는 방법을 단계별로 안내합니다.

멤버십 튜토리얼에서 Google 로그인으로 플레이어 식별 정보를 확보한 뒤, 이 문서를 이어서 진행하는 것을 권장합니다. Android 멤버십 설정은 멤버십 가이드 - 플랫폼 로그인 (Android)를 참고하세요.

정보

결제용 유니크 ID (Player ID)

PlayStore 결제·상품 예약·PreInit에는 유저를 식별하는 유니크 ID가 필요합니다. 이 ID는 게임 서버가 빌링 백엔드 API(상품 예약·검증·완료 등)를 호출할 때 사용하는 값과 동일해야 합니다.

  • SDK API 파라미터 이름: PreInit(playerid)playerid
  • UnrealStarter 샘플: 플랫폼 로그인 결과의 imid 를 그대로 전달
  • 반드시 imid를 써야 하는 것은 아닙니다. 게임 서버·빌링 백엔드가 약속한 유저 ID이면 됩니다. 샘플만 편의상 imid를 사용합니다.

클라이언트 PreInit에 넘기는 ID와 게임 서버 예약 API에 쓰는 ID가 다르면 결제·지급·복구가 어긋날 수 있습니다. 자세한 내용은 Step 4빌링 가이드 - PreInit을 참고하세요.

튜토리얼을 마치면 다음을 할 수 있습니다.

  • Billing Agent를 등록하고, 게임 서버·빌링 백엔드와 동일한 유저 ID로 PlayStore 결제 환경을 초기화(PreInit) 한다.
  • Google Play에 등록된 상품 정보를 조회하고 상점 UI를 구성한다.
  • 게임 서버 예약(boid) 후 구매(StartPurchase) 를 수행한다.
  • 결제 완료 후 Verify → Complete → OnCompletedPurchase 흐름을 처리한다.
  • Restore 로 미처리 구매를 복구한다. (구매 연동과 동등하게 필수)
위험

구매 복구(Restore)는 선택 기능이 아닙니다

PlayStore에서 결제는 성공했지만 앱이 영수증을 받지 못하거나, 게임 서버 전달·완료 처리에 실패하면 유저는 결제했으나 아이템을 받지 못하는 상황이 발생합니다. 구매(StartPurchase) 연동과 함께 반드시 복구 플로우를 구현·테스트해야 합니다.

정보

이 문서와 빌링 가이드의 차이

빌링 가이드는 스팀·PG·iOS·원스토어 등 모든 결제 수단과 API 레퍼런스를 다루는 참고 문서입니다. 이 튜토리얼은 처음 연동할 때 손으로 따라 하는 실습 문서이며, Android + PlayStore 에 집중합니다.

튜토리얼에서 PreInit, StartPurchase, OnPurchasesUpdatedAPI 이름은 빌링 가이드 API Reference로 연결되어 있습니다.

경고

이 튜토리얼에서 다루지 않는 것

  • Windows PC 결제(스팀, PG, Xsolla 등) — Unreal Engine 빌링 해당 섹션 참고
  • iOS App Store, 원스토어빌링 가이드 해당 섹션 참고
  • 실제 게임 서버 HTTP 연동 — 샘플의 VirtualGameServerSubsystem(가상 게임 서버) 흐름까지만 다룹니다
  • Google Play Console 상품 등록·심사 전체 절차 — 기술 PM·퍼블리싱 담당자와 협의

사전 요구사항

항목내용
OSAndroid 10 이상 실기기 권장 (에뮬레이터는 Google Play 결제 테스트 제한)
Unreal Engine5.4 이상 (5.4.4, 5.5.4에서 테스트됨)
개발 환경Android Studio, JDK, NDK (언리얼 Android SDK 설정 완료)
샘플 프로젝트UnrealStarter (UnrealStarter_x.x.x.zip)
Google 계정Play Console 내부 테스트 참여·라이선스 테스터 등록된 계정
멤버십플랫폼 Google 로그인 완료, 결제용 유니크 ID 확보 (샘플: imid) (멤버십 튜토리얼 또는 Android 로그인 가이드)
네트워크QA(또는 dev) 플랫폼 환경 접근 가능
위험

PlayStore 결제 테스트는 Play Console에 업로드한 빌드로 진행해야 합니다.

로컬에서 빌드한 APK를 직접 설치(sideload)한 경우, Play Store 결제 모듈이 정상 동작하지 않을 수 있습니다. 내부 테스트 트랙에 AAB/APK를 업로드하고, 테스터 링크를 통해 설치하세요.


Step 0. Android 빌드 환경 준비

0-1. Android 프로젝트 설정

  1. 언리얼 에디터에서 Edit → Project Settings → Platforms → Android 를 엽니다.
  2. SDK/NDK/JDK 경로가 올바른지 확인합니다.
  3. Package Name이 Play Console에 등록된 앱 ID와 일치하는지 확인합니다.

0-2. 앱 사이닝 (Google 로그인·결제 공통)

Google 로그인과 PlayStore 결제 모두 배포용 사이닝이 적용된 빌드가 필요합니다.

  1. Project Settings → Platforms → Android → Distribution Signing 에 키스토어를 설정합니다.
  2. Play Console에 등록한 업로드 키·앱 서명 구성과 일치하는지 확인합니다.
정보

사이닝을 적용하지 않으면 Google 로그인 인증이 실패합니다. (멤버십 가이드 - 앱 사이닝)

0-3. Firebase 설정 (google-services.json)

Android Google 로그인·FCM 등에 Firebase 설정 파일이 필요합니다.

  1. 기술 PM에게 전달받은 google-services.json을 프로젝트에 복사합니다.
    • 기본 경로: Config/Firebase/google-services.json
  2. DefaultEngine.ini에서 경로를 변경할 수 있습니다.
Config/DefaultEngine.ini (발췌)
[HybeGamePlatform]
AndroidGoogleServiceJson=Config/Firebase/google-services.json

자세한 내용은 멤버십 가이드 - 푸시 연동 (Android)을 참고하세요.

0-4. Play Console 테스트 준비

PlayStore 결제 실습 전에 아래를 확인하세요.

항목확인 내용
내부 테스트 트랙최신 빌드가 업로드되어 있고, 테스터가 참여 수락 완료
라이선스 테스터Play Console → 설정 → 라이선스 테스트에 테스트 계정 등록
인앱 상품QA 환경에 등록된 상품 ID(SKU)가 Play Console에 존재
빌드 버전기기에 설치된 앱 versionCode가 Play Console 업로드 버전과 동일
경고

결제 창이 나오지 않는 가장 흔한 원인

실행 중인 앱 버전이 Play Console에 등록된 버전과 다르면 결제 UI가 표시되지 않습니다. 내부 테스트 링크로 재설치한 뒤 다시 시도하세요. (빌링 가이드 - Troubleshooting)

0-5. 접속 환경 설정 (drimage_config.json)

샘플은 Content/UnrealStarter/Data/DrimagePlatform/drimage_config.json 에서 플랫폼 접속 환경을 읽습니다.

Content/UnrealStarter/Data/DrimagePlatform/drimage_config.json
{
"QA-1202": {
"BuildEnv": "qa",
"ProjectId": "1202",
"WebClientId": "134676729186-dfbh87213kflsoqq17koq9fa1er1oviu.apps.googleusercontent.com",
"ServiceId": "12020020",
"BillingAuth": "test-auth-key"
}
}
필드Android 빌링에서의 역할
BuildEnv, ProjectId, WebClientIdMount() — 멤버십·빌링 공통
ServiceId, BillingAuth(샘플앱 전용) 가상 게임 서버 HTTP 헤더. 플랫폼 SDK Plugin은 사용하지 않음

실제 게임 연동 시 Mount JSON에는 SDK가 요구하는 필드만 포함하면 되며, ServiceId·BillingAuth는 샘플의 가상 게임 서버 데모용입니다. (멤버십 튜토리얼 - Configuration JSON)

0-6. Android 빌드 및 설치

  1. 언리얼 에디터에서 Platforms → Android → Package Project 로 AAB(또는 APK)를 빌드합니다.
  2. Play Console 내부 테스트 트랙에 업로드합니다.
  3. 테스터 링크(Play Console → 테스트 → 내부 테스트 → 테스트 참여 방법)로 기기에 설치합니다.

Step 1. 샘플 앱 빌링 흐름 이해

PlayStore 결제에 관련된 화면과 소스 파일의 대응 관계는 아래와 같습니다.

구분샘플 파일역할
Billing AgentUnrealStarter/Agent/USBillingAgent.*결제 이벤트·Verify/Complete 처리
상점 UIUnrealStarter/UI/Page/StorePage.cpp상품 조회·구매·복구 UI
게임 서버 연동UnrealStarter/Helper/VirtualGameServerSubsystem.*상품 예약·검증·완료 API (샘플 전용)
초기화UnrealStarter/UI/Page/InitPage.cppBilling Agent 등록·Mount()
토큰 갱신UnrealStarter/Agent/USPlatformAuthAgent.cppPreInit 호출 시점

구매 플로우

정상적인 상품 구매 시퀀스입니다.

미처리 구매 복구 플로우

결제는 완료됐지만 클라이언트·서버 처리가 끝나지 않은 미처리 구매를 복구하는 시퀀스입니다. OnPurchasesUpdated동일한 Verify → Complete → OnCompletedPurchase 를 수행해야 합니다.

구분구매 플로우복구 플로우
시작 APIStartPurchaseRestore
SDK 콜백OnPurchasesUpdatedOnRestore
선행 조건상품 예약(boid)PreInitOnPrepared(OK), 게임 서버 로그인
완료 처리Verify → Complete → OnCompletedPurchase동일

상세 다이어그램은 빌링 가이드 - PlayStore 결제 연동을 참고하세요.

PlayStore vs Windows PC 결제 차이

항목Android PlayStoreWindows (PG/스팀 등)
SetAvailablePayment자동 설정 (호출 불필요)Mount 전 명시적 지정 필요
커스텀 결제 핸들러불필요스팀·PG·GPC·Xsolla는 필수
결제 UIGoogle Play 결제 모듈스팀 오버레이·외부 브라우저 등
Restore지원PG·Xsolla는 미지원
Init 화면 PG 선택불필요 (Android는 환경·언어만 선택)Windows는 PG 선택 필요

Step 2. Billing Agent 구현

PlayStore 결제 이벤트를 수신하기 위해 HybeBillingAgent를 상속한 USBillingAgent 클래스를 구현합니다.

2-1. 클래스 선언

UnrealStarter/Agent/USBillingAgent.h
class UNREALSTARTER_API USBillingAgent : public HybeBillingAgent
{
public:
virtual void OnPrepared(BillingResultCode result) override;
virtual void OnPurchasesUpdated(std::shared_ptr<Purchase> purchase) override;
virtual void OnPurchasesCanceled() override;
virtual void OnPurchasesError(BillingStatus code, const FString& message) override;
virtual void OnRestore(const TArray<std::shared_ptr<Purchase>>& details) override;

static void PrepareBillingPlatform(const FString& playerId);

private:
static void RegisterPurchaseHandler();
void HandleVerify(std::shared_ptr<Purchase> purchase, const CommonResponse& Response);
void HandleComplete(const FString& boid, const FString& ProductId, const CommonResponse& Response);
};

PlayStore 튜토리얼에서 반드시 이해해야 할 콜백은 다음과 같습니다.

콜백호출 시점게임에서 할 일
OnPreparedPreInit 완료 후결제 가능 상태 확인, Restore 호출 (Step 8)
OnPurchasesUpdatedGoogle Play 결제 완료게임 서버 Verify → Complete → OnCompletedPurchase
OnPurchasesCanceled사용자 결제 취소UI 안내, 예약(boid) 정리
OnRestoreRestore() 후 미처리 구매 발견OnPurchasesUpdated동일한 Verify → Complete → OnCompletedPurchase (필수 구현)

2-2. OnPrepared — 빌링 초기화 완료

UnrealStarter/Agent/USBillingAgent.cpp
void USBillingAgent::OnPrepared(BillingResultCode result)
{
if (result == BillingResultCode::OK)
{
// 빌링 플랫폼 연동 완료
// 권장: 게임 서버 로그인이 끝난 뒤 Restore() 호출 (Step 8 참고)
HybeBillingPlatform::Instance().Restore();
}
else
{
// UNREGISTERED_AGENT, UNSUPPORTED_STORE, FAIL 등 — 로그 확인
}
}
정보

OnPrepared 직후 Restore() 호출

PreInit 성공 직후 미처리 구매를 조회하는 것이 가장 일반적인 패턴입니다. 게임 서버 로그인이 OnPrepared보다 늦게 끝나는 구조라면, 서버 로그인 완료 콜백에서 Restore()를 호출하세요.

2-3. OnPurchasesUpdated — 결제 완료 (핵심)

PlayStore에서는 Google Play 결제 UI가 닫힌 뒤 구매 정보(Purchase)가 전달됩니다. 샘플은 게임 서버에 Verify → Complete 를 요청합니다.

UnrealStarter/Agent/USBillingAgent.cpp
void USBillingAgent::OnPurchasesUpdated(std::shared_ptr<Purchase> purchase)
{
#if PLATFORM_WINDOWS
// PG/Xsolla는 이 튜토리얼 범위 밖 — Android PlayStore는 아래 분기로 진행
PaymentType paymentType = HybeBillingPlatform::Instance().GetPayment();
if (paymentType == PaymentType::PG || paymentType == PaymentType::XSOLLA)
{
return;
}
#endif

UVirtualGameServerSubsystem* Server =
USHelper::GetVirtualGameServerSubsystemGlobal();
if (Server)
{
Server->VerifyPurchase(purchase, [this, purchase](CommonResponse Response)
{
HandleVerify(purchase, Response);
});
}
}

HandleVerify 성공 시 CompletePurchase를 호출하고, HandleComplete에서 SDK에 결과를 알립니다.

UnrealStarter/Agent/USBillingAgent.cpp - HandleComplete
void USBillingAgent::HandleComplete(const FString& boid, const FString& ProductId,
const CommonResponse& Response)
{
if (Response.resultCode == TEXT("SUCCESS"))
{
GetBillingPlatform().OnCompletedPurchase(boid, ProductId, true);
}
else
{
GetBillingPlatform().OnCompletedPurchase(boid, ProductId, false);
}
}
정보

PlayStore 소비 처리

Android에서는 영수증 소비(consume) 가 주로 서버 측 결제 프로세스에서 처리됩니다. 클라이언트는 수신한 영수증을 게임 서버에 전달하여 소비요청 완료 후 OnCompletedPurchase로 SDK에 결과를 전달해야합니다. iOS App Store와 달리 앱에서의 소비 처리 의무는 상대적으로 낮지만, 서버 완료 후 OnCompletedPurchase(true) 호출은 권장됩니다. (빌링 개발 연동 체크리스트)

2-4. OnRestore — 미처리 구매 복구 (필수)

Restore() 호출 시 SDK가 Google Play에 남아 있는 미처리 구매를 조회하고, 건별로 OnRestorePurchase 배열을 전달합니다. 구현 내용은 OnPurchasesUpdated와 동일해야 합니다.

UnrealStarter/Agent/USBillingAgent.cpp
void USBillingAgent::OnRestore(const TArray<std::shared_ptr<Purchase>>& details)
{
for (const auto& purchase : details)
{
// OnPurchasesUpdated와 동일: Verify → Complete → OnCompletedPurchase
OnPurchasesUpdated(purchase);
}
}
Purchase 필드복구 시 활용
boid예약 ID — 서버 Verify/Complete에 전달
productId상품 ID — OnCompletedPurchase에 전달
playerId구매 당시 유저 ID — PreInit에 등록한 결제용 유니크 ID와 일치해야 함
details영수증·서명 등 — 서버 검증에 사용
위험

OnRestore를 비우거나 OnPurchasesUpdated와 다른 로직을 쓰면 안 됩니다

미처리 구매가 반복 누적되고, 유저는 결제했지만 아이템을 받지 못하는 상태가 지속됩니다. 출시 전 반드시 복구 시나리오를 테스트하세요.

자세한 시나리오·호출 시점·서버 협업은 Step 8을 참고하세요.


Step 3. Mount 시 Billing Agent 등록

플랫폼 SDK Mount() 이전에 Billing Agent를 등록해야 합니다.

3-1. Init 화면 — Android 선택 사항

Android에서는 Windows와 달리 결제 플랫폼(PG) 버튼 선택이 필요 없습니다. Init 화면에서 환경(QA)언어만 선택한 뒤 Apply를 누릅니다.

Init UI 선택샘플 코드API 반영
환경 버튼OnConfig_ButtonClickedCachedConfigMount(configJson)BuildEnv, ProjectId, WebClientId
언어 버튼OnLanguage_ButtonClickedCachedLanguageSetClientLanguage

멤버십 튜토리얼의 Init UI 매핑을 참고하세요.

3-2. MountSDK() 내 빌링 등록

UnrealStarter/UI/Page/InitPage.cpp - MountSDK() (발췌)
void UInitPage::MountSDK()
{
// ... Auth/Webview Agent 등록, SetWritableStorageDirectory, SetClientLanguage ...

HybePlatformAgent::Instance().SetPlatformBillingAgent(new USBillingAgent());

#if PLATFORM_WINDOWS
// Windows 전용: 결제 수단 명시. Android PlayStore는 자동 설정.
HybeBillingPlatform::SetAvailablePayment(CachedPaymentType);
#endif

HybePlatformAgent::Instance().Mount(JsonString);
}
APIAndroid PlayStore에서의 역할
SetPlatformBillingAgent필수 — 결제 이벤트 수신자 등록
SetAvailablePayment불필요 — 모바일은 SDK가 GOOGLE_PLAY 자동 설정 (빌링 가이드)
RegisterPgPurchaseHandler불필요 — PlayStore는 커스텀 핸들러 없음

이 단계 완료 기준


Step 4. 빌링 플랫폼 초기화 (PreInit)

PreInit은 결제 모듈을 초기화할 때 현재 플레이어의 유니크 ID를 SDK에 등록하는 API입니다. 이 문서에서는 SDK 파라미터명 playerid결제용 유니크 ID로 부릅니다.

용어설명
결제용 유니크 ID (playerid)게임 서버가 빌링 백엔드 API 호출 시 사용하는 동일한 유저 식별자
샘플에서의 값플랫폼 로그인 JSON의 imid (GetClientContext().GetImid())
필수 여부imid 문자열 자체가 필수는 아님. 서버·백엔드와 같은 ID 체계가 필수

게임 서버 로그인이 끝나 위 ID를 확정한 뒤 PreInit을 호출합니다. UnrealStarter 샘플은 토큰 갱신 시점에 imid를 넘깁니다.

경고

클라이언트 ID ≠ 서버 ID 이면 결제가 깨집니다

예: PreInit에는 플랫폼 imid를 넣었는데, 게임 서버 예약 API에는 자체 characterId만 보내는 경우 — 예약·결제·지급·복구가 서로 다른 유저로 처리될 수 있습니다. 게임 서버 담당자와 빌링 백엔드에 쓰는 유저 ID 하나로 통일하세요.

4-1. 호출 시점

UnrealStarter/Agent/USPlatformAuthAgent.cpp - OnRefreshVerifyToken
void USPlatformAuthAgent::OnRefreshVerifyToken(const FString& token)
{
// ... loginToken 저장 ...
// 샘플: 게임 서버·빌링 백엔드와 동일한 ID로 imid 사용
USBillingAgent::PrepareBillingPlatform(GetClientContext().GetImid());
}
UnrealStarter/Agent/USBillingAgent.cpp - PrepareBillingPlatform
void USBillingAgent::PrepareBillingPlatform(const FString& playerId)
{
#if PLATFORM_WINDOWS
RegisterPurchaseHandler(); // Android PlayStore에서는 no-op
#endif
GetBillingPlatform().PreInit(playerId); // → OnPrepared
}
항목내용
호출 시점게임 서버 연동 이후, 플레이 가능 상태(로비·마을 진입 등). ID가 확정된 뒤
인자 playerId게임 서버가 빌링 백엔드 예약·검증 API에 넣는 유저 ID와 동일한 값. 샘플은 imid
계정 변경 시다른 계정 로그인 후 ID가 바뀌면 반드시 PreInit 재호출
정보

imid는 샘플의 선택

OnLoginSuccess JSON의 imid가 곧 플랫폼·게임 서버·빌링 백엔드에서 쓰는 유저 ID인 QA 환경에서는 그대로 PreInit에 넘기면 됩니다. 자체 게임에서 서버가 다른 키(예: 내부 accountId)를 쓰면, 서버와 합의된 그 값PreInit에 전달하세요. (빌링 가이드 - playerid 란?)

경고

OnLoginSuccess 직후 곧바로 PreInit을 호출하지 마세요. 샘플은 게임 서버 토큰 갱신(RefreshVerifyToken) 이후에 호출합니다. 실제 게임에서도 서버 로그인 완료 후 초기화하는 것이 안전합니다.

4-2. 샘플 앱에서 확인

  1. Google 로그인 후 Lobby 화면으로 진입합니다.
  2. Account 화면에서 토큰 갱신을 실행하거나, 샘플이 자동으로 RefreshVerifyToken을 호출하는 흐름을 따릅니다.
  3. Output Log에서 OnPrepared / 빌링 초기화 성공 로그를 확인합니다.

Step 5. 상품 조회 (QueryProductDetails)

상점 UI에 표시할 상품명·가격·통화 정보를 Google Play에서 조회합니다.

5-1. 상품 ID 목록

샘플 StorePage는 QA 환경에 등록된 상품 ID 배열을 사용합니다. 실제 프로젝트에서는 기술 PM·백오피스에서 전달받은 SKU를 사용하세요.

UnrealStarter/UI/Page/StorePage.cpp (개념)
TArray<FString> ProductIds = { TEXT("item_01"), TEXT("item_02") /* ... */ };

HybeBillingPlatform::Instance().QueryProductDetails(ProductIds,
[this](bool bSuccess)
{
if (bSuccess)
{
FetchAndDisplayItems(); // HybeProductManager에서 캐시 읽기
}
});

5-2. 캐시에서 상품 정보 읽기

조회 성공 시 상품은 HybeProductManager에 캐싱됩니다.

StorePage.cpp (개념)
ProductDetail detail = HybeProductManager::GetProduct(TEXT("item_01"));
if (detail.IsPurchasable())
{
// detail.GetFormattedPrice(), GetName() 등으로 UI 구성
}
정보

GetProduct로 캐시에 없는 상품이면 QueryProductDetails를 호출해 서버 트래픽을 최소화하세요. (빌링 개발 연동 체크리스트)

5-3. 구매 가능 상태 확인

StorePage.cpp
auto billingStatus = HybeBillingPlatform::Instance().CanPurchable();
if (billingStatus != HybePlatform::BillingStatus::OK)
{
// BILLING_SERVICE_DISCONNECTED, SERVICE_UNAVAILABLE 등 — 사용자 안내
}

Step 6. 상품 구매 (StartPurchase)

PlayStore 구매는 예약 → 구매 요청 → Google Play UI → 완료 처리 순서입니다.

6-1. 게임 서버 상품 예약 (boid)

구매 전 게임 서버(샘플: VirtualGameServerSubsystem)에 상품 예약을 요청하고 예약 ID(boid) 를 받습니다. 예약 API에도 PreInit과 동일한 결제용 유니크 ID가 사용됩니다. 샘플 가상 서버는 로그인·예약·검증 HTTP에 동일한 imid(또는 loginToken과 연계된 계정)를 전제로 동작합니다.

StorePage.cpp (개념)
Server->StartPurchaseAsync(productId, 1, 0,
[this, productId](ReserveResponse Response)
{
if (Response.bSuccess)
{
HandleStartPurchaseResponse(productId, Response.boid, Response.paymentUrl);
}
});
단계주체설명
예약게임 서버 → 빌링 백엔드boid 발급. 요청 시 결제용 유니크 ID 사용 (샘플: imid). PlayStore는 paymentUrl 불필요
구매클라이언트 → SDKStartPurchase(productId, boid). SDK는 PreInit에 등록된 동일 유저 컨텍스트로 결제 진행

6-2. 구매 요청

StorePage.cpp - HandleStartPurchaseResponse (개념)
void UStorePage::HandleStartPurchaseResponse(const FString& productId,
const FString& boid, const FString& paymentUrl)
{
// PlayStore: paymentUrl 없이 boid만 전달
HybeBillingPlatform::Instance().StartPurchase(productId, boid);
}

Google Play 결제 UI가 표시됩니다. 사용자가 결제를 완료하면 OnPurchasesUpdated가 호출됩니다.

6-3. 결제 취소

사용자가 Google Play 결제 창에서 취소하면 OnPurchasesCanceled가 호출됩니다. 예약된 boid에 대한 서버 측 정리 정책은 게임 서버 스펙을 따르세요.


Step 7. 샘플 앱에서 따라 하기

아래 순서로 UnrealStarter Android 빌드를 실행하며 PlayStore 결제를 확인합니다.

7-1. 초기화·로그인

  1. 앱 실행 → Init 화면에서 QA 환경·언어 선택 → Apply
  2. Google 로그인 (사이닝·WebClientId·내부 테스트 설치 확인)
  3. Lobby 화면 진입 확인

7-2. 빌링 초기화

  1. Account토큰 갱신 (또는 샘플 자동 갱신 흐름)
  2. PreInitOnPrepared(OK) 확인

7-3. 상점에서 구매

  1. LobbyStore 이동
  2. 상품 목록·가격이 표시되는지 확인 (QueryProductDetails 성공)
  3. 상품 선택 → 구매
  4. Google Play 결제 UI에서 테스트 결제 완료
  5. 게임 내 구매 완료 안내·아이템 지급(샘플 UI) 확인

Step 8. 미처리 구매 복구 (Restore) — 필수

구매(StartPurchase) 연동만으로는 부족합니다. 네트워크 끊김, 앱 강제 종료, Google·서버 일시 장애 등으로 결제는 됐지만 아이템 지급이 안 된 미처리 구매가 실제 서비스에서 자주 발생합니다. PlayStore는 Restore를 지원하므로, 출시 전 구매와 동등한 우선순위로 복구 플로우를 구현해야 합니다.

상세 배경은 빌링 가이드 - 소비처리되지 않은 구매 복구, 빌링 개발 연동 체크리스트를 참고하세요.

8-1. 미처리 구매가 생기는 대표 시나리오

시나리오결제앱 영수증 수신게임 서버 전달대응
A. 영수증 미수신완료실패 (앱 종료·크래시)실패로그인 후 Restore()OnRestore → 서버 Verify
B. 서버 전달 실패완료성공실패 (네트워크)Restore()로 재전달 또는 서버 미완료 건 처리
C. 서버 처리 성공완료성공성공서버가 로그인 시 미완료 건 점검. 클라이언트 Restore()는 빈 결과 가능
D. Complete 후 OnCompletedPurchase 누락완료성공지급 완료Restore() 시 이미 지급 건이 올 수 있음 → 서버가 "이미 지급" 응답 시에도 OnCompletedPurchase(true) 호출
경고

클라이언트 Restore() 전에 서버 확인 (권장 순서)

  1. 게임 서버 로그인 완료
  2. 서버가 해당 계정의 미완료 결제 건을 조회·처리 (빌링 개발 연동 - Android)
  3. 클라이언트에서 Restore() 호출 — 서버로 영수증이 전달되지 않은 건을 SDK·Google Play에서 가져옴

PlayStore는 영수증이 서버에 한 번 전달된 뒤에는 서버·빌링 백엔드가 결제를 끝내야 합니다. 클라이언트 Restore()앱이 영수증을 받지 못한 경우의 안전망입니다.

8-2. Restore 호출 시점 (권장)

아래 최소 한 곳 이상에서 Restore()를 호출하도록 구현하세요.

시점설명샘플 참고
OnPrepared(OK) 직후PreInit 성공 후 가장 먼저 미처리 건 조회USBillingAgent::OnPrepared
로비·상점 진입 시플레이 가능 상태에서 재조회StorePage 진입 시
수동 복구 UI설정·상점에 구매 복구 버튼 제공 (체크리스트 권장)StorePage::OnRestore_ButtonClicked
재로그인 후계정 변경·PreInit 재호출 이후OnRefreshVerifyTokenPreInitRestore
정보

호출 조건

  • PreInit 완료 후 OnPrepared(OK)를 받은 뒤에만 호출
  • 게임 서버에 구매 완료(Verify/Complete)를 요청할 수 있는 상태여야 함
  • OnRestore 처리 중에는 중복 Restore() 호출을 피하도록 플래그·큐 관리 권장

8-3. 구현 — Restore() 호출

StorePage.cpp
void UStorePage::OnRestore_ButtonClicked()
{
HybeBillingPlatform::Instance().Restore();
}

자동 호출 예시 (OnPrepared):

USBillingAgent.cpp
void USBillingAgent::OnPrepared(BillingResultCode result)
{
if (result == BillingResultCode::OK)
{
HybeBillingPlatform::Instance().Restore();
}
}

8-4. 구현 — OnRestore 처리

OnRestore로 받은 각 Purchase에 대해 구매 완료와 동일한 서버 연동을 수행합니다.

USBillingAgent.cpp - OnRestore (발췌)
void USBillingAgent::OnRestore(const TArray<std::shared_ptr<Purchase>>& details)
{
if (details.Num() == 0)
{
return; // 미처리 구매 없음
}

for (const auto& purchase : details)
{
// 구매 완료와 동일한 경로 — 반드시 공통 함수로 묶는 것을 권장
ProcessPurchaseCompletion(purchase);
}
}

void USBillingAgent::ProcessPurchaseCompletion(std::shared_ptr<Purchase> purchase)
{
Server->VerifyPurchase(purchase, [this, purchase](CommonResponse Response)
{
HandleVerify(purchase, Response);
});
}

OnPurchasesUpdated에서도 ProcessPurchaseCompletion을 호출하면 구매·복구 로직이 한곳에 모여 유지보수가 쉬워집니다.

8-5. 서버·클라이언트 역할 분담 (PlayStore)

단계Android PlayStore
결제용 유니크 IDPreInit·서버 예약·검증 API 동일 ID (샘플: imid)
영수증 소비(consume)주로 서버 결제 프로세스에서 처리
클라이언트 OnCompletedPurchase서버 완료 후 권장 — SDK에 처리 결과 전달
Restore() 역할앱이 받지 못한 미처리 영수증을 Google Play에서 다시 가져옴
서버 로그인 시미완료 결제 건 서버 측 선행 처리

8-6. 샘플 앱에서 복구 테스트

구매 연동(Step 7) 후 반드시 아래를 수행하세요.

  1. 테스트 구매를 한 뒤, OnPurchasesUpdated 처리 직전에 앱을 강제 종료하거나 네트워크를 끊어 영수증 전달을 실패시킵니다.
  2. 앱을 다시 실행 → Google 로그인 → 동일 유저 ID로 토큰 갱신(PreInit)까지 진행합니다.
  3. Store → 복구(Restore) 버튼을 누르거나, OnPrepared 후 자동 Restore()가 동작하는지 확인합니다.
  4. OnRestore → Verify → Complete → OnCompletedPurchase(true) 로그·아이템 지급을 확인합니다.
정보

복구 대상이 없을 때

미처리 구매가 없으면 OnRestore빈 배열이 전달되거나 콜백이 호출되지 않을 수 있습니다. 이는 정상 동작입니다.

8-7. 자주 하는 실수

실수결과
Restore() 미구현결제만 되고 아이템 미지급 — CS·환불 증가
OnRestore에서 OnPurchasesUpdated와 다른 처리일부 구매만 복구 실패
PreInitRestore() 호출복구 실패 또는 예기치 않은 동작
서버 완료 없이 OnCompletedPurchase(true)상태 불일치
재로그인 후 PreInit만 하고 Restore 생략이전 계정 미처리 건 누락
복구 UI 없음유저가 스스로 복구할 수 없음

이 단계 완료 기준

  • OnPrepared(OK) 또는 상점 진입 시 Restore() 호출 구현
  • OnRestoreOnPurchasesUpdated동일한 Verify → Complete → OnCompletedPurchase 수행
  • 수동 구매 복구 버튼(또는 설정 메뉴) 제공
  • 강제 종료·네트워크 차단 시나리오로 복구 테스트 완료

검증 체크리스트

튜토리얼을 완료했는지 아래 항목을 확인하세요.

  • Play Console 내부 테스트 빌드로 앱이 설치되어 있다.
  • Init 화면에서 QA 환경·언어 선택 후 Apply·Mount 성공
  • Google 로그인 성공, 결제용 유니크 ID 확보 (샘플: imid, 서버·백엔드와 동일 ID인지 확인)
  • 토큰 갱신 후 PreInit(동일 유저 ID)OnPrepared(OK) 수신
  • Store에서 상품명·가격이 표시된다 (QueryProductDetails 성공)
  • Google Play 결제 UI에서 테스트 구매 완료
  • OnPurchasesUpdated → 서버 Verify/Complete → OnCompletedPurchase(true) 흐름 확인
  • Restore() 호출 시점 구현 (OnPrepared 직후 또는 상점 진입)
  • OnRestore 가 구매 완료와 동일한 Verify → Complete → OnCompletedPurchase 수행
  • 구매 복구 UI(Store 복구 버튼 등) 제공
  • 강제 종료·네트워크 차단 후 복구 시나리오 테스트 완료

자주 발생하는 문제 (Android + PlayStore)

결제 창이 나오지 않음

  • Play Console 업로드 versionCode와 기기 설치 버전이 일치하는지 확인
  • 내부 테스트 링크로 설치했는지 확인 (sideload APK 비권장)
  • 빌링 가이드 - Troubleshooting 참고

테스트 카드 결제 실패

  • Play Console → 설정 → 라이선스 테스트에 계정 등록 여부 확인

구입 상품 정보를 조회할 수 없음

  • 내부 테스트 테스터 참여 링크에서 수락했는지 확인
  • Play Console → 테스트 → 내부 테스트 → 테스트 참여 방법 링크 사용

OnPrepared 실패 (UNREGISTERED_AGENT 등)

Google 로그인 실패 (Login cancel)

구매 후 아이템 미지급

  • OnPurchasesUpdated에서 게임 서버 Verify/Complete 호출 여부
  • 서버에 영수증 전달 후 OnCompletedPurchase(true) 호출 여부
  • 서버 로그·빌링 백엔드 예약(boid) 상태 확인
  • Restore() 구현·호출 여부 — 미처리 구매가 쌓였는지 Logcat에서 OnRestore 확인

복구(Restore)가 동작하지 않음

  • PreInitOnPrepared(OK) 이후Restore()를 호출했는지 확인
  • 게임 서버 로그인이 끝나기 Restore()를 호출하지 않았는지 확인
  • OnRestore가 비어 있거나 OnPurchasesUpdated와 다른 코드 경로를 쓰지 않았는지 확인
  • 이미 서버에 영수증이 전달된 건은 서버 측 미완료 처리가 필요할 수 있음 (Step 8-1)

기타

더 많은 문제 해결 방법은 빌링 가이드 - Troubleshooting을 참고하세요.


다음에 읽을 문서

문서내용
Unreal Engine 빌링스팀·PG·iOS·원스토어 등 전 결제 수단, API Reference
Unreal Engine 멤버십 튜토리얼Windows Google 로그인 실습 (멤버십 Part 1)
Unreal Engine 멤버십Android/iOS 로그인 상세, API Reference
빌링 개발 연동 체크리스트출시 전 빌링 연동 점검
빌링 체크리스트빌링 정책·연령 확인

핵심 소스 파일 요약

파일빌링 튜토리얼에서의 역할
UnrealStarter/Agent/USBillingAgent.*OnPrepared, OnPurchasesUpdated, OnRestore, Verify/Complete
UnrealStarter/UI/Page/StorePage.cppQueryProductDetails, 예약, StartPurchase, Restore
UnrealStarter/UI/Page/InitPage.cppSetPlatformBillingAgent, Mount()
UnrealStarter/Agent/USPlatformAuthAgent.cppPrepareBillingPlatform 호출 시점
UnrealStarter/Helper/VirtualGameServerSubsystem.*샘플 전용 상품 예약·검증·완료 HTTP
UnrealStarter/UI/Component/USItemPurchasePopup.cpp(Windows PG용) Android PlayStore 실습에서는 미사용
Config/Firebase/google-services.jsonAndroid Firebase·Google 로그인
Content/.../drimage_config.jsonQA/dev 환경·WebClientId
Plugins/HybePlatform/플랫폼 SDK 언리얼 플러그인 (Play Billing 포함)