Du kannst mit Play Billing digitale Produkte in deinem Spiel verkaufen und so Einnahmen erzielen. Das SDK bietet APIs, mit denen du verfügbare Produkte anzeigen, den Kaufvorgang starten und Käufe verarbeiten kannst. Aufrufe dieser Abrechnungs-APIs werden mit dem Google-Konto ausgeführt, mit dem das Spiel im Google Play Spiele-Client gestartet wurde. Zusätzliche Anmeldeschritte sind nicht erforderlich.
Wenn du die Android Play Billing Library eingebunden hast, sollten dir diese Play Billing APIs bekannt vorkommen. Alle serverseitigen Einbindungen in Play Billing können für PC-Titel wiederverwendet werden, da sie sowohl für Android als auch für PC gleich sind.
Voraussetzungen
Schließen Sie die SDK-Einrichtung ab.
Übersicht zum Abrechnungssystem von Google Play lesen.
Schritt 1: BillingClient erstellen
Ab Version 26.3.312.0
Verwende für SDK-Version 26.3.312.0 und höher
BillingClientParameters, um einen BillingClient zu konfigurieren und
zu instanziieren. So kannst du 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 Version 26.3.312.0
In SDK-Versionen vor 26.3.312.0 unterstützt 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 deiner Anwendung abgeschlossen wurden
Wenn deine Anwendung gestartet wird oder wieder in den Vordergrund tritt, suche nach Käufen. Dies ist erforderlich, um Käufe zu erkennen, die außerhalb deines Spiels getätigt wurden, oder um den Zugriff auf Käufe freizuschalten, die der Nutzer zuvor getätigt hat.
Suche mit
BillingClient::QueryPurchasesnach Käufen.
// 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
Du kannst jetzt nach deinen verfügbaren Produkten suchen und sie deinen Nutzern präsentieren. Die Suche nach Produktdetails ist ein wichtiger Schritt, bevor du deinen Nutzern Produkte präsentierst, da sie lokalisierte Produktinformationen zurückgibt.
Bevor du ein Produkt zum Verkauf anbietest, prüfe, ob der Nutzer es bereits besitzt. Wenn der Nutzer ein Verbrauchsgut hat, das sich noch in seinem Kaufverlauf befindet, musst du das Produkt verbrauchen, bevor er es noch einmal kaufen kann.
- Suche mit
BillingClient::QueryProductDetailsnach Produktdetails. Gib die Produkt-IDs an, die du in der Google Play Console registriert hast. - Rendere die
ProductDetails, einschließlich des Produkts lokalisierten Namens & Angebotspreises. - Behalte 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 Produkt zeigt, das du ihm präsentiert hast, kannst du den Kaufvorgang starten.
- Rufe zuerst
BillingClient::LaunchPurchaseFlow()auf. Gib dasoffer_tokenan, das du beim Abfragen der Produktdetails erhalten hast. - Sobald der Kauf abgeschlossen ist, wird die Fortsetzungsfunktion mit dem Ergebnis aufgerufen.
- Wenn der Vorgang erfolgreich war, enthält die Fortsetzung ein
ProductPurchaseDetails. Verarbeite den Kauf .
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
Sende bei Spielen mit einem Backend-Server das
purchase_token an deinen Backend-Server, um die Verarbeitung abzuschließen. Schließe 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üfe, ob die Zahlung des Nutzers aussteht, indem du überprüfst, ob
ProductPurchaseDetails::purchase_stategleichPurchaseState::kPurchaseStatePurchasedist. Wenn der Kaufstatus „Ausstehend“ ist, teile dem Nutzer mit, dass er zusätzliche Schritte ausführen muss, bevor er das gekaufte Produkt erhält.Gewähre dem Nutzer Zugriff auf das gekaufte Produkt und aktualisiere den Berechtigungsspeicher deines Spiels.
Prüfe bei nicht verbrauchbaren Käufen (Produkte, die nur einmal gekauft werden können) ob der Kauf bereits mit
ProductPurchaseDetails::is_acknowledgedbestätigt wurde.- Wenn der Kauf nicht bestätigt wurde, teile Google mit, dass dem Nutzer
eine Berechtigung für das Produkt gewährt wird, indem du
BillingClient::AcknowledgePurchaseaufrufst.
- Wenn der Kauf nicht bestätigt wurde, teile Google mit, dass dem Nutzer
eine Berechtigung für das Produkt gewährt wird, indem du
Teile Google bei verbrauchbaren Käufen (Produkte, die mehrmals gekauft werden können) mit, dass dem Nutzer eine Berechtigung für das Produkt gewährt wird, indem du
BillingClient::ConsumePurchaseaufrufst.
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 Kaufbestätigung
Du erhältst die signature von
ProductPurchaseDetails. Das Feld signature ist mit deinem privaten Schlüssel und dem Signaturalgorithmus SHA1withRSA signiert. Du kannst die Signatur mit deinem öffentlichen Schlüssel so überprüfen:
#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
Du kannst jetzt deine 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, findest du in der Dokumentation zum Testen der Einbindung der Google Play Billing Library.