Google Play for Native PC Billing Library को अपने ऐप्लिकेशन में इंटिग्रेट करें

Play Billing का इस्तेमाल करके, डिजिटल प्रॉडक्ट बेचकर अपने गेम से कमाई करें. एसडीके टूल, एपीआई उपलब्ध कराता है. इनकी मदद से, खरीदारी के लिए उपलब्ध प्रॉडक्ट दिखाए जा सकते हैं, खरीदारी का फ़्लो लॉन्च किया जा सकता है, और खरीदारी की प्रोसेस पूरी की जा सकती है. बिलिंग के इन एपीआई के लिए कॉल, Google Play Games क्लाइंट में गेम लॉन्च करने वाले Google खाते का इस्तेमाल करके किए जाते हैं. इनके लिए, साइन-इन करने के किसी अतिरिक्त चरण की ज़रूरत नहीं होती.

अगर आपने Android Play Billing की लाइब्रेरी को इंटिग्रेट किया है, तो Play Billing के ये एपीआई आपको जाने-पहचाने लगेंगे. Play Billing के साथ सर्वर-साइड पर किए गए किसी भी इंटिग्रेशन को पीसी टाइटल के लिए फिर से इस्तेमाल किया जा सकता है. ऐसा इसलिए, क्योंकि ये इंटिग्रेशन Android और पीसी, दोनों के लिए एक जैसे होते हैं.

ज़रूरी शर्तें

पहला चरण: BillingClient बनाना

वर्शन 26.3.312.0 से

एसडीके टूल के वर्शन 26.3.312.0 और उसके बाद के वर्शन के लिए, BillingClientParameters को कॉन्फ़िगर और इंस्टैंशिएट करने के लिए, BillingClient का इस्तेमाल करें. इससे, शुरू करने के दौरान कुछ खास सुविधाएं चालू की जा सकती हैं. जैसे, लंबित खरीदारी.

// Set up initialization parameters
BillingClientParams params;
params.enable_pending_purchases = true;

// Instantiate the BillingClient with parameters
BillingClient billing_client(params);

वर्शन 26.3.312.0 से पहले

एसडीके टूल के 26.3.312.0 से पहले के वर्शन में, BillingClient को सिर्फ़ डिफ़ॉल्ट कंस्ट्रक्टर का इस्तेमाल करके इंस्टैंशिएट किया जा सकता है. इन वर्शन में, बेहतर कॉन्फ़िगरेशन के विकल्प उपलब्ध नहीं हैं. .

BillingClient billing_client;

दूसरा चरण: पिछली खरीदारी और आपके ऐप्लिकेशन के बाहर की गई खरीदारी के बारे में क्वेरी करना

जब आपका ऐप्लिकेशन शुरू होता है या जब वह फिर से फ़ोरग्राउंड में आता है, तब खरीदारी के बारे में क्वेरी करें. यह आपके गेम के बाहर की गई खरीदारी का पता लगाने या उपयोगकर्ता की ओर से पहले की गई खरीदारी का ऐक्सेस अनलॉक करने के लिए ज़रूरी है.

  1. BillingClient::QueryPurchases का इस्तेमाल करके, खरीदारी के बारे में क्वेरी करें.

  2. खरीदारी की प्रोसेस पूरी करके आगे बढ़ें.

// 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
}

तीसरा चरण: खरीदारी के लिए उपलब्ध प्रॉडक्ट दिखाना

आपके पास, उपलब्ध प्रॉडक्ट के बारे में क्वेरी करने और उन्हें अपने उपयोगकर्ताओं को दिखाने का विकल्प है. उपयोगकर्ताओं को प्रॉडक्ट दिखाने से पहले, प्रॉडक्ट की जानकारी के बारे में क्वेरी करना ज़रूरी है. ऐसा इसलिए, क्योंकि इससे स्थानीय भाषा में प्रॉडक्ट की जानकारी मिलती है.

किसी प्रॉडक्ट को बिक्री के लिए उपलब्ध कराने से पहले, यह देखें कि उपयोगकर्ता के पास वह प्रॉडक्ट पहले से मौजूद न हो. अगर उपयोगकर्ता के पास कोई ऐसा प्रॉडक्ट है जिसे कंज़्यूम किया जा सकता है और वह अब भी उसकी खरीदारी के इतिहास में मौजूद है, तो उसे दोबारा खरीदने से पहले, आपको उस प्रॉडक्ट को कंज़्यूम करना होगा.

  1. BillingClient::QueryProductDetails का इस्तेमाल करके, प्रॉडक्ट की जानकारी के बारे में क्वेरी करें. Google Play Console में रजिस्टर किए गए प्रॉडक्ट के आईडी पास करें.
  2. ProductDetails रेंडर करें. इसमें प्रॉडक्ट का स्थानीय भाषा में नाम और ऑफ़र की कीमत शामिल होती है.
  3. प्रॉडक्ट के 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
}

चौथा चरण: खरीदारी का फ़्लो लॉन्च करना

जब उपयोगकर्ता, आपके दिखाए गए किसी प्रॉडक्ट को खरीदने का इरादा दिखाता है, तब खरीदारी का फ़्लो लॉन्च किया जा सकता है.

खरीदारी का फ़्लो, गेम में WebView पर आधारित एक आसान ओवरले का इस्तेमाल करके पूरा किया जाता है:

गेम में आसानी से खरीदारी करने के लिए चेकआउट इंटरफ़ेस
गेम में, खरीदारी की आसान चेकआउट प्रोसेस
  1. BillingClient::LaunchPurchaseFlow() को कॉल करके शुरू करें. प्रॉडक्ट की जानकारी के बारे में क्वेरी करते समय मिला offer_token पास करें.
  2. खरीदारी पूरी होने के बाद, नतीजे के साथ कंटीन्यूएशन फ़ंक्शन को कॉल किया जाएगा.
  3. अगर खरीदारी पूरी हो जाती है, तो कंटीन्यूएशन में 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
}

पांचवा चरण: खरीदारी की प्रोसेस पूरी करना

बैकएंड सर्वर की मदद से प्रोसेस करना

बैकएंड सर्वर वाले गेम के लिए, purchase_token को अपने बैकएंड सर्वर पर भेजकर प्रोसेस पूरी करें. सर्वर-साइड Play Billing के एपीआई का इस्तेमाल करके, प्रोसेस का बाकी हिस्सा पूरा करें. सर्वर-साइड पर किया गया यह इंटिग्रेशन, Play Billing के साथ इंटिग्रेट किए गए 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
}

बैकएंड सर्वर के बिना प्रोसेस करना

  1. यह देखकर पक्का करें कि उपयोगकर्ता का पेमेंट लंबित न हो ProductPurchaseDetails::purchase_state है या नहीं, यह देखकर पक्का करें कि उपयोगकर्ता का पेमेंट लंबित न हो PurchaseState::kPurchaseStatePurchased. अगर खरीदारी की स्थिति लंबित है, तो उपयोगकर्ता को सूचना दें कि खरीदे गए प्रॉडक्ट को पाने के लिए, उसे कुछ और चरण पूरे करने होंगे.

  2. उपयोगकर्ता को खरीदे गए प्रॉडक्ट का ऐक्सेस दें और अपने गेम के एनटाइटलमेंट स्टोरेज को अपडेट करें.

  3. ऐसे प्रॉडक्ट की खरीदारी के लिए जिन्हें कंज़्यूम नहीं किया जा सकता (ऐसे प्रॉडक्ट जिन्हें सिर्फ़ एक बार खरीदा जा सकता है) यह देखें कि ProductPurchaseDetails::is_acknowledged का इस्तेमाल करके, खरीदारी की पुष्टि पहले ही की जा चुकी है या नहीं.

    1. अगर खरीदारी की पुष्टि नहीं की गई है, तो Google को सूचना दें कि उपयोगकर्ता को प्रॉडक्ट का एनटाइटलमेंट दिया जा रहा है. इसके लिए, को कॉल करें BillingClient::AcknowledgePurchase.
  4. ऐसे प्रॉडक्ट की खरीदारी के लिए जिन्हें कंज़्यूम किया जा सकता है (ऐसे प्रॉडक्ट जिन्हें एक से ज़्यादा बार खरीदा जा सकता है) Google को सूचना दें कि उपयोगकर्ता को प्रॉडक्ट का एनटाइटलमेंट दिया जा रहा है. इसके लिए, BillingClient::ConsumePurchase को कॉल करें.

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;
}

क्लाइंट-साइड पर खरीदारी की पुष्टि की सुविधा

क्लाइंट-साइड पर पुष्टि की सुविधा, सुरक्षा की एक अतिरिक्त लेयर उपलब्ध कराती है. इसके लिए, आपके गेम क्लाइंट में सीधे तौर पर खरीदारी के हस्ताक्षर की पुष्टि की जाती है:

क्लाइंट-साइड पर खरीदारी के हस्ताक्षर की पुष्टि करने वाला आर्किटेक्चर
क्लाइंट-साइड पर खरीदारी की पुष्टि की सुविधा का आर्किटेक्चर

ProductPurchaseDetails से signature मिलता है. signature फ़ील्ड को SHA1withRSA हस्ताक्षर एल्गोरिदम का इस्तेमाल करके, आपके निजी पासकोड से साइन किया जाता है. सार्वजनिक पासकोड का इस्तेमाल करके, इस तरह पुष्टि की जा सकती है:

#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;
}

छठा चरण: इंटिग्रेशन की जांच करना

अब Play Billing के साथ अपने इंटिग्रेशन की जांच की जा सकती है. डेवलपमेंट के दौरान जांच करने के लिए, हमारा सुझाव है कि लाइसेंस टेस्टर का इस्तेमाल करें. लाइसेंस टेस्टर के पास, टेस्ट पेमेंट का ऐक्सेस होता है. इनकी मदद से, खरीदारी के लिए असली पैसे नहीं चुकाने पड़ते.

लाइसेंस टेस्टर सेट अप करने के तरीके और मैन्युअल टेस्ट के सुइट के बारे में निर्देश पाने के लिए, हमारा सुझाव है कि Google Play Billing Library के इंटिग्रेशन की जांच करने के तरीके से जुड़ा दस्तावेज़ देखें .