Play 결제를 사용하여 디지털 제품을 판매하여 게임으로 수익을 창출하세요. SDK는 구매할 수 있는 제품을 표시하고, 구매 흐름을 시작하고, 구매를 처리하는 API를 제공합니다. 이러한 결제 API 호출은 Google Play 게임즈 클라이언트 내에서 게임을 실행한 Google 계정을 사용하여 실행되며 추가 로그인 단계가 필요하지 않습니다.
Android Play 결제 라이브러리와 통합한 경우 이러한 Play 결제 API가 익숙할 것입니다. Play 결제와의 서버 측 통합은 Android와 PC에서 동일하므로 PC 타이틀에서 재사용할 수 있습니다.
기본 요건
SDK 설정을 완료합니다.
Google Play 결제 시스템 개요를 읽어 보세요.
Play 결제 설정을 완료합니다.
1단계: 이전 구매 및 애플리케이션 외부에서 완료된 구매 쿼리
애플리케이션이 시작되거나 포그라운드로 다시 진입할 때 구매를 쿼리합니다. 이는 게임 외부에서 발생한 구매를 감지하거나 사용자가 이전에 구매한 항목에 대한 액세스 권한을 잠금 해제하는 데 필요합니다.
BillingClient::QueryPurchases
를 사용하여 구매를 쿼리합니다.구매를 처리하여 계속 진행합니다.
// Query for purchases when:
// - Application starts up
// - Application window re-enters the foreground
auto promise = std::make_shared<std::promise<QueryPurchasesResult>>();
billing_client.QueryPurchases([promise](QueryPurchasesResult result) {
promise->set_value(std::move(result));
});
auto query_purchases_result = promise->get_future().get();
if (query_purchases_result.ok()) {
auto purchases = query_purchases_result.value().product_purchase_details;
// Process the purchases
} else {
// Handle the error
}
2단계: 구매할 수 있는 제품 표시
이제 구매 가능한 제품을 쿼리하여 사용자에게 표시할 준비가 된 것입니다. 제품 세부정보 쿼리는 현지화된 제품 정보를 반환하므로 사용자에게 제품을 표시하기에 앞서 진행해야 하는 중요한 단계입니다.
판매할 제품을 제공하기 전에 사용자가 그 제품을 이미 소유하고 있지 않은지 확인합니다. 사용자의 구매 내역 중 아직 소비되지 않은 소비성 항목이 있는 경우 사용자가 다시 구매하기 전에 먼저 제품을 소비해야 합니다.
BillingClient::QueryProductDetails
를 사용하여 제품 세부정보를 쿼리합니다. Google Play Console 내에서 등록한 제품 ID를 전달합니다.- 제품의 현지화된 이름 및 제품 가격이 포함된
ProductDetails
를 렌더링합니다. - 제품의
offer_token
에 대한 참조를 유지합니다. 혜택의 구매 흐름을 시작하는 데 사용됩니다.
QueryProductDetailsParams params;
params.product_ids.push_back({"example_costmetic_1", ProductType::kTypeInApp});
params.product_ids.push_back({"example_costmetic_1", ProductType::kTypeInApp});
params.product_ids.push_back({"example_battle_pass", ProductType::kTypeInApp});
auto promise = std::make_shared<std::promise<QueryProductDetailsResult>>();
billing_client.QueryProductDetails(params, [promise](QueryProductDetailsResult result) {
promise->set_value(std::move(result));
});
auto query_product_details_result = promise->get_future().get();
if (query_product_details_result.ok()) {
auto product_details = query_product_details_result.value().product_details;
// Display the available products and their offers to the user
} else {
// Handle the error
}
3단계: 구매 흐름 시작
사용자가 제품 구매 의도를 보이면 구매 흐름을 시작할 준비가 되었다고 알립니다.
- 먼저
BillingClient::LaunchPurchaseFlow()
를 호출합니다. 제품 세부정보를 쿼리할 때 가져온offer_token
를 전달합니다. - 구매가 완료되면 연속 함수가 결과와 함께 호출됩니다.
- 성공하면 연속 내에
ProductPurchaseDetails
가 포함됩니다. 구매를 처리하여 계속 진행합니다.
LaunchPurchaseFlowParams params { product_offer.offer_token };
auto promise = std::make_shared<std::promise<LaunchPurchaseFlowResult>>();
billing_client.LaunchPurchaseFlow(params, [promise](LaunchPurchaseFlowResult result) {
promise->set_value(std::move(result));
});
// The purchase flow has started and is now in progress.
auto launch_purchase_flow_result = promise->get_future().get();
// The purchase flow has now completed.
if (launch_purchase_flow_result.ok()) {
auto purchase = launch_purchase_flow_result.value().product_purchase_details;
// Process the purchase
} else if (launch_purchase_flow_result.code() == BillingError::kUserCanceled) {
// Handle an error caused by the user canceling the purchase flow
} else {
// Handle any other error codes
}
4단계: 구매 처리
백엔드 서버로 처리
백엔드 서버가 있는 게임의 경우 백엔드 서버에 purchase_token
를 전송하여 처리를 완료합니다. 서버 측 Play 결제 API를 사용하여 나머지 처리를 완료합니다. 이 서버 측 통합은 Play 결제와 통합된 Android 게임에서 수행하는 것과 동일합니다.
void ProcessPurchasesWithServer(std::vector<ProductPurchaseDetails> purchases) {
std::vector<std::string> purchase_tokens;
for (const auto& purchase : purchases) {
purchase_tokens.push_back(purchase.purchase_token);
}
// Send purchase tokens to backend server for processing
}
백엔드 서버 없이 처리
ProductPurchaseDetails::purchase_state
가PurchaseState::kPurchaseStatePurchased
인지 확인하여 사용자의 결제가 대기 중인지 확인합니다. 구매 상태가 대기 중인 경우 사용자에게 구매한 제품을 수령하려면 추가 단계를 완료해야 한다고 알립니다.사용자에게 구매한 제품에 대한 액세스 권한을 부여하고 게임의 사용 권한 저장소를 업데이트합니다.
비소비성 구매 (한 번만 구매할 수 있는 제품)의 경우
ProductPurchaseDetails::is_acknowledged
를 사용하여 구매가 이미 확인되었는지 확인합니다.- 구매가 확인되지 않은 경우
BillingClient::AcknowledgePurchase
를 호출하여 사용자에게 제품 사용 권한이 부여되고 있다고 Google에 알립니다.
- 구매가 확인되지 않은 경우
소비성 구매 (두 번 이상 구매할 수 있는 제품)의 경우
BillingClient::ConsumePurchase
를 호출하여 사용자에게 제품 사용 권한이 부여되고 있음을 Google에 알립니다.
void ProcessPurchasesWithoutServer(std::vector<ProductPurchaseDetails> purchases) {
std::vector<std::string> entitled_product_ids;
for (const auto& purchase : purchases) {
auto was_successful = ProcessPurchasePurchaseWithoutServer(purchase);
if (was_successful) {
entitled_product_ids.push_back(purchase.product_id);
}
}
// Note that non-consumable products that were previously purchased may have
// been refunded. These purchases will stop being returned by
// `QueryPurchases()`. If your game has given a user access to one of these
// products storage they should be revoked.
//
// ...
}
bool ProcessPurchasePurchaseWithoutServer(ProductPurchaseDetails purchase) {
auto is_purchase_completed =
purchase.purchase_state == PurchaseState::kPurchaseStatePurchased;
if (!is_purchase_completed) {
// Notify the user that they need to take additional steps to complete
// this purchase.
return false;
}
// Determine if the product ID is associated with a consumable product.
auto is_consumable = IsConsumableProductId(purchase.product_id);
if (is_consumable) {
// Grant an entitlement to the product to the user.
// ...
// Then, notify Google by consuming the purchase.
ConsumePurchaseParams params { purchase.purchase_token };
auto promise = std::make_shared<std::promise<ConsumePurchaseResult>>();
billing_client.ConsumePurchase(params, [promise](ConsumePurchaseResult result) {
promise->set_value(std::move(result));
});
auto consume_purchase_result = promise->get_future().get();
if (!consume_purchase_result.ok()) {
// Examine the failure code & message for more details & notify user
// of failure.
// ...
return false;
}
return true;
}
// Otherwise the product is assumed to be a non-consumable.
// Grant an entitlement to the product to the user.
// ...
// Then, notify Google by acknowledging the purchase (if not already done).
if (purchase.is_acknowledged) {
return true;
}
AcknowledgePurchaseParams params { purchase.purchase_token };
auto promise = std::make_shared<std::promise<AcknowledgePurchaseResult>>();
billing_client.AcknowledgePurchase(params, [promise](AcknowledgePurchaseResult result) {
promise->set_value(std::move(result));
});
auto acknowledge_purchase_result = promise->get_future().get();
if (!acknowledge_purchase_result.ok()) {
// Examine the failure code & message for more details & notify user
// of failure.
// ...
return false;
}
return true;
}
5단계: 통합 테스트
이제 Play 결제와의 통합을 테스트할 준비가 되었습니다. 개발 단계 중에 테스트하려면 라이선스 테스터를 활용하는 것이 좋습니다. 라이선스 테스터는 구매 시 실제 비용이 청구되지 않는 테스트 결제에 액세스할 수 있습니다.
라이선스 테스터 및 수동 테스트 모음을 설정하는 방법에 관한 안내는 Google Play 결제 라이브러리 통합을 테스트하는 방법에 관한 문서를 참고하세요.