প্লে বিলিং ব্যবহার করে ডিজিটাল পণ্য বিক্রি করে আপনার গেম থেকে আয় করুন। এই SDK-তে এমন API রয়েছে যা দিয়ে কেনার জন্য উপলব্ধ পণ্য দেখানো, ক্রয় প্রক্রিয়া শুরু করা এবং কেনাকাটা সম্পন্ন করা যায়। এই বিলিং API-গুলিতে কলগুলি সেই Google অ্যাকাউন্ট ব্যবহার করেই করা হয়, যা Google Play Games ক্লায়েন্টের মধ্যে গেমটি চালু করেছিল এবং এর জন্য কোনো অতিরিক্ত সাইন-ইন করার প্রয়োজন হয় না।
আপনি যদি অ্যান্ড্রয়েড প্লে বিলিং লাইব্রেরির সাথে ইন্টিগ্রেট করে থাকেন, তাহলে এই প্লে বিলিং এপিআইগুলো আপনার কাছে পরিচিত মনে হবে। প্লে বিলিং-এর সাথে যেকোনো সার্ভার-সাইড ইন্টিগ্রেশন পিসি গেমগুলোতে পুনরায় ব্যবহার করা যেতে পারে, কারণ সেগুলো অ্যান্ড্রয়েড এবং পিসি উভয় ক্ষেত্রেই একই রকম।
পূর্বশর্ত
এসডিকে সেটআপ সম্পূর্ণ করুন।
গুগল প্লে-এর বিলিং সিস্টেমের সংক্ষিপ্ত বিবরণ পড়ুন।
সম্পূর্ণ প্লে বিলিং সেটআপ করুন ।
ধাপ ১ : বিলিং ক্লায়েন্ট তৈরি করুন
২৬.৩.৩১২.০ থেকে
SDK ভার্সন 26.3.312.0 এবং তার পরবর্তী ভার্সনগুলোর জন্য, একটি BillingClient কনফিগার ও ইনস্ট্যানশিয়েট করতে BillingClientParameters ব্যবহার করুন। এর মাধ্যমে আপনি ইনিশিয়ালাইজেশনের সময় পেন্ডিং পারচেজের মতো নির্দিষ্ট ফিচারগুলো চালু করতে পারবেন।
// Set up initialization parameters
BillingClientParams params;
params.enable_pending_purchases = true;
// Instantiate the BillingClient with parameters
BillingClient billing_client(params);
২৬.৩.৩১২.০ এর আগে
26.3.312.0 এর পূর্ববর্তী SDK সংস্করণগুলিতে, BillingClient শুধুমাত্র ডিফল্ট কনস্ট্রাক্টর ব্যবহার করে ইনস্ট্যানসিয়েশন সমর্থন করে। এই সংস্করণগুলিতে উন্নত কনফিগারেশন বিকল্পগুলি উপলব্ধ নেই।
BillingClient billing_client;
ধাপ ২ : পূর্ববর্তী কেনাকাটা এবং আপনার অ্যাপ্লিকেশনের বাইরে সম্পন্ন হওয়া কেনাকাটা সম্পর্কে অনুসন্ধান করুন।
যখন আপনার অ্যাপ্লিকেশনটি চালু হয় বা যখন এটি পুনরায় ফোরগ্রাউন্ডে আসে, তখন কেনাকাটার তথ্য অনুসন্ধান করুন। আপনার গেমের বাইরে হওয়া কেনাকাটা শনাক্ত করতে অথবা ব্যবহারকারীর পূর্বে করা কেনাকাটার অ্যাক্সেস আনলক করতে এটি প্রয়োজনীয়।
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
}
ধাপ ৩ : কেনার জন্য উপলব্ধ পণ্যগুলো দেখান
আপনি আপনার উপলব্ধ পণ্যগুলো অনুসন্ধান করতে এবং ব্যবহারকারীদের কাছে তা প্রদর্শন করতে প্রস্তুত। ব্যবহারকারীদের কাছে পণ্য প্রদর্শন করার আগে পণ্যের বিবরণ অনুসন্ধান করা একটি গুরুত্বপূর্ণ পদক্ষেপ, কারণ এটি স্থানীয় ভাষার পণ্যের তথ্য প্রদান করে।
কোনো পণ্য বিক্রির জন্য দেওয়ার আগে, যাচাই করে নিন যে ব্যবহারকারীর কাছে পণ্যটি আগে থেকেই আছে কি না। যদি ব্যবহারকারীর ক্রয় ইতিহাসে কোনো ব্যবহারযোগ্য পণ্য থেকে থাকে, তবে তিনি সেটি পুনরায় কেনার আগে আপনাকে অবশ্যই পণ্যটি ব্যবহার করে ফেলতে হবে।
-
BillingClient::QueryProductDetailsব্যবহার করে পণ্যের বিবরণ জানতে অনুসন্ধান করুন। গুগল প্লে কনসোলে আপনার রেজিস্টার করা প্রোডাক্ট আইডিগুলো পাস করুন। -
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
}
ধাপ ৪ : একটি ক্রয় প্রক্রিয়া চালু করুন
যখন ব্যবহারকারী কোনো পণ্য কেনার আগ্রহ প্রকাশ করেন, তার মানে আপনি তাকে বুঝিয়ে দিয়েছেন যে আপনি ক্রয় প্রক্রিয়াটি শুরু করার জন্য প্রস্তুত।
- প্রথমে
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
}
ধাপ ৫ : ক্রয় প্রক্রিয়া সম্পন্ন করুন
ব্যাকএন্ড সার্ভারের সাথে প্রক্রিয়া
যেসব গেমের ব্যাকএন্ড সার্ভার আছে, সেগুলোর ক্ষেত্রে আপনার ব্যাকএন্ড সার্ভারে purchase_token পাঠিয়ে প্রসেসিং সম্পন্ন করুন। সার্ভার-সাইড প্লে বিলিং এপিআই (Play Billing APIs) ব্যবহার করে বাকি প্রসেসিং সম্পন্ন করুন। এই সার্ভার-সাইড ইন্টিগ্রেশনটি, প্লে বিলিং-এর সাথে ইন্টিগ্রেট করা একটি অ্যান্ড্রয়েড গেমের জন্য করা ইন্টিগ্রেশনের মতোই।
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_statePurchaseState::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;
}
ক্লায়েন্ট-সাইড ক্রয় যাচাইকরণ
আপনি 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;
}
ধাপ ৬ : আপনার ইন্টিগ্রেশন পরীক্ষা করুন
আপনি এখন প্লে বিলিং-এর সাথে আপনার ইন্টিগ্রেশন পরীক্ষা করার জন্য প্রস্তুত। ডেভেলপমেন্ট পর্যায়ে পরীক্ষা করার জন্য, আমরা লাইসেন্স টেস্টার ব্যবহার করার পরামর্শ দিই। লাইসেন্স টেস্টারদের এমন টেস্ট পেমেন্টের অ্যাক্সেস থাকে, যা কেনাকাটার জন্য আসল টাকা চার্জ করা এড়িয়ে চলে।
লাইসেন্স টেস্টার এবং আমাদের প্রস্তাবিত ম্যানুয়াল টেস্টগুলো কীভাবে সেটআপ করতে হয়, সে সম্পর্কিত নির্দেশাবলীর জন্য আপনার গুগল প্লে বিলিং লাইব্রেরি ইন্টিগ্রেশন কীভাবে পরীক্ষা করবেন তার ডকুমেন্টেশন দেখুন।