Phân tích tình trạng bỏ dở giao dịch mua sản phẩm trong Play Billing

1. Giới thiệu

Trong lớp học lập trình này, bạn sẽ tập trung vào việc tạo sản phẩm tính phí một lần, tích hợp ứng dụng với Thư viện Play Billing (PBL) và phân tích lý do khiến người dùng bỏ qua giao dịch mua.

Lưu ý: Để hoàn tất lớp học lập trình này thành công, bạn cần có quyền truy cập vào tính năng Nhiều lựa chọn mua và ưu đãi cho sản phẩm tính phí một lần. Tính năng này nằm trong chương trình tiếp cận sớm (EAP). Các sản phẩm và tính năng trong EAP được cung cấp "nguyên trạng" và có thể có phạm vi hỗ trợ hạn chế. Để truy cập vào tính năng EAP, hãy gửi yêu cầu bằng Biểu mẫu bày tỏ sự quan tâm với EAP cho sản phẩm tính phí một lần. Tuy nhiên, nếu bạn chỉ muốn tìm hiểu cách phân tích tình trạng người dùng bỏ qua giao dịch mua bằng cách sử dụng mã phản hồi của Play Billing, hãy chuyển trực tiếp đến phần Phân tích tình trạng người dùng bỏ qua giao dịch mua của lớp học lập trình này.

Đối tượng người xem

Lớp học lập trình này nhắm đến các nhà phát triển ứng dụng Android đang sử dụng Thư viện Play Billing (PBL) hoặc muốn sử dụng PBL để kiếm tiền từ các sản phẩm tính phí một lần.

Bạn sẽ học được...

  • Cách tạo sản phẩm tính phí một lần trong Google Play Console.
  • Cách tích hợp ứng dụng với PBL.
  • Cách xử lý các giao dịch mua sản phẩm tính phí một lần có thể tiêu thụ và không thể tiêu thụ trong PBL.
  • Cách phân tích tình trạng người dùng bỏ qua giao dịch mua.

Những điều bạn cần...

2. Tạo ứng dụng mẫu

Ứng dụng mẫu được thiết kế để trở thành một ứng dụng Android hoạt động đầy đủ, có mã nguồn hoàn chỉnh thể hiện các khía cạnh sau:

  • Tích hợp ứng dụng với PBL
  • Tìm nạp sản phẩm tính phí một lần
  • Bắt đầu quy trình mua cho các sản phẩm tính phí một lần
  • Các tình huống mua dẫn đến các phản hồi thanh toán sau:
    • BILLING_UNAVAILABLE
    • USER_CANCELLED
    • OK
    • ITEM_ALREADY_OWNED

Video minh hoạ sau đây cho thấy ứng dụng mẫu sẽ trông như thế nào và hoạt động ra sao sau khi được triển khai và chạy.

Điều kiện tiên quyết

Trước khi tạo và triển khai ứng dụng mẫu, hãy làm như sau:

Tạo

Mục tiêu của bước tạo này là tạo một tệp gói ứng dụng Android có chữ ký của ứng dụng mẫu.

Để tạo gói ứng dụng Android, hãy làm theo các bước sau:

  1. Tải ứng dụng mẫu xuống từ GitHub.
  2. Tạo ứng dụng mẫu. Trước khi tạo, hãy thay đổi tên gói của ứng dụng mẫu rồi tạo. Nếu bạn có các gói của ứng dụng khác trong Play Console, hãy đảm bảo tên gói mà bạn cung cấp cho ứng dụng mẫu là duy nhất.

    Lưu ý: Việc tạo ứng dụng mẫu chỉ tạo một tệp APK mà bạn có thể dùng để kiểm thử cục bộ. Tuy nhiên, việc chạy ứng dụng sẽ không tìm nạp sản phẩm và giá vì các sản phẩm chưa được định cấu hình trong Play Console. Bạn sẽ thực hiện việc này sau trong lớp học lập trình này.
  3. Tạo gói ứng dụng Android có chữ ký.
    1. Tạo khoá tải lên và kho khoá
    2. Ký ứng dụng bằng khoá tải lên
    3. Định cấu hình Tính năng ký ứng dụng của Play

Bước tiếp theo là tải gói ứng dụng Android lên Google Play Console.

3. Tạo sản phẩm tính phí một lần trong Play Console

Để tạo sản phẩm tính phí một lần trong Google Play Console, bạn cần có một ứng dụng trong Play Console. Tạo một ứng dụng trong Play Console, sau đó tải gói ứng dụng có chữ ký đã tạo trước đó lên.

Tạo ứng dụng

Cách tạo ứng dụng:

  1. Đăng nhập vào Google Play Console bằng tài khoản nhà phát triển.
  2. Nhấp vào Create app (Tạo ứng dụng). Thao tác này sẽ mở trang Create app (Tạo ứng dụng).
  3. Nhập tên ứng dụng, chọn ngôn ngữ mặc định và các thông tin chi tiết khác liên quan đến ứng dụng.
  4. Nhấp vào Create app (Tạo ứng dụng). Thao tác này sẽ tạo một ứng dụng trong Google Play Console.

Bây giờ, bạn có thể tải gói ứng dụng có chữ ký của ứng dụng mẫu lên.

Tải gói ứng dụng có chữ ký lên

  1. Tải gói ứng dụng có chữ ký lên kênh thử nghiệm nội bộ của Google Play Console. Chỉ sau khi tải lên, bạn mới có thể định cấu hình các tính năng liên quan đến việc kiếm tiền trong Play Console.
  2. Nhấp vào Test and release > Testing > Internal release > Create new release (Kiểm thử và phát hành > Kiểm thử > Bản phát hành nội bộ > Tạo bản phát hành mới).
  3. Nhập tên bản phát hành và tải tệp gói ứng dụng có chữ ký lên.
  4. Nhấp vào Next (Tiếp theo), sau đó nhấp vào Save and publish (Lưu và phát hành).

Bây giờ, bạn có thể tạo sản phẩm tính phí một lần.

Tạo sản phẩm tính phí một lần

Cách tạo sản phẩm tính phí một lần:

  1. Trong Google Play Console, trên trình đơn điều hướng bên trái, hãy chuyển đến Monetize with Play (Kiếm tiền bằng Play) > Products (Sản phẩm) > One-time products (Sản phẩm tính phí một lần).
  2. Nhấp vào Create one-time product (Tạo sản phẩm tính phí một lần).
  3. Nhập thông tin chi tiết về sản phẩm sau:
    • Product ID (Mã sản phẩm): Nhập mã sản phẩm duy nhất. Nhập one_time_product_01.
    • (Không bắt buộc) Thẻ: Thêm các thẻ có liên quan.
    • Name (Tên): Nhập tên sản phẩm. Ví dụ: Product name (Tên sản phẩm).
    • Description (Mô tả): Nhập nội dung mô tả sản phẩm. Ví dụ: Product description (Mô tả sản phẩm).
    • (Không bắt buộc) Thêm hình ảnh biểu tượng: Tải biểu tượng đại diện cho sản phẩm của bạn lên.
    Lưu ý: Để phục vụ mục đích của lớp học lập trình này, bạn có thể bỏ qua việc định cấu hình phần Tax, compliance, and programs (Thuế, tuân thủ và chương trình).
  4. Nhấp vào Next (Tiếp theo).
  5. Thêm lựa chọn mua và định cấu hình phạm vi cung cấp theo khu vực. Sản phẩm tính phí một lần cần có ít nhất một lựa chọn mua, xác định cách cấp quyền sử dụng, giá và phạm vi cung cấp theo khu vực. Đối với lớp học lập trình này, chúng ta sẽ thêm lựa chọn Buy (Mua) tiêu chuẩn cho sản phẩm.Trong phần Purchase option (Lựa chọn mua), hãy nhập các thông tin sau:
    • Purchase Option ID (Mã lựa chọn mua): Nhập mã lựa chọn mua. Ví dụ: buy.
    • Purchase type (Loại giao dịch mua): Chọn Buy (Mua).
    • (Không bắt buộc) Thẻ: Thêm các thẻ dành riêng cho lựa chọn mua này.
    • (Không bắt buộc) Nhấp vào Advanced options (Tuỳ chọn nâng cao) để định cấu hình các tuỳ chọn nâng cao. Để phục vụ mục đích của lớp học lập trình này, bạn có thể bỏ qua việc định cấu hình các tuỳ chọn nâng cao.
  6. Trong phần Availability and pricing (Phạm vi cung cấp và giá), hãy nhấp vào Set prices (Đặt giá) > Bulk edit pricing (Chỉnh sửa giá hàng loạt).
  7. Chọn tuỳ chọn Country / region (Quốc gia/khu vực). Thao tác này sẽ chọn tất cả các khu vực.
  8. Nhấp vào Continue (Tiếp tục). Thao tác này sẽ mở một hộp thoại để nhập giá. Nhập 10 USD rồi nhấp vào Apply (Áp dụng).
  9. Nhấp vào Save (Lưu) rồi nhấp vào Activate (Kích hoạt). Thao tác này sẽ tạo và kích hoạt lựa chọn mua.

Để phục vụ mục đích của lớp học lập trình này, hãy tạo thêm 3 sản phẩm tính phí một lần có mã sản phẩm sau:

  • consumable_product_01
  • consumable_product_02
  • consumable_product_03

Ứng dụng mẫu được định cấu hình để sử dụng các mã sản phẩm này. Bạn có thể cung cấp các mã sản phẩm khác. Trong trường hợp đó, bạn sẽ phải sửa đổi ứng dụng mẫu để sử dụng mã sản phẩm mà bạn đã cung cấp.

Mở ứng dụng mẫu trong Google Play Console rồi chuyển đến Monetize with Play (Kiếm tiền bằng Play) > Products (Sản phẩm) > One-time products (Sản phẩm tính phí một lần). Sau đó, nhấp vào Create one-time product (Tạo sản phẩm tính phí một lần) và lặp lại các bước từ 3 đến 9.

Video về cách tạo sản phẩm tính phí một lần

Video mẫu sau đây cho thấy các bước tạo sản phẩm tính phí một lần đã được mô tả trước đó.

4. Tích hợp với PBL

Bây giờ, chúng ta sẽ xem cách tích hợp ứng dụng với Thư viện Play Billing (PBL). Phần này mô tả các bước tích hợp cấp cao và cung cấp một đoạn mã cho từng bước. Bạn có thể sử dụng các đoạn mã này làm hướng dẫn để triển khai quy trình tích hợp thực tế.

Để tích hợp ứng dụng với PBL, hãy làm theo các bước sau:

  1. Thêm phần phụ thuộc Thư viện Play Billing vào ứng dụng mẫu.
    dependencies {
    val billing_version = "8.0.0"
    
    implementation("com.android.billingclient:billing-ktx:$billing_version")
    }
    
  2. Khởi động BillingClient. BillingClient là SDK ứng dụng nằm trên ứng dụng của bạn và giao tiếp với Thư viện Play Billing. Đoạn mã sau đây cho biết cách khởi động ứng dụng thanh toán.
    protected BillingClient createBillingClient() {
    return BillingClient.newBuilder(activity)
        .setListener(purchasesUpdatedListener)
        .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
        .enableAutoServiceReconnection()
        .build();
    }
    
  3. Kết nối với Google Play.Đoạn mã sau đây cho biết cách kết nối với Google Play.
    public void startBillingConnection(ImmutableList<Product> productList) {
    Log.i(TAG, "Product list sent: " + productList);
    Log.i(TAG, "Starting connection");
    billingClient.startConnection(
        new BillingClientStateListener() {
          @Override
          public void onBillingSetupFinished(BillingResult billingResult) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
              // Query product details to get the product details list.
              queryProductDetails(productList);
            } else {
              // BillingClient.enableAutoServiceReconnection() will retry the connection on
              // transient errors automatically.
              // We don't need to retry on terminal errors (e.g., BILLING_UNAVAILABLE,
              // DEVELOPER_ERROR).
              Log.e(TAG, "Billing connection failed: " + billingResult.getDebugMessage());
              Log.e(TAG, "Billing response code: " + billingResult.getResponseCode());
            }
          }
    
          @Override
          public void onBillingServiceDisconnected() {
            Log.e(TAG, "Billing Service connection lost.");
          }
        });
    }
    
  4. Tìm nạp chi tiết sản phẩm tính phí một lần.Sau khi tích hợp ứng dụng với PBL, bạn phải tìm nạp chi tiết sản phẩm tính phí một lần vào ứng dụng. Đoạn mã sau đây cho biết cách tìm nạp chi tiết sản phẩm tính phí một lần trong ứng dụng.
    private void queryProductDetails(ImmutableList<Product> productList) {
    Log.i(TAG, "Querying products for: " + productList);
    QueryProductDetailsParams queryProductDetailsParams =
        QueryProductDetailsParams.newBuilder().setProductList(productList).build();
    billingClient.queryProductDetailsAsync(
        queryProductDetailsParams,
        new ProductDetailsResponseListener() {
          @Override
          public void onProductDetailsResponse(
              BillingResult billingResult, QueryProductDetailsResult productDetailsResponse) {
            // check billingResult
            Log.i(TAG, "Billing result after querying: " + billingResult.getResponseCode());
            // process returned productDetailsList
            Log.i(
                TAG,
                "Print unfetched products: " + productDetailsResponse.getUnfetchedProductList());
            setupProductDetailsMap(productDetailsResponse.getProductDetailsList());
            billingServiceClientListener.onProductDetailsFetched(productDetailsMap);
          }
        });
    }
    
    Việc tìm nạp ProductDetails sẽ cung cấp cho bạn một phản hồi tương tự như sau:
    {
        "productId": "consumable_product_01",
        "type": "inapp",
        "title": "Shadow Coat (Yolo's Realm | Play Samples)",
        "name": "Shadow Coat",
        "description": "A sleek, obsidian coat for stealth and ambushes",
        "skuDetailsToken": "<---skuDetailsToken--->",
        "oneTimePurchaseOfferDetails": {},
        "oneTimePurchaseOfferDetailsList": [
            {
                "priceAmountMicros": 1990000,
                "priceCurrencyCode": "USD",
                "formattedPrice": "$1.99",
                "offerIdToken": "<--offerIdToken-->",
                "purchaseOptionId": "buy",
                "offerTags": []
            }
        ]
    },
    {
        "productId": "consumable_product_02",
        "type": "inapp",
        "title": "Emperor Den (Yolo's Realm | Play Samples)",
        "name": "Emperor Den",
        "description": "A fair lair glowing with molten rock and embers",
        "skuDetailsToken": "<---skuDetailsToken--->",
        "oneTimePurchaseOfferDetails": {},
        "oneTimePurchaseOfferDetailsList": [
            {
                "priceAmountMicros": 2990000,
                "priceCurrencyCode": "USD",
                "formattedPrice": "$2.99",
                "offerIdToken": "<--offerIdToken-->",
                "purchaseOptionId": "buy",
                "offerTags": []
            }
        ]
    }
    
  5. Bắt đầu quy trình thanh toán.
    public void launchBillingFlow(String productId) {
    ProductDetails productDetails = productDetailsMap.get(productId);
    if (productDetails == null) {
      Log.e(
          TAG, "Cannot launch billing flow: ProductDetails not found for productId: " + productId);
      billingServiceClientListener.onBillingResponse(
          BillingResponseCode.ITEM_UNAVAILABLE,
          BillingResult.newBuilder().setResponseCode(BillingResponseCode.ITEM_UNAVAILABLE).build());
      return;
    }
    ImmutableList<ProductDetailsParams> productDetailsParamsList =
        ImmutableList.of(
            ProductDetailsParams.newBuilder().setProductDetails(productDetails).build());
    
    BillingFlowParams billingFlowParams =
        BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(productDetailsParamsList)
            .build();
    
    billingClient.launchBillingFlow(activity, billingFlowParams);
    }
    
  6. Phát hiện và xử lý giao dịch mua. Trong bước này, bạn cần:
    1. Xác minh giao dịch mua
    2. Cấp quyền sử dụng cho người dùng
    3. Thông báo cho người dùng
    4. Thông báo cho Google về quy trình mua
    Trong số này, các bước a, b và c phải được thực hiện trong phần phụ trợ của bạn, do đó, nằm ngoài phạm vi của lớp học lập trình này.Đoạn mã sau đây cho biết cách thông báo cho Google về một sản phẩm tính phí một lần có thể tiêu thụ:
    private void handlePurchase(Purchase purchase) {
    // Step 1: Send the purchase to your secure backend to verify the purchase following
    // https://developer.android.com/google/play/billing/security#verify
    
    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.
    
    // Step 3: Notify the user using appropriate messaging.
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
      for (String product : purchase.getProducts()) {
        Log.d(TAG, product + " purchased successfully! ");
      }
    }
    
    // Step 4: Notify Google the purchase was processed.
    // For one-time products, acknowledge the purchase.
    // This sample app (client-only) uses billingClient.acknowledgePurchase().
    // For consumable one-time products, consume the purchase
    // This sample app (client-only) uses billingClient.consumeAsync()
    // If you have a secure backend, you must acknowledge purchases on your server using the
    // server-side API.
    // See https://developer.android.com/google/play/billing/security#acknowledge
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED && !purchase.isAcknowledged()) {
    
      if (shouldConsume(purchase)) {
        ConsumeParams consumeParams =
            ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();
        billingClient.consumeAsync(consumeParams, consumeResponseListener);
    
      } else {
        AcknowledgePurchaseParams acknowledgePurchaseParams =
            AcknowledgePurchaseParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();
        billingClient.acknowledgePurchase(
            acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
      }
     }
    }
    

5. Phân tích tình trạng người dùng bỏ qua giao dịch mua

Cho đến nay trong lớp học lập trình này, các phản hồi của Play Billing tập trung vào các tình huống hạn chế như phản hồi USER_CANCELLED, BILLING_UNAVAILABLE, OKITEM_ALREADY_OWNED. Tuy nhiên, Play Billing có thể trả về 13 mã phản hồi khác nhau có thể được kích hoạt bởi nhiều yếu tố trong thế giới thực.

Phần này trình bày chi tiết về nguyên nhân gây ra các phản hồi lỗi USER_CANCELLEDBILLING_UNAVAILABLE, đồng thời đề xuất các hành động khắc phục có thể triển khai.

Mã lỗi phản hồi USER_CANCELED

Mã phản hồi này cho biết người dùng đã bỏ qua giao diện người dùng của quy trình mua trước khi hoàn tất giao dịch mua.

Nguyên nhân có thể xảy ra

Bạn có thể làm những việc gì?

  • Có thể cho thấy những người dùng có ý định mua thấp và nhạy cảm với giá.
  • Giao dịch mua đang chờ xử lý hoặc khoản thanh toán bị từ chối.

Mã lỗi phản hồi BILLING_UNAVAILABLE

Mã phản hồi này có nghĩa là không thể hoàn tất giao dịch mua do có vấn đề với nhà cung cấp dịch vụ thanh toán của người dùng hoặc phương thức thanh toán mà họ chọn. Ví dụ: thẻ tín dụng của người dùng đã hết hạn hoặc người dùng đang ở một quốc gia không được hỗ trợ. Mã này không cho biết lỗi với chính hệ thống thanh toán của Play Billing.

Nguyên nhân có thể xảy ra

Bạn có thể làm những việc gì?

  • Ứng dụng Cửa hàng Play trên thiết bị của người dùng đã lỗi thời.
  • Người dùng đang ở một quốc gia không được Play hỗ trợ.
  • Người dùng là người dùng doanh nghiệp và quản trị viên doanh nghiệp của họ đã không cho phép người dùng mua hàng.
  • Google Play không thể tính phí phương thức thanh toán của người dùng. Ví dụ: thẻ tín dụng của người dùng có thể đã hết hạn.
  • Theo dõi xu hướng về các vấn đề hệ thống và ở các khu vực cụ thể
  • Cân nhắc di chuyển sang PBL 8 vì PBL 8 hỗ trợ mã phản hồi phụ PAYMENT_DECLINED_DUE_TO_INSUFFICIENT_FUNDS chi tiết hơn. Nếu nhận được mã phản hồi này, hãy cân nhắc thông báo cho người dùng về lỗi hoặc đề xuất các phương thức thanh toán thay thế.
  • Mã phản hồi này được thiết kế để thử lại, cho phép bạn triển khai các chiến lược thử lại phù hợp.
    Trong trường hợp này, việc thử lại tự động khó có thể giúp ích. Tuy nhiên, việc thử lại thủ công có thể giúp ích nếu người dùng giải quyết điều kiện gây ra vấn đề. Ví dụ: nếu người dùng cập nhật phiên bản Cửa hàng Play lên phiên bản được hỗ trợ, thì việc thử lại thủ công thao tác ban đầu có thể hoạt động.

    Nếu bạn nhận được mã phản hồi này khi người dùng không hoạt động, thì việc thử lại có thể không có ý nghĩa. Nếu lỗi xảy ra do quy trình mua, rất có thể người dùng đã nhận được phản hồi của Google Play trong quá trình mua và có khả năng họ đã biết đó là lỗi gì. Trong trường hợp này, bạn có thể hiển thị thông báo lỗi cho biết đã xảy ra sự cố và cung cấp nút `Thử lại` để cho người dùng lựa chọn thử lại thủ công sau khi họ giải quyết vấn đề.

Chiến lược thử lại cho mã lỗi phản hồi

Chiến lược thử lại hiệu quả cho lỗi có thể khôi phục từ Thư viện Play Billing (PBL) sẽ khác nhau tuỳ thuộc vào bối cảnh, chẳng hạn như tương tác của người dùng trong phiên (như trong quá trình mua) so với các thao tác trong nền (chẳng hạn như truy vấn giao dịch mua khi tiếp tục ứng dụng). Việc triển khai các chiến lược này là rất quan trọng vì một số giá trị BillingResponseCode biểu thị các vấn đề tạm thời có thể được giải quyết bằng cách thử lại, trong khi các giá trị khác là vĩnh viễn và không yêu cầu thử lại.

Đối với các lỗi gặp phải khi người dùng đang hoạt động, bạn nên sử dụng chiến lược thử lại đơn giản với số lần thử tối đa đã đặt để giảm thiểu sự gián đoạn cho trải nghiệm người dùng. Ngược lại, đối với các thao tác trong nền như xác nhận giao dịch mua mới (không yêu cầu thực thi ngay lập tức), exponential backoff (thời gian chờ luỹ thừa) là phương pháp được đề xuất.

Để biết thông tin chi tiết về các mã phản hồi cụ thể và chiến lược thử lại được đề xuất tương ứng, hãy xem bài viết Xử lý mã phản hồi BillingResult.

6. Các bước tiếp theo

Tài liệu tham khảo

7. Xin chúc mừng!

Xin chúc mừng! Bạn đã điều hướng thành công Google Play Console để tạo một sản phẩm tính phí một lần mới, kiểm thử mã phản hồi thanh toán, phân tích tình trạng người dùng bỏ qua giao dịch mua.

Khảo sát

Chúng tôi rất coi trọng ý kiến phản hồi của bạn về lớp học lập trình này. Hãy dành vài phút để hoàn thành bản khảo sát của chúng tôi.