کتابخانه صورت‌حساب Google Play را در برنامه خود ادغام کنید

این سند نحوه ادغام کتابخانه پرداخت گوگل پلی در برنامه شما را برای شروع فروش محصولات شرح می‌دهد.

عمر یک خرید

در اینجا یک جریان خرید معمول برای خرید یک‌باره یا اشتراک ارائه شده است.

  1. به کاربر نشان دهید که چه چیزهایی می‌تواند بخرد.
  2. جریان خرید را برای کاربر راه‌اندازی کنید تا خرید را بپذیرد.
  3. خرید را روی سرور خود تأیید کنید.
  4. محتوا را به کاربر بدهید.
  5. تحویل محتوا را تأیید کنید. برای محصولات مصرفی، خرید را مصرف کنید تا کاربر بتواند دوباره آن کالا را خریداری کند.

اشتراک‌ها تا زمان لغو به طور خودکار تمدید می‌شوند. یک اشتراک می‌تواند مراحل زیر را طی کند:

  • فعال : کاربر در وضعیت خوبی قرار دارد و به اشتراک دسترسی دارد.
  • لغو شده : کاربر لغو کرده است اما تا زمان انقضا همچنان دسترسی دارد.
  • در دوره مهلت : کاربر با مشکل پرداخت مواجه شده است، اما همچنان به سیستم دسترسی دارد، در حالی که گوگل در حال امتحان مجدد روش پرداخت است.
  • در انتظار : کاربر در پرداخت با مشکل مواجه شده و دیگر به سیستم دسترسی ندارد، در حالی که گوگل در حال امتحان مجدد روش پرداخت است.
  • متوقف شده : کاربر دسترسی خود را متوقف کرده و تا زمانی که دوباره آن را فعال نکند، دسترسی نخواهد داشت.
  • منقضی شده : کاربر اشتراک را لغو کرده و دسترسی به آن را از دست داده است. در زمان انقضا، کاربر از عضویت انصراف داده شده تلقی می‌شود.

اتصال به گوگل پلی را آغاز کنید

اولین قدم برای ادغام با سیستم پرداخت گوگل پلی، اضافه کردن کتابخانه پرداخت گوگل پلی به برنامه شما و ایجاد یک اتصال اولیه است.

وابستگی کتابخانه پرداخت گوگل پلی را اضافه کنید

همانطور که نشان داده شده است، وابستگی کتابخانه صورتحساب گوگل پلی را به فایل 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")
}

اگر از کاتلین استفاده می‌کنید، ماژول KTX کتابخانه صورتحساب گوگل پلی شامل افزونه‌ها و پشتیبانی از کوروتین‌های کاتلین است که به شما امکان می‌دهد هنگام استفاده از کتابخانه صورتحساب گوگل پلی، کاتلین ایدیوماتیک بنویسید. برای افزودن این افزونه‌ها به پروژه خود، وابستگی زیر را همانطور که نشان داده شده است به فایل 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

پس از افزودن وابستگی به کتابخانه صورتحساب گوگل پلی، باید یک نمونه از BillingClient را مقداردهی اولیه کنید. BillingClient رابط اصلی برای ارتباط بین کتابخانه صورتحساب گوگل پلی و بقیه برنامه شما است. BillingClient روش‌های راحتی، چه همزمان و چه غیرهمزمان، را برای بسیاری از عملیات رایج صورتحساب ارائه می‌دهد. به موارد زیر توجه کنید:

  • توصیه می‌شود که همزمان یک اتصال فعال BillingClient باز داشته باشید تا از فراخوانی‌های چندگانه PurchasesUpdatedListener برای یک رویداد واحد جلوگیری شود.
  • It's recommended to initiate a connection for the BillingClient when your app is launched or comes to the foreground to ensure your app processes purchases in a timely manner. This can be accomplished by using ActivityLifecycleCallbacks registered by registerActivityLifecycleCallbacks and listening for onActivityResumed to initialize a connection when you first detect an activity being resumed. Refer to the section on processing purchases for more details on why this best practice should be followed. Also remember to end the connection when your app is closed.

برای ایجاد یک 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();

اتصال به گوگل پلی

پس از ایجاد BillingClient ، باید به Google Play متصل شوید.

برای اتصال به گوگل پلی، startConnection را فراخوانی کنید. فرآیند اتصال ناهمزمان است و شما باید یک BillingClientStateListener پیاده‌سازی کنید تا پس از اتمام راه‌اندازی کلاینت و آماده شدن آن برای ارسال درخواست‌های بیشتر، یک فراخوانی مجدد دریافت کند.

If you haven't enabled automatic service reconnection , you must also implement retry logic to handle lost connections to Google Play. To implement retry logic, override the onBillingServiceDisconnected() callback method, and make sure that the BillingClient calls the startConnection() method to reconnect to Google Play before making further requests. If you have enabled automatic service reconnection , you can implement this method as a no-op.

مثال زیر نحوه شروع یک اتصال و آزمایش آماده بودن آن برای استفاده را نشان می‌دهد:

کاتلین

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

برقراری مجدد خودکار اتصال

With the introduction of the enableAutoServiceReconnection() method in BillingClient.Builder in version 8.0.0, the Play Billing Library can now automatically re-establish the service connection if an API call is made while the service is disconnected. This can lead to a reduction in SERVICE_DISCONNECTED responses since the reconnection is handled internally before the API call is made.

نحوه فعال کردن اتصال مجدد خودکار

هنگام ساخت یک نمونه 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();

نمایش محصولات موجود برای خرید

پس از برقراری اتصال به گوگل پلی، آماده‌اید تا محصولات موجود خود را جستجو کرده و آنها را به کاربران خود نمایش دهید.

جستجوی جزئیات محصول، گامی مهم قبل از نمایش محصولات به کاربران است، زیرا اطلاعات محصول بومی‌سازی‌شده را برمی‌گرداند. برای اشتراک‌ها، مطمئن شوید که نمایش محصول شما از همه خط‌مشی‌های Play پیروی می‌کند .

برای جستجوی جزئیات محصول یکبار مصرف، متد queryProductDetailsAsync را فراخوانی کنید. این متد می‌تواند بر اساس پیکربندی محصول یکبار مصرف شما، چندین پیشنهاد را برگرداند. برای اطلاعات بیشتر، به گزینه‌های خرید چندگانه و پیشنهادات برای محصولات یکبار مصرف مراجعه کنید.

برای مدیریت نتیجه عملیات ناهمزمان، باید یک شنونده (listener) نیز مشخص کنید که رابط 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 باشد.

پرس و جو با افزونه‌های کاتلین

اگر از افزونه‌های کاتلین استفاده می‌کنید ، می‌توانید با فراخوانی تابع افزونه‌ی queryProductDetails() جزئیات محصول یک‌بار مصرف را جستجو کنید.

queryProductDetails() از کوروتین‌های کاتلین استفاده می‌کند، بنابراین نیازی به تعریف یک شنونده جداگانه ندارید. در عوض، تابع تا زمان تکمیل پرس‌وجو به حالت تعلیق در می‌آید و پس از آن می‌توانید نتیجه را پردازش کنید:

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 Billing Library 7 بیاموزید.

نتیجه را پردازش کنید

The Google Play Billing Library stores the query results in a QueryProductDetailsResult object. QueryProductDetailsResult contains a List of ProductDetails objects. You can then call a variety of methods on each ProductDetails object in the list to view relevant information about a successfully fetched one-time product, such as its price or description. To view the available product detail information, see the list of methods in the ProductDetails class.

QueryProductDetailsResult همچنین شامل List از اشیاء UnfetchedProduct است. سپس می‌توانید برای دریافت کد وضعیت مربوط به دلیل عدم موفقیت در واکشی، از هر UnfetchedProduct پرس‌وجو کنید. برای مشاهده اطلاعات محصول unfetched موجود، به لیست متدهای موجود در کلاس UnfetchedProduct مراجعه کنید.

قبل از ارائه یک کالا برای فروش، بررسی کنید که کاربر از قبل آن کالا را نداشته باشد. اگر کاربر کالای مصرفی دارد که هنوز در کتابخانه کالای او موجود است، باید قبل از خرید مجدد آن کالا، آن را مصرف کند.

قبل از ارائه اشتراک، مطمئن شوید که کاربر قبلاً مشترک نشده باشد. همچنین به موارد زیر توجه کنید:

  • For subscriptions, the queryProductDetailsAsync() method returns subscription product details and a maximum of 50 user eligible offers per subscription. If the user attempts to purchase an ineligible offer (for example, if the app is displaying an outdated list of eligible offers), Play informs the user that they are ineligible, and the user can choose to purchase the base plan instead.

  • برای محصولات یک‌بارمصرف، متد 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() ، سیستم صفحه خرید گوگل پلی را نمایش می‌دهد. شکل 1 صفحه خرید اشتراک را نشان می‌دهد:

صفحه خرید گوگل پلی، اشتراکی را نشان می‌دهد که برای خرید در دسترس است
شکل ۱. صفحه خرید گوگل پلی، اشتراکی را نشان می‌دهد که برای خرید در دسترس است.

گوگل پلی تابع 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.
  }
}

یک خرید موفق، صفحه‌ای مشابه شکل ۲ با عنوان «خرید موفق در گوگل پلی» ایجاد می‌کند.

صفحه موفقیت خرید گوگل پلی
شکل ۲. صفحه موفقیت خرید در گوگل پلی.

A successful purchase also generates a purchase token, which is a unique identifier that represents the user and the product ID for the one-time product they purchased. Your apps can store the purchase token locally, though we strongly recommend passing the token to your secure backend server where you can then verify the purchase and protect against fraud. This process is further described in Detecting and Processing Purchases .

همچنین رسید تراکنش حاوی شناسه سفارش یا شناسه منحصر به فرد تراکنش برای کاربر ایمیل می‌شود. کاربران برای هر خرید یکباره محصول و همچنین برای خرید اولیه اشتراک و تمدیدهای خودکار بعدی، ایمیلی با شناسه سفارش منحصر به فرد دریافت می‌کنند. می‌توانید از شناسه سفارش برای مدیریت بازپرداخت‌ها در کنسول گوگل پلی استفاده کنید.

قیمت شخصی‌سازی‌شده را اعلام کنید

اگر برنامه شما می‌تواند در اتحادیه اروپا بین کاربران توزیع شود، هنگام فراخوانی launchBillingFlow از متد setIsOfferPersonalized() استفاده کنید تا به کاربران اطلاع دهید که قیمت یک کالا با استفاده از تصمیم‌گیری خودکار شخصی‌سازی شده است.

صفحه خرید گوگل پلی که نشان می‌دهد قیمت برای کاربر سفارشی‌سازی شده است.
شکل ۳. صفحه خرید گوگل پلی که نشان می‌دهد قیمت برای کاربر سفارشی‌سازی شده است.

برای تعیین اینکه آیا قیمتی که به کاربران ارائه می‌دهید شخصی‌سازی شده است یا خیر، باید به ماده 6 (1) (ea) CRD از دستورالعمل حقوق مصرف‌کننده 2011/83/EU مراجعه کنید.

setIsOfferPersonalized() یک ورودی بولی می‌گیرد. وقتی true ، رابط کاربری Play شامل افشای اطلاعات می‌شود. وقتی false ، رابط کاربری افشای اطلاعات را حذف می‌کند. مقدار پیش‌فرض آن false است.

برای اطلاعات بیشتر به مرکز پشتیبانی مصرف‌کنندگان مراجعه کنید.

شناسه‌های کاربر را پیوست کنید

When you launch the purchase flow your app can attach any user identifiers you have for the user making the purchase using obfuscatedAccountId or obfuscatedProfileId . An example identifier could be an obfuscated version of the user's login in your system. Setting these parameters can help Google detect fraud . Additionally, it can help you ensure that purchases are attributed to the right user as discussed in granting entitlements to users .

تشخیص و پردازش خریدها

تشخیص و پردازش خریدی که در این بخش شرح داده شده است، برای همه انواع خریدها، از جمله خریدهای خارج از برنامه مانند بازخریدهای تبلیغاتی، قابل اجرا است.

برنامه شما خریدهای جدید و خریدهای در حال انجام را به یکی از روش‌های زیر تشخیص می‌دهد:

  1. وقتی onPurchasesUpdated در نتیجه فراخوانی launchBillingFlow توسط برنامه شما (همانطور که در بخش قبلی بحث شد) فراخوانی می‌شود، یا اگر برنامه شما با اتصال فعال Billing Library در حال اجرا باشد، زمانی که خریدی خارج از برنامه شما انجام شده یا خریدی در حال انتظار تکمیل شده است. به عنوان مثال، یکی از اعضای خانواده خریدی در حال انتظار را در دستگاه دیگری تأیید می‌کند.
  2. وقتی برنامه شما queryPurchasesAsync را برای پرس و جو از خریدهای کاربر فراخوانی می‌کند.

For #1 onPurchasesUpdated will automatically be called for new or completed purchases as long as your app is running and has an active Google Play Billing Library connection. If your application is not running or your app doesn't have an active Google Play Billing library connection, onPurchasesUpdated won't be invoked. Remember, it is recommended for your app to try to keep an active connection as long as your app is in the foreground so that your app gets timely purchase updates.

For #2 you must call BillingClient.queryPurchasesAsync() to ensure your app processes all purchases. It is recommended that you do this when your app successfully establishes a connection with the Google Play Billing Library (which is recommended when your app is launched or comes to the foreground as discussed in initialize a BillingClient . This can be accomplished by calling queryPurchasesAsync when receiving a successful result to onServiceConnected . Following this recommendation is critical to handle events and situations such as:

  • مشکلات شبکه در حین خرید : یک کاربر می‌تواند خرید موفقی انجام دهد و از گوگل تأیید دریافت کند، اما دستگاه او قبل از اینکه دستگاه و برنامه شما از طریق PurchasesUpdatedListener اعلان خرید را دریافت کند، اتصال به شبکه را از دست می‌دهد.
  • چندین دستگاه : یک کاربر ممکن است یک کالا را در یک دستگاه خریداری کند و سپس انتظار داشته باشد که هنگام تغییر دستگاه، همان کالا را ببیند.
  • مدیریت خریدهای انجام شده خارج از برنامه شما : برخی از خریدها، مانند بازخریدهای تبلیغاتی، می‌توانند خارج از برنامه شما انجام شوند.
  • مدیریت انتقال وضعیت خرید : ممکن است کاربری در حالی که برنامه شما در حال اجرا نیست، پرداخت یک خرید در حال انتظار را تکمیل کند و انتظار داشته باشد هنگام باز کردن برنامه شما، تأیید تکمیل خرید را دریافت کند.
  • اشتراک‌های معلق : یک اشتراک ممکن است در طول چرخه حیات اشتراک به حالت تعلیق درآید. BillingClient.queryPurchasesAsync() فقط در صورتی اشتراک‌های معلق را برمی‌گرداند که پارامتر includeSuspendedSubscriptions روی QueryPurchasesParams.Builder تنظیم شده باشد. اشتراک‌های معلق در PurchasesUpdatedListener برگردانده نمی‌شوند.

به محض اینکه برنامه شما یک خرید جدید یا تکمیل شده را تشخیص دهد، باید:

  • خرید را تأیید کنید.
  • برای خریدهای تکمیل‌شده، به کاربر محتوا اعطا کنید.
  • به کاربر اطلاع دهید.
  • به گوگل اطلاع دهید که برنامه شما خریدهای تکمیل‌شده را پردازش کرده است.

این مراحل در بخش‌های بعدی به تفصیل مورد بحث قرار گرفته و پس از آن بخشی برای خلاصه کردن تمام مراحل ارائه شده است.

خرید را تأیید کنید

برنامه شما باید همیشه قبل از اعطای مزایا به کاربر، مشروعیت خریدها را تأیید کند. این کار را می‌توان با پیروی از دستورالعمل‌های شرح داده شده در «تأیید خریدها قبل از اعطای حق امتیاز» انجام داد. تنها پس از تأیید خرید، برنامه شما باید به پردازش خرید و اعطای حق امتیاز به کاربر ادامه دهد، که در بخش بعدی مورد بحث قرار خواهد گرفت.

اعطای حق دسترسی به کاربر

Once your app has verified a purchase it can continue to grant the entitlement to the user and notify the user. Before granting entitlement, verify that your app is checking that the purchase state is PURCHASED . If the purchase is in PENDING state, your app should notify the user that they still need to complete actions to complete the purchase before entitlement is granted. Only grant entitlement when the purchase transitions from PENDING to PURCHASED. Additional information can be found in Handling pending transactions .

If you have attached user identifiers to the purchase as discussed in attaching user identifiers you can retrieve and use them to attribute to the correct user in your system. This technique is useful when reconciling purchases where your app may have lost context about which user a purchase is for. Note, purchases made outside your app won't have these identifiers set. In this case your app can either grant the entitlement to the logged in user, or prompt the user to select a preferred account.

برای پیش‌سفارش‌ها ، خرید قبل از رسیدن به زمان انتشار در حالت PENDING (در انتظار خرید) است. خرید پیش‌سفارش در زمان انتشار تکمیل می‌شود و بدون انجام اقدامات اضافی، وضعیت به PURCHASED (خریداری شده) تغییر می‌کند.

به کاربر اطلاع دهید

After granting entitlement to the user, your app should show a notification to acknowledge the successful purchase. Because of the notification, the user is not confused as to whether the purchase completed successfully, which could result in the user stopping using your app, contacting user support, or complaining about it on social media. Be aware that your app may detect purchase updates at any time during your application lifecycle. For example, a parent approves a pending purchase on another device, in which case your app may want to delay notifying the user to an appropriate time. Some examples where a delay would be appropriate are:

  • در طول بخش اکشن بازی یا میان‌پرده‌ها، نمایش یک پیام ممکن است حواس کاربر را پرت کند. در این صورت، باید پس از پایان بخش اکشن، به کاربر اطلاع دهید.
  • During the initial tutorial and user setup parts of the game. For example, a user may have made a purchase outside your app before installing it. We recommend you notify new users of the reward immediately after they open the game or during initial user setup. If your app requires the user to create an account or logging in before granting entitlement to the user it is recommended to communicate to your user which steps to complete to claim their purchase. This is critical since purchases are refunded after 3 days if your app has not processed the purchase.

هنگام اطلاع‌رسانی به کاربر در مورد خرید، گوگل پلی مکانیسم‌های زیر را توصیه می‌کند:

  • نمایش یک کادر محاوره‌ای درون برنامه‌ای
  • پیام را به یک صندوق پیام درون برنامه‌ای ارسال کنید و به وضوح بیان کنید که پیام جدیدی در صندوق پیام درون برنامه‌ای وجود دارد.
  • از پیام اعلان سیستم عامل استفاده کنید.

The notification should notify the user about the benefit they received. For example, "You purchased 100 Gold Coins!". Additionally, if the purchase was a result of a benefit of a program such as Play Pass your app communicates this to the user. For example "Items received! You just got 100 Gems with Play Pass. Continue.". Each program may have guidance on the recommended text to display to users to communicate benefits.

به گوگل اطلاع دهید که خرید پردازش شده است

After your app has granted entitlement to the user and notified them about the successful transaction, your app needs to notify Google that the purchase was successfully processed. This is done by acknowledging the purchase and must be done within three days so that the purchase isn't automatically refunded and entitlement revoked . The process for acknowledging different types of purchases is described in the following sections.

محصولات مصرفی

For consumables, if your app has a secure backend, we recommend that you use Purchases.products:consume to reliably consume purchases. Make sure the purchase wasn't already consumed by checking the consumptionState from the result of calling Purchases.products:get . If your app is client-only without a backend, use consumeAsync() from the Google Play Billing Library. Both methods fulfill the acknowledgement requirement and indicate that your app has granted entitlement to the user. These methods also enable your app to make the one-time product corresponding to the input purchase token available for repurchase. With consumeAsync() you must also pass an object that implements the ConsumeResponseListener interface. This object handles the result of the consumption operation. You can override the onConsumeResponse() method, which the Google Play Billing Library calls when the operation is complete.

مثال زیر نحوه‌ی استفاده از یک محصول را با استفاده از کتابخانه‌ی پرداخت گوگل پلی و با استفاده از توکن خرید مربوطه نشان می‌دهد:

کاتلین

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

محصولات غیر مصرفی

برای تأیید خریدهای غیرقابل مصرف، اگر برنامه شما دارای یک backend امن است، توصیه می‌کنیم 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.
        });
    }
}

اشتراک‌ها

Subscriptions are handled similarly to non-consumables. If possible, use Purchases.subscriptions.acknowledge from the Google Play Developer API to reliably acknowledge the purchase from your secure backend. Verify that the purchase hasn't been previously acknowledged by checking the acknowledgementState in the purchase resource from Purchases.subscriptions:get . Otherwise, you can acknowledge a subscription using BillingClient.acknowledgePurchase() from the Google Play Billing Library after checking isAcknowledged() . All initial subscription purchases need to be acknowledged. Subscription renewals don't need to be acknowledged. For more information about when subscriptions need to be acknowledged, see the Sell subscriptions topic.

خلاصه

در ادامه خلاصه‌ای از این مراحل آمده است.

  1. قبل از اعطای مجوز، خرید را به پشتیبان امن خود ارسال کنید تا خریدها تأیید شوند .

  2. فضای ذخیره‌سازی حق امتیاز خود را با خرید به‌روزرسانی کنید. اگر خرید در حالت PENDING است، مطمئن شوید که حق امتیاز به عنوان Pending علامت‌گذاری شده است و کاربر هنوز مزایا را دریافت نکرده است. توصیه می‌شود این مرحله در backend امن شما انجام شود و می‌تواند در فراخوانی API به backend شما در مرحله 1 ترکیب شود.

  3. با استفاده از پیام‌رسانی مناسب به کاربر اطلاع دهید (در صورت نیاز، همانطور که قبلاً بحث شد، اطلاع‌رسانی را به تأخیر بیندازید).

  4. با استفاده از مراحلی که در بخش پردازش خریدها توضیح داده شد، به گوگل اطلاع دهید که خرید پردازش شده است.

برای تأیید اینکه برنامه شما این مراحل را به درستی اجرا کرده است، می‌توانید راهنمای آزمایش را دنبال کنید.

رسیدگی به تراکنش‌های در حال انتظار

گوگل پلی از تراکنش‌های در حال انتظار یا تراکنش‌هایی که نیاز به یک یا چند مرحله اضافی بین شروع خرید توسط کاربر و پردازش روش پرداخت برای خرید دارند، پشتیبانی می‌کند. برنامه شما نباید تا زمانی که گوگل به شما اطلاع ندهد که روش پرداخت کاربر با موفقیت انجام شده است، مجوز این نوع خریدها را اعطا کند.

For example, a user can initiate a transaction by choosing a physical store where they'll pay later with cash. The user receives a code through both notification and email. When the user arrives at the physical store, they can redeem the code with the cashier and pay with cash. Google then notifies both you and the user that payment has been received. Your app can then grant entitlement to the user.

فراخوانی enablePendingPurchases() به عنوان بخشی از مقداردهی اولیه BillingClient برای فعال کردن تراکنش‌های در حال انتظار برای برنامه شما. برنامه شما باید تراکنش‌های در حال انتظار را برای محصولات یک‌بار مصرف فعال و پشتیبانی کند. قبل از افزودن پشتیبانی، مطمئن شوید که چرخه عمر خرید برای تراکنش‌های در حال انتظار را درک می‌کنید.

وقتی برنامه شما خرید جدیدی را دریافت می‌کند، چه از طریق PurchasesUpdatedListener و چه در نتیجه فراخوانی queryPurchasesAsync ، از متد getPurchaseState() برای تعیین اینکه آیا وضعیت خرید PURCHASED است یا PENDING استفاده کنید. شما باید فقط زمانی که وضعیت PURCHASED است، مجوز را اعطا کنید.

If your app is running and you have an active Play Billing Library connection when the user completes the purchase, your PurchasesUpdatedListener is called again, and the PurchaseState is now PURCHASED . At this point, your app can process the purchase using the standard method for Detecting and Processing Purchases . Your app should also call queryPurchasesAsync() in your app's onResume() method to handle purchases that have transitioned to the PURCHASED state while your app was not running.

When the purchase transitions from PENDING to PURCHASED , your real_time_developer_notifications client receives a ONE_TIME_PRODUCT_PURCHASED or SUBSCRIPTION_PURCHASED notification. If the purchase is cancelled, you will receive a ONE_TIME_PRODUCT_CANCELED or SUBSCRIPTION_PENDING_PURCHASE_CANCELED notification. This can happen if your customer does not complete payment in the required timeframe. Note that you can always use the Google Play Developer API to check the current state of a purchase.

خریدهای چند مقداری را مدیریت کنید

گوگل پلی که در نسخه‌های ۴.۰ و بالاتر کتابخانه پرداخت گوگل پلی پشتیبانی می‌شود، به مشتریان این امکان را می‌دهد که با مشخص کردن تعداد از سبد خرید، بیش از یک محصول یکبار مصرف را در یک تراکنش خریداری کنند. انتظار می‌رود برنامه شما خریدهای چند مقداری را مدیریت کند و بر اساس تعداد خرید مشخص شده، حق خرید را اعطا کند.

برای پشتیبانی از خریدهای چند مقداری، منطق تأمین برنامه شما باید تعداد کالا را بررسی کند. می‌توانید از یکی از API های زیر به فیلد quantity دسترسی داشته باشید:

بعد از اینکه منطق مدیریت خریدهای چند مقداری را اضافه کردید، باید ویژگی چند مقداری را برای محصول مربوطه در صفحه مدیریت محصول یکبار مصرف در کنسول توسعه‌دهندگان گوگل پلی فعال کنید.

پرس و جو در مورد پیکربندی صورتحساب کاربر

getBillingConfigAsync() کشوری را که کاربر برای گوگل پلی استفاده می‌کند، ارائه می‌دهد.

You can query the user's billing configuration after creating a BillingClient . The following code snippet describes how to make a call to getBillingConfigAsync() . Handle the response by implementing the BillingConfigResponseListener . This listener receives updates for all billing config queries initiated from your app.

If the returned BillingResult contains no errors, you can then check the countryCode field in the BillingConfig object to obtain the user's Play Country.

Kotlin

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