משלבים את ספריית החיובים ב-Google Play באפליקציה

בנושא הזה נסביר איך לשלב את ספריית החיובים ב-Google Play עם את האפליקציה כדי להתחיל למכור מוצרים.

חיי הרכישה

זה תהליך הרכישה הרגיל של רכישה חד-פעמית או מינוי.

  1. צריך להראות למשתמשים מה הם יכולים לקנות.
  2. מפעילים את תהליך הרכישה כדי שהמשתמשים יאשרו את הרכישה.
  3. מאמתים את הרכישה בשרת.
  4. מספקים תוכן למשתמש.
  5. לאשר את מסירת התוכן. למוצרים מתכלים, כדי שהמשתמש יוכל לקנות אותו שוב.

מינויים מתחדשים באופן אוטומטי עד שהם מבוטלים. יש לך מינוי באמצעות המדינות הבאות:

  • פעיל: המשתמש במצב ניהול תקין ויש לו גישה למינוי.
  • בוטל: המשתמש ביטל את המינוי אבל עדיין יש לו גישה עד שהתוקף שלו פג.
  • בתקופת חסד: משתמש נתקל בבעיית תשלום אבל עדיין יש לו גישה בזמן ש-Google מנסה שוב את אמצעי התשלום.
  • בהמתנה: למשתמש הייתה בעיה בתשלום ואין לו יותר גישה בזמן Google מנסה שוב את אמצעי התשלום.
  • מושהית: המשתמש השהה את הגישה שלו ואין לו גישה עד שהוא בקורות החיים שלו.
  • התוקף פג: המשתמש ביטל את המינוי ואיבד את הגישה אליו. משתמש נחשב כנוטש כשהתוקף שלו פג.

אתחול החיבור ל-Google Play

השלב הראשון בשילוב עם מערכת החיוב של Google Play הוא להוסיף את ספריית החיובים ב-Google Play לאפליקציה שלך והפעלה של החיבור.

הוספת התלות של ספריית החיובים ב-Google Play

הוספת התלות של ספריית החיובים ב-Google Play אל build.gradle של האפליקציה קובץ כפי שמוצג:

מגניב

dependencies {
    def billing_version = "7.0.0"

    implementation "com.android.billingclient:billing:$billing_version"
}

Kotlin

dependencies {
    val billing_version = "7.0.0"

    implementation("com.android.billingclient:billing:$billing_version")
}

אם משתמשים ב-Kotlin, מודול KTX של ספריית החיובים ב-Google Play כולל תוספי Kotlin ותמיכה בקורוטינים שמאפשרים לכתוב אידיומטי Kotlin כשמשתמשים בספריית החיובים של Google Play. כדי לכלול את הרכיבים האלה: בפרויקט שלך, עליך להוסיף את התלות הבאה קובץ build.gradle כפי שמוצג:

מגניב

dependencies {
    def billing_version = "7.0.0"

    implementation "com.android.billingclient:billing-ktx:$billing_version"
}

Kotlin

dependencies {
    val billing_version = "7.0.0"

    implementation("com.android.billingclient:billing-ktx:$billing_version")
}

אתחול לקוח חיוב

לאחר שמוסיפים תלות בספריית החיובים ב-Google Play, צריך כדי לאתחל מכונה BillingClient. BillingClient הוא המשבצת הראשית ממשק התקשורת בין ספריית החיובים ב-Google Play שאר האפליקציה. BillingClient מספק שיטות נוחות, גם סינכרוניות ואסינכרונית, לביצוע פעולות חיוב נפוצות רבות. מומלץ מאוד שיש לכם חיבור BillingClient פעיל אחד שפתוח בכל פעם כדי להימנע מריבוי PurchasesUpdatedListener קריאות חוזרות (callback) במהלך אירוע יחיד.

כדי ליצור BillingClient, צריך להשתמש ב-newBuilder(). אפשר להעביר כל הקשר ל-newBuilder(), ו-BillingClient משתמש בו כדי לקבל הקשר של אפליקציה. המשמעות היא שאין צורך לחשוש מדליפות זיכרון. כדי לקבל עדכונים בנושא של רכישות, עליך לקרוא גם ל-setListener(), להעביר הפניה אל PurchasesUpdatedListener המאזינים האלה מקבלים עדכונים לכל רכישות באפליקציה שלך.

Kotlin

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

Java

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 לפני שמבצעים בקשות נוספות.

הדוגמה הבאה ממחישה איך להתחיל חיבור ולבדוק שהוא מוכן לשימוש:

Kotlin

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() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

Java

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

הצגת מוצרים שזמינים לקנייה

לאחר יצירת החיבור ל-Google Play, אפשר לשלוח שאילתה של המוצרים הזמינים שלכם ולהציג אותם למשתמשים.

שליחת פרטי המוצר היא שלב חשוב לפני הצגת של המשתמשים, כי הוא מחזיר פרטי מוצרים מותאמים לשוק המקומי. עבור מינויים, צריך לוודא שתצוגת המוצר עומדת בכל סעיפי המדיניות של Play.

כדי לשלוח שאילתה על פרטי מוצר מתוך האפליקציה, צריך להתקשר למספר queryProductDetailsAsync().

כדי לטפל בתוצאה של הפעולה האסינכרונית, עליך לציין גם שמטמיע את הממשק של ProductDetailsResponseListener. לאחר מכן אפשר לשנות את הערך של onProductDetailsResponse(), כדי שתישלח התראה ל: כשהשאילתה מסתיימת, כפי שמוצג בדוגמה הבאה:

Kotlin

val queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build()

billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
    billingResult,
    productDetailsList ->
      // check billingResult
      // process returned productDetailsList
}

Java

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,
                List<ProductDetails> productDetailsList) {
            // check billingResult
            // process returned productDetailsList
        }
    }
)

כשאתם שולחים שאילתה על פרטי מוצר, עליכם להעביר מופע של QueryProductDetailsParams שמציינת רשימת מחרוזות של מזהי מוצרים. שנוצר ב-Google Play Console יחד עם 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 5 guide.

עיבוד התוצאה

תוצאות השאילתה נשמרות בספריית החיובים של Google Play ב-List ProductDetails אובייקטים. לאחר מכן אפשר להפעיל מגוון שיטות בכל אובייקט אחד (ProductDetails) ברשימה כדי להציג מידע רלוונטי על אפליקציה בתוך האפליקציה של המוצר, כגון המחיר או התיאור שלו. כדי לצפות בפרטי המוצר הזמינים מידע נוסף, אפשר לראות את רשימת השיטות במחלקה ProductDetails.

לפני שמציעים פריט למכירה, צריך לבדוק שלמשתמש אין כבר בעלות על שימושי. אם למשתמש יש פריט מתכלה שעדיין נמצא בספריית הפריטים שלו, חייבים לצרוך את הפריט לפני שהם יוכלו לקנות אותו שוב.

לפני שמציעים מינוי, חשוב לוודא שהמשתמש עדיין לא רשום כמנוי. כמו כן, חשוב לשים לב לדברים הבאים:

  • פרטי המוצר במסגרת המינוי להחזרת מוצרים של queryProductDetailsAsync() עד 50 מבצעים לכל מינוי.
  • queryProductDetailsAsync() מחזיר רק מוצרים שעבורם המשתמש כשיר/ה. אם המשתמש מנסה לרכוש הצעה שבשבילה הוא לא כשיר (לדוגמה, אם האפליקציה מציגה רשימה מיושנת של מוצרים שעומדים בדרישות), מערכת Play מודיעה למשתמשים שהם לא עומדים בדרישות), המשתמש יכול לבחור לרכוש את המינוי הבסיסי במקום זאת.

הפעלת תהליך הרכישה

כדי להתחיל תהליך רכישה מהאפליקציה, צריך להתקשר למוקד של launchBillingFlow() מה-thread הראשי של האפליקציה. השיטה הזו מתייחסת אל אובייקט BillingFlowParams שמכיל את האובייקט הרלוונטי התקבל אובייקט ProductDetails מקריאה queryProductDetailsAsync(). כדי ליצור אובייקט BillingFlowParams, צריך להשתמש הכיתה BillingFlowParams.Builder.

Kotlin

// 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)
        // For One-time product, "setOfferToken" method shouldn't be called.
        // For subscriptions, to get an offer token, call ProductDetails.subscriptionOfferDetails()
        // 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)

Java

// An activity reference from which the billing flow will be launched.
Activity activity = ...;

ImmutableList<ProductDetailsParams> productDetailsParamsList =
    ImmutableList.of(
        ProductDetailsParams.newBuilder()
             // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
            .setProductDetails(productDetails)
            // For one-time products, "setOfferToken" method shouldn't be called.
            // For subscriptions, to get an offer token, call
            // ProductDetails.subscriptionOfferDetails() 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. איור 1 מציג מסך רכישה של מינוי:

במסך הרכישה ב-Google Play מוצג מינוי
            זמינים לרכישה
איור 1. במסך הרכישה ב-Google Play מוצג שזמין לרכישה.

Google Play מתקשרת למספר onPurchasesUpdated() כדי לספק את תוצאת הרכישה ל-listener שמטמיע את PurchasesUpdatedListener גרפי. ה-listener מצוין באמצעות ה-method setListener() כאשר אתחול הלקוח.

כדי לטפל בקודי תגובה אפשריים, עליכם להטמיע את onPurchasesUpdated(). הדוגמה הבאה מראה איך לבטל את onPurchasesUpdated():

Kotlin

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           handlePurchase(purchase)
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user cancelling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

Java

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user cancelling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

רכישה מוצלחת יוצרת מסך השלמת רכישה ב-Google Play, שדומה לזה איור 2.

מסך השלמת הרכישה ב-Google Play
איור 2. הרכישה בוצעה בהצלחה ב-Google Play מסך.

רכישה מוצלחת גם יוצרת אסימון רכישה, שהוא מזהה שמייצג את המשתמש ואת מזהה המוצר של המוצר מתוך האפליקציה שהם רכשו. האפליקציות שלך יכולות לאחסן את אסימון הרכישה באופן מקומי, למרות שאנחנו מומלץ להעביר את האסימון לשרת העורפי המאובטח, שבו תוכלו לאמת את הרכישה ולהגן מפני הונאה. התהליך הזה מפורט יותר בקטע הבא.

המשתמש יקבל גם קבלה באימייל על העסקה שמכילה את מזהה ההזמנה, או מזהה ייחודי של העסקה. המשתמשים מקבלים אימייל עם מזהה הזמנה ייחודי בכל רכישה חד-פעמית של מוצר, וגם עבור המינוי הראשוני של רכישות וחידושים אוטומטיים קבועים עתידיים. אפשר להשתמש במזהה ההזמנה כדי לנהל החזרים כספיים ב-Google Play Console.

ציון מחיר מותאם אישית

אם אפשר להפיץ את האפליקציה למשתמשים באיחוד האירופי, יש להשתמש ב שיטה setIsOfferPersonalized() להצגת גילוי נאות למשתמשים כשמחיר הפריט מותאמת אישית באמצעות קבלת החלטות אוטומטית.

מסך הרכישה ב-Google Play, שמציין שהמחיר הותאם אישית עבור המשתמש.
איור 3. מסך הרכישה ב-Google Play שמציין שהמחיר הותאם אישית למשתמש.

עליך להתייעץ עם סעיף 6 (1) (ea) CRD של Consumer Rights Directive 2011/83/EU כדי לקבוע אם המחיר שאתם מציעים למשתמשים הוא בהתאמה אישית.

setIsOfferPersonalized() מקבל קלט בוליאני. כאשר true, ממשק המשתמש של Play שכולל את הגילוי הנאות. כאשר false, ממשק המשתמש לא יחשוף את הגילוי הנאות. ברירת המחדל הערך הוא false.

מידע נוסף זמין במרכז העזרה לצרכנים.

מתבצע עיבוד של הרכישות

אחרי שמשתמש משלים רכישה, האפליקציה צריכה לעבד את הרכישה. ברוב המקרים, האפליקציה מקבלת התראות על רכישות דרך PurchasesUpdatedListener אבל יש מקרים שבהם האפליקציה יידע אותך לגבי רכישות על ידי חיוג אל BillingClient.queryPurchasesAsync() כמו שמתואר במאמר אחזור רכישות.

בנוסף, אם יש לכם לקוח התראות בזמן אמת למפתחים ב- בקצה העורפי המאובטח, תוכלו לרשום רכישות חדשות על ידי קבלת subscriptionNotification או oneTimeProductNotification עם התראות על רכישה חדשה. לאחר קבלת ההתראות האלה, אפשר להתקשר ל-Google Play API למפתחים כדי לקבל את הסטטוס המלא ולעדכן את מצב הקצה העורפי שלך.

האפליקציה שלך צריכה לעבד רכישה באופן הבא:

  1. מאמתים את הרכישה.
  2. לתת תוכן למשתמש ולאשר את מסירת התוכן. אפשר לסמן את הפריט כפריט שנצרך כדי שהמשתמש יוכל לקנות אותו שוב.

כדי לאמת רכישה, קודם צריך לוודא שמצב הרכישה הוא PURCHASED. אם הרכישה היא PENDING, עליך לעבד את purchase כפי שמתואר במאמר טיפול בעסקאות בהמתנה. לרכישות שהתקבלו מ-onPurchasesUpdated() או queryPurchasesAsync(), צריך לאמת עוד את הרכישה כדי להבטיח לגיטימיות לפני שהאפליקציה מעניקה אישור אם יש לכם הרשאה. כדי לקבל מידע נוסף על אימות רכישה, אפשר לעיין במאמר אימות רכישות לפני שמעניקים הרשאות.

לאחר אימות הרכישה, האפליקציה מוכנה להעניק הרשאה ל משתמש. אפשר לזהות את חשבון המשתמש שמשויך לרכישה באמצעות ProductPurchase.obfuscatedExternalAccountId הוחזר על ידי Purchases.products:get לרכישות של מוצרים מתוך האפליקציה SubscriptionPurchase.obfuscatedExternalAccountId הוחזר על ידי Purchases.subscriptions:get למינויים בצד השרת, או obfuscatedAccountId החל מ-Purchase.getAccountIdentifiers() בצד הלקוח, אם הוגדר עם setObfuscatedAccountId כאשר בוצעה.

לאחר הענקת ההרשאה, האפליקציה צריכה לאשר את הרכישה. הזה אישור מדווח ל-Google Play על כך שהענקתם הרשאה עבור הרכישה.

התהליך להענקת הרשאה ולאישור הרכישה תלוי בגורמים הבאים: הרכישה היא מינוי מתכלה, מינוי שאינו מתכלה או מינוי.

מוצרים מתכלים

למוצרים מתכלים, אם לאפליקציה שלך יש קצה עורפי מאובטח, מומלץ להשתמש Purchases.products:consume כדי לצרוך רכישות בצורה אמינה. צריך לוודא שהמאפיינים הרכישה לא נצפתה כבר על ידי בדיקת consumptionState של התוצאה של הקריאה ל-Purchases.products:get. אם האפליקציה מיועדת ללקוחות בלבד ללא קצה עורפי, צריך להשתמש ב-consumeAsync() ספריית החיובים ב-Google Play. שתי השיטות עונות על האישור ולציין שהאפליקציה שלך העניקה למשתמש הרשאה. השיטות האלה גם מאפשרות לאפליקציה ליצור את המוצר החד-פעמי שתואם ל- את אסימון הרכישה שהוזן שזמין לרכישה חוזרת. עם consumeAsync() איתך חייב גם להעביר אובייקט שמטמיע את ConsumeResponseListener גרפי. האובייקט הזה מטפל בתוצאה של פעולת הצריכה. אפשר לשנות את השיטה onConsumeResponse(), לאחר השלמת הפעולה, תופעל ספריית החיובים ב-Google Play.

הדוגמה הבאה ממחישה איך צריך להשתמש במוצר עם ספריית החיובים ב-Google Play עם אסימון הרכישה המשויך:

Kotlin

suspend fun handlePurchase(purchase: Purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener.
    val purchase : Purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    val consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build()
    val consumeResult = withContext(Dispatchers.IO) {
        client.consumePurchase(consumeParams)
    }
}

Java

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener.
    Purchase purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    ConsumeParams consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build();

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String 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:

Kotlin

val client: BillingClient = ...
val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ...

suspend fun handlePurchase() {
    if (purchase.purchaseState === PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged) {
            val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
            val ackPurchaseResult = withContext(Dispatchers.IO) {
               client.acknowledgePurchase(acknowledgePurchaseParams.build())
            }
        }
     }
}

Java

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
        }
    }
}

מינויים

הטיפול במינויים דומה לזה של מוצרים שאינם מתכלים. אם אפשר, השתמשו Purchases.subscriptions.acknowledge מתוך ממשק API של Google Play למפתחים כדי לאשר באופן מהימן את הרכישה מ- הקצה העורפי מאובטח. מוודאים שהרכישה לא אושרה לפני כן על ידי לבדוק את acknowledgementState במשאב הרכישה מ- Purchases.subscriptions:get אחרת, תוכלו לאשר מנוי באמצעות BillingClient.acknowledgePurchase() מ ספריית החיובים ב-Google Play אחרי הבדיקה isAcknowledged(). הכול רכישות ראשוניות של מינויים צריכות לכלול אישור. חידושי מינויים לא צריך לקבל הכרה. לקבלת מידע נוסף על מתי המינויים נדרש אישור. יש לעיין בנושא מכירת מינויים.

מתבצע אחזור של רכישות

האזנה לעדכוני רכישות באמצעות PurchasesUpdatedListener לא מספיקים כדי להבטיח שהאפליקציה תעבד את כל הרכישות. ייתכן ייתכן שהאפליקציה לא מודעת לכל הרכישות שהמשתמש ביצע. הנה כמה דוגמאות תרחישים שבהם אתם עלולים לאבד את המעקב אחר הרכישות באפליקציה שלכם או שהיא לא תהיה מודעת לרכישות:

  • בעיות ברשת במהלך הרכישה: משתמש מבצע רכישה מוצלחת ומקבל אישור מ-Google, אבל המכשיר שלו מתנתק מהרשת לפני שהמכשיר מקבל הודעה על הרכישה באמצעות PurchasesUpdatedListener.
  • מכשירים מרובים: משתמש קונה פריט במכשיר אחד ולאחר מכן מצפה לראות את הפריט כשהם מחליפים מכשיר.
  • טיפול ברכישות שבוצעו מחוץ לאפליקציה: חלק מהרכישות, כמו למימוש מבצעים מחוץ לאפליקציה.

כדי לטפל במצבים כאלה, צריך לוודא שהאפליקציה BillingClient.queryPurchasesAsync() בשיטה onResume() כדי לוודא שכל הרכישות מעובדות בהצלחה, כפי שמתואר בעיבוד רכישות.

הדוגמה הבאה מראה איך לאחזר נתונים של רכישות מינוי של משתמש. הערה: queryPurchasesAsync() מחזיר רק מינויים פעילים ו רכישות חד-פעמיות שלא נוצלו.

Kotlin

val params = QueryPurchasesParams.newBuilder()
               .setProductType(ProductType.SUBS)

// uses queryPurchasesAsync Kotlin extension function
val purchasesResult = billingClient.queryPurchasesAsync(params.build())

// check purchasesResult.billingResult
// process returned purchasesResult.purchasesList, e.g. display the plans user owns

Java

billingClient.queryPurchasesAsync(
    QueryPurchasesParams.newBuilder()
      .setProductType(ProductType.SUBS)
      .build(),
    new PurchasesResponseListener() {
      public void onQueryPurchasesResponse(BillingResult billingResult, List<Purchase> purchases) {
        // check billingResult
        // process returned purchase list, e.g. display the plans user owns

      }
    }
);

טיפול ברכישות שבוצעו מחוץ לאפליקציה

רכישות מסוימות, כמו מימוש מבצעים, יכולות להתבצע מחוץ לאפליקציה. כשמשתמשים מבצעים רכישה מחוץ לאפליקציה, הם מצפים שהאפליקציה תופיע הודעה בתוך האפליקציה, או להשתמש במנגנון התראות כלשהו כדי לאפשר למשתמש לדעת שהאפליקציה קיבלה את הרכישה ועיבדה בצורה נכונה. מקובל עליי מנגנונים הם:

  • הצגת חלון קופץ בתוך האפליקציה.
  • להעביר את ההודעה לתיבת ההודעות בתוך האפליקציה, ולציין בבירור שהיא היא הודעה חדשה בתיבת ההודעות בתוך האפליקציה.
  • להשתמש בהודעה של מערכת ההפעלה.

חשוב לזכור: האפליקציה יכולה להיות בכל מצב שבו היא מזהה את הרכישה. ייתכן אפילו שהאפליקציה שלך לא לאחר ביצוע הרכישה. המשתמשים מצפים לקבל את הרכישה כשהם ממשיכים להפעיל את האפליקציה, בלי קשר למצב שבו האפליקציה נמצאת.

עליך לזהות רכישות בלי קשר למצב שבו האפליקציה נמצאת בוצעה. עם זאת, יש כמה חריגים שבהם אנחנו עשויים לקבל את האישור. כדי לא להודיע למשתמש באופן מיידי שהפריט התקבל. לדוגמה:

  • בשלב האקשן במשחק, שבו הצגת הודעה עלולה להסיח את הדעת משתמש. במקרה כזה, צריך להודיע למשתמש אחרי שהחלק של הפעולה מסתיים.
  • במהלך קטעי מעבר, שבהם הצגת הודעה עלולה להסיח את דעת המשתמש. כאן עליכם להודיע למשתמש אחרי שקטע החיתוך מסתיים.
  • במהלך המדריך הראשוני ותהליך ההגדרה של המשתמש, בחלקים של המשחק. ההמלצות שלנו מודיעים למשתמשים חדשים על הפרס מיד לאחר שהם פותחים את המשחק, או במהלך ההגדרה הראשונית של המשתמש. עם זאת, ניתן להמתין עד רצף המשחקים זמין כדי ליידע את המשתמש.

תמיד חשוב לזכור את המשתמש כשהוא מחליטים מתי ואיך להודיע למשתמשים על רכישות שבוצעו מחוץ לאפליקציה. בכל פעם שמשתמש לא מקבל תשלום באופן מיידי התראה, הם עלולים לבלבל אותם, והם עלולים להפסיק להשתמש באפליקציה, ליצור קשר עם המשתמש או להתלונן על זה ברשתות החברתיות. הערה: PurchasesUpdatedListener רשום באפליקציה שלך הקשר לטיפול בעדכונים בנושא רכישות, כולל רכישות שבוצעו מחוץ לארגון של האפליקציה. המשמעות היא שאם תהליך הבקשה שלכם לא קיים, PurchasesUpdatedListener לא יקבל הודעה. לכן צריך לשפר את האפליקציה קוראים לפונקציה BillingClient.queryPurchasesAsync() בשיטה onResume() בתור שמוזכר בקטע אחזור רכישות.

טיפול בעסקאות בהמתנה

Google Play תומכת בעסקאות בהמתנה, או בעסקאות שמצריכות המרה אחת או שלבים נוספים בין הרגע שבו המשתמש מבצע רכישה לבין המועד שבו אמצעי התשלום לביצוע הרכישה נמצא בתהליך עיבוד. האפליקציה לא צריכה להעניק הרשאה לרכישות מהסוגים האלה עד ש-Google תודיע לכם אמצעי התשלום של המשתמש חויב בהצלחה.

לדוגמה, משתמש יכול ליזום עסקה על ידי בחירה בחנות פיזית שבו ישלמו מאוחר יותר במזומן. המשתמש מקבל קוד דרך בהודעה ובאימייל. כשהמשתמש מגיע לחנות הפיזית, יכול לממש את הקוד אצל הקופאי ולשלם במזומן. לאחר מכן Google שולחת התראה גם לך וגם למשתמש שהתשלום התקבל. לאחר מכן האפליקציה יכולה להעניק הרשאה למשתמש.

קוראים לפונקציה enablePendingPurchases() כחלק מאתחול הפקודה BillingClient כדי להפעיל עסקאות בהמתנה באפליקציה. האפליקציה שלך חייבת להפעיל ולתמוך בעסקאות בהמתנה עבור מוצרים בחיוב חד-פעמי. לפני הוספת תמיכה, ודאו שאתם מבינים את מחזור החיים של רכישות בהמתנה עסקאות.

כשהאפליקציה שלך מקבלת רכישה חדשה, דרך PurchasesUpdatedListener או כתוצאה משיחה queryPurchasesAsync(), צריך להשתמש בשיטה getPurchaseState() כדי לקבוע אם מצב הרכישה הוא PURCHASED או PENDING. אתם צריכים להעניק הרשאה רק כשהמדינה (State) היא PURCHASED.

אם האפליקציה שלך פועלת כשהמשתמש משלים את הרכישה, מתבצעת שיחה חוזרת אל PurchasesUpdatedListener, והPurchaseState נקרא PURCHASED בשלב הזה, האפליקציה תוכל לעבד את הרכישה באמצעות לעיבוד רכישות. האפליקציה צריכה להתקשר גם queryPurchasesAsync() בשיטה onResume() של האפליקציה שלך כדי לטפל ברכישות שעברו למצב PURCHASED בזמן שהאפליקציה לא פעלה.

כשהרכישה עוברת מ-PENDING אל PURCHASED, לקוח ההתראות בזמן אמת למפתחים מקבל ONE_TIME_PRODUCT_PURCHASED או התראת SUBSCRIPTION_PURCHASED. אם הרכישה תבוטל, מקבל ONE_TIME_PRODUCT_CANCELED או התראת SUBSCRIPTION_PENDING_PURCHASE_CANCELED. הדבר יכול להתרחש אם הלקוח לא משלים את התשלום במסגרת הזמן הנדרשת. שימו לב: יכולים להשתמש תמיד בממשק API של Google Play למפתחים כדי לבדוק את המצב הנוכחי רכישה.

טיפול ברכישות בכמות גדולה

נתמך בגרסאות 4.0 ואילך של ספריית החיובים ב-Google Play. ב-Google Play, לקוחות יכולים לקנות יותר מפריט אחד של אותה אפליקציה מוצר בעסקה אחת על ידי ציון כמות מעגלת הרכישה. שלך האפליקציה צפויה לטפל ברכישות בכמות גדולה ולהעניק הרשאה בהתאם בכמות הרכישה שצוינה.

כדי לאשר רכישות בכמות גדולה, צריך לבדוק את לוגיקת הקצאת ההרשאות של האפליקציה לכמות של פריט. אפשר לגשת לשדה quantity דרך ממשקי ה-API הבאים:

אחרי שמוסיפים לוגיקה לטיפול ברכישות בכמות גדולה, צריך להפעיל את התכונה 'כמות מרובה' למוצר המתאים באפליקציה דף ניהול המוצר ב-Google Play Console.

שליחת שאילתה לגבי הגדרות החיוב של המשתמש

getBillingConfigAsync() מציין את המדינה שעבורה המשתמש משתמש Google Play.

אפשר להריץ שאילתה על הגדרת החיוב של המשתמש אחרי ויוצרים BillingClient. קטע הקוד הבא מתאר איך להתקשר אל getBillingConfigAsync(). צריך לטפל בתשובה באמצעות מוטמעת BillingConfigResponseListener. המאזינים האלה מקבלים עדכונים לכל השאילתות של הגדרות החיוב שנשלחות מהאפליקציה שלך.

אם הערך של BillingResult שהוחזר לא מכיל שגיאות, אפשר לבדוק את השדה countryCode באובייקט BillingConfig כדי לקבל את ה-Play של המשתמש מדינה.

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 {
                // TODO: Handle errors
            }
        }
    })

Java

// Use the default GetBillingConfigParams.
GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build();
billingClient.getBillingConfigAsync(getBillingConfigParams,
    new BillingConfigResponseListener() {
      public void onBillingConfigResponse(
          BillingResult billingResult, BillingConfig billingConfig) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK
            && billingConfig != null) {
            String countryCode = billingConfig.getCountryCode();
            ...
         } else {
            // TODO: Handle errors
        }
      }
    });