این سند نحوه ادغام کردن «کتابخانه خدمات صورتحساب Google Play» در برنامهتان را برای شروع فروش محصولات شرح میدهد.
طول عمر خرید
در اینجا جریان خرید معمولی برای خرید یکباره یا اشتراک آورده شده است.
- به کاربر نشان دهید چه چیزی میتواند بخرد.
- جریان خرید را راهاندازی کنید تا کاربر خرید را بپذیرد.
- خرید را در سرورتان تأیید کنید.
- محتوا را به کاربر ارائه دهید.
- تحویل محتوا را تأیید کنید. برای محصولات مصرفی، خرید را مصرف کنید تا کاربر بتواند محصول را دوباره بخرد.
اشتراکها بهطور خودکار تمدید میشوند تا زمانی که لغو شوند. اشتراک میتواند وضعیتهای زیر را داشته باشد:
- فعال: کاربر خوشحساب است و به اشتراک دسترسی دارد.
- لغوشده: کاربر لغو کرده است اما تا زمان انقضا همچنان دسترسی دارد.
- در دوره ارفاقی: کاربر با مشکل پرداخت مواجه شده است اما همچنان دسترسی دارد درحالیکه Google درحال تلاش مجدد برای استفاده از روش پرداخت است.
- تعلیق: کاربر با مشکل پرداخت مواجه شده است و دیگر دسترسی ندارد، درحالیکه Google درحال امتحان مجدد روش پرداخت است.
- موقتاً متوقفشده: کاربر دسترسی خود را موقتاً متوقف کرده است و تا زمانی که آن را ازسر نگیرد، دسترسی ندارد.
- منقضیشده: کاربر اشتراک را لغو کرده و دسترسی به آن را ازدست داده است. کاربر در زمان انقضا ریزشکرده درنظر گرفته میشود.
اتصال به Google Play را مقداردهی اولیه کنید
اولین گام برای ادغام با سیستم صورتحساب Google Play، افزودن «کتابخانه صورتحساب Google Play» به برنامهتان و مقداردهی اولیه اتصال است.
افزودن وابستگی «کتابخانه خدمات صورتحساب Google Play»
وابستگی «کتابخانه خدمات صورتحساب Google Play» را به فایل build.gradle برنامهتان اضافه کنید، همانطور که نشان داده شده است:
گرووی
dependencies { def billing_version = "9.1.0" implementation "com.android.billingclient:billing:$billing_version" }
کاتلین
dependencies { val billing_version = "9.1.0" implementation("com.android.billingclient:billing:$billing_version") }
اگر از Kotlin استفاده میکنید، واحد KTX «کتابخانه خدمات صورتحساب Google Play» حاوی
افزونههای Kotlin و پشتیبانی از روتینهای همزمان است که به شما امکان میدهد هنگام استفاده از «کتابخانه خدمات صورتحساب Google Play»،
Kotlin اصطلاحی بنویسید. برای گنجاندن این
افزونهها در پروژهتان، وابستگی زیر را به فایل
build.gradle برنامهتان اضافه کنید، همانطور که نشان داده شده است:
گرووی
dependencies { def billing_version = "9.1.0" implementation "com.android.billingclient:billing-ktx:$billing_version" }
کاتلین
dependencies { val billing_version = "9.1.0" implementation("com.android.billingclient:billing-ktx:$billing_version") }
مقداردهی اولیه BillingClient
وقتی وابستگی به «کتابخانه خدمات صورتحساب Google Play» را اضافه کردید، باید
نمونه BillingClient را مقداردهی اولیه کنید. BillingClient میانای اصلی برای ارتباط بین «کتابخانه خدمات صورتحساب Google Play» و بقیه برنامه شما است. BillingClient روشهای راحتی را برای بسیاری از عملیاتهای رایج صورتحساب، هم بهصورت همزمان و هم ناهمزمان، ارائه میدهد. به موارد زیر توجه کنید:
- توصیه میشود که در هر زمان فقط یک اتصال
BillingClientفعال باز داشته باشید تا از چندین فراخوانی برگشتیPurchasesUpdatedListenerبرای یک رویداد جلوگیری شود. - توصیه میشود وقتی برنامهتان راهاندازی میشود یا به پیشزمینه میآید، اتصالی برای BillingClient آغاز کنید تا مطمئن شوید برنامهتان خریدها را بهموقع پردازش میکند. این کار را میتوان بااستفاده از
ActivityLifecycleCallbacksثبتشده توسطregisterActivityLifecycleCallbacksو گوش دادن به onActivityResumed برای مقداردهی اولیه اتصال هنگام اولین تشخیص ازسرگیری فعالیت انجام داد. برای جزئیات بیشتر درباره اینکه چرا باید این روال مطلوب دنبال شود، به بخش پردازش خریدها مراجعه کنید. همچنین بهخاطر داشته باشید که وقتی برنامهتان بسته است، اتصال را قطع کنید.
برای ایجاد BillingClient، از newBuilder استفاده کنید. میتوانید هر بافتی را به
newBuilder() ارسال کنید و BillingClient از آن برای دریافت بافت برنامه استفاده میکند. یعنی
لازم نیست نگران نشت حافظه باشید. برای دریافت بهروزرسانی درباره
خریدها، باید setListener را نیز فراخوانی کنید و مرجعی را به
PurchasesUpdatedListener ارسال کنید. این شنونده بهروزرسانیهای همه خریدهای درونبرنامه را دریافت میکند.
کاتلین
private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases -> // To be implemented in a later section. } private var billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) // Configure other settings. .build()
جاوا
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
// To be implemented in a later section.
}
};
private BillingClient billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
// Configure other settings.
.build();
اتصال به Google Play
پساز ایجاد BillingClient، باید اتصالی به Google Play برقرار کنید.
برای اتصال به Google Play، با startConnection تماس بگیرید. فرایند اتصال
ناهمزمان است و باید BillingClientStateListener را برای
دریافت کردن برگشت تماس پساز تکمیل راهاندازی کارخواه و آماده شدن آن برای
ایجاد درخواستهای بیشتر پیادهسازی کنید.
اگر اتصال مجدد خودکار سرویس را فعال نکردهاید، باید
منطق تلاش مجدد را نیز برای مدیریت اتصالات ازدسترفته به Google Play پیادهسازی کنید. برای پیادهسازی منطق تکرار،
روش فراخوانی onBillingServiceDisconnected() را لغو کنید و مطمئن شوید که
BillingClient قبلاز ایجاد درخواستهای بیشتر، روش startConnection() را برای اتصال مجدد به Google Play فراخوانی میکند. اگر اتصال مجدد خودکار سرویس را فعال کردهاید، میتوانید این روش را بهعنوان یک عملیات بدون اثر پیادهسازی کنید.
مثال زیر نشان میدهد که چگونه میتوان اتصال را شروع کرد و آماده بودن آن را برای استفاده آزمایش کرد:
کاتلین
billingClient.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: BillingResult) { if (billingResult.responseCode == BillingResponseCode.OK) { // The BillingClient is ready. You can query purchases here. } } override fun onBillingServiceDisconnected() { // If automatic service reconnection is enabled, this can be left empty (no-op) // because the library handles retries. You can still use this for non-retry // tasks like logging or updating the UI to reflect a disconnected state. // Otherwise, try to restart the connection on the next request to // Google Play by calling the startConnection() method. } })
جاوا
billingClient.startConnection( new BillingClientStateListener() { @Override public void onBillingSetupFinished(BillingResult billingResult) { if (billingResult.getResponseCode() == BillingResponseCode.OK) { // The BillingClient is ready. You can query purchases here. // It's a good practice to query products after the connection is established. queryProductDetails(); } } @Override public void onBillingServiceDisconnected() { // Try to restart the connection on the next request to // Google Play by calling the startConnection() method. // This is automatically handled by the library when you call a method that requires a connection. } });
برقراری مجدد اتصال بهطور خودکار
با معرفی روش enableAutoServiceReconnection() در
BillingClient.Builder در نسخه ۸.۰.۰، «کتابخانه خدمات صورتحساب Play» اکنون میتواند
بهطور خودکار اتصال سرویس را مجدداً برقرار کند اگر فراخوانی میانای برنامهسازی کاربردی درحالیکه
سرویس قطع است انجام شود. این امر میتواند به کاهش پاسخهای SERVICE_DISCONNECTED منجر شود، زیرا اتصال مجدد بهصورت داخلی قبلاز برقراری تماس میانای برنامهسازی کاربردی انجام میشود.
نحوه فعال کردن اتصال مجدد خودکار
هنگام ساختن نمونه BillingClient، از روش
enableAutoServiceReconnection() در BillingClient.Builder برای
فعال کردن اتصال مجدد خودکار استفاده کنید.
کاتلین
val billingClient = BillingClient.newBuilder(context) .setListener(listener) .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build()) .enableAutoServiceReconnection() // Add this line to enable reconnection .build()
جاوا
BillingClient billingClient = BillingClient.newBuilder(context)
.setListener(listener)
.enablePendingPurchases()
.enableAutoServiceReconnection() // Add this line to enable reconnection
.build();
نمایش محصولات دردسترس برای خرید
پساز برقراری اتصال به Google Play، آمادهاید تا محصولات دردسترس را جستجو کنید و آنها را به کاربرانتان نمایش دهید.
پرسوجو برای جزئیات محصول گام مهمی قبلاز نمایش محصولاتتان به کاربران است، زیرا اطلاعات محصول بومیسازیشده را برمیگرداند. برای اشتراکها، تأیید کنید که نمایش محصول شما از همه خطمشیهای Play پیروی میکند.
برای پُرسمان کردن جزئیات محصول یکباره، queryProductDetailsAsync
متد را فراخوانی کنید. این روش میتواند چندین پیشنهاد ویژه را براساس پیکربندی محصول یکباره شما برگرداند. برای اطلاعات بیشتر، چند گزینه خرید و پیشنهاد ویژه
برای محصولات یکباره را ببینید.
برای مدیریت نتیجه عملیات ناهمزمان، باید شنودگری را نیز مشخص کنید که رابط ProductDetailsResponseListener را پیادهسازی میکند.
سپس میتوانید onProductDetailsResponse را لغو کنید، که وقتی پُرسمان تمام میشود به شنونده اطلاع میدهد، همانطور که در مثال زیر نشان داده شده است:
کاتلین
val queryProductDetailsParams = QueryProductDetailsParams.newBuilder() .setProductList( listOf( Product.newBuilder() .setProductId("product_id_example") .setProductType(ProductType.SUBS) .build() ) ) .build() billingClient.queryProductDetailsAsync(queryProductDetailsParams) { billingResult, queryProductDetailsResult -> if (billingResult.responseCode == BillingResponseCode.OK) { for (productDetails in queryProductDetailsResult.productDetailsList) { // Process successfully retrieved product details here. } for (unfetchedProduct in queryProductDetailsResult.unfetchedProductList) { // Handle any unfetched products as appropriate. } } }
جاوا
QueryProductDetailsParams queryProductDetailsParams =
QueryProductDetailsParams.newBuilder()
.setProductList(
ImmutableList.of(
Product.newBuilder()
.setProductId("product_id_example")
.setProductType(ProductType.SUBS)
.build()))
.build();
billingClient.queryProductDetailsAsync(
queryProductDetailsParams,
new ProductDetailsResponseListener() {
public void onProductDetailsResponse(BillingResult billingResult,
QueryProductDetailsResult queryProductDetailsResult) {
if (billingResult.getResponseCode() == BillingResponseCode.OK) {
for (ProductDetails productDetails : queryProductDetailsResult.getProductDetailsList()) {
// Process successfully retrieved product details here.
}
for (UnfetchedProduct unfetchedProduct : queryProductDetailsResult.getUnfetchedProductList()) {
// Handle any unfetched products as appropriate.
}
}
}
}
)
هنگام پرسمان برای جزئیات محصول، نمونهای از
QueryProductDetailsParams را ارسال کنید که فهرستی از رشتههای شناسه محصول
ایجادشده در «کنسول Google Play» را بههمراه ProductType مشخص میکند. ProductType میتواند ProductType.INAPP برای محصولات یکباره یا ProductType.SUBS برای اشتراکها باشد.
پُرسمان با افزونههای Kotlin
اگر از افزونههای Kotlin استفاده میکنید، میتوانید با فراخوانی تابع افزونه queryProductDetails()، جزئیات محصول یکباره را پُرسمان کنید.
queryProductDetails() از روالهای Kotlin استفاده میکند تا نیازی به تعریف شنونده جداگانه نداشته باشید. درعوض، تابع تا زمانی که پُرسمان تکمیل شود تعلیق میشود،
سپس میتوانید نتیجه را پردازش کنید:
suspend fun processPurchases() { val productList = listOf( QueryProductDetailsParams.Product.newBuilder() .setProductId("product_id_example") .setProductType(BillingClient.ProductType.SUBS) .build() ) val params = QueryProductDetailsParams.newBuilder() params.setProductList(productList) // leverage queryProductDetails Kotlin extension function val productDetailsResult = withContext(Dispatchers.IO) { billingClient.queryProductDetails(params.build()) } // Process the result. }
بهندرت، برخیاز دستگاهها نمیتوانند از ProductDetails و queryProductDetailsAsync() پشتیبانی کنند، معمولاً بهدلیل قدیمی بودن نسخههای خدمات Google Play. برای ارائه پشتیبانی مناسب برای این سناریو، با نحوه استفاده از ویژگیهای سازگاری با نسخه قدیمی در راهنمای انتقال به «کتابخانه خدمات صورتحساب Play» نسخه ۷ آشنا شوید.
پردازش نتیجه
«کتابخانه خدمات صورتحساب Google Play» نتایج پُرسمان را در شیء
QueryProductDetailsResult ذخیره میکند. QueryProductDetailsResult
حاوی List از اشیای ProductDetails است. سپس میتوانید روشهای مختلفی را برای هر شیء ProductDetails در فهرست فراخوانی کنید تا اطلاعات مربوط به محصول یکبارهای را که با موفقیت واکشی شده است، مانند قیمت یا شرح آن، مشاهده کنید. برای مشاهده اطلاعات جزئیات محصول دردسترس،
فهرست روشها را در کلاس ProductDetails ببینید.
QueryProductDetailsResult همچنین حاوی List از
اشیاء UnfetchedProduct است. سپس میتوانید هر UnfetchedProduct را پُرسمان کنید
تا کد وضعیتی مطابق با دلیل ناموفق بودن واکشی دریافت کنید.
برای مشاهده اطلاعات محصول دردسترس که واکشی نشده است،
فهرست روشها را در کلاس UnfetchedProduct ببینید.
قبلاز ارائه محصول برای فروش، بررسی کنید که کاربر ازقبل مالک آن محصول نباشد. اگر کاربر محصول مصرفشدنیای دارد که هنوز در کتابخانه محصول او است، باید آن محصول را مصرف کند تا بتواند دوباره آن را بخرد.
قبلاز ارائه اشتراک، بررسی کنید که کاربر قبلاً مشترک نشده باشد. همچنین به موارد زیر توجه کنید:
برای اشتراکها، روش
queryProductDetailsAsync()جزئیات محصول اشتراک و حداکثر ۵۰ پیشنهاد ویژه واجدشرایط کاربر را برای هر اشتراک برمیگرداند. اگر کاربر تلاش کند پیشنهاد ویژهای را که واجدشرایط آن نیست خریداری کند (برای مثال، اگر برنامه فهرست قدیمیای از پیشنهادهای ویژه واجدشرایط را نمایش دهد)، Play به کاربر اطلاع میدهد که واجدشرایط نیست و کاربر میتواند بهجای آن طرح پایه را خریداری کند.برای محصولات یکباره، روش
queryProductDetailsAsync()فقط پیشنهادهای ویژه واجدشرایط کاربر را برمیگرداند. اگر کاربر تلاش کند پیشنهادی را خریداری کند که واجدشرایط آن نیست (برای مثال، اگر کاربر به حد مجاز تعداد خرید رسیده باشد)، Play به کاربر اطلاع میدهد که واجدشرایط نیست و کاربر میتواند بهجای آن، پیشنهاد گزینه خرید را خریداری کند.
راهاندازی جریان خرید
برای شروع درخواست خرید از برنامهتان، launchBillingFlow()
روش را از رشته اصلی برنامهتان فراخوانی کنید. این روش مرجعی را به یک
BillingFlowParams شیء که حاوی شیء
ProductDetails مربوطه است که ازطریق فراخوانی
queryProductDetailsAsync بهدست آمده است میگیرد. برای ایجاد شیء BillingFlowParams، از
کلاس BillingFlowParams.Builder استفاده کنید.
کاتلین
// An activity reference from which the billing flow will be launched. val activity : Activity = ... val productDetailsParamsList = listOf( BillingFlowParams.ProductDetailsParams.newBuilder() // retrieve a value for productDetails by calling queryProductDetailsAsync() .setProductDetails(productDetails) // Get the offer token: // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList() // for a list of offers that are available to the user. // b. For subscriptions, call ProductDetails.getSubscriptionOfferDetails() // for a list of offers that are available to the user. .setOfferToken(selectedOfferToken) .build() ) val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build() // Launch the billing flow val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
جاوا
// An activity reference from which the billing flow will be launched. Activity activity = ...; ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList = ImmutableList.of( BillingFlowParams.ProductDetailsParams.newBuilder() // retrieve a value for "productDetails" by calling queryProductDetailsAsync() .setProductDetails(productDetails) // Get the offer token: // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList() // for a list of offers that are available to the user. // b. For subscriptions, call ProductDetails.getSubscriptionOfferDetails() // for a list of offers that are available to the user. .setOfferToken(selectedOfferToken) .build() ); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build(); // Launch the billing flow BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
روش launchBillingFlow() یکی از چندین کد پاسخ فهرستشده در
BillingClient.BillingResponseCode را برمیگرداند. حتماً این نتیجه را بررسی کنید تا
تأیید کنید که هیچ خطایی در راهاندازی جریان خرید وجود نداشته است. BillingResponseCode
OK نشاندهنده راهاندازی موفق است.
درصورت موفقیتآمیز بودن فراخوانی launchBillingFlow()، سیستم صفحه خرید Google Play را نمایش میدهد. شکل ۱ صفحه خرید اشتراک را نشان میدهد:
Google Play با onPurchasesUpdated() تماس میگیرد تا نتیجه عملیات خرید را به شنوندهای که رابط PurchasesUpdatedListener را پیادهسازی میکند، ارائه دهد. شنونده بااستفاده از روش setListener() هنگام
مقداردهی اولیه مشتری مشخص میشود.
برای رسیدگی به کدهای پاسخ احتمالی باید onPurchasesUpdated() را پیادهسازی کنید. مثال زیر نشان میدهد که چگونه onPurchasesUpdated() را لغو کنید:
کاتلین
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) { if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) { for (purchase in purchases) { // Process the purchase as described in the next section. } } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) { // Handle an error caused by a user canceling the purchase flow. } else { // Handle any other error codes. } }
جاوا
@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
if (billingResult.getResponseCode() == BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
// Process the purchase as described in the next section.
}
} else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user canceling the purchase flow.
} else {
// Handle any other error codes.
}
}
خرید موفقیتآمیز صفحه موفقیت خرید Google Play مشابه شکل ۲ را ایجاد میکند.
خرید موفق همچنین یک توکن خرید تولید میکند که یک شناسه یکتا است که کاربر و شناسه محصول را برای محصول یکبارهای که خریداری کردهاند نشان میدهد. برنامههایتان میتوانند کد خرید را بهصورت محلی ذخیره کنند، هرچند اکیداً توصیه میکنیم کد را به سرور زیرینه ایمن خود ارسال کنید تا بتوانید خرید را درستیسنجی کنید و دربرابر کلاهبرداری محافظت کنید. این فرایند در تشخیص و پردازش خریدها بیشتر توضیح داده شده است.
رسید تراکنش حاوی «شناسه سفارش» یا شناسه یکتای تراکنش نیز به کاربر ایمیل میشود. کاربران برای هر خرید محصول یکباره و همچنین برای خرید اشتراک اولیه و تمدیدهای خودکار تکرارشونده بعدی، ایمیلی با «شناسه سفارش» منحصربهفرد دریافت میکنند. میتوانید از «شناسه سفارش» برای مدیریت بازپرداختها در «کنسول Google Play» استفاده کنید.
قیمت شخصیسازیشده را نشان دهید
اگر برنامه شما میتواند برای کاربران ساکن اتحادیه اروپا توزیع شود، هنگام فراخوانی launchBillingFlow از روش
setIsOfferPersonalized() استفاده کنید تا به کاربران اطلاع دهید که قیمت محصول بااستفاده از تصمیمگیری خودکار شخصیسازی شده است.
باید با «ماده» مشورت کنید. ماده ۶ (۱) (ea) از «دستور حقوق مصرفکننده» ۲۰۱۱/۸۳/EU برای تشخیص اینکه آیا قیمتی که به کاربران ارائه میدهید شخصی شده است یا نه.
setIsOfferPersonalized() ورودی بولی میگیرد. وقتی true، واسط کاربر Play
شامل شفافسازی است. وقتی false، میانای کاربر شفافسازی را حذف میکند. مقدار پیشفرض
false است.
برای اطلاعات بیشتر، به مرکز راهنمایی مصرفکننده مراجعه کنید.
پیوست کردن شناسههای کاربر
وقتی جریان خرید را راهاندازی میکنید، برنامهتان میتواند هر شناسه کاربری را که برای کاربر خریدار دارید بااستفاده از obfuscatedAccountId یا obfuscatedProfileId پیوست کند. یک شناسه نمونه میتواند نسخه مبهمشدهای از ورود کاربر به سیستم شما باشد. تنظیم این پارامترها میتواند به Google کمک کند کلاهبرداری را شناسایی کند. علاوهبراین، میتواند به شما کمک کند مطمئن شوید که خریدها به کاربر مناسب نسبت داده شده است، همانطور که در اعطای حق استفاده به کاربران بحث شد.
تشخیص و پردازش خریدها
تشخیص و پردازش خرید شرحدادهشده در این بخش برای همه انواع خریدها، ازجمله خریدهای خارج از برنامه مانند پسخرید تبلیغ، قابلاجرا است.
برنامه شما خریدهای جدید و خریدهای معلقه تکمیلشده را به یکی از روشهای زیر تشخیص میدهد:
- وقتی
onPurchasesUpdatedدرنتیجه فراخوانی برنامهتان فراخوانده میشودlaunchBillingFlow(همانطور که در بخش قبلی توضیح داده شد) یا اگر برنامهتان با اتصال فعال «کتابخانه خدمات صورتحساب» اجرا میشود، وقتی خریدی خارج از برنامهتان انجام میشود یا خرید معلقهای تکمیل میشود. برای مثال، یکی از اعضای خانواده خرید معلقه را در دستگاه دیگری تأیید میکند. - وقتی برنامهتان queryPurchasesAsync را برای پرسمان خریدهای کاربر فراخوانی میکند.
برای #1، onPurchasesUpdatedتا زمانی که برنامه شما درحال اجرا باشد و اتصال «کتابخانه
خدمات صورتحساب Google Play» فعال داشته باشد، بهطور خودکار برای خریدهای جدید یا تکمیلشده
فراخوانده خواهد شد. اگر برنامه شما درحال اجرا نباشد یا برنامه شما اتصال فعال به کتابخانه «خدمات صورتحساب Google Play» نداشته باشد، onPurchasesUpdated فراخوانی نخواهد شد. بهخاطر داشته باشید که توصیه میشود برنامهتان تا زمانی که در پیشنما است، اتصال فعالی را حفظ کند تا بهروزرسانیهای خرید را بهموقع دریافت کند.
برای مورد ۲، باید BillingClient.queryPurchasesAsync() را فراخوانی کنید تا مطمئن شوید برنامهتان همه خریدها را پردازش میکند. توصیه میشود این کار را زمانی انجام دهید که برنامهتان با موفقیت با «کتابخانه خدمات صورتحساب Google Play» ارتباط برقرار کند (که توصیه میشود وقتی برنامهتان راهاندازی میشود یا به پیشزمینه میآید انجام شود، همانطور که در راهاندازی BillingClient بحث شده است. این کار را میتوان با فراخوانی queryPurchasesAsync هنگام دریافت نتیجه موفقیتآمیز برای onServiceConnected انجام داد. دنبال کردن این توصیه برای مدیریت رویدادها و موقعیتهایی مثل موارد زیر حیاتی است:
- مشکلات شبکه درطول خرید: کاربر میتواند خرید موفقی انجام دهد و تأییدیه را از Google دریافت کند، اما دستگاه او اتصال شبکه را قبلاز اینکه دستگاه و برنامه شما اعلان خرید را ازطریق
PurchasesUpdatedListenerدریافت کند، ازدست میدهد. - چندین دستگاه: کاربر ممکن است کالایی را در یک دستگاه بخرد و سپس انتظار داشته باشد که وقتی دستگاهش را عوض میکند آن کالا را ببیند.
- مدیریت خریدهای انجامشده در خارج از برنامه: برخیاز خریدها، مثل پسخریدن تبلیغ، میتواند در خارج از برنامه انجام شود.
- مدیریت انتقالهای حالت خرید: ممکن است کاربر پرداخت خرید «معلقه» را زمانی تکمیل کند که برنامه شما درحال اجرا نیست و انتظار داشته باشد که هنگام باز کردن برنامه شما، تأییدیه تکمیل خرید را دریافت کند.
- اشتراکهای تعلیقشده: اشتراک ممکن است درطول
چرخه حیات اشتراک تعلیق شود. BillingClient.queryPurchasesAsync()
فقط درصورتی اشتراکهای تعلیقشده را برمیگرداند که پارامتر
includeSuspendedSubscriptionsرویQueryPurchasesParams.Builderتنظیم شده باشد. اشتراکهای تعلیقشده درPurchasesUpdatedListenerبرگردانده نمیشوند.
وقتی برنامهتان خرید جدید یا تکمیلشدهای را تشخیص داد، برنامه شما باید:
- خرید را درستیسنجی کنید.
- محتوا را برای خریدهای تکمیلشده به کاربر اعطا کنید.
- به کاربر اطلاع دهید.
- به Google اطلاع دهید که برنامهتان خریدهای تکمیلشده را پردازش کرده است.
این مراحل در بخشهای زیر بهتفصیل مورد بحث قرار گرفتهاند و پساز آن بخشی برای خلاصه کردن همه مراحل آمده است.
درستیسنجی خرید
برنامه شما باید همیشه قبلاز اعطای مزایا به کاربر، قانونی بودن خریدها را درستیسنجی کند. این کار را میتوان با دنبال کردن دستورالعملهای شرحدادهشده در درستیسنجی خریدها قبلاز اعطای حقوق انجام داد. برنامه شما باید فقط پساز درستیسنجی خرید، پردازش خرید را ادامه دهد و حقوق استفاده را به کاربر اعطا کند، که در بخش بعدی درباره آن بحث میشود. اگر خریدی در بررسیهای درستیسنجی، واجدشرایط بودن، یا سوءاستفاده رد شد، به رسیدگی به خریدهای ردشده یا تکمیلنشده مراجعه کنید.
اعطای حق استفاده به کاربر
پساز اینکه برنامهتان خرید را درستیسنجی کرد، میتواند به اعطای حق مالکیت به کاربر ادامه دهد و به کاربر اطلاع دهد. قبلاز اعطای حق مالکیت، تأیید کنید که برنامه شما بررسی میکند که وضعیت خرید PURCHASED باشد. اگر خرید در وضعیت «معلقه» باشد، برنامه شما باید به کاربر اطلاع دهد که هنوز باید اقداماتی را برای تکمیل خرید قبلاز اعطای حق استفاده انجام دهد.
فقط زمانی حق استفاده را اعطا کنید که خرید از حالت «درحال انتظار» به «خریداریشده» تغییر کند.
اطلاعات بیشتر را میتوانید در رسیدگی به تراکنشهای معلق پیدا کنید.
اگر شناسههای کاربر را به خرید پیوست کردهاید، همانطور که در پیوست کردن شناسههای کاربر توضیح داده شد، میتوانید آنها را بازیابی کنید و از آنها برای نسبت دادن به کاربر صحیح در سیستم خود استفاده کنید. این تکنیک هنگام تطبیق خریدها مفید است جایی که برنامه شما ممکن است زمینه مربوط به اینکه خرید برای کدام کاربر است را ازدست داده باشد. برای محافظت دربرابر سوءاستفاده، Google Play توصیه میکند که این شناسهها را پیشاز اعطای حقوق در پسای امن خود درستیسنجی کنید. برای اطلاعات بیشتر، درستیسنجی خریدها پیشاز اعطای حقوق را ببینید. توجه داشته باشید که خریدهای انجامشده در خارج از برنامه شما این شناسهها را تنظیم نخواهند کرد. در این حالت، برنامهتان میتواند حق استفاده را به کاربر واردشده به سیستم اعطا کند یا از کاربر بخواهد حساب ترجیحی را انتخاب کند.
برای پیشسفارشها، خرید قبلاز رسیدن به زمان انتشار در وضعیت «معلقه» قرار دارد. خرید پیشسفارش در زمان انتشار تکمیل میشود و بدون کنشهای اضافی، وضعیت به «خریداریشده» تغییر میکند.
به کاربر اطلاع دهید
پساز اعطای حق استفاده به کاربر، برنامهتان باید اعلانی برای تأیید خرید موفق نشان دهد. بهدلیل این اعلان، کاربر درباره اینکه خرید باموفقیت تکمیل شده است یا نه دچار سردرگمی نمیشود، که میتواند منجر به توقف استفاده کاربر از برنامه شما، تماس با پشتیبانی کاربر، یا شکایت از آن در رسانههای اجتماعی شود. توجه داشته باشید که برنامهتان ممکن است در هر زمانی درطول چرخه حیات برنامهتان، بهروزرسانیهای خرید را شناسایی کند. برای مثال، ولی خرید معلقه را در دستگاه دیگری تأیید میکند، در این حالت ممکن است برنامهتان بخواهد اعلان به کاربر را تا زمان مناسبی بهتأخیر بیندازد. چند نمونه از مواردی که تأخیر مناسب است:
- در بخش کنش بازی یا صحنههای سینمایی، نمایش پیام ممکن است کاربر را منحرف کند. در این حالت، باید پساز پایان بخش اقدام به کاربر اطلاع دهید.
- درطول بخشهای آموزش اولیه و راهاندازی کاربر در بازی. برای مثال، ممکن است کاربر قبلاز نصب برنامهتان، خریدی خارج از آن انجام داده باشد. توصیه میکنیم بلافاصله پساز اینکه کاربران جدید بازی را باز کردند یا درطول راهاندازی اولیه کاربر، پاداش را به آنها اطلاع دهید. اگر برنامهتان کاربر را ملزم میکند قبلاز اعطای حق استفاده، حساب ایجاد کند یا به سیستم وارد شود، توصیه میشود به کاربرتان اطلاع دهید که برای مطالبه خریدش چه مراحلی را باید تکمیل کند. این موضوع بسیار مهم است زیرا اگر برنامهتان خرید را پردازش نکرده باشد، خریدها پساز ۳ روز مسترد میشوند.
هنگام اطلاعرسانی به کاربر درباره خرید، Google Play سازوکارهای زیر را توصیه میکند:
- کادر گفتگوی درونبرنامهای نشان دهید.
- پیام را به صندوق پیام درونبرنامه ارسال کنید و بهوضوح بیان کنید که پیام جدیدی در صندوق پیام درونبرنامه وجود دارد.
- از پیام اعلان سیستمعامل استفاده کنید.
اعلان باید کاربر را از مزایایی که دریافت کرده است مطلع کند. برای مثال، «۱۰۰ سکه طلا خریدید!». علاوهبراین، اگر خرید نتیجه مزایای برنامهای مانند Play Pass باشد، برنامه شما این موضوع را به کاربر اطلاع میدهد. برای مثال «موارد دریافت شد! ۱۰۰ Gem با Play Pass دریافت کردید. ادامه بده.". هر برنامه ممکن است راهنماییهایی درباره نوشتار توصیهشده برای نمایش به کاربران جهت اطلاعرسانی درباره مزایا داشته باشد.
به Google اطلاع دهید که خرید پردازش شده است
پساز اینکه برنامهتان حق استفاده را به کاربر اعطا کرد و او را از موفقیتآمیز بودن تراکنش مطلع کرد، برنامهتان باید به Google اطلاع دهد که خرید با موفقیت پردازش شده است. این کار با تأیید خرید انجام میشود و باید ظرف سه روز انجام شود تا خرید بهطور خودکار مسترد نشود و حق استفاده لغو نشود. فرایند تأیید انواع مختلف خرید در بخشهای زیر شرح داده شده است.
محصولات مصرفی
برای محصولات مصرفی، اگر برنامه شما زیرینه امن دارد، توصیه میکنیم از
Purchases.products:consume برای مصرف قابلاعتماد خریدها استفاده کنید. با بررسی consumptionState از نتیجه فراخوانی Purchases.products:get، مطمئن شوید که خرید قبلاً مصرف نشده باشد. اگر برنامهتان فقط سمت کارخواه است و
پشتیبان ندارد، از consumeAsync() از
«کتابخانه خدمات صورتحساب Google Play» استفاده کنید. هر دو روش شرط تأیید را برآورده میکنند و نشان میدهند که برنامه شما حق استفاده را به کاربر اعطا کرده است.
این روشها همچنین به برنامه شما امکان میدهند محصول یکباره مربوط به
ژتون خرید ورودی را برای خرید مجدد دردسترس قرار دهد. با consumeAsync() باید
یک شیء را نیز که میانای ConsumeResponseListener را پیادهسازی میکند
ارسال کنید. این شیء نتیجه عملیات مصرف را مدیریت میکند. میتوانید
روش onConsumeResponse() را لغو کنید، که
«کتابخانه خدمات صورتحساب Google Play» پساز تکمیل عملیات آن را فراخوانی میکند.
مثال زیر مصرف محصولی را بااستفاده از «کتابخانه خدمات صورتحساب Google Play» و بااستفاده از کد خرید مرتبط نشان میدهد:
کاتلین
val consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build() val consumeResult = withContext(Dispatchers.IO) { client.consumePurchase(consumeParams) }
جاوا
ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build(); ConsumeResponseListener listener = (billingResult, purchaseToken) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK) { // Handle the success of the consume operation. } }; billingClient.consumeAsync(consumeParams, listener);
محصولات غیرمصرفی
برای تأیید خریدهای غیرمصرفی، اگر برنامه شما زیرینه امنی دارد، توصیه میکنیم از Purchases.products:acknowledge برای تأیید مطمئن خریدها استفاده کنید. با بررسی acknowledgementState از نتیجه تماس با Purchases.products:get مطمئن شوید که خرید قبلاً تأیید نشده است.
اگر برنامهتان فقط مشتری است، از BillingClient.acknowledgePurchase() از
«کتابخانه خدمات صورتحساب Google Play» در برنامهتان استفاده کنید. قبلاز تأیید
خرید، برنامهتان باید بااستفاده از روش
isAcknowledged() در «کتابخانه خدمات صورتحساب Google Play» بررسی کند که آیا قبلاً تأیید شده است یا نه.
مثال زیر نشان میدهد که چگونه بااستفاده از «کتابخانه خدمات صورتحساب Google Play» خرید را تأیید کنید:
کاتلین
val client: BillingClient = billingClient val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.purchaseToken) val ackPurchaseResult = withContext(Dispatchers.IO) { client.acknowledgePurchase(acknowledgePurchaseParams.build()) }
جاوا
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { if (!purchase.isAcknowledged()) { AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); billingClient.acknowledgePurchase(acknowledgePurchaseParams, (billingResult) -> { // Acknowledgment handled. }); } }
اشتراکها
اشتراکها مشابه محصولات غیرمصرفی مدیریت میشوند. درصورت امکان، از
Purchases.subscriptions.acknowledge از
Google Play Developer API استفاده کنید تا خرید را بهطور مطمئن از
پشتصحنه ایمن خود تأیید کنید. با بررسی acknowledgementState در منبع خرید از Purchases.subscriptions:get،
تأیید کنید که خرید قبلاً تأیید نشده است. درغیراینصورت، میتوانید پساز بررسی isAcknowledged()، بااستفاده از BillingClient.acknowledgePurchase() از «کتابخانه خدمات صورتحساب Google Play»، اشتراک را تأیید کنید. همه خریدهای اشتراک اولیه باید تأیید شوند. تمدید اشتراک
نیازی به تأیید ندارد. برای اطلاعات بیشتر درباره زمان اعلام کردن اشتراکها،
موضوع فروش اشتراک را ببینید.
رسیدگی به خریدهای ردشده یا انجامنشده
اگر خریدی در درستیسنجی باطن، بررسیهای واجدشرایط بودن کاربر، بررسیهای کلاهبرداری و سوءاستفاده ناموفق بود، یا اگر سیستم شما نمیتواند حق استفاده را برآورده کند، حق استفاده را اعطا نکنید و خرید را تأیید یا مصرف نکنید.
درحالیکه خریدهای تأییدنشده پساز منقضی شدن دوره تأیید بهعنوان یک شبکه ایمنی مصرفکننده بهطور خودکار مسترد و لغو میشوند، برای رد کردن خریدها به این دوره انقضا تکیه نکنید. اگر اجازه دهید پنجره تأیید منقضی شود، سفارش در وضعیت تأییدنشده باقی میماند و برای Google Play مبهم است، چراکه نمیتواند رد عمدی توسعهدهنده را از وقفه پردازش پشتصحنه تشخیص دهد.
درعوض، خریدهای ردشده را بلافاصله از زیرینه خود بااستفاده از Google Play Developer API باطل و مسترد کنید:
- اشتراکها: با
purchases.subscriptionsv2.revokeبا"fullRefund": {}درrevocationContextتماس بگیرید. این کار بلافاصله اشتراک را خاتمه میدهد، دسترسی را لغو میکند، و استرداد کامل مبلغ را آغاز میکند. - محصولات یکباره:
orders.refundرا با پارامترrevokeتنظیمشده رویtrueفراخوانی کنید تا سفارش فوراً مسترد شود و حق استفاده از آن لغو شود. هنگام فراخوانی این روش، باید شناسه سفارش خرید را ارسال کنید که میتوان آن را ازطریقPurchase.getOrderId()یاpurchases.productsv2.getproductpurchasev2بازیابی کرد. هرگز باconsumeAsync()،acknowledgePurchase()،Purchases.products:consume، یاPurchases.products:acknowledgeدرباره خریدهای ردشده تماس نگیرید. - رفتار RTDN و Voided Purchases: ابطالها و استردادهای آغازشده توسط توسعهدهنده (
purchases.subscriptionsv2.revokeوorders.refundباrevokeتنظیمشده رویtrue) «اعلانهای بیدرنگ توسعهدهنده» را راهاندازی میکنند و در Voided Purchases API (purchases.voidedpurchases.listبا فیلد پاسخvoidedSourceتنظیمشده روی1برای توسعهدهنده) ثبت میشوند:- اشتراکها: هم اعلان
SUBSCRIPTION_REVOKEDو همVoidedPurchaseNotificationرا ارسال میکند. - محصولات یکباره:
VoidedPurchaseNotification(و اگر برنامهتان در «کنسول Play» پیکربندی شده باشد که همه اعلانهای محصول یکباره را دریافت کند، اعلانONE_TIME_PRODUCT_CANCELED) ارسال میکند. - فراخوانی
orders.refundبدون تنظیمrevokeرویtrue، هزینه سفارش را بدون لغو کردن حق استفاده مسترد میکند و اعلان لغو یا خرید باطلشده را راهاندازی نمیکند. برای جزئیات بیشتر، مدیریت اشتراکها و خریدهای یکباره را ببینید.
- اشتراکها: هم اعلان
خلاصه
در زیر خلاصهای از این مراحل آورده شده است.
خرید را به زیرینه امن خود ارسال کنید تا خریدها را قبلاز اعطای حق مالکیت درستیسنجی کند.
فضای ذخیرهسازی استحقاقیتان را با خرید بهروز کنید. اگر خرید در وضعیت «درانتظار» است، مطمئن شوید که حق استفاده بهعنوان «درانتظار» علامتگذاری شده باشد و کاربر هنوز مزایا را دریافت نکرده باشد. توصیه میشود این مرحله در زیرینه امن شما انجام شود و میتواند در فراخوانی API به زیرینه شما در مرحله ۱ ترکیب شود.
بااستفاده از پیامرسانی مناسب به کاربر اطلاع دهید (درصورت نیاز، همانطور که قبلاً بحث شد، اعلان را بهتأخیر بیندازید).
بااستفاده از مراحلی که در بخش پردازش خریدها توضیح داده شده است، به Google اطلاع دهید که خرید پردازش شده است.
برای درستیسنجی اینکه برنامهتان این مراحل را بهدرستی پیادهسازی کرده است، میتوانید راهنمای آزمایش را دنبال کنید.
رسیدگی به تراکنشهای معلقه
Google Play از تراکنشهای معلقه یا تراکنشهایی که بین زمانی که کاربر خرید را شروع میکند و زمانی که روش پرداخت برای خرید پردازش میشود به یک یا چند مرحله اضافی نیاز دارند پشتیبانی میکند. برنامه شما نباید تا زمانی که Google به شما اطلاع دهد که هزینه روش پرداخت کاربر باموفقیت کسر شده است، حق مالکیت این نوع خریدها را اعطا کند.
برای مثال، کاربر میتواند با انتخاب فروشگاه فیزیکی که بعداً در آن با پول نقد پرداخت خواهد کرد، تراکنشی را آغاز کند. کاربر کدی ازطریق اعلان و ایمیل دریافت میکند. وقتی کاربر به فروشگاه فیزیکی میرسد، میتواند کد را به صندوقدار بدهد و با پول نقد پرداخت کند. سپس Google به شما و کاربر اطلاع میدهد که پرداخت دریافت شده است. سپس برنامهتان میتواند حق استفاده را به کاربر اعطا کند.
enablePendingPurchases() را بهعنوان بخشی از مقداردهی اولیه
BillingClient فراخوانی کنید تا تراکنشهای معلق برای برنامهتان فعال شود. برنامهتان باید
تراکنشهای معلق را برای محصولات یکباره فعال و پشتیبانی کند. قبلاز افزودن پشتیبانی، مطمئن شوید که چرخه حیات خرید برای تراکنشهای معلقه را درک میکنید.
وقتی برنامهتان خرید جدیدی را ازطریق
PurchasesUpdatedListener یا درنتیجه فراخوانی
queryPurchasesAsync دریافت میکند، از روش getPurchaseState() برای
تعیین اینکه وضعیت خرید PURCHASED یا PENDING است استفاده کنید. باید
حق استفاده را فقط زمانی اعطا کنید که وضعیت PURCHASED باشد.
اگر برنامهتان درحال اجرا باشد و اتصال «کتابخانه خدمات صورتحساب Play» فعال داشته باشید،
وقتی کاربر خرید را تکمیل میکند، PurchasesUpdatedListener شما
دوباره فراخوانی میشود و PurchaseState اکنون PURCHASED است. در این مرحله،
برنامه شما میتواند خرید را بااستفاده از روش استاندارد برای شناسایی و
پردازش خریدها پردازش کند. برنامه شما باید queryPurchasesAsync() را در روش onResume() برنامهتان نیز فراخوانی کند تا خریدهایی را که در زمانی که برنامهتان درحال اجرا نبوده است به حالت PURCHASED منتقل شدهاند مدیریت کند.
وقتی خرید از PENDING به PURCHASED تغییر وضعیت میدهد، کارخواه
real_time_developer_notifications اعلان ONE_TIME_PRODUCT_PURCHASED یا SUBSCRIPTION_PURCHASED دریافت میکند. اگر خرید لغو شود، اعلان ONE_TIME_PRODUCT_CANCELED یا SUBSCRIPTION_PENDING_PURCHASE_CANCELED دریافت خواهید کرد. این اتفاق میتواند زمانی رخ دهد که مشتری شما پرداخت را در بازه زمانی موردنیاز تکمیل نکند. توجه داشته باشید که همیشه میتوانید از Google Play Developer API برای بررسی وضعیت فعلی خرید استفاده کنید.
مدیریت خریدهای چندتایی
در نسخههای ۴.۰ و بالاتر «کتابخانه خدمات صورتحساب Google Play» پشتیبانی میشود، Google Play به مشتریان اجازه میدهد با مشخص کردن تعداد در سبد خرید، بیشاز یک عدد از یک محصول یکباره را در یک تراکنش خریداری کنند. انتظار میرود برنامه شما خریدهای چندتایی را مدیریت کند و براساس تعداد خرید مشخصشده، حق استفاده اعطا کند.
برای رعایت خریدهای چندمقداری، منطق آمادهسازی برنامه شما باید مقدار محصول را بررسی کند. میتوانید از یکی از میاناهای برنامهسازی کاربردی زیر به فیلد quantity دسترسی پیدا کنید:
getQuantity()از «کتابخانه خدمات صورتحساب Google Play».Purchases.products.quantityاز Google Play Developer API
پساز اینکه منطق را برای مدیریت خریدهای چندتایی اضافه کردید، باید ویژگی چندتایی را برای محصول مربوطه در صفحه مدیریت محصول یکباره در «کنسول Google Play Developer» فعال کنید.
پیکربندی صورتحساب کاربر را پُرسمان میکند
getBillingConfigAsync() کشوری را که کاربر برای Google Play استفاده میکند ارائه میدهد.
پساز ایجاد BillingClient، میتوانید پیکربندی صورتحساب کاربر را پُرسمان کنید. تکه کد زیر نحوه تماس با getBillingConfigAsync() را شرح میدهد. با پیادهسازی
BillingConfigResponseListener، پاسخ را مدیریت کنید. این شنونده بهروزرسانیهای همه پُرسمانهای پیکربندی
صورتحساب را که از برنامهتان آغاز شده است دریافت میکند.
اگر BillingResult برگرداندهشده حاوی هیچ خطایی نباشد، میتوانید فیلد countryCode را در شیء BillingConfig بررسی کنید تا کشور Play کاربر را بهدست آورید.
کاتلین
// Use the default GetBillingConfigParams. val getBillingConfigParams = GetBillingConfigParams.newBuilder().build() billingClient.getBillingConfigAsync( getBillingConfigParams, object : BillingConfigResponseListener { override fun onBillingConfigResponse( billingResult: BillingResult, billingConfig: BillingConfig? ) { if (billingResult.responseCode == BillingResponseCode.OK && billingConfig != null ) { val countryCode = billingConfig.countryCode // ... } else { // Handle errors } } } )
جاوا
GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build(); billingClient.getBillingConfigAsync( getBillingConfigParams, (billingResult, billingConfig) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK && billingConfig != null) { String countryCode = billingConfig.getCountryCode(); } else { // TODO: Handle errors } });