Sie können mit Play Billing digitale Produkte in Ihrem Spiel verkaufen und so Einnahmen erzielen. Das SDK bietet APIs, mit denen Sie verfügbare Produkte anzeigen, den Kaufvorgang starten und Käufe verarbeiten können. Aufrufe dieser Abrechnungs-APIs werden mit dem Google-Konto ausgeführt, mit dem das Spiel im Google Play Spiele-Client gestartet wurde. Es sind keine zusätzlichen Anmeldeschritte erforderlich.
Wenn Sie die Android Play Billing Library eingebunden haben, sollten Ihnen diese Play Billing APIs bekannt sein. Alle serverseitigen Einbindungen in Play Billing können für PC-Titel wiederverwendet werden, da sie für Android und PC gleich sind.
Vorbereitung
Richten Sie das SDK ein.
Lesen Sie die Übersicht zum Abrechnungssystem von Google Play.
Richten Sie Play Billing ein.
Schritt 1: BillingClient erstellen
Seit 26.3.312.0
Verwenden Sie für SDK-Version 26.3.312.0 und höher
BillingClientParameters, um einen BillingClient zu konfigurieren und
zu instanziieren. So können Sie bei der Initialisierung bestimmte Funktionen aktivieren, z. B. ausstehende Käufe.
// Set up initialization parameters
BillingClientParams params;
params.enable_pending_purchases = true;
// Instantiate the BillingClient with parameters
BillingClient billing_client(params);
Vor 26.3.312.0
In SDK-Versionen vor 26.3.312.0 unterstützt der BillingClient
nur die Instanziierung mit dem Standardkonstruktor. Erweiterte Konfigurationsoptionen sind in diesen Versionen nicht verfügbar.
.
BillingClient billing_client;
Schritt 2: Nach früheren Käufen und Käufen suchen, die außerhalb Ihrer Anwendung abgeschlossen wurden
Wenn Ihre Anwendung gestartet wird oder wieder in den Vordergrund tritt, suchen Sie nach Käufen. Dies ist erforderlich, um Käufe zu erkennen, die außerhalb Ihres Spiels getätigt wurden, oder um den Zugriff auf Käufe freizuschalten, die der Nutzer zuvor getätigt hat.
Suchen Sie mit
BillingClient::QueryPurchasesnach Käufen.Fahren Sie fort, indem Sie die Käufe verarbeiten.
// 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
}
Schritt 3: Verfügbare Produkte anzeigen
Sie können jetzt nach Ihren verfügbaren Produkten suchen und sie Ihren Nutzern präsentieren. Die Abfrage von Produktdetails ist ein wichtiger Schritt, bevor Sie Ihren Nutzern Produkte präsentieren, da dadurch lokalisierte Produktinformationen zurückgegeben werden.
Bevor Sie ein Produkt zum Verkauf anbieten, prüfen Sie, ob der Nutzer es bereits besitzt. Wenn der Nutzer ein Verbrauchsgut hat, das sich noch in seinem Kaufverlauf befindet, müssen Sie das Produkt verbrauchen, bevor er es noch einmal kaufen kann.
- Suchen Sie mit
BillingClient::QueryProductDetailsnach Produktdetails. Übergeben Sie die Produkt-IDs, die Sie in der Google Play Console registriert haben. - Rendern Sie die
ProductDetails, die den lokalisierten Namen und den Angebotspreis des Produkts enthalten. - Behalten Sie einen Verweis auf das
offer_tokendes Produkts bei. Damit wird ein Kaufvorgang für das Angebot gestartet.
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
}
Schritt 4: Kaufvorgang starten
Wenn der Nutzer Interesse an einem von Ihnen präsentierten Produkt zeigt, können Sie den Kaufvorgang starten.
Der Kaufvorgang wird im Spiel über ein nahtloses, WebView-basiertes Overlay abgeschlossen:
- Rufen Sie zuerst
BillingClient::LaunchPurchaseFlow()auf. Übergeben Sie dasoffer_token, das Sie beim Abfragen der Produktdetails erhalten haben. - Sobald der Kauf abgeschlossen ist, wird die Fortsetzungsfunktion mit dem Ergebnis aufgerufen.
- Im Erfolgsfall enthält die Fortsetzung ein
ProductPurchaseDetails. Fahren Sie fort, indem Sie den Kauf verarbeiten.
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
}
Schritt 5: Kauf verarbeiten
Mit einem Backend-Server verarbeiten
Bei Spielen mit einem Backend-Server senden Sie das
purchase_token an Ihren Backend-Server, um die Verarbeitung abzuschließen. Schließen Sie die
restliche Verarbeitung mit den
serverseitigen Play Billing APIs ab. Diese serverseitige Einbindung ist dieselbe wie bei einem Android-Spiel, das in Play Billing eingebunden ist.
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
}
Ohne Backend-Server verarbeiten
Prüfen Sie, ob die Zahlung des Nutzers aussteht, indem Sie prüfen, ob
ProductPurchaseDetails::purchase_stategleichPurchaseState::kPurchaseStatePurchasedist. Wenn der Kaufstatus „Ausstehend“ ist, benachrichtigen Sie den Nutzer, dass er zusätzliche Schritte ausführen muss, bevor er das gekaufte Produkt erhält.Gewähren Sie dem Nutzer Zugriff auf das gekaufte Produkt und aktualisieren Sie den Berechtigungsspeicher Ihres Spiels.
Bei nicht verbrauchbaren Käufen (Produkte, die nur einmal gekauft werden können) prüfen Sie mit
ProductPurchaseDetails::is_acknowledged, ob der Kauf bereits bestätigt wurde.- Wenn der Kauf noch nicht bestätigt wurde, benachrichtigen Sie Google, dass dem Nutzer
eine Berechtigung für das Produkt gewährt wird, indem Sie
BillingClient::AcknowledgePurchaseaufrufen.
- Wenn der Kauf noch nicht bestätigt wurde, benachrichtigen Sie Google, dass dem Nutzer
eine Berechtigung für das Produkt gewährt wird, indem Sie
Bei verbrauchbaren Käufen (Produkte, die mehrmals gekauft werden können) benachrichtigen Sie Google, dass dem Nutzer eine Berechtigung für das Produkt gewährt wird, indem Sie
BillingClient::ConsumePurchaseaufrufen.
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;
}
Clientseitige Bestätigung von Käufen
Die clientseitige Bestätigung bietet eine zusätzliche Sicherheitsebene, da die Kaufsignatur direkt in Ihrem Spielclient bestätigt wird:
Sie erhalten die signature von
ProductPurchaseDetails. Das Feld signature ist mit Ihrem privaten Schlüssel und dem Signaturalgorithmus SHA1withRSA signiert. Sie können die Bestätigung mit Ihrem öffentlichen Schlüssel so durchführen:
#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;
}
Schritt 6: Integration testen
Sie können jetzt Ihre Einbindung in Play Billing testen. Für Tests während der Entwicklungsphase empfehlen wir die Verwendung von Lizenztestern. Lizenztester haben Zugriff auf Testzahlungen, bei denen für Käufe kein echtes Geld abgebucht wird.
Eine Anleitung zum Einrichten von Lizenztestern und eine Reihe manueller Tests die wir empfehlen, finden Sie in der Dokumentation zum Testen der Einbindung der Google Play Billing Library.