หัวข้อนี้จะอธิบายวิธีผสานรวม Google Play Billing Library เข้ากับแอปเพื่อเริ่มขายผลิตภัณฑ์
ตลอดอายุการซื้อ
ขั้นตอนการซื้อทั่วไปสำหรับการซื้อแบบครั้งเดียวหรือการสมัครใช้บริการมีดังนี้
- แสดงให้ผู้ใช้เห็นสิ่งที่ซื้อได้
- เปิดใช้งานขั้นตอนการซื้อเพื่อให้ผู้ใช้ยอมรับการซื้อ
- ยืนยันการซื้อในเซิร์ฟเวอร์
- มอบเนื้อหาให้ผู้ใช้
- ยอมรับการนำส่งเนื้อหา สำหรับผลิตภัณฑ์ที่ใช้แล้วหมดไป ให้ใช้การซื้อ เพื่อให้ผู้ใช้สามารถซื้อสินค้านั้นได้อีกครั้ง
การสมัครใช้บริการจะต่ออายุโดยอัตโนมัติจนกว่าจะยกเลิก การสมัครใช้บริการอาจมีสถานะต่อไปนี้
- ใช้งานอยู่: ผู้ใช้อยู่ในสถานะดีและมีสิทธิ์เข้าถึงการสมัครใช้บริการ
- ยกเลิก: ผู้ใช้ยกเลิกแล้ว แต่ยังคงมีสิทธิ์เข้าถึงจนกว่าจะหมดอายุ
- อยู่ในระยะเวลาผ่อนผัน: ผู้ใช้พบปัญหาการชำระเงินแต่ยังคงมีสิทธิ์เข้าถึงขณะที่ Google พยายามใช้วิธีการชำระเงินนั้นอีกครั้ง
- ถูกระงับ: ผู้ใช้พบปัญหาเกี่ยวกับการชำระเงินและไม่มีสิทธิ์เข้าถึงอีกต่อไปขณะที่ Google พยายามเรียกเก็บเงินจากวิธีการชำระเงินนั้นอีกครั้ง
- หยุดชั่วคราว: ผู้ใช้หยุดการเข้าถึงไว้ชั่วคราวและจะไม่มีสิทธิ์เข้าถึงจนกว่าจะกลับมาเปิดใช้อีกครั้ง
- หมดอายุ: ผู้ใช้ยกเลิกและเสียสิทธิ์เข้าถึงการสมัครใช้บริการ ระบบจะถือว่าผู้ใช้เลิกใช้งานเมื่อหมดอายุ
เริ่มต้นการเชื่อมต่อกับ Google Play
ขั้นตอนแรกในการผสานรวมกับระบบการเรียกเก็บเงินของ Google Play คือการเพิ่ม Google Play Billing Library ลงในแอปและเริ่มต้นการเชื่อมต่อ
เพิ่มทรัพยากร Dependency ของ Google Play Billing Library
เพิ่มการพึ่งพา 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 และการสนับสนุน coroutines ซึ่งช่วยให้คุณเขียน 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
Listener นี้จะได้รับการอัปเดต
สำหรับการซื้อทั้งหมดในแอปของคุณ
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 ที่ขาดหายไปด้วย
หากต้องการใช้ตรรกะการลองอีกครั้ง ให้ลบล้างเมธอด Callback 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 คุณจึงไม่ต้องกำหนด 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 จะเรียกใช้ onPurchasesUpdated()
ให้นำส่งผลลัพธ์ของการดำเนินการซื้อไปยังผู้ฟังที่ใช้อินเทอร์เฟซ PurchasesUpdatedListener
ระบบจะระบุ Listener โดยใช้เมธอด 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 Console ได้
ระบุราคาสำหรับคุณโดยเฉพาะ
หากแอปของคุณเผยแพร่แก่ผู้ใช้ในสหภาพยุโรปได้ ให้ใช้วิธี setIsOfferPersonalized()
เพื่อเปิดเผยให้ผู้ใช้ทราบว่าราคาของสินค้าได้รับการปรับเปลี่ยนในแบบของคุณโดยใช้การตัดสินใจอัตโนมัติ
คุณต้องปรึกษา Art 6 (1) (ea) CRD ของคำสั่งว่าด้วยสิทธิของผู้บริโภค 2011/83/EU เพื่อตรวจสอบว่าราคาที่คุณเสนอให้นั้นมีการปรับเปลี่ยนตามโปรไฟล์ของผู้ใช้หรือไม่
setIsOfferPersonalized()
รับอินพุตบูลีน เมื่อเป็น true
UI ของ Play จะแสดงการเปิดเผยข้อมูล เมื่อเป็น false
ระบบ UI จะละเว้นการเปิดเผย ค่าเริ่มต้นคือ false
ดูข้อมูลเพิ่มเติมได้ที่ศูนย์ช่วยเหลือผู้บริโภค
กำลังประมวลผลการซื้อ
เมื่อผู้ใช้ทำการซื้อเสร็จสมบูรณ์แล้ว แอปของคุณจะต้องประมวลผลการซื้อดังกล่าว
ในกรณีส่วนใหญ่ แอปจะได้รับการแจ้งเตือนการซื้อผ่านPurchasesUpdatedListener
อย่างไรก็ตาม มีบางกรณีที่แอปทราบเกี่ยวกับการซื้อด้วยการเรียกใช้ BillingClient.queryPurchasesAsync()
ตามที่อธิบายไว้ในการดึงข้อมูลการซื้อ
นอกจากนี้ หากคุณมีไคลเอ็นต์การแจ้งเตือนแบบเรียลไทม์สำหรับนักพัฒนาแอปในแบ็กเอนด์ที่ปลอดภัย คุณจะลงทะเบียนการซื้อใหม่ได้โดยรับ subscriptionNotification
หรือ oneTimeProductNotification
ที่แจ้งเตือนการซื้อใหม่ หลังจากได้รับการแจ้งเตือนเหล่านี้ ให้เรียกใช้ API สำหรับนักพัฒนาซอฟต์แวร์
Google Play เพื่อดูสถานะทั้งหมดและอัปเดตสถานะแบ็กเอนด์ของคุณเอง
แอปของคุณควรประมวลผลการซื้อด้วยวิธีต่อไปนี้
- ยืนยันการซื้อ
- ส่งเนื้อหาให้ผู้ใช้และรับทราบการนำส่งเนื้อหา (ไม่บังคับ) ทำเครื่องหมายรายการว่าใช้แล้วเพื่อให้ผู้ใช้ซื้อรายการนั้นอีกครั้งได้
หากต้องการยืนยันการซื้อ ให้ตรวจสอบก่อนว่าสถานะการซื้อเป็น 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 รองรับธุรกรรมที่รอดำเนินการหรือธุรกรรมที่ต้องดำเนินการเพิ่มเติมอย่างน้อย 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 รองรับการซื้อไอเทมที่ซื้อในแอปรายการเดียวกันมากกว่า 1 รายการในธุรกรรมเดียวโดยระบุจำนวนจากรถเข็นการซื้อ ซึ่งใช้ได้กับไลบรารีการเรียกเก็บเงินของ Google Play เวอร์ชัน 4.0 ขึ้นไป คุณต้องจัดการการซื้อแบบหลายจำนวนและให้สิทธิ์ตามจำนวนการซื้อที่ระบุ
หากต้องการใช้การซื้อแบบหลายจำนวน ตรรกะการจัดสรรของแอปจะต้องตรวจสอบจำนวนรายการ คุณสามารถเข้าถึงฟิลด์ quantity
จาก API รายการใดรายการหนึ่งต่อไปนี้
getQuantity()
จากคลัง Google Play BillingPurchases.products.quantity
จาก Google Play Developer 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
}
}
});