این سند نحوه ادغام «کتابخانه خدمات صورتحساب 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» اکنون میتواند اگر درحالیکه سرویس قطع است تماسی با API برقرار شود، اتصال سرویس را بهطور خودکار مجدداً برقرار کند. این امر میتواند منجر به کاهش پاسخهای SERVICE_DISCONNECTED شود، زیرا اتصال مجدد بهصورت داخلی قبلاز تماس با API انجام میشود.
نحوه فعال کردن اتصال مجدد خودکار
هنگام ساختن نمونه 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) از «دستور حقوق مصرفکننده» 2011/83/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 بوده است، برنامه شما این موضوع را به کاربر اطلاع میدهد. برای مثال «موارد دریافت شد! همین حالا ۱۰۰ «الماس» با 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،
تأیید کنید که خرید قبلاً تأیید نشده است. درغیراینصورت، میتوانید اشتراک را بااستفاده از BillingClient.acknowledgePurchase() از «کتابخانه خدمات صورتحساب Google Play» پساز بررسی isAcknowledged() تأیید کنید. همه خریدهای اشتراک اولیه باید تأیید شوند. تمدید اشتراک
نیازی به تأیید ندارد. برای اطلاعات بیشتر درباره زمان تأیید اشتراکها، موضوع فروش اشتراکها را ببینید.
خلاصه
در زیر خلاصه این مراحل آمده است.
خرید را به زیرینه امن خود ارسال کنید تا خریدها را قبلاز اعطای حق مالکیت درستیسنجی کند.
فضای ذخیرهسازی حق مالکیت خود را با خرید بهروز کنید. اگر خرید در وضعیت «معلقه» است، مطمئن شوید که حق استفاده بهعنوان معلقه علامتگذاری شده است و کاربر هنوز مزایا را دریافت نمیکند. توصیه میشود این مرحله در زیرینه امن شما انجام شود و میتواند در فراخوانی API به زیرینه شما در مرحله ۱ ترکیب شود.
بااستفاده از پیامرسانی مناسب به کاربر اطلاع دهید (درصورت نیاز، همانطور که قبلاً بحث شد، اعلان را بهتأخیر بیندازید).
بااستفاده از مراحلی که در بخش پردازش خریدها توضیح داده شده است، به Google اطلاع دهید که خرید پردازش شده است.
برای درستیسنجی اینکه برنامهتان این مراحل را بهدرستی پیادهسازی کرده است، میتوانید راهنمای آزمایش را دنبال کنید.
رسیدگی به تراکنشهای معلقه
Google Play از تراکنشهای معلقه یا تراکنشهایی که بین زمانی که کاربر خرید را شروع میکند و زمانی که روش پرداخت برای خرید پردازش میشود به یک یا چند مرحله اضافی نیاز دارند پشتیبانی میکند. برنامه شما نباید تا زمانی که Google به شما اطلاع دهد که هزینه روش پرداخت کاربر باموفقیت کسر شده است، حق مالکیت این نوع خریدها را اعطا کند.
برای مثال، کاربر میتواند با انتخاب فروشگاه فیزیکی که بعداً در آن با پول نقد پرداخت خواهد کرد، تراکنشی را آغاز کند. کاربر کدی ازطریق اعلان و ایمیل دریافت میکند. وقتی کاربر به فروشگاه واقعی میرسد، میتواند کد را نزد صندوقدار پسخرید کند و با پول نقد پرداخت کند. سپس Google به شما و کاربر اطلاع میدهد که پرداخت دریافت شده است. سپس برنامه شما میتواند به کاربر حق مالکیت اعطا کند.
بهعنوان بخشی از مقداردهی اولیه BillingClient، با enablePendingPurchases() تماس بگیرید تا تراکنشهای معلقه را برای برنامهتان فعال کنید. برنامه شما باید تراکنشهای معلقه را برای محصولات یکباره فعال و پشتیبانی کند. قبلاز افزودن پشتیبانی، مطمئن شوید که چرخه حیات خرید را برای تراکنشهای معلقه درک میکنید.
وقتی برنامه شما خرید جدیدی دریافت میکند، چه ازطریق
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» فعال کنید.
پیکربندی صورتحساب کاربر را پُرسمان میکند
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 } });