ผสานรวม Google Play Billing Library ไว้ในแอปของคุณ

หัวข้อนี้จะอธิบายวิธีผสานรวม Google Play Billing Library เข้ากับแอปเพื่อเริ่มขายผลิตภัณฑ์

ตลอดอายุการซื้อ

ขั้นตอนการซื้อทั่วไปสำหรับการซื้อแบบครั้งเดียวหรือการสมัครใช้บริการมีดังนี้

  1. แสดงสิ่งที่ผู้ใช้ซื้อได้
  2. เปิดใช้งานขั้นตอนการซื้อเพื่อให้ผู้ใช้ยอมรับการซื้อ
  3. ยืนยันการซื้อในเซิร์ฟเวอร์
  4. มอบเนื้อหาให้ผู้ใช้
  5. ยอมรับการนำส่งเนื้อหา สำหรับผลิตภัณฑ์ที่ใช้แล้วหมดไป ให้ใช้การซื้อเพื่อให้ผู้ใช้ซื้อไอเทมนั้นอีกครั้งได้

การสมัครใช้บริการจะต่ออายุโดยอัตโนมัติจนกว่าจะยกเลิก การสมัครใช้บริการอาจมีสถานะต่อไปนี้

  • ใช้งานอยู่: ผู้ใช้มีสถานะดีและมีสิทธิ์เข้าถึงการสมัครใช้บริการ
  • ยกเลิกแล้ว: ผู้ใช้ยกเลิกแล้ว แต่ยังมีสิทธิ์เข้าถึงจนกว่าจะหมดอายุ
  • อยู่ในระยะเวลาผ่อนผัน: ผู้ใช้พบปัญหาการชำระเงินแต่ยังคงมีสิทธิ์เข้าถึงขณะที่ Google พยายามใช้วิธีการชำระเงินนั้นอีกครั้ง
  • ถูกระงับ: ผู้ใช้พบปัญหาเกี่ยวกับการชำระเงินและไม่มีสิทธิ์เข้าถึงอีกต่อไปขณะที่ Google พยายามเรียกเก็บเงินจากวิธีการชำระเงินนั้นอีกครั้ง
  • หยุดชั่วคราว: ผู้ใช้หยุดการเข้าถึงไว้ชั่วคราวและจะไม่มีสิทธิ์เข้าถึงจนกว่าจะกลับมาเปิดใช้อีกครั้ง
  • หมดอายุแล้ว: ผู้ใช้ยกเลิกและสูญเสียสิทธิ์เข้าถึงการสมัครใช้บริการ ระบบจะถือว่าผู้ใช้เลิกใช้งานเมื่อหมดอายุ

เริ่มต้นการเชื่อมต่อกับ Google Play

ขั้นตอนแรกในการผสานรวมกับระบบการเรียกเก็บเงินของ Google Play คือการเพิ่ม Google Play Billing Library ลงในแอปและเริ่มต้นการเชื่อมต่อ

เพิ่มข้อกำหนดของไลบรารี Google Play Billing

เพิ่มการพึ่งพา Google Play Billing Library ลงใน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 Billing Library จะมีส่วนขยายของ Kotlin และการสนับสนุนโครูทีนที่ช่วยให้คุณเขียน Kotlin สำนวนได้เมื่อใช้ Google Play Billing Library หากต้องการรวมส่วนขยายเหล่านี้ไว้ในโปรเจ็กต์ ให้เพิ่มการพึ่งพาต่อไปนี้ลงในไฟล์ 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

เมื่อเพิ่มทรัพยากร Dependency ใน Google Play Billing Library แล้ว คุณจะต้องเริ่มต้นอินสแตนซ์ BillingClient BillingClient เป็นอินเทอร์เฟซหลักสำหรับการติดต่อสื่อสารระหว่าง Google Play Billing Library กับส่วนที่เหลือของแอป BillingClient มีวิธีการที่สะดวกทั้งแบบซิงค์และแบบไม่ซิงค์สําหรับการดำเนินการเรียกเก็บเงินทั่วไปหลายรายการ เราขอแนะนําอย่างยิ่งให้คุณเปิดการเชื่อมต่อ BillingClient ที่ใช้งานอยู่เพียงครั้งละ 1 รายการเพื่อหลีกเลี่ยงการเรียกกลับ PurchasesUpdatedListener หลายรายการสําหรับเหตุการณ์เดียว

หากต้องการสร้าง 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() ซึ่งจะแจ้งเตือน Listener เมื่อการค้นหาเสร็จสิ้น ดังที่แสดงในตัวอย่างต่อไปนี้

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 คุณจึงไม่ต้องกำหนด Listener แยกต่างหาก แต่ฟังก์ชันจะระงับไว้จนกว่าการค้นหาจะเสร็จสมบูรณ์ จากนั้นคุณจะประมวลผลผลลัพธ์ได้หลังจากนั้น

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 5 เพื่อให้มั่นใจว่าจะได้รับการสนับสนุนที่เหมาะสมสำหรับสถานการณ์นี้

ประมวลผลผลลัพธ์

Google Play Billing Library จะจัดเก็บผลการค้นหาไว้ในออบเจ็กต์ List จาก ProductDetails รายการ จากนั้นคุณสามารถเรียกใช้เมธอดต่างๆ ในออบเจ็กต์ ProductDetails แต่ละรายการในรายการเพื่อดูข้อมูลที่เกี่ยวข้องกับไอเทมที่ซื้อในแอป เช่น ราคาหรือคำอธิบาย หากต้องการดูข้อมูลรายละเอียดผลิตภัณฑ์ที่มีอยู่ โปรดดูรายการเมธอดในคลาส ProductDetails

ก่อนที่จะเสนอขายสินค้า โปรดตรวจสอบว่าผู้ใช้ไม่ได้เป็นเจ้าของสินค้าดังกล่าวอยู่ หากผู้ใช้มีอุปกรณ์สิ้นเปลืองที่ยังอยู่ในคลังไอเทม ผู้ใช้ก็ต้องบริโภคไอเทมนั้นก่อนจึงจะซื้อได้อีกครั้ง

ก่อนที่จะเสนอการสมัครใช้บริการ ให้ตรวจสอบว่าผู้ใช้ไม่ได้สมัครใช้บริการแล้ว และโปรดทราบว่า

  • queryProductDetailsAsync() จะแสดงรายละเอียดผลิตภัณฑ์ที่ต้องสมัครใช้บริการและข้อเสนอสูงสุด 50 รายการต่อการสมัครใช้บริการ 1 รายการ
  • 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 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 ระบุตัวรับฟังโดยใช้เมธอด 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 ที่ระบุว่าราคาได้รับการปรับแต่งสำหรับผู้ใช้

คุณต้องปรึกษา Art 6 (1) (ea) CRD ของคำสั่งว่าด้วยสิทธิของผู้บริโภค 2011/83/EU เพื่อตรวจสอบว่าราคาที่คุณเสนอให้นั้นมีการปรับเปลี่ยนตามโปรไฟล์ของผู้ใช้หรือไม่

setIsOfferPersonalized() รับอินพุตบูลีน เมื่อ true UI ของ Play จะมีการเปิดเผย เมื่อเป็น false ระบบ UI จะละเว้นการเปิดเผย ค่าเริ่มต้นคือ false

ดูข้อมูลเพิ่มเติมได้ที่ศูนย์ช่วยเหลือผู้บริโภค

กำลังดำเนินการซื้อ

เมื่อผู้ใช้ทำการซื้อเสร็จสมบูรณ์แล้ว แอปของคุณจะต้องประมวลผลการซื้อดังกล่าว ในกรณีส่วนใหญ่ แอปจะได้รับการแจ้งเตือนการซื้อผ่านPurchasesUpdatedListener อย่างไรก็ตาม อาจมีบางกรณีที่แอปจะรับรู้การซื้อโดยการเรียกใช้ BillingClient.queryPurchasesAsync() ตามที่อธิบายไว้ในการเรียกข้อมูลการซื้อ

นอกจากนี้ หากคุณมีไคลเอ็นต์การแจ้งเตือนแบบเรียลไทม์สำหรับนักพัฒนาแอปในแบ็กเอนด์ที่ปลอดภัย คุณจะลงทะเบียนการซื้อใหม่ได้โดยรับ subscriptionNotification หรือ oneTimeProductNotification ที่แจ้งเตือนการซื้อใหม่ หลังจากได้รับการแจ้งเตือนเหล่านี้แล้ว ให้เรียกใช้ Google Play Developer API เพื่อดูสถานะทั้งหมดและอัปเดตสถานะแบ็กเอนด์ของคุณเอง

แอปของคุณควรประมวลผลการซื้อด้วยวิธีต่อไปนี้

  1. ยืนยันการซื้อ
  2. ส่งเนื้อหาให้ผู้ใช้และรับทราบการนำส่งเนื้อหา (ไม่บังคับ) ทำเครื่องหมายรายการว่าใช้แล้วเพื่อให้ผู้ใช้ซื้อรายการนั้นอีกครั้งได้

หากต้องการยืนยันการซื้อ ก่อนอื่นให้ตรวจสอบว่าสถานะการซื้อคือ PURCHASED หากการซื้อเป็น PENDING คุณควรดำเนินการซื้อตามที่อธิบายไว้ในการจัดการธุรกรรมที่รอดำเนินการ สำหรับการซื้อที่ได้รับจาก 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 Billing Library ทั้ง 2 วิธีจะปฏิบัติตามข้อกำหนดในการรับทราบ และระบุว่าแอปได้ให้สิทธิแก่ผู้ใช้ นอกจากนี้ วิธีการเหล่านี้ยังช่วยให้แอปของคุณทำให้ผลิตภัณฑ์แบบครั้งเดียวที่สอดคล้องกับโทเค็นการซื้อที่ป้อนพร้อมสำหรับการซื้ออีกครั้งได้ เมื่อใช้ consumeAsync() คุณยังต้องส่งออบเจ็กต์ที่ใช้อินเทอร์เฟซ ConsumeResponseListener ด้วย ออบเจ็กต์นี้จะจัดการผลลัพธ์ของการดำเนินการบริโภค คุณสามารถลบล้างเมธอด onConsumeResponse() ซึ่ง Google Play Billing Library จะเรียกใช้เมื่อการดำเนินการเสร็จสมบูรณ์

ตัวอย่างต่อไปนี้แสดงการใช้ผลิตภัณฑ์ด้วย Google Play Billing Library โดยใช้โทเค็นการซื้อที่เกี่ยวข้อง

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 Billing Library ในแอปของคุณ ก่อนยอมรับการซื้อ แอปควรตรวจสอบว่ามีการยอมรับการซื้อแล้วหรือไม่โดยใช้เมธอด isAcknowledged() ใน Google Play Billing Library

ตัวอย่างต่อไปนี้แสดงวิธีรับทราบการซื้อโดยใช้ Google Play Billing Library

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 จาก Google Play Developer API เพื่อตอบรับการซื้อจากแบ็กเอนด์ที่ปลอดภัย ตรวจสอบว่าไม่มีการตอบรับการซื้อก่อนหน้านี้โดยตรวจสอบ acknowledgementState ในแหล่งข้อมูลการซื้อจาก Purchases.subscriptions:get หรือจะรับการสมัครใช้บริการโดยใช้ BillingClient.acknowledgePurchase() จากคลัง Google Play Billing หลังจากที่ตรวจสอบ 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

      }
    }
);

จัดการการซื้อนอกแอป

การซื้อบางอย่างอาจเกิดขึ้นนอกแอป เช่น การแลกรับโปรโมชันหรือการช่วยเตือนเกี่ยวกับการหยุดกลางคันในรถเข็นสำหรับการซื้อในแอปของ Google Play Games (IAP) เมื่อผู้ใช้ทำการซื้อนอกแอป ผู้ใช้จะคาดหวังว่าแอปจะแสดงข้อความในแอป หรือใช้กลไกการแจ้งเตือนบางอย่างเพื่อแจ้งให้ผู้ใช้ทราบว่าแอปได้รับและประมวลผลการซื้ออย่างถูกต้อง กลไกที่ยอมรับได้บางส่วน ได้แก่

  • แสดงป๊อปอัปในแอป
  • ส่งข้อความไปยังกล่องข้อความในแอป และระบุอย่างชัดเจนว่ามีข้อความใหม่ในกล่องข้อความในแอป
  • ใช้ข้อความการแจ้งเตือนของระบบปฏิบัติการ

โปรดทราบว่าแอปของคุณอาจอยู่ในสถานะใดก็ได้เมื่อแอปจดจำการซื้อ แม้แต่แอปของคุณอาจยังไม่ได้ติดตั้งเมื่อทำการซื้อ ผู้ใช้คาดหวังว่าจะได้รับสิ่งที่ซื้อไปเมื่อกลับมาใช้งานแอปอีกครั้ง ไม่ว่าแอปจะอยู่ในสถานะใดก็ตาม

คุณต้องตรวจหาการซื้อโดยไม่คํานึงถึงสถานะของแอปเมื่อการซื้อเกิดขึ้น อย่างไรก็ตาม มีข้อยกเว้นบางประการที่อาจยอมรับได้หากไม่แจ้งผู้ใช้โดยทันทีว่าได้รับไอเทมแล้ว เช่น

  • ในระหว่างส่วนที่แอ็กชันของเกม ซึ่งการแสดงข้อความอาจทำให้ผู้ใช้เสียสมาธิ ในกรณีนี้ คุณต้องแจ้งให้ผู้ใช้ทราบหลังจากการดำเนินการสิ้นสุดลง
  • ในระหว่างฉากตัด ซึ่งการแสดงข้อความอาจทำให้ผู้ใช้เสียสมาธิ ในกรณีนี้ คุณต้องแจ้งให้ผู้ใช้ทราบหลังจากฉากตัดจบ
  • ในระหว่างบทแนะนำเบื้องต้นและการตั้งค่าผู้ใช้ของเกม เราขอแนะนำให้แจ้งให้ผู้ใช้ใหม่ทราบเกี่ยวกับรางวัลทันทีหลังจากที่ผู้ใช้เปิดเกมหรือในระหว่างการตั้งค่าผู้ใช้ครั้งแรก อย่างไรก็ตาม คุณรอจนกว่าลำดับเกมหลักจะพร้อมใช้งานเพื่อแจ้งให้ผู้ใช้ทราบ

โปรดคํานึงถึงผู้ใช้เสมอเมื่อตัดสินใจว่าจะแจ้งเตือนผู้ใช้เกี่ยวกับการซื้อที่ดำเนินการนอกแอปของคุณเมื่อใดและอย่างไร เมื่อใดก็ตามที่ผู้ใช้ไม่ได้รับการแจ้งเตือนทันที ผู้ใช้อาจสับสน และอาจหยุดใช้แอป ติดต่อทีมสนับสนุนผู้ใช้ หรือร้องเรียนเกี่ยวกับเรื่องนี้บนโซเชียลมีเดีย

การช่วยเตือนการละทิ้งรถเข็นกลางคันในหน้าแรกของ Google Play Games (เปิดใช้โดยค่าเริ่มต้น)

สําหรับนักพัฒนาแอปเกมที่สร้างรายได้ผ่าน IAP วิธีหนึ่งในการขายหน่วยสินค้าคงคลัง (SKU) ที่ใช้งานอยู่ใน Google Play Console นอกแอปของคุณคือฟีเจอร์การช่วยเตือนการละทิ้งรถเข็นกลางคัน ซึ่งจะกระตุ้นให้ผู้ใช้ดำเนินการซื้อที่หยุดกลางคันก่อนหน้านี้ให้เสร็จสมบูรณ์ขณะเลือกดูใน Google Play Store การซื้อเหล่านี้เกิดขึ้นนอกแอปของคุณ จากหน้าแรกของ Google Play Games ใน Google Play Store

ฟีเจอร์นี้จะเปิดใช้โดยค่าเริ่มต้นเพื่อช่วยให้ผู้ใช้ได้กลับมาดูรายการที่เลือกไว้ก่อนหน้านี้ และเพื่อช่วยนักพัฒนาแอปเพิ่มยอดขายให้ได้สูงสุด อย่างไรก็ตาม คุณเลือกไม่ให้แอปใช้ฟีเจอร์นี้ได้โดยการส่งแบบฟอร์มเลือกไม่ใช้ฟีเจอร์การช่วยเตือนการละทิ้งรถเข็นกลางคัน ดูแนวทางปฏิบัติแนะนำในการจัดการ SKU ภายใน Google Play Console ได้ที่ สร้างไอเทมที่ซื้อในแอป

รูปภาพต่อไปนี้แสดงการช่วยเตือนการหยุดกลางคันในรถเข็นที่ปรากฏใน Google Play Store

หน้าจอ Google Play Store แสดงข้อความแจ้งให้ซื้อสำหรับการซื้อที่ยกเลิกไปก่อนหน้านี้
รูปที่ 2 หน้าจอ Google Play Store จะแสดงข้อความแจ้งให้ซื้อสำหรับการซื้อที่ยกเลิกไปก่อนหน้านี้

หน้าจอ Google Play Store แสดงข้อความแจ้งให้ซื้อสำหรับการซื้อที่ยกเลิกไปก่อนหน้านี้
รูปที่ 3 หน้าจอ Google Play Store จะแสดงข้อความแจ้งให้ซื้อสำหรับการซื้อที่ยกเลิกไปก่อนหน้านี้

การจัดการธุรกรรมที่รอดำเนินการ

Google Play รองรับธุรกรรมที่รอดำเนินการหรือธุรกรรมที่ต้องดำเนินการเพิ่มเติมอย่างน้อย 1 ขั้นตอนระหว่างที่ผู้ใช้เริ่มซื้อและเมื่อระบบประมวลผลวิธีการชำระเงินสำหรับการซื้อ แอปของคุณไม่ควรให้สิทธิ์ในการซื้อประเภทเหล่านี้จนกว่า Google จะแจ้งให้คุณทราบว่าเรียกเก็บเงินจากวิธีการชำระเงินของผู้ใช้เรียบร้อยแล้ว

ตัวอย่างเช่น ผู้ใช้สามารถเริ่มธุรกรรมโดยเลือกกิจการที่มีหน้าร้านจริง ซึ่งจะชำระเงินภายหลังด้วยเงินสดได้ ผู้ใช้จะได้รับรหัสทั้งทางอีเมลและการแจ้งเตือน เมื่อผู้ใช้มาถึงร้านค้า ผู้ใช้จะแลกรหัสกับแคชเชียร์และชำระเงินด้วยเงินสดได้ จากนั้น Google จะแจ้งให้คุณและผู้ใช้ทราบว่าได้รับการชำระเงินแล้ว จากนั้นแอปของคุณจะให้สิทธิ์แก่ผู้ใช้ได้

เรียกใช้ enablePendingPurchases() ในขั้นตอนเริ่มต้น BillingClient เพื่อเปิดใช้ธุรกรรมที่รอดำเนินการสำหรับแอป แอปของคุณต้องเปิดใช้และรองรับธุรกรรมที่รอดำเนินการสำหรับผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียว ก่อนเพิ่มการสนับสนุน โปรดทำความเข้าใจวงจรการซื้อสำหรับธุรกรรมที่รอดำเนินการ

เมื่อแอปได้รับการซื้อใหม่ผ่าน PurchasesUpdatedListener หรือจากการเรียกใช้ queryPurchasesAsync() ให้ใช้เมธอด getPurchaseState() เพื่อระบุว่าสถานะการซื้อคือ PURCHASED หรือ PENDING คุณควรให้สิทธิ์เฉพาะเมื่อสถานะเป็น PURCHASED

หากแอปทำงานอยู่เมื่อผู้ใช้ทำการซื้อเสร็จสมบูรณ์ ระบบจะเรียกใช้ PurchasesUpdatedListener อีกครั้ง และ PurchaseState เปลี่ยนเป็น PURCHASED แล้ว ณ จุดนี้ แอปประมวลผลการซื้อได้โดยใช้วิธีการมาตรฐานสำหรับการประมวลผลการซื้อ แอปของคุณควรเรียกใช้ queryPurchasesAsync() ในเมธอด onResume() ของแอปเพื่อจัดการการซื้อที่เปลี่ยนเป็นสถานะ PURCHASED ขณะที่แอปไม่ได้ทำงานอยู่

เมื่อการซื้อเปลี่ยนจาก PENDING เป็น PURCHASED ลูกค้าการแจ้งเตือนแบบเรียลไทม์สำหรับนักพัฒนาแอปจะได้รับการแจ้งเตือน ONE_TIME_PRODUCT_PURCHASED หรือ SUBSCRIPTION_PURCHASED หากการซื้อถูกยกเลิก คุณจะได้รับการแจ้งเตือน ONE_TIME_PRODUCT_CANCELED หรือ SUBSCRIPTION_PENDING_PURCHASE_CANCELED กรณีนี้อาจเกิดขึ้นได้หากลูกค้าชำระเงินไม่เสร็จในกรอบเวลาที่กำหนด โปรดทราบว่าคุณใช้ Google Play Developer API เพื่อตรวจสอบสถานะปัจจุบันของการซื้อได้ทุกเมื่อ

การจัดการกับการซื้อแบบหลายจำนวน

ซึ่งรองรับใน Google Play Billing Library เวอร์ชัน 4.0 ขึ้นไป Google Play ช่วยให้ลูกค้าซื้อไอเทมที่ซื้อในแอปเดียวกันได้มากกว่า 1 รายการในธุรกรรมเดียว โดยระบุจำนวนจากรถเข็นที่ซื้อ แอปของคุณควรจัดการการซื้อหลายรายการและมอบสิทธิ์ตามจำนวนการซื้อที่ระบุ

ตรรกะการจัดสรรของแอปต้องตรวจสอบจำนวนสินค้าเพื่อรองรับการซื้อหลายรายการ คุณสามารถเข้าถึงฟิลด์ quantity จาก API รายการใดรายการหนึ่งต่อไปนี้

เมื่อเพิ่มตรรกะเพื่อจัดการการซื้อหลายรายการแล้ว คุณจะต้องเปิดใช้ฟีเจอร์การซื้อหลายรายการสำหรับผลิตภัณฑ์ที่เกี่ยวข้องในหน้าการจัดการผลิตภัณฑ์ในแอปใน Google Play Developer 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
        }
      }
    });