เอกสารนี้อธิบายวิธีผสานรวม Google Play Billing Library เข้ากับแอปเพื่อเริ่มขายผลิตภัณฑ์
วงจรชีวิตของการซื้อ
ขั้นตอนการซื้อโดยทั่วไปสำหรับการซื้อครั้งเดียวหรือการสมัครใช้บริการมีดังนี้
- แสดงให้ผู้ใช้เห็นว่าซื้ออะไรได้บ้าง
- เปิดขั้นตอนการซื้อเพื่อให้ผู้ใช้ยอมรับการซื้อ
- ยืนยันการซื้อบนเซิร์ฟเวอร์ของคุณ
- แสดงเนื้อหาให้ผู้ใช้เห็น
- รับทราบการส่งเนื้อหา สำหรับไอเทมที่ใช้แล้วหมดไป ให้ใช้การซื้อเพื่อให้ผู้ใช้ซื้อไอเทมได้อีกครั้ง
การสมัครใช้บริการจะต่ออายุโดยอัตโนมัติจนกว่าจะมีการยกเลิก การสมัครใช้บริการอาจมีสถานะต่อไปนี้
- ใช้งานอยู่: ผู้ใช้มีสถานะดีและมีสิทธิ์เข้าถึงการสมัครใช้บริการ
- ยกเลิกแล้ว: ผู้ใช้ยกเลิกแล้ว แต่ยังคงมีสิทธิ์เข้าถึงจนกว่าจะหมดอายุ
- อยู่ในระยะเวลาผ่อนผัน: ผู้ใช้พบปัญหาในการชำระเงิน แต่ยังคงมีสิทธิ์เข้าถึงในขณะที่ Google กำลังลองเรียกเก็บเงินจากวิธีการชำระเงินอีกครั้ง
- ถูกระงับ: ผู้ใช้พบปัญหาเกี่ยวกับการชำระเงินและไม่มีสิทธิ์เข้าถึงอีกต่อไปขณะที่ Google กำลังลองใช้วิธีการชำระเงินอีกครั้ง
- หยุดชั่วคราว: ผู้ใช้หยุดการเข้าถึงชั่วคราวและจะไม่มีสิทธิ์เข้าถึงจนกว่าจะกลับมาใช้งานอีกครั้ง
- หมดอายุ: ผู้ใช้ยกเลิกและเสียสิทธิ์เข้าถึงการสมัครใช้บริการแล้ว ระบบจะถือว่าผู้ใช้เลิกใช้งานเมื่อการสมัครใช้บริการหมดอายุ
เริ่มต้นการเชื่อมต่อกับ Google Play
ขั้นตอนแรกในการผสานรวมกับระบบการเรียกเก็บเงินของ Google Play คือการเพิ่ม Google Play Billing Library ลงในแอปและเริ่มต้นการเชื่อมต่อ
เพิ่มทรัพยากร Dependency ของ Google Play Billing Library
เพิ่มทรัพยากร Dependency ของ Google Play Billing Library ลงในไฟล์ build.gradle ของแอปดังที่แสดงด้านล่าง
ดึงดูด
dependencies { def billing_version = "9.1.0" implementation "com.android.billingclient:billing:$billing_version" }
Kotlin
dependencies { val billing_version = "9.1.0" implementation("com.android.billingclient:billing:$billing_version") }
หากคุณใช้ Kotlin โมดูล KTX ของ Google Play Billing Library จะมีส่วนขยาย Kotlin และการรองรับ Coroutine ที่ช่วยให้คุณเขียน Kotlin ตามสำนวนได้เมื่อใช้ Google Play Billing Library หากต้องการรวมส่วนขยายเหล่านี้ในโปรเจ็กต์ ให้เพิ่มทรัพยากร Dependency ต่อไปนี้ลงในไฟล์ build.gradle ของแอปตามที่แสดง
ดึงดูด
dependencies { def billing_version = "9.1.0" implementation "com.android.billingclient:billing-ktx:$billing_version" }
Kotlin
dependencies { val billing_version = "9.1.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 เมื่อแอปของคุณเปิดขึ้นหรือทำงานอยู่เบื้องหน้า เพื่อให้แอปประมวลผลการซื้อได้อย่างทันท่วงที ซึ่งทำได้โดยใช้
ActivityLifecycleCallbacksที่ลงทะเบียนโดยregisterActivityLifecycleCallbacksและฟัง onActivityResumed เพื่อเริ่มต้นการเชื่อมต่อเมื่อตรวจพบกิจกรรมที่กลับมาทำงานอีกครั้งเป็นครั้งแรก ดูรายละเอียดเพิ่มเติมเกี่ยวกับสาเหตุที่ควรปฏิบัติตามแนวทางปฏิบัติแนะนำนี้ได้ที่ส่วนการประมวลผลการซื้อ และอย่าลืมสิ้นสุดการเชื่อมต่อเมื่อปิดแอป
หากต้องการสร้าง 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() { // If automatic service reconnection is enabled, this can be left empty (no-op) // because the library handles retries. You can still use this for non-retry // tasks like logging or updating the UI to reflect a disconnected state. // Otherwise, try to restart the connection on the next request to // Google Play by calling the startConnection() method. } })
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. // It's a good practice to query products after the connection is established. queryProductDetails(); } } @Override public void onBillingServiceDisconnected() { // Try to restart the connection on the next request to // Google Play by calling the startConnection() method. // This is automatically handled by the library when you call a method that requires a connection. } });
สร้างการเชื่อมต่ออีกครั้งโดยอัตโนมัติ
เมื่อมีการเปิดตัวเมธอด enableAutoServiceReconnection() ใน BillingClient.Builder เวอร์ชัน 8.0.0 ตอนนี้ Play Billing Library สามารถสร้างการเชื่อมต่อบริการอีกครั้งโดยอัตโนมัติหากมีการเรียก API ขณะที่บริการถูกตัดการเชื่อมต่อ ซึ่งอาจทำให้การตอบกลับ SERVICE_DISCONNECTED ลดลงเนื่องจากระบบจะจัดการการเชื่อมต่อใหม่ภายในก่อนที่จะทำการเรียก API
วิธีเปิดใช้การเชื่อมต่อใหม่อัตโนมัติ
เมื่อสร้างอินสแตนซ์ BillingClient ให้ใช้เมธอด enableAutoServiceReconnection() ใน BillingClient.Builder เพื่อเปิดใช้การเชื่อมต่อใหม่อัตโนมัติ
Kotlin
val billingClient = BillingClient.newBuilder(context) .setListener(listener) .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build()) .enableAutoServiceReconnection() // Add this line to enable reconnection .build()
Java
BillingClient billingClient = BillingClient.newBuilder(context)
.setListener(listener)
.enablePendingPurchases()
.enableAutoServiceReconnection() // Add this line to enable reconnection
.build();
แสดงผลิตภัณฑ์ที่พร้อมจำหน่าย
หลังจากสร้างการเชื่อมต่อกับ Google Play แล้ว คุณก็พร้อมที่จะค้นหาผลิตภัณฑ์ที่พร้อมจำหน่ายและแสดงต่อผู้ใช้
การค้นหารายละเอียดสินค้าเป็นขั้นตอนสำคัญก่อนที่จะแสดงสินค้าต่อผู้ใช้ เนื่องจากจะแสดงข้อมูลสินค้าที่แปลแล้ว สำหรับการสมัครใช้บริการ ให้ตรวจสอบว่าการแสดงผลิตภัณฑ์เป็นไปตามนโยบายทั้งหมดของ Play
หากต้องการค้นหารายละเอียดสินค้าแบบเรียกเก็บเงินครั้งเดียว ให้เรียกใช้เมธอด queryProductDetailsAsync เมธอดนี้สามารถแสดงผลข้อเสนอหลายรายการตามการกำหนดค่าผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียวของคุณ ดูข้อมูลเพิ่มเติมได้ที่ตัวเลือกการซื้อและข้อเสนอหลายรายการสำหรับผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียว
หากต้องการจัดการผลลัพธ์ของการดำเนินการแบบอะซิงโครนัส คุณต้องระบุ Listener ที่ใช้ProductDetailsResponseListenerอินเทอร์เฟซด้วย
จากนั้นคุณสามารถลบล้าง onProductDetailsResponse ซึ่งจะแจ้งให้ผู้ฟังทราบเมื่อการค้นหาเสร็จสิ้น ดังที่แสดงในตัวอย่างต่อไปนี้
Kotlin
val queryProductDetailsParams = QueryProductDetailsParams.newBuilder() .setProductList( listOf( Product.newBuilder() .setProductId("product_id_example") .setProductType(ProductType.SUBS) .build() ) ) .build() billingClient.queryProductDetailsAsync(queryProductDetailsParams) { billingResult, queryProductDetailsResult -> if (billingResult.responseCode == BillingResponseCode.OK) { for (productDetails in queryProductDetailsResult.productDetailsList) { // Process successfully retrieved product details here. } for (unfetchedProduct in queryProductDetailsResult.unfetchedProductList) { // Handle any unfetched products as appropriate. } } }
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,
QueryProductDetailsResult queryProductDetailsResult) {
if (billingResult.getResponseCode() == BillingResponseCode.OK) {
for (ProductDetails productDetails : queryProductDetailsResult.getProductDetailsList()) {
// Process successfully retrieved product details here.
}
for (UnfetchedProduct unfetchedProduct : queryProductDetailsResult.getUnfetchedProductList()) {
// Handle any unfetched products as appropriate.
}
}
}
}
)
เมื่อค้นหารายละเอียดสินค้า ให้ส่งอินสแตนซ์ของ QueryProductDetailsParams ที่ระบุรายการสตริงรหัสสินค้าที่สร้างใน Google Play 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 7
ประมวลผลผลลัพธ์
Google Play Billing Library จะจัดเก็บผลการค้นหาไว้ในออบเจ็กต์ QueryProductDetailsResult QueryProductDetailsResult มี List ของออบเจ็กต์ ProductDetails จากนั้นคุณจะเรียกใช้เมธอดต่างๆ ในออบเจ็กต์ ProductDetails แต่ละรายการในลิสต์เพื่อดูข้อมูลที่เกี่ยวข้องเกี่ยวกับผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียวที่ดึงข้อมูลสำเร็จ เช่น ราคาหรือคำอธิบาย หากต้องการดูข้อมูลรายละเอียดสินค้าที่พร้อมใช้งาน โปรดดูรายการเมธอดในคลาส ProductDetails
QueryProductDetailsResult ยังมี List ของออบเจ็กต์ UnfetchedProduct ด้วย จากนั้นคุณสามารถค้นหา UnfetchedProduct แต่ละรายการเพื่อดูรหัสสถานะที่สอดคล้องกับสาเหตุที่ดึงข้อมูลไม่สำเร็จ
หากต้องการดูข้อมูลสินค้าที่ยังไม่ได้ดึงข้อมูลซึ่งพร้อมใช้งาน ให้ดูรายการเมธอดในคลาส UnfetchedProduct
ก่อนเสนอขายไอเทม ให้ตรวจสอบว่าผู้ใช้ยังไม่ได้เป็นเจ้าของไอเทมนั้น หากผู้ใช้มีไอเทมที่ใช้แล้วหมดไปซึ่งยังอยู่ในคลังไอเทม ผู้ใช้ต้องใช้ไอเทมนั้นก่อนจึงจะซื้อได้อีกครั้ง
ก่อนเสนอการสมัครใช้บริการ ให้ตรวจสอบว่าผู้ใช้ยังไม่ได้สมัครใช้บริการ นอกจากนี้ โปรดทราบสิ่งต่อไปนี้
สำหรับการสมัครใช้บริการ เมธอด
queryProductDetailsAsync()จะแสดงรายละเอียดผลิตภัณฑ์ที่ต้องสมัครใช้บริการและข้อเสนอที่ผู้ใช้มีสิทธิ์สูงสุด 50 รายการต่อการสมัครใช้บริการ หากผู้ใช้พยายามซื้อข้อเสนอที่ไม่มีสิทธิ์ (เช่น หากแอปแสดงรายการข้อเสนอที่มีสิทธิ์ที่ล้าสมัย) Play จะแจ้งให้ผู้ใช้ทราบว่าไม่มีสิทธิ์ และผู้ใช้สามารถเลือกซื้อแพ็กเกจเริ่มต้นแทนได้สำหรับผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียว เมธอด
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) // Get the offer token: // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList() // for a list of offers that are available to the user. // b. For subscriptions, call ProductDetails.getSubscriptionOfferDetails() // for a list of offers that are available to the user. .setOfferToken(selectedOfferToken) .build() ) val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build() // Launch the billing flow val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
Java
// An activity reference from which the billing flow will be launched. Activity activity = ...; ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList = ImmutableList.of( BillingFlowParams.ProductDetailsParams.newBuilder() // retrieve a value for "productDetails" by calling queryProductDetailsAsync() .setProductDetails(productDetails) // Get the offer token: // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList() // for a list of offers that are available to the user. // b. For subscriptions, call ProductDetails.getSubscriptionOfferDetails() // for a list of offers that are available to the user. .setOfferToken(selectedOfferToken) .build() ); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build(); // Launch the billing flow BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
เมธอด launchBillingFlow() จะแสดงผลโค้ดตอบกลับรายการใดรายการหนึ่งจากหลายรายการที่แสดงใน BillingClient.BillingResponseCode โปรดตรวจสอบผลลัพธ์นี้เพื่อยืนยันว่าไม่มีข้อผิดพลาดในการเปิดขั้นตอนการซื้อ BillingResponseCode
จาก OK บ่งบอกถึงการเปิดตัวที่ประสบความสำเร็จ
เมื่อเรียกใช้ launchBillingFlow() สำเร็จ ระบบจะแสดงหน้าจอการซื้อใน Google Play รูปที่ 1 แสดงหน้าจอการซื้อสำหรับการสมัครใช้บริการ
Google Play จะเรียกใช้ onPurchasesUpdated() เพื่อส่งผลลัพธ์ของการดำเนินการซื้อไปยัง Listener ที่ใช้อินเทอร์เฟซ 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) { // 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 Console ได้
ระบุราคาที่ปรับเปลี่ยนในแบบของคุณ
หากแอปของคุณจัดจำหน่ายให้แก่ผู้ใช้ในสหภาพยุโรปได้ ให้ใช้เมธอด setIsOfferPersonalized() เมื่อเรียกใช้ launchBillingFlow เพื่อเปิดเผยต่อผู้ใช้ว่าราคาของรายการได้รับการปรับเปลี่ยนให้เหมาะกับแต่ละบุคคลโดยใช้การตัดสินใจอัตโนมัติ
คุณต้องปรึกษา Art 6 (1) (ea) CRD ในคำสั่งว่าด้วยสิทธิของผู้บริโภค 2011/83/EU เพื่อตรวจสอบว่าราคาที่คุณเสนอให้นั้นมีการปรับเปลี่ยนตามโปรไฟล์ของผู้ใช้หรือไม่
setIsOfferPersonalized() รับอินพุตบูลีน เมื่อtrue UI ของ Play จะมีการเปิดเผยข้อมูล เมื่อ false UI จะไม่แสดงการเปิดเผยข้อมูล ค่าเริ่มต้นคือ false
ดูข้อมูลเพิ่มเติมได้ที่ศูนย์ช่วยเหลือผู้บริโภค
แนบตัวระบุผู้ใช้
เมื่อเปิดใช้ขั้นตอนการซื้อ แอปของคุณจะแนบตัวระบุผู้ใช้ที่คุณมีสำหรับผู้ใช้ที่ทำการซื้อโดยใช้ obfuscatedAccountId หรือ obfuscatedProfileId ได้ ตัวอย่างตัวระบุอาจเป็นเวอร์ชันที่สร้างความสับสนของการเข้าสู่ระบบของผู้ใช้ในระบบของคุณ การตั้งค่าพารามิเตอร์เหล่านี้จะช่วยให้ Google ตรวจจับการประพฤติมิชอบได้ นอกจากนี้ยังช่วยให้คุณมั่นใจได้ว่าการซื้อจะได้รับการระบุแหล่งที่มาของผู้ใช้ที่ถูกต้องตามที่กล่าวถึงในการให้สิทธิ์แก่ผู้ใช้
ตรวจจับและประมวลผลการซื้อ
การตรวจจับและการประมวลผลการซื้อที่อธิบายไว้ในส่วนนี้ใช้ได้กับการซื้อทุกประเภท รวมถึงการซื้อนอกแอป เช่น การแลกรับโปรโมชัน
แอปของคุณจะตรวจพบการซื้อใหม่และการซื้อที่รอดำเนินการที่เสร็จสมบูรณ์ด้วยวิธีใดวิธีหนึ่งต่อไปนี้
- เมื่อมีการเรียกใช้
onPurchasesUpdatedอันเป็นผลมาจากการที่แอปของคุณเรียกใช้launchBillingFlow(ตามที่กล่าวถึงในส่วนก่อนหน้า) หรือหากแอปของคุณทำงานโดยมีการเชื่อมต่อ Billing Library ที่ใช้งานอยู่เมื่อมีการซื้อนอกแอปหรือการซื้อที่รอดำเนินการเสร็จสมบูรณ์ เช่น สมาชิกในครอบครัวอนุมัติการซื้อที่รอดำเนินการในอุปกรณ์อื่น - เมื่อแอปเรียกใช้ queryPurchasesAsync เพื่อค้นหาการซื้อของผู้ใช้
สำหรับข้อ 1 ระบบจะเรียกใช้ onPurchasesUpdated โดยอัตโนมัติสำหรับการซื้อใหม่หรือการซื้อที่เสร็จสมบูรณ์ ตราบใดที่แอปของคุณทำงานอยู่และมีการเชื่อมต่อ Google Play Billing Library ที่ใช้งานอยู่ หากแอปพลิเคชันไม่ทำงานหรือแอปไม่มีการเชื่อมต่อ Google Play Billing Library ที่ใช้งานอยู่ ระบบจะไม่เรียกใช้ onPurchasesUpdated โปรดทราบว่าเราขอแนะนำให้แอปพยายามรักษาการเชื่อมต่อที่ใช้งานอยู่ตราบใดที่แอปทำงานอยู่เบื้องหน้า เพื่อให้แอปได้รับการอัปเดตการซื้ออย่างทันท่วงที
สำหรับข้อ 2 คุณต้องเรียกใช้ BillingClient.queryPurchasesAsync() เพื่อให้แอปประมวลผลการซื้อทั้งหมด เราขอแนะนำให้คุณดำเนินการนี้เมื่อแอปสร้างการเชื่อมต่อกับ Google Play Billing Library สำเร็จ (ซึ่งเราขอแนะนำให้ทำเมื่อแอปเปิดตัวหรือทำงานอยู่เบื้องหน้าตามที่กล่าวถึงในเริ่มต้น BillingClient ซึ่งทำได้โดยเรียกใช้ queryPurchasesAsync เมื่อได้รับผลลัพธ์ที่สำเร็จไปยัง onServiceConnected การปฏิบัติตามคำแนะนำนี้เป็นสิ่งสำคัญในการจัดการเหตุการณ์และสถานการณ์ต่างๆ เช่น
- ปัญหาเกี่ยวกับเครือข่ายระหว่างการซื้อ: ผู้ใช้สามารถทำการซื้อได้สำเร็จและได้รับการยืนยันจาก Google แต่อุปกรณ์ขาดการเชื่อมต่อเครือข่ายก่อนที่อุปกรณ์และแอปจะได้รับการแจ้งเตือนการซื้อผ่าน
PurchasesUpdatedListener - อุปกรณ์หลายเครื่อง: ผู้ใช้อาจซื้อสินค้าในอุปกรณ์เครื่องหนึ่งและคาดหวังว่าจะเห็นสินค้าดังกล่าวเมื่อเปลี่ยนอุปกรณ์
- การจัดการการซื้อที่ดำเนินการนอกแอป: การซื้อบางรายการ เช่น การแลกสิทธิ์โปรโมชัน สามารถดำเนินการนอกแอปได้
- การจัดการการเปลี่ยนสถานะการซื้อ: ผู้ใช้อาจชำระเงินสำหรับการซื้อที่รอดำเนินการ (PENDING) เสร็จสมบูรณ์ขณะที่แอปพลิเคชันของคุณไม่ได้ทำงาน และคาดหวังว่าจะได้รับการยืนยันว่าทำการซื้อเสร็จสมบูรณ์เมื่อเปิดแอป
- การสมัครใช้บริการที่ถูกระงับ: การสมัครใช้บริการอาจถูกระงับในระหว่างวงจรการสมัครใช้บริการ BillingClient.queryPurchasesAsync() จะแสดงผลการสมัครใช้บริการที่ถูกระงับก็ต่อเมื่อมีการตั้งค่าพารามิเตอร์
includeSuspendedSubscriptionsในQueryPurchasesParams.Builderเท่านั้น ระบบจะไม่แสดงการสมัครใช้บริการที่ถูกระงับในPurchasesUpdatedListener
เมื่อแอปตรวจพบการซื้อใหม่หรือการซื้อที่เสร็จสมบูรณ์แล้ว แอปควรดำเนินการดังนี้
- ยืนยันการซื้อ
- ให้สิทธิ์เข้าถึงเนื้อหาแก่ผู้ใช้สำหรับการซื้อที่เสร็จสมบูรณ์
- แจ้งให้ผู้ใช้ทราบ
- แจ้งให้ Google ทราบว่าแอปของคุณประมวลผลการซื้อที่เสร็จสมบูรณ์แล้ว
เราจะอธิบายขั้นตอนเหล่านี้โดยละเอียดในส่วนต่อไปนี้ ตามด้วยส่วนสรุปขั้นตอนทั้งหมด
ยืนยันการซื้อ
แอปของคุณควรยืนยันความถูกต้องของการซื้อก่อนให้สิทธิประโยชน์แก่ผู้ใช้เสมอ ซึ่งทำได้โดยทำตามหลักเกณฑ์ที่อธิบายไว้ในยืนยันการซื้อก่อนให้สิทธิ์ หลังจากยืนยันการซื้อแล้วเท่านั้น แอปของคุณจึงควรดำเนินการซื้อต่อและให้สิทธิ์แก่ผู้ใช้ ซึ่งจะอธิบายในส่วนถัดไป หากการซื้อไม่ผ่านการยืนยัน การตรวจสอบสิทธิ์ หรือการตรวจสอบการละเมิด โปรดดูหัวข้อจัดการการซื้อที่ถูกปฏิเสธหรือดำเนินการไม่สำเร็จ
ให้สิทธิ์แก่ผู้ใช้
เมื่อแอปยืนยันการซื้อแล้ว แอปจะให้สิทธิ์แก่ผู้ใช้และแจ้งให้ผู้ใช้ทราบต่อไปได้ ก่อนให้สิทธิ์ โปรดตรวจสอบว่าแอปของคุณตรวจสอบว่าสถานะการซื้อเป็น PURCHASED หากการซื้ออยู่ในสถานะ "รอดำเนินการ" แอปควรแจ้งให้ผู้ใช้ทราบว่ายังต้องดำเนินการให้เสร็จสมบูรณ์เพื่อทำการซื้อให้เสร็จสิ้นก่อนที่จะได้รับสิทธิ์
ให้สิทธิ์เมื่อการซื้อเปลี่ยนจาก "รอดำเนินการ" เป็น "ซื้อแล้ว" เท่านั้น
ดูข้อมูลเพิ่มเติมได้ในการจัดการธุรกรรมที่รอดำเนินการ
หากคุณแนบตัวระบุผู้ใช้กับการซื้อตามที่อธิบายไว้ในการแนบตัวระบุผู้ใช้ คุณจะดึงข้อมูลและใช้ตัวระบุเหล่านั้นเพื่อระบุแหล่งที่มาของผู้ใช้ที่ถูกต้องในระบบได้ เทคนิคนี้มีประโยชน์เมื่อปรับยอดการซื้อที่แอปของคุณอาจสูญเสียบริบทเกี่ยวกับผู้ใช้ที่ทำการซื้อ เพื่อป้องกันการละเมิด Google Play ขอแนะนำให้คุณยืนยันตัวระบุเหล่านี้ในแบ็กเอนด์ที่ปลอดภัยก่อนให้สิทธิ์ ดูข้อมูลเพิ่มเติมได้ที่ยืนยันการซื้อก่อนให้สิทธิ์ โปรดทราบว่าการซื้อที่ทำนอกแอปจะไม่มีการตั้งค่าตัวระบุเหล่านี้ ในกรณีนี้ แอปของคุณสามารถให้สิทธิ์แก่ผู้ใช้ที่เข้าสู่ระบบ หรือแจ้งให้ผู้ใช้เลือกบัญชีที่ต้องการก็ได้
สำหรับการสั่งจองล่วงหน้า การซื้อจะอยู่ในสถานะ PENDING ก่อนถึงเวลาวางจำหน่าย การซื้อแบบสั่งจองล่วงหน้าจะเสร็จสมบูรณ์เมื่อถึงเวลาวางจำหน่ายและเปลี่ยนสถานะเป็น PURCHASED โดยไม่ต้องดำเนินการใดๆ เพิ่มเติม
แจ้งเตือนผู้ใช้
หลังจากให้สิทธิ์แก่ผู้ใช้แล้ว แอปควรแสดงการแจ้งเตือนเพื่อรับทราบการซื้อที่สำเร็จ การแจ้งเตือนนี้จะช่วยให้ผู้ใช้ไม่สับสนว่าการซื้อเสร็จสมบูรณ์หรือไม่ ซึ่งอาจส่งผลให้ผู้ใช้หยุดใช้แอป ติดต่อทีมสนับสนุนผู้ใช้ หรือร้องเรียนเกี่ยวกับเรื่องนี้ในโซเชียลมีเดีย โปรดทราบว่าแอปของคุณอาจตรวจพบการอัปเดตการซื้อได้ทุกเมื่อในระหว่างวงจรการใช้งานแอปพลิเคชัน ตัวอย่างเช่น ผู้ปกครองอนุมัติการซื้อที่รอดำเนินการในอุปกรณ์อื่น ซึ่งในกรณีนี้แอปของคุณอาจต้องการเลื่อนการแจ้งเตือนผู้ใช้ไปยังเวลาที่เหมาะสม ตัวอย่างกรณีที่ควรหน่วงเวลา ได้แก่
- ในระหว่างส่วนแอ็กชันของเกมหรือฉากคัตซีน การแสดงข้อความอาจทำให้ผู้ใช้เสียสมาธิ ในกรณีนี้ คุณต้องแจ้งให้ผู้ใช้ทราบหลังจากที่ส่วนการดำเนินการสิ้นสุดลง
- ในระหว่างบทแนะนำเริ่มต้นและส่วนการตั้งค่าผู้ใช้ของเกม เช่น ผู้ใช้อาจทำการซื้อนอกแอปก่อนที่จะติดตั้งแอป เราขอแนะนำให้คุณแจ้งให้ผู้ใช้ใหม่ทราบเกี่ยวกับรางวัลทันทีที่เปิดเกมหรือในระหว่างการตั้งค่าผู้ใช้ครั้งแรก หากแอปกำหนดให้ผู้ใช้สร้างบัญชีหรือเข้าสู่ระบบก่อนให้สิทธิ์แก่ผู้ใช้ เราขอแนะนำให้แจ้งให้ผู้ใช้ทราบถึงขั้นตอนที่ต้องดำเนินการเพื่ออ้างสิทธิ์การซื้อ ขั้นตอนนี้สำคัญมากเนื่องจากระบบจะคืนเงินสำหรับการซื้อหลังจากผ่านไป 3 วันหากแอปของคุณไม่ได้ประมวลผลการซื้อ
เมื่อแจ้งเตือนผู้ใช้เกี่ยวกับการซื้อ Google Play ขอแนะนำให้ใช้กลไกต่อไปนี้
- แสดงกล่องโต้ตอบในแอป
- ส่งข้อความไปยังกล่องข้อความในแอป และระบุอย่างชัดเจนว่ามีข้อความใหม่ในกล่องข้อความในแอป
- ใช้ข้อความแจ้งเตือนของระบบปฏิบัติการ
การแจ้งเตือนควรแจ้งให้ผู้ใช้ทราบเกี่ยวกับสิทธิประโยชน์ที่ได้รับ เช่น "คุณซื้อเหรียญทอง 100 เหรียญ" นอกจากนี้ หากการซื้อเป็นผลมาจากสิทธิประโยชน์ของโปรแกรม เช่น Play Pass แอปของคุณจะแจ้งให้ผู้ใช้ทราบ เช่น "ได้รับไอเทมแล้ว คุณเพิ่งได้รับ 100 เพชรด้วย Play Pass ต่อไป" แต่ละโปรแกรมอาจมีคำแนะนำเกี่ยวกับข้อความที่แนะนำให้แสดงต่อผู้ใช้เพื่อสื่อสารสิทธิประโยชน์
แจ้ง Google ว่าประมวลผลการซื้อแล้ว
หลังจากแอปให้สิทธิ์แก่ผู้ใช้และแจ้งให้ผู้ใช้ทราบเกี่ยวกับธุรกรรมที่สำเร็จแล้ว แอปจะต้องแจ้งให้ Google ทราบว่าการซื้อได้รับการประมวลผลเรียบร้อยแล้ว ซึ่งทำได้โดยการรับทราบการซื้อและต้องดำเนินการภายใน 3 วัน เพื่อไม่ให้ระบบคืนเงินโดยอัตโนมัติและเพิกถอนสิทธิ์ กระบวนการรับทราบการซื้อประเภทต่างๆ มีอธิบายไว้ในส่วนต่อไปนี้
ผลิตภัณฑ์ที่ใช้แล้วหมดไป
สำหรับสินค้าที่ใช้สิ้นเปลือง หากแอปมีแบ็กเอนด์ที่ปลอดภัย เราขอแนะนำให้ใช้ 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
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 = (billingResult, 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 = billingClient val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.purchaseToken) val ackPurchaseResult = withContext(Dispatchers.IO) { client.acknowledgePurchase(acknowledgePurchaseParams.build()) }
Java
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { if (!purchase.isAcknowledged()) { AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchase.getPurchaseToken()) .build(); billingClient.acknowledgePurchase(acknowledgePurchaseParams, (billingResult) -> { // Acknowledgment handled. }); } }
การติดตาม
ระบบจะจัดการการสมัครใช้บริการในลักษณะเดียวกับไอเทมที่ใช้ไม่ได้ หากเป็นไปได้ ให้ใช้ Purchases.subscriptions.acknowledge จาก Google Play Developer API เพื่อตอบรับการซื้อจากแบ็กเอนด์ที่ปลอดภัยอย่างเชื่อถือได้ ยืนยันว่าการซื้อยังไม่ได้รับการรับทราบก่อนหน้านี้โดยตรวจสอบ acknowledgementState ในทรัพยากรการซื้อจาก Purchases.subscriptions:get หรือคุณจะรับทราบการสมัครใช้บริการโดยใช้ BillingClient.acknowledgePurchase() จาก Google Play Billing Library หลังจากตรวจสอบ isAcknowledged() ก็ได้ การซื้อการสมัครใช้บริการครั้งแรกทั้งหมดต้องได้รับการรับทราบ การต่ออายุการสมัครใช้บริการไม่จำเป็นต้องได้รับการรับทราบ ดูข้อมูลเพิ่มเติมเกี่ยวกับเวลาที่ต้องรับทราบการสมัครใช้บริการได้ที่หัวข้อขายการสมัครใช้บริการ
จัดการการซื้อที่ถูกปฏิเสธหรือไม่สำเร็จ
หากการซื้อไม่ผ่านการยืนยันแบ็กเอนด์ การตรวจสอบสิทธิ์ของผู้ใช้ การตรวจสอบการประพฤติมิชอบและการละเมิด หรือหากระบบไม่สามารถให้สิทธิ์ได้ โปรดอย่าให้สิทธิ์และอย่ารับทราบหรือใช้การซื้อนั้น
แม้ว่าระบบจะคืนเงินและเพิกถอนการซื้อที่ไม่ได้รับทราบโดยอัตโนมัติหลังจากกรอบเวลารับทราบหมดอายุเพื่อความปลอดภัยของผู้บริโภค แต่อย่าพึ่งพากรอบเวลาหมดอายุนี้เพื่อปฏิเสธการซื้อ การปล่อยให้กรอบเวลาการรับทราบหมดอายุจะทำให้คำสั่งซื้ออยู่ในสถานะไม่ได้รับการรับทราบและไม่ชัดเจนสำหรับ Google Play ซึ่งไม่สามารถแยกแยะการปฏิเสธโดยเจตนาของนักพัฒนาแอปจากการหมดเวลาการประมวลผลแบ็กเอนด์ได้
แต่ให้เพิกถอนและคืนเงินสำหรับการซื้อที่ถูกปฏิเสธจากแบ็กเอนด์ทันทีโดยใช้ Google Play Developer API ดังนี้
- การสมัครใช้บริการ: โทร
purchases.subscriptionsv2.revokeโดยใช้"fullRefund": {}ในrevocationContextการดำเนินการนี้จะสิ้นสุดการสมัครใช้บริการทันที เพิกถอนสิทธิ์เข้าถึง และเริ่มการคืนเงินเต็มจำนวน - ผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียว: เรียกใช้
orders.refundโดยตั้งค่าพารามิเตอร์revokeเป็นtrueเพื่อคืนเงินสำหรับคำสั่งซื้อและเพิกถอนสิทธิ์ทันที เมื่อเรียกใช้เมธอดนี้ คุณต้องส่งรหัสคำสั่งซื้อของการซื้อ ซึ่งคุณสามารถดึงข้อมูลได้โดยใช้Purchase.getOrderId()หรือpurchases.productsv2.getproductpurchasev2อย่าเรียกใช้consumeAsync(),acknowledgePurchase(),Purchases.products:consumeหรือPurchases.products:acknowledgeสำหรับการซื้อที่ถูกปฏิเสธ - ลักษณะการทำงานของ RTDN และการซื้อที่เป็นโมฆะ: การเพิกถอนและการคืนเงินที่นักพัฒนาแอปเป็นผู้เริ่ม (
purchases.subscriptionsv2.revokeและorders.refundโดยตั้งค่าrevokeเป็นtrue) จะทริกเกอร์การแจ้งเตือนแบบเรียลไทม์สำหรับนักพัฒนาแอปและบันทึกไว้ใน Voided Purchases API (purchases.voidedpurchases.listโดยตั้งค่าช่องการตอบกลับvoidedSourceเป็น1สำหรับนักพัฒนาแอป)- การสมัครใช้บริการ: ส่งทั้งการแจ้งเตือน
SUBSCRIPTION_REVOKEDและVoidedPurchaseNotification - ผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียว: ส่ง
VoidedPurchaseNotification(และการแจ้งเตือนONE_TIME_PRODUCT_CANCELEDหากแอปของคุณกำหนดค่าไว้ใน Play Console ให้รับการแจ้งเตือนผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียวทั้งหมด) - การเรียกใช้
orders.refundโดยไม่ได้ตั้งค่าrevokeเป็นtrueจะเป็นการคืนเงินสำหรับคำสั่งซื้อโดยไม่เพิกถอนสิทธิ์ และจะไม่ทริกเกอร์การเพิกถอนหรือการแจ้งเตือนการซื้อที่เป็นโมฆะ ดูรายละเอียดเพิ่มเติมได้ที่หัวข้อจัดการการสมัครใช้บริการและการซื้อครั้งเดียว
- การสมัครใช้บริการ: ส่งทั้งการแจ้งเตือน
สรุป
ต่อไปนี้เป็นสรุปขั้นตอนเหล่านี้
ส่งการซื้อไปยังแบ็กเอนด์ที่ปลอดภัยเพื่อยืนยันการซื้อก่อนให้สิทธิ์
อัปเดตพื้นที่เก็บข้อมูลสิทธิ์ด้วยการซื้อ หากการซื้ออยู่ในสถานะรอดำเนินการ ให้ตรวจสอบว่าการให้สิทธิ์มีการทำเครื่องหมายเป็นรอดำเนินการและผู้ใช้ยังไม่ได้รับสิทธิประโยชน์ เราขอแนะนำให้ทำขั้นตอนนี้ในแบ็กเอนด์ที่ปลอดภัย และสามารถรวมไว้ในการเรียก API ไปยังแบ็กเอนด์ในขั้นตอนที่ 1 ได้
แจ้งให้ผู้ใช้ทราบโดยใช้ข้อความที่เหมาะสม (เลื่อนการแจ้งเตือนออกไปหากจำเป็นตามที่ได้อธิบายไว้ก่อนหน้านี้)
แจ้ง Google ว่าการซื้อได้รับการประมวลผลแล้วโดยใช้ขั้นตอนที่กล่าวถึงในส่วนการประมวลผลการซื้อ
หากต้องการยืนยันว่าแอปได้ทำตามขั้นตอนเหล่านี้อย่างถูกต้องแล้ว คุณสามารถทำตามคู่มือการทดสอบได้
จัดการธุรกรรมที่รอดำเนินการ
Google Play รองรับธุรกรรมที่รอดำเนินการ หรือธุรกรรมที่ต้องใช้ขั้นตอนเพิ่มเติมอย่างน้อย 1 ขั้นตอนระหว่างที่ผู้ใช้เริ่มทำการซื้อและเมื่อมีการประมวลผลวิธีการชำระเงินสำหรับการซื้อนั้น แอปของคุณไม่ควรให้สิทธิ์สำหรับการซื้อประเภทนี้จนกว่า Google จะแจ้งให้คุณทราบว่าเรียกเก็บเงินจากวิธีการชำระเงินของผู้ใช้เรียบร้อยแล้ว
เช่น ผู้ใช้สามารถเริ่มธุรกรรมโดยเลือกกิจการที่มีหน้าร้านจริงที่ตนจะชำระเงินด้วยเงินสดในภายหลัง ผู้ใช้จะได้รับรหัสผ่านทั้งการแจ้งเตือนและอีเมล เมื่อผู้ใช้ไปถึงกิจการที่มีหน้าร้านจริง ก็จะสามารถแลกใช้รหัสกับแคชเชียร์และชำระเงินด้วยเงินสดได้ จากนั้น Google จะแจ้งให้ทั้งคุณและผู้ใช้ทราบว่าได้รับการชำระเงินแล้ว จากนั้นแอปของคุณจะให้สิทธิ์แก่ผู้ใช้ได้
เรียกใช้ enablePendingPurchases() เป็นส่วนหนึ่งของการเริ่มต้น BillingClient เพื่อเปิดใช้ธุรกรรมที่รอดำเนินการสำหรับแอปของคุณ แอปต้องเปิดใช้และรองรับธุรกรรมที่รอดำเนินการสำหรับผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียว ก่อนเพิ่มการรองรับ โปรดทำความเข้าใจวงจรการซื้อสำหรับธุรกรรมที่รอดำเนินการ
เมื่อแอปของคุณได้รับการซื้อใหม่ ไม่ว่าจะผ่าน PurchasesUpdatedListener หรือเป็นผลมาจากการเรียกใช้ queryPurchasesAsync ให้ใช้เมธอด getPurchaseState() เพื่อระบุว่าสถานะการซื้อเป็น PURCHASED หรือ PENDING คุณควรให้สิทธิ์เฉพาะเมื่อสถานะเป็น PURCHASED
หากแอปของคุณทำงานอยู่และคุณมีการเชื่อมต่อ Play Billing Library ที่ใช้งานอยู่เมื่อผู้ใช้ทำการซื้อเสร็จสมบูรณ์ ระบบจะเรียกใช้ PurchasesUpdatedListener อีกครั้ง และตอนนี้ PurchaseState จะเป็น PURCHASED ในขั้นตอนนี้ แอปของคุณจะประมวลผลการซื้อได้โดยใช้วิธีมาตรฐานสำหรับการตรวจจับและประมวลผลการซื้อ แอปของคุณควรเรียกใช้ queryPurchasesAsync() ในเมธอด 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 รองรับ Google Play Billing Library เวอร์ชัน 4.0 ขึ้นไป โดยอนุญาตให้ลูกค้าซื้อผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียวรายการเดียวกันได้มากกว่า 1 รายการในธุรกรรมเดียวด้วยการระบุจำนวนจากรถเข็นการซื้อ แอปของคุณควรจัดการการซื้อแบบหลายจำนวนและให้สิทธิ์ตามจำนวนการซื้อที่ระบุ
หากต้องการรองรับการซื้อหลายรายการ ตรรกะการจัดสรรของแอปจะต้องตรวจสอบจำนวนสินค้า คุณเข้าถึงฟิลด์ quantity ได้จาก API รายการใดรายการหนึ่งต่อไปนี้
getQuantity()จาก Google Play Billing LibraryPurchases.products.quantityจาก Google Play Developer API
หลังจากเพิ่มตรรกะเพื่อจัดการการซื้อหลายรายการแล้ว คุณจะต้องเปิดใช้ฟีเจอร์หลายรายการสำหรับผลิตภัณฑ์ที่เกี่ยวข้องในหน้าการจัดการผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียวใน Google Play Developer Console
ค้นหาการกำหนดค่าการเรียกเก็บเงินของผู้ใช้
getBillingConfigAsync() จะระบุประเทศที่ผู้ใช้ใช้ Google Play
คุณสามารถค้นหาการกำหนดค่าการเรียกเก็บเงินของผู้ใช้หลังจากสร้าง BillingClient แล้ว ข้อมูลโค้ดต่อไปนี้อธิบายวิธีเรียกใช้ getBillingConfigAsync() จัดการการตอบกลับโดยใช้ BillingConfigResponseListener Listener นี้จะได้รับการอัปเดตสำหรับการค้นหาการกำหนดค่าการเรียกเก็บเงินทั้งหมดที่เริ่มต้นจากแอปของคุณ
หาก 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 { // Handle errors } } } )
Java
GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build(); billingClient.getBillingConfigAsync( getBillingConfigParams, (billingResult, billingConfig) -> { if (billingResult.getResponseCode() == BillingResponseCode.OK && billingConfig != null) { String countryCode = billingConfig.getCountryCode(); } else { // TODO: Handle errors } });