기본 스토어 운영
게임 내 스토어는 일반적으로 다음 세 가지 기본 작업과 관련이 있습니다.
이 문서에서는 각 작업과 관련된 샘플 코드에 대해 설명합니다. 이것은 모범 사례를 반영하도록 지속적으로 업데이트되는 InGameStore 샘플에서 얻은 것입니다.
XStore API 호출 준비
모든 XStore
API는 XStoreCreateContext를 사용하여 생성된 XStoreContextHandle 상에서 작동합니다.
이 컨텍스트를 사용하면 본체에서 지정된 사용자와 PC에서 사용할 수 있는 기본 사용자의 컨텍스트에서 스토어 작업을 수행할 수 있습니다. 본체에서 컨텍스트는 일시 중단 또는 신속한 재개 이벤트 후에 무효화됩니다. 해당 조건을 안전하게 처리하려면 일시 중단 상태에서 게임이 다시 시작될 때마다 XStoreContextHandle을 닫고 다시 만드는 것이 좋습니다.
1. 구매할 수 있는 항목 확인
게임이 일반적으로 구입을 위해 제공하는 것은 추가 기능입니다. 다음 코드는 게임에서 사용 가능한 제품을 파악하는 데 필요한 기본 XStoreQueryAssociatedProductsAsync API 호출에 대해 설명합니다.
게임과 관련된 구매할 수 있는 추가 기능만 이 쿼리에서 자동으로 반환됩니다. 동일한 게시자와 연결된 관련 없는 제품은(동일한 파트너 센터 계정으로 구성됨) 역시 해당 게임이 파트너 센터의 제품 관계 설정 섹션에서 이 제품에 대해 “판매 가능” 관계로 설정되어 있는 한, 이 호출에서 반환하도록 만들 수 있습니다.
bool CALLBACK ProductEnumerationCallback(const XStoreProduct* product, void* context)
{
// Handle adding the product to the game
printf("%s %s %u\n", product->title, product->storeId, product->productKind);
return true;
}
void QueryCatalog()
{
auto async = new XAsyncBlock{};
async->queue = m_asyncQueue;
async->callback = [](XAsyncBlock* async)
{
XStoreProductQueryHandle queryHandle = nullptr;
HRESULT hr = XStoreQueryAssociatedProductsResult(async, &queryHandle);
if (SUCCEEDED(hr))
{
hr = XStoreEnumerateProductsQuery(queryHandle, async->context, ProductEnumerationCallback);
if (SUCCEEDED(hr))
{
printf("Enumeration complete\n");
}
XStoreCloseProductsQueryHandle(queryHandle);
delete async;
}
};
XStoreProductKind typeFilter =
XStoreProductKind::Consumable |
XStoreProductKind::Durable |
XStoreProductKind::Game;
HRESULT hr = XStoreQueryAssociatedProductsAsync(
m_xStoreContext,
typeFilter,
UINT8_MAX, // placeholder maximum, see Paging
async)
if (FAILED(hr))
{
delete async;
}
}
참고 사항
- 구매 가능한 제품만
XStoreQueryAssociatedProductsAsync
과(와) 함께 반환됩니다. 번들로만 허가되거나 독립적으로 구매 가능하도록 설정되지 않은 제품은 반환되지 않습니다.XStoreQueryProductsAsync
을(를) 사용하는 해당 제품은 아래를 참조하세요. - 반환되는 제품 수에 대한 사전 지식이 없으므로, 카운트를 누적해야 합니다.
페이징
이제 페이징 처리가 선택 사항입니다. 카탈로그의 예상 수명 크기보다 큰 최대값을 선택합니다. 대규모 카탈로그 또는 결과 처리를 분할하여 진행률을 표시하려는 경우 페이징 검색 및 처리를 구현할 수 있습니다. 자세한 내용은 XStoreQueryAssociatedProductsAsync 를 참조하세요.
기타 옵션
Microsoft Store ID를 알 수 없거나 다른 actionFilters
을(를) 원하는 경우 XStoreQueryProductsAsync를 사용하여 특정 제품을 쿼리할 수 있습니다.
"작업"은 구매, 라이선스, 기프트 및 사용과 같은 동사를 포함하는 제품에 적용되는 사용 시나리오 입니다.
XStoreQueryAssociatedProductsForStoreIdAsync 를 사용하여 연결된 제품에서 다른 게임을 쿼리할 수 있습니다. 예를 들어 다른 타이틀의 추가 기능을 교차 판매하는 데 유용할 수 있습니다.
XStoreQueryProductForCurrentGameAsync는 현재 실행 중인 게임에 대한 제품만 쿼리하는 것입니다.
XStoreShowAssociatedProductsUIAsync는 사용자를 Microsoft Store 앱의 제품 종류별로 필터링할 수 있는 관련 제품 보기로 전환합니다. 이는 게임 내 인터페이스에 제공될 사용 가능한 제품을 열거해야 하는 대안이 될 수 있습니다.
2. 소유하고 있는 제품 평가
여기에는 기본적으로 위의 코드와 동일한 코드가 포함됩니다.
- XStoreQueryAssociatedProductsAsync → XStoreQueryEntitledProductsAsync
- XStoreQueryAssociatedProductsResult → XStoreQueryEntitledProductsResult
이러한 API를 통해 결과는 호출하는 사용자의 계정에 의해 특별히 권한이 부여된 제품으로 구성됩니다. 권한을 부여 받는 것은 직접 소유한다는 것을 의미할 수 있지만 상위 번들 또는 구독을 소유함으로써 충족된다는 것을 의미할 수도 있습니다.
XStoreProduct에는 자격이 있을 때 true로 설정된 필드가 포함되어 isInUserCollection
있으므로 XStoreQueryAssociatedProductsAsync(및 관련 함수)의 결과에 따라 확인할 수도 있습니다.
소모품 소유권
소모품 수량은 XStoreProduct.skus[i].collectionData.quantity
에 나와 있습니다.
일반적으로 소모품에 대해서는 하나의 SKU만 있습니다.
수량은 XStoreQueryConsumableBalanceRemainingAsync를 사용하여 개별적으로 문의할 수 있지만, 서비스 호출이 발생할 수 있으므로 이것은 다수의 소모품에 대해 개별적으로는 권장되지 않습니다.
또한 소모성 기반 에코시스템의 무결성을 유지하려면 서비스 유효성 검사 및 소모성 제품 상환을 활용하는 것이 좋습니다. 자세한 정보는 소모성 기반 에코시스템을 참조하세요.
지속성 있는 소유권
인게임 제품을 사용할 수 있는 권한이 있는지 확인하기 위해 계정이 제품을 소유하고 있는지 확인하는 것만으로는 충분하지 않습니다. 지속성 콘텐츠는 게임용 제품 공유 모델에 설명된 콘텐츠 공유 정책을 준수해야 합니다.
XStoreAcquireLicenseForPackageAsync는 패키지가 있는 지속성 콘텐츠에 사용되어 콘텐츠 공유 규정에 따라 라이선스를 부여받을 수 있는지 여부를 결정합니다.
XStoreAcquireLicenseForDurablesAsync는 패키지가 없는 지속성 콘텐츠에 대해서도 동일한 작업을 수행하는 데 사용됩니다.
XStoreQueryAddOnLicensesAsync는 디지털로 라이선스를 받은 게임에 사용하여 라이선스 부여 가능한 지속성 콘텐츠(패키지 없는)를 반환할 수 있습니다.
자세한 내용은 다운로드 가능한 콘텐츠 관리 및 라이선스 그리고 패키지 없이 지속성 콘텐츠를 사용하는 법에서 확인할 수 있습니다.
3. 적격 제품 구입
구매 가능한 제품에 대한 구입 흐름을 표시하는 것은 Microsoft Store ID를 XStoreShowPurchaseUIAsync API에 전달하는 것만큼 간단합니다.
void MakePurchase(const char* storeId)
{
auto async = new XAsyncBlock{};
async->context = &storeId;
async->queue = m_asyncQueue;
async->callback = [](XAsyncBlock *async)
{
const char* = reinterpret_cast<const char*>(async->context);
HRESULT hr = XStoreShowPurchaseUIResult(async);
if (SUCCEEDED(hr))
{
printf("Purchase succeeded (%s)\n", storeId);
// Refresh ownership and update game
}
else
{
printf("Purchase failed (%s) 0x%x\n", storeId, hr);
if (hr == E_GAMESTORE_ALREADY_PURCHASED)
{
printf("Already own this\n");
}
}
delete async;
};
HRESULT hr = XStoreShowPurchaseUIAsync(
m_xStoreContext,
storeId,
nullptr, // Can be used to override the title bar text
nullptr, // Can be used to provide extra details to purchase
async);
if (FAILED(hr))
{
delete async;
printf("Error calling XStoreShowPurchaseUIAsync : 0x%x\n", hr);
return;
}
}
비동기 콜백에서 잠재적인 구입 결과를 처리할 뿐만 아니라 Microsoft Store로 명시적으로 전환하여 게임 외부에서 구입할 수도 있습니다. 이들은 또한 Xbox.com, PC, 모바일 앱 또는 기타 아울렛에서도 만들어질 수 있습니다. 게임 내 스토어로 전환하거나 설정의 어딘가에서만 초기 로그인 흐름의 일부분이 아니라 주문형 제품 소유권을 안정적으로 새로 고치는 장소가 게임에 있는 것이 좋습니다.