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

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

מחזור החיים של רכישה

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

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

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

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

איך מפעילים חיבור ל-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 ותמיכה ב-coroutines, שמאפשרים לכם לכתוב קוד 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")
}

איך מפעילים את BillingClient

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

  • מומלץ לפתוח חיבור פעיל אחד של BillingClient בכל פעם כדי להימנע מכמה קריאות חזרה (callbacks) של PurchasesUpdatedListener לאירוע יחיד.
  • מומלץ ליצור חיבור ל-BillingClient כשהאפליקציה מופעלת או עוברת לחזית, כדי לוודא שהאפליקציה מעבדת את הרכישות בזמן. כדי לעשות זאת, משתמשים ב-ActivityLifecycleCallbacks שרשום על ידי registerActivityLifecycleCallbacks ומקשיבים ל-onActivityResumed כדי לאתחל את החיבור בפעם הראשונה שמזהים פעילות שממשיכה. בקטע עיבוד רכישות מוסבר בפירוט למה כדאי לפעול לפי השיטה המומלצת הזו. חשוב גם לסיים את החיבור כשהאפליקציה סגורה.

כדי ליצור 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 כדי לקבל קריאה חוזרת (callback) לאחר השלמת ההגדרה של הלקוח והכנתו לשליחת בקשות נוספות.

צריך גם להטמיע לוגיקה של ניסיון חוזר כדי לטפל בחיבורים שאבדו ל-Google Play. כדי להטמיע לוגיקה של ניסיונות חוזרים, משנים את השיטה onBillingServiceDisconnected() של הקריאה החוזרת (callback), ומוודאים שה-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() נעשה שימוש ב-coroutines של 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 Services. כדי להבטיח תמיכה מתאימה בתרחיש הזה, כדאי לעיין במדריך להעברה ל-Play Billing Library 5 כדי ללמוד איך להשתמש בתכונות התאימות לאחור.

עיבוד התוצאה

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

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

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

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

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

כדי להתחיל בקשת רכישה מהאפליקציה, צריך להפעיל את השיטה launchBillingFlow() מהשרשור הראשי של האפליקציה. השיטה מקבלת הפניה לאובייקט 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 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()
)

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() כדי להעביר את התוצאה של פעולת הרכישה למאזין שמטמיע את הממשק PurchasesUpdatedListener. מציינים את המאזין באמצעות השיטה setListener() כשמאתחלים את הלקוח.

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

Kotlin


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

Java


@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, בדומה לזה שמוצג באיור 2.

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

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

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

ציון מחיר בהתאמה אישית

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

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

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

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

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

צירוף מזהי משתמשים

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

זיהוי ועיבוד של רכישות

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

האפליקציה מזהה רכישות חדשות ורכישות בהמתנה שהושלמו באחת מהדרכים הבאות:

  1. כשמתבצעת קריאה ל-onPurchasesUpdated כתוצאה מקריאה של האפליקציה ל-launchBillingFlow (כפי שמתואר בקטע הקודם), או אם האפליקציה פועלת עם חיבור פעיל ל-Billing Library כשמתבצעת רכישה מחוץ לאפליקציה או כשרכישה בהמתנה מושלמת. לדוגמה, אחד מחברי המשפחה מאשר רכישה בהמתנה במכשיר אחר.
  2. כשהאפליקציה קוראת ל-queryPurchasesAsync כדי לשלוח שאילתה לגבי הרכישות של המשתמש.

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

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

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

אחרי שהאפליקציה מזהה רכישה חדשה או רכישה שהושלמו, היא צריכה:

  • מאמתים את הרכישה.
  • הענקת תוכן למשתמש על רכישות שהושלמו.
  • מודיעים למשתמש.
  • מודיעים ל-Google שהאפליקציה עיבד רכישות שהושלמו.

השלבים האלה מפורטים בהמשך, ואחריהם מופיע סיכום של כל השלבים.

אימות הרכישה

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

הענקת הרשאה למשתמש

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

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

שליחת הודעה למשתמש

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

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

כשאתם מעדכנים את המשתמש על רכישה, מומלץ להשתמש במנגנונים הבאים ב-Google Play:

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

ההודעה צריכה להודיע למשתמש על ההטבה שהוא קיבל. לדוגמה, 'רכשת 100 מטבעות זהב!'. בנוסף, אם הרכישה נעשתה כתוצאה מהטבה של תוכנית כמו Play Pass, האפליקציה תודיע על כך למשתמש. לדוגמה, 'הפריטים התקבלו! קיבלת עכשיו 100 יהלומים עם Play Pass. Continue". לכל תוכנית יכולה להיות הנחיה לגבי הטקסט המומלץ להצגה למשתמשים כדי להעביר את היתרונות.

הודעה ל-Google על השלמת הרכישה

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

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

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

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

Kotlin


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

Java


    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 = ...

val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
    .setPurchaseToken(purchase.purchaseToken)
val ackPurchaseResult = withContext(Dispatchers.IO) {
     client.acknowledgePurchase(acknowledgePurchaseParams.build())
}

Java


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

AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
 client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);

מינויים

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

Recap

קטע הקוד הבא מציג סיכום של השלבים האלה.

Kotlin


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

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify
.
    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

Java


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

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify

    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

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

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

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

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

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

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

אם האפליקציה פועלת ויש לכם חיבור פעיל לספריית החיובים ב-Play כשהמשתמש משלים את הרכישה, PurchasesUpdatedListener ייכלל שוב, והערך של PurchaseState יהיה PURCHASED. בשלב הזה, האפליקציה יכולה לעבד את הרכישה באמצעות השיטה הרגילה לזיהוי עיבוד רכישות. האפליקציה צריכה גם להפעיל את queryPurchasesAsync() ב-method‏ 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 יש תמיכה באפשרות הזו בגרסאות 4.0 ואילך של ספריית החיוב ב-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
        }
      }
    });

תזכורות על עזיבה של עגלת קניות בדף הבית של Google Play Games (מופעלות כברירת מחדל)

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

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

בתמונות הבאות מוצגת התזכורת על עזיבה של עגלת קניות שמופיעה בחנות Google Play:

במסך של חנות Google Play מוצגת בקשה להשלים רכישה שננטשה בעבר
איור 2. במסך של חנות Google Play מוצגת בקשה להשלים רכישה שננטשה בעבר.

במסך של חנות Google Play מוצגת בקשה להשלים רכישה שננטשה בעבר
איור 3. במסך של חנות Google Play מוצגת בקשה להשלים רכישה שננטשה בעבר.