ผสานรวม 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 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 และการรองรับโครูทีนที่ช่วยให้คุณเขียน 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

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

  • เราขอแนะนำให้คุณเปิดการเชื่อมต่อ BillingClient ที่ใช้งานอยู่เพียง 1 รายการในแต่ละครั้งเพื่อหลีกเลี่ยงการเรียกกลับ PurchasesUpdatedListener หลายครั้งสำหรับเหตุการณ์เดียว
  • เราขอแนะนำให้เริ่มการเชื่อมต่อสำหรับ BillingClient เมื่อแอปของคุณเปิดขึ้นหรือมาที่เบื้องหน้า เพื่อให้แอปประมวลผลการซื้อได้อย่างทันท่วงที ซึ่งทำได้โดยใช้ ActivityLifecycleCallbacks ที่ลงทะเบียนโดย registerActivityLifecycleCallbacks และฟัง onActivityResumed เพื่อเริ่มต้นการเชื่อมต่อเมื่อตรวจพบกิจกรรมที่กลับมาทำงานอีกครั้งเป็นครั้งแรก ดูรายละเอียดเพิ่มเติมเกี่ยวกับเหตุผลที่ควรปฏิบัติตามแนวทางปฏิบัติแนะนำนี้ได้ที่ส่วนการประมวลผลการซื้อ และอย่าลืมสิ้นสุดการเชื่อมต่อเมื่อปิดแอป

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

Kotlin

private val purchasesUpdatedListener =
    PurchasesUpdatedListener { billingResult, purchases ->
        // To be implemented in a later section.
    }

private var billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    // Configure other settings.
    .build()

Java

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
  @Override
  public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
      // To be implemented in a later section.
  }
};

private BillingClient billingClient = BillingClient.newBuilder(context)
  .setListener(purchasesUpdatedListener)
  // Configure other settings.
  .build();

เชื่อมต่อกับ Google Play

หลังจากสร้าง BillingClient แล้ว คุณต้องสร้างการเชื่อมต่อกับ Google Play

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

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

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 แสดงการสมัครใช้บริการที่
            พร้อมให้ซื้อ
รูปที่ 1 หน้าจอการซื้อใน Google Play จะแสดง การสมัครใช้บริการที่พร้อมให้ซื้อ

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
รูปที่ 2 หน้าจอการซื้อสำเร็จของ Google Play

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

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

ระบุราคาที่ปรับเปลี่ยนในแบบของคุณ

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

หน้าจอการซื้อใน Google Play ที่ระบุว่ามีการปรับแต่งราคาสำหรับผู้ใช้
รูปที่ 3 หน้าจอการซื้อใน Google Play ที่ระบุว่า ราคาได้รับการปรับแต่งสำหรับผู้ใช้

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

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

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

แนบตัวระบุผู้ใช้

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

ตรวจจับและประมวลผลการซื้อ

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

แอปจะตรวจหาการซื้อใหม่และการซื้อที่รอดำเนินการที่เสร็จสมบูรณ์ด้วยวิธีใดวิธีหนึ่งต่อไปนี้

  1. เมื่อมีการเรียกใช้ onPurchasesUpdated อันเป็นผลมาจากการที่แอปของคุณเรียกใช้ launchBillingFlow (ตามที่กล่าวถึงในส่วนก่อนหน้า) หรือหากแอปของคุณทำงานโดยมีการเชื่อมต่อ Billing Library ที่ใช้งานอยู่เมื่อมีการซื้อนอกแอปหรือการซื้อที่รอดำเนินการเสร็จสมบูรณ์ เช่น สมาชิกในครอบครัวอนุมัติการซื้อที่รอดำเนินการในอุปกรณ์อื่น
  2. เมื่อแอปเรียกใช้ queryPurchasesAsync เพื่อค้นหาการซื้อของผู้ใช้

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

สำหรับข้อ 2 คุณต้องเรียกใช้ BillingClient.queryPurchasesAsync() เพื่อให้แน่ใจว่าแอปจะประมวลผลการซื้อทั้งหมด เราขอแนะนำให้คุณทำเช่นนี้เมื่อแอป สร้างการเชื่อมต่อกับ Google Play Billing Library ได้สำเร็จ (ซึ่งขอแนะนำเมื่อแอปเปิดตัวหรือมาที่เบื้องหน้าตามที่อธิบายไว้ในเริ่มต้น BillingClient ซึ่งทำได้โดยการเรียกใช้ queryPurchasesAsync เมื่อได้รับผลลัพธ์ที่สำเร็จไปยัง onServiceConnected การปฏิบัติตามคำแนะนำนี้เป็นสิ่งสำคัญในการจัดการเหตุการณ์และสถานการณ์ต่างๆ เช่น

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

เมื่อแอปตรวจพบการซื้อใหม่หรือการซื้อที่เสร็จสมบูรณ์แล้ว แอปควรดำเนินการดังนี้

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

เราจะอธิบายขั้นตอนเหล่านี้อย่างละเอียดในส่วนต่อไปนี้ และตามด้วยส่วนสรุปขั้นตอนทั้งหมด

ยืนยันการซื้อ

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

ให้สิทธิ์แก่ผู้ใช้

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

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

สำหรับการสั่งล่วงหน้า การซื้อจะอยู่ในสถานะรอดำเนินการก่อนถึงเวลาวางจำหน่าย การซื้อแบบสั่งล่วงหน้าจะเสร็จสมบูรณ์เมื่อถึงเวลาวางจำหน่ายและเปลี่ยนสถานะเป็น "ซื้อแล้ว" โดยไม่ต้องดำเนินการใดๆ เพิ่มเติม

แจ้งให้ผู้ใช้ทราบ

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

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

เมื่อแจ้งให้ผู้ใช้ทราบเกี่ยวกับการซื้อ Google Play ขอแนะนำให้ใช้กลไกต่อไปนี้

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

การแจ้งเตือนควรแจ้งให้ผู้ใช้ทราบถึงสิทธิประโยชน์ที่ได้รับ เช่น "คุณซื้อเหรียญทอง 100 เหรียญ" นอกจากนี้ หากการซื้อเป็นผลมาจากสิทธิประโยชน์ของโปรแกรม เช่น Play Pass แอปของคุณจะสื่อสารเรื่องนี้กับผู้ใช้ เช่น "ได้รับไอเทมแล้ว คุณเพิ่งได้รับ 100 Gem ด้วย 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 เพื่อตอบรับการซื้อจากแบ็กเอนด์ที่ปลอดภัย ได้อย่างน่าเชื่อถือ ตรวจสอบว่าก่อนหน้านี้ Purchases.subscriptions:get ไม่ได้ยืนยันการซื้อแล้วโดยดูacknowledgementStateในแหล่งข้อมูลการซื้อ หรือคุณจะรับทราบการสมัครใช้บริการโดยใช้ 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 จะคืนเงิน คำสั่งซื้อโดยไม่เพิกถอนสิทธิ์และไม่ทริกเกอร์การเพิกถอนหรือ การแจ้งเตือนการซื้อที่ถูกยกเลิก ดูรายละเอียดเพิ่มเติมได้ที่หัวข้อจัดการการสมัครใช้บริการและการซื้อครั้งเดียว

สรุป

ต่อไปนี้คือสรุปขั้นตอนเหล่านี้

  1. ส่งการซื้อไปยังแบ็กเอนด์ที่ปลอดภัยเพื่อยืนยันการซื้อก่อนให้สิทธิ์

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

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

  4. แจ้งให้ 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 Billing Library เวอร์ชัน 4.0 ขึ้นไปรองรับการอนุญาตให้ลูกค้าซื้อผลิตภัณฑ์แบบเรียกเก็บเงินครั้งเดียวรายการเดียวกันมากกว่า 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 {
                // 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
            }
        });