Monetiza tu juego vendiendo productos digitales con la Facturación Play. El SDK ofrece APIs para mostrar productos disponibles para comprar, iniciar el flujo de compra y procesar compras. Las llamadas a estas APIs de facturación se realizan con la Cuenta de Google que inició el juego en el cliente de Google Play Juegos y no requieren pasos de acceso adicionales.
Si realizaste la integración con la biblioteca de Facturación Play para Android, estas APIs de Play Billing te resultarán familiares. Los títulos para PC pueden reutilizar cualquier integración del servidor con Play Billing, ya que son las mismas en Android y PC.
Requisitos previos
Completa la configuración del SDK.
Lee la descripción general del sistema de facturación de Google Play.
Completa la configuración de Facturación Play.
Paso 1: Consulta las compras anteriores y las compras completadas fuera de tu aplicación
Cuando se inicie tu aplicación o cuando vuelva a entrar en primer plano, consulta las compras. Esto es necesario para detectar las compras que se realizaron fuera de tu juego o para desbloquear el acceso a las compras que el usuario realizó anteriormente.
Consulta las compras con
BillingClient::QueryPurchases.Continúa procesando las compras.
// 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
}
Paso 2: Muestra los productos disponibles para comprar
Ya puedes consultar los productos disponibles y mostrarlos a los usuarios. La consulta de detalles de productos es un paso importante antes de mostrar los productos a los usuarios, ya que devuelve información localizada sobre los productos.
Antes de ofrecer un producto en venta, verifica que el usuario no lo tenga. Si el usuario todavía tiene un producto consumible en su historial de compras, debes procesar su compra antes de que pueda volver a comprarlo.
- Consulta los detalles del producto con
BillingClient::QueryProductDetails. Pasa los IDs de producto que registraste en Google Play Console. - Renderiza el
ProductDetails, que incluye el nombre localizado del producto y el precio de la oferta. - Mantén una referencia al
offer_tokendel producto. Se usa para iniciar un flujo de compra de la oferta.
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
}
Paso 3: Inicia un flujo de compra
Cuando el usuario muestre su intención de comprar un producto que le mostraste, estarás listo para iniciar el flujo de compra.
- Comienza llamando a
BillingClient::LaunchPurchaseFlow(). Pasa el objetooffer_tokenque se obtuvo cuando se consultaron los detalles del producto. - Una vez que se complete la compra, se llamará a la función de continuación con el resultado.
- Si la operación se realiza correctamente, la continuación contiene un
ProductPurchaseDetails. Para continuar, procesa la compra.
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
}
Paso 4: Procesa una compra
Proceso con un servidor de backend
En el caso de los juegos con un servidor de backend, envía el objeto purchase_token a tu servidor de backend para completar el procesamiento. Completa el resto del procesamiento con las APIs de Facturación Play del servidor. Esta integración del servidor es la misma que se realiza para un juego para Android que se integró con la Facturación Play.
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
}
Procesa sin un servidor de backend
Para asegurarte de que el pago del usuario no esté pendiente, verifica que
ProductPurchaseDetails::purchase_stateseaPurchaseState::kPurchaseStatePurchased. Si el estado de la compra es pendiente, notifica al usuario que debe completar pasos adicionales antes de recibir el producto que compró.Otorga al usuario acceso al producto comprado y actualiza el almacenamiento de derechos de tu juego.
En el caso de las compras de productos no consumibles (productos que solo se pueden comprar una vez), verifica si ya se confirmó la compra con
ProductPurchaseDetails::is_acknowledged.- Si no se confirmó la compra, notifica a Google que se le otorga al usuario un derecho al producto llamando a
BillingClient::AcknowledgePurchase.
- Si no se confirmó la compra, notifica a Google que se le otorga al usuario un derecho al producto llamando a
En el caso de las compras de productos consumibles (productos que se pueden comprar más de una vez), llama a
BillingClient::ConsumePurchasepara notificar a Google que se le otorga al usuario un derecho al producto.
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;
}
Verificación de compras del cliente
Obtienes el signature de ProductPurchaseDetails. El campo signature se firma con tu clave privada con el algoritmo de firma SHA1withRSA. Puedes realizar la verificación con tu clave pública de la siguiente manera:
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
// Decodes a Base64 string into a vector of bytes using OpenSSL BIOs.
std::vector<unsigned char> base64_decode(const std::string& base64_string) {
BIO *bio, *b64;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bio = BIO_new_mem_buf(base64_string.data(), base64_string.length());
bio = BIO_push(b64, bio);
std::vector<unsigned char> decoded_data;
decoded_data.resize(base64_string.length());
int length = BIO_read(bio, decoded_data.data(), decoded_data.size());
if (length > 0) {
decoded_data.resize(length);
} else {
decoded_data.clear();
}
BIO_free_all(bio);
return decoded_data;
}
// Reads a PEM-encoded public key string and returns an EVP_PKEY object.
EVP_PKEY* createPublicKey(const std::string& publicKeyPem) {
BIO* bio = BIO_new_mem_buf(publicKeyPem.data(), publicKeyPem.length());
EVP_PKEY* pkey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr);
BIO_free(bio);
return pkey;
}
// Verifies the RSA-SHA1 signature of given data using a public key.
bool verifySignature(const std::string& publicKeyPem,
const std::string& originalData,
const std::string& signature_b64) {
std::vector<unsigned char> signature = base64_decode(signature_b64);
EVP_PKEY* pkey = createPublicKey(publicKeyPem);
if (!pkey) {
std::cerr << "Error loading public key." << std::endl;
ERR_print_errors_fp(stderr);
return false;
}
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
if (!mdctx) {
std::cerr << "EVP_MD_CTX_new failed." << std::endl;
EVP_PKEY_free(pkey);
return false;
}
if (EVP_DigestVerifyInit(mdctx, nullptr, EVP_sha1(), nullptr, pkey) <= 0 ||
EVP_DigestVerifyUpdate(mdctx, originalData.c_str(),
originalData.length()) <= 0) {
EVP_MD_CTX_free(mdctx);
EVP_PKEY_free(pkey);
std::cerr << "Error during EVP_DigestVerifyInit or EVP_DigestVerifyUpdate."
<< std::endl;
return false;
}
int result = EVP_DigestVerifyFinal(
mdctx, reinterpret_cast<const unsigned char*>(signature.data()),
signature.size());
EVP_MD_CTX_free(mdctx);
EVP_PKEY_free(pkey);
if (result == 0) {
std::cerr << "Signature verification failed." << std::endl;
return false;
} else if (result != 1) {
std::cerr << "Error during signature verification." << std::endl;
ERR_print_errors_fp(stderr);
return false;
}
return true;
}
Paso 5: Prueba tu integración
Ya puedes probar tu integración con la Facturación Play. Para realizar las pruebas durante la fase de desarrollo, recomendamos aprovechar los verificadores con licencia. Los verificadores con licencia tienen acceso a pagos de prueba que evitan los cargos reales por las compras.
Si deseas obtener instrucciones para configurar verificadores con licencia y un conjunto de pruebas manuales que te recomendamos realizar, consulta la documentación sobre cómo probar la integración de la Biblioteca de Facturación Google Play.