BlogRight arrowTutorialRight ArrowMua trong ứng dụng Android, phần 5: xác thực mua phía máy chủ
BlogRight arrowTutorialRight ArrowMua trong ứng dụng Android, phần 5: xác thực mua phía máy chủ

Mua trong ứng dụng Android, phần 5: xác thực mua phía máy chủ

Mua trong ứng dụng Android, phần 5: xác thực mua phía máy chủ
Listen to the episode
Mua trong ứng dụng Android, phần 5: xác thực mua phía máy chủ

Xác thực phía máy chủ (server-side) có thể giúp bạn xác thực tính xác thực của giao dịch mua. Thiết bị sẽ đặt yêu cầu đến máy chủ Google để tìm xem giao dịch mua có thực sự diễn ra và có hợp lệ không.

Trong hướng dẫn này, chúng ta sẽ thảo luận cách cấu hình xác thực phía máy chủ cho ứng dụng Android.

Tại sao phải xác thực giao dịch mua

Cần lưu ý rằng bạn không bắt buộc phải xác thực phía máy chủ — mua trong ứng dụng vẫn hoạt động mà không cần xác thực. Tuy nhiên xác thực có một số lợi ích đáng kể:

  1. Phân tích thanh toán nâng cao,  đặc biệt quan trọng đối với các gói đăng ký do thiết bị không xử lý những việc xảy ra sau khi kích hoạt. Khi không có xử lý giao dịch mua phía máy chủ, bạn sẽ không thể truy xuất trạng thái gói đăng ký hiện tại và không thể biết liệu người dùng đã gia hạn hay huỷ gói đăng ký, liệu có còn vấn đề thanh toán nào hay không, v.v.
  2. Có khả năng xác minh tính xác thực của giao dịch mua. Bạn có thể chắc chắn rằng giao dịch không phải là vụ lừa đảo và người dùng đã thực sự trả tiền cho sản phẩm của bạn.
  3. Gói đăng ký đa nền tảng. Nếu bạn có thể kiểm tra trạng thái gói đăng ký của người dùng theo thời gian thực, bạn có thể đồng bộ trạng thái này trên các nền tảng khác. Ví dụ: người dùng mua gói đăng ký trên thiết bị iOS sẽ có thể dùng gói đăng ký này trên Android, Web và các nền tảng khác. 
  4. Có khả năng kiểm soát quyền truy cập nội dung từ phía máy chủ, giúp bảo vệ bạn trước những người dùng cố gắng truy cập vào dữ liệu mà không có gói đăng ký, đơn giản bằng cách thực thi yêu cầu tới máy chủ. 

Theo kinh nghiệm của chúng tôi, chỉ lợi ích thứ nhất cũng đủ để thiết lập quy trình giao dịch mua phía máy chủ.

⭐️ Download our guide on in-app techniques which will make in-app purchases in your app perfect

Xác thực thanh toán

Chúng ta có thể tóm tắt quá trình xác thực thanh toán Android với sơ đồ sau:

Xác thực cho yêu cầu Google Play Developer API

Để có thể làm việc với Google Play Developer API, trước tiên, bạn cần tạo một khóa để ký tên vào yêu cầu. Đầu tiên bạn sẽ phải liên kết tài khoản Google Play Console (nơi bạn quản lý ứng dụng) đến tài khoản Google Cloud (nơi bạn sẽ tạo khoá để ký tên vào yêu cầu). Sau khi đã cấu hình xong tất cả, bạn sẽ phải trao quyền quản lý giao dịch mua cho người dùng. Quy trình này cần cả một bài viết riêng để mô tả. May mắn thay, chúng tôi đã trình bày quy trình này trong bài hướng dẫn từng bước có tại tài liệu Adapty

Vui lòng lưu ý, sau khi tạo khoá, bạn sẽ phải đợi từ 24 giờ trở lên để khoá bắt đầu hoạt động. Để tránh chờ đợi, bạn chỉ cần cập nhật mô tả cho bất kỳ gói đăng ký hoặc sản phẩm trong ứng dụng nào để kích hoạt khoá ngay lập tức. 

Chúng tôi sử dụng thư viện google-api-python-client chính thức để làm việc với Google Play Developer API. Thư viện này có sẵn trong phần lớn các ngôn ngữ phổ biến và tôi khuyến nghị bạn sử dụng vì thư viện hỗ trợ tất cả các phương thức bạn cần.

Xác thực giao dịch đăng ký

Khác với xác thực phía máy chủ iOS, trong Android, cả xác thực gói đăng ký và xác thực sản phẩm khác đều được triển khai bằng nhiều phương thức khác nhau. Do vậy, khi xác thực giao dịch, bạn cần biết rằng bạn đang xử lý sản phẩm hay gói đăng ký. Trong thực tiễn, điều này có nghĩa là bạn sẽ cần chuyển dữ liệu này từ ứng dụng di động đồng thời giữ cờ trong kho dữ liệu phòng trường hợp cần xác thực lại token.

Điểm khác biệt quan trọng thứ hai là trong khi mỗi giao dịch trong Android có một token riêng, tất cả giao dịch iOS dùng chung một khoá bí mật chia sẻ (shared secret) dành riêng cho ứng dụng để lưu trữ toàn bộ lịch sử giao dịch. Có nghĩa là nếu bạn muốn có thể khôi phục giao dịch mua của người dùng bất kỳ lúc nào, bạn sẽ cần lưu trữ toàn bộ token giao dịch mua trái với việc chọn một token đơn tuỳ ý.

Để xác thực gói đăng ký bạn sẽ cần gọi phương thức purchases.subscriptions.get.  Đó cơ bản là một lệnh gọi yêu cầu GET:

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}

Yêu cầu tất cả thông số:

  • packageName: Mã nhận dạng ứng dụng, ví dụ như com.adapty.sample_app.
  • subscriptionId: Mã nhận dạng gói đăng ký dành cho gói đăng ký cần được xác thực, ví dụ như com.adapty.sample_app.weekly_sub.
  • token: Token giao dịch duy nhất. Token xuất hiện sau khi đã xử lý giao dịch mua trên phía ứng dụng di động.

Đầu tiên, hãy xem thông báo lỗi bạn cần xử lý để đảm bảo mọi thành phần hoạt động như dự kiến:

  • 400, Trao quyền không hợp lệ: không tìm thấy tài khoản: Thông báo lỗi này có nghĩa là khoá xác thực yêu cầu được tạo không đúng. Đảm bảo rằng tài khoản của bạn đã được liên kết, bạn đang sử dụng đúng tài khoản có đủ quyền hạn và đã kích hoạt tất cả API yêu cầu. Xem hướng dẫn về cách cấu hình tất cả mọi thứ trong phần bên dưới: Hãy lưu ý mẹo trên bản cập nhật mô tả sản phẩm.
  • 400, Token giao dịch mua không khớp với tên gói: Thông báo lỗi này thường xuất hiện trong các giao dịch gian lận. Nếu bạn gặp tin nhắn này khi đang thử nghiệm, đảm bảo rằng token giao dịch mua bạn đang sử dụng không phải của ứng dụng khác.
  • 403, Hạn mức vượt quá chỉ số đo hạn mức ‘Truy vấn’ và giới hạn ‘Truy vấn mỗi ngày’ của dịch vụ 'androidpublisher.googleapis.com': Có nghĩa là đã vượt quá hạn mức hàng ngày cho yêu cầu Google API. Theo mặc định, mức truy vấn mỗi ngày bạn có thể thi hành lên đến 200.000 . Có thể tăng hạn mức nhưng con số này nên đủ dùng cho đa số các ứng dụng. Nếu bạn đạt đỉnh hạn mức, có lẽ bạn nên xem lại logic của ứng dụng của bạn và đảm bảo mọi thứ đều đúng.
  • 410, Giao dịch mua gói đăng ký không còn khả dụng để truy vấn vì giao dịch mua đã hết hạn từ lâu: Thông báo lỗi này xuất hiện trong giao dịch có gói đăng ký đã hết hạn từ hơn 60 ngày trước. Đây không phải là thông báo lỗi thực sự và không cần xử lý.
Start for free

You don't need to write server code yourself,

because we did it for you. Try Adapty SDK!

Start for free

Giao dịch mua gói đăng ký 

Nếu xác thực thành công, bạn sẽ nhận được phản hồi là dữ liệu giao dịhc.

Dữ liệu giao dịch (cho gói đăng ký):

{
    "expiryTimeMillis": "1631116261362",
    "paymentState": 1,
    "acknowledgementState": 1,
    "kind": "androidpublisher#subscriptionPurchase",
    "orderId": "GPA.3382-9215-9042-70164",
    "startTimeMillis": "1630504367892",
    "autoRenewing": true,
    "priceCurrencyCode": "USD",
    "priceAmountMicros": "1990000",
    "countryCode": "US",
    "developerPayload": ""
}

Để biết được liệu người dùng có thể truy cập các tuỳ chọn cao cấp do ứng dụng cung cấp hay không — nghĩa là, liệu họ có gói đăng ký đang hoạt động hay không — bạn cần phải:

  1. Kiểm tra tham số startTimeMillisexpiryTimeMillis. Thời gian hiện tại cần nằm giữa hai tham số này.
  2. Thêm vào đó, bạn phải đảm bảo tham số paymentState không có giá trị '0'. Giá trị 0 nghĩa là giao dịch mua gói đăng ký vẫn đang chờ xử lý, do vậy, chưa cần trao cho người dùng quyền truy cập chức năng cao cấp. 
  3. Khi giao dịch có thuộc tính autoResumeTimeMillis, gói đăng ký sẽ tạm dừng. Thuộc tính này có nghĩa là không trao quyền truy cập chức năng cao cấp cho người dùng trước ngày chỉ định.

Hãy xem các thuộc tính quan trọng của giao dịch mua gói đăng ký:

  • kind: Loại giao dịch. Đối với gói đăng ký, thuộc tính này luôn có giá trị androidpublisher#subscriptionPurchase. Với tham số này, bạn có thể biết được mình đang xử lý gói đăng ký hay sản phẩm và chọn logic xử lý tương ứng.
  • paymentState: Trạng thái thanh toán. Thuộc tính này không tồn tại đối với giao dịch đã hết hạn. Các giá trị có thể có là:
    0: Giao dịch mua này vẫn chưa được xử lý. Ở một số quốc gia, người dùng có thể trả tiền mua gói đăng ký bằng hình thức giao dịch trực tiếp. Nghĩa là, người dùng sẽ khởi tạo giao dịch mua gói đăng ký bằng thiết bị của họ và trả tiền tại điểm đầu cuối gần đó. Nhìn chung, trường hợp này rất hiếm nhưng vẫn nên ghi nhớ.
    1: Gói đăng ký đã được mua.
    2: Gói đăng ký đang trong thời gian dùng thử (trial).
    3: Gói đăng ký sẽ được nâng hoặc hạ cấp trong kỳ tiếp theo Có nghĩa là gói đăng ký sẽ có thay đổi.
  • acknowledgementState: Trạng thái xác nhận giao dịch mua. Đây là tham số quan trọng để ghi nhận xem người dùng đã nhận được quyền truy cập cho những gì họ đã mua hay không. Giá trị ‘0’ nghĩa là người dùng vẫn chưa nhận được và ‘1’ nghĩa là người dùng đã nhận được. Nhà phát triển chịu trách nhiệm xác định trạng thái này. Việc này có thể được thực hiện trên cả ứng dụng di động và phía máy chủ. Nếu bạn không ghi nhận giao dịch mua trong 3 ngày sau khi giao dịch được tạo, giao dịch sẽ tự động toàn tiền. Tôi khuyến nghị triển khai logic này: sau khi bạn nhận được giao dịch có acknowledgementState=0, máy chủ sẽ đổi tham số. Về cách triển khai, tôi sẽ chia sẻ trong phần bên dưới. 
  • orderId: Mã nhận dạng giao dịch duy nhất. Mỗi giao dịch mua hoặc gia hạn gói đăng ký sẽ có mã nhận dạng riêng có thể dùng để tìm hiểu xem giao dịch đã được xử lý trước đó hay chưa. Mỗi mã giao dịch gia hạn sẽ có nửa đầu bất biến, theo sau bởi hai dấu chấm và số đếm gia hạn gói đăng ký (subscription renewal) (bắt đầu bằng số 0). Nếu gói đăng ký có mã nhận dạng GPA.3382-9215-9042-70164 khi kích hoạt, thì mã nhận dạng gia hạn lần đầu là GPA.3382-9215-9042-70164..0, mã nhận dạng gia hạn lần hai là GPA.3382-9215-9042-70164..1, v.v. Bằng cách này, bạn có thể tạo chuỗi giao dịch và theo dõi số đếm gia hạn.
  • startTimeMillis: Ngày gói đăng ký bắt đầu.
  • expiryTimeMillis: Ngày gói đăng ký hết hạn.
  • autoRenewing: Cờ cho biết rằng gói đăng ký có được gia hạn sang kỳ tiếp theo hay không.
  • priceCurrencyCode: Đơn vị tiền tệ mua có định dạng 3 ký tự, ví dụ USD.
  • priceAmountMicros: Giá mua. Chia giá trị này cho 1000000 để có giá trị giá mua thông thường. Nghĩa là 1990000 thật ra là 1.99. 
  • countryCode: Quốc gia mua có định dạng 2 ký tự, ví dụ US.
  • purchaseType: Loại giao dịch mua. Khoá này sẽ không hiển thị trong hầu hết các trường hợp. Đưa khoá này vào tính toán vẫn quan trọng vì khoá này giúp bạn biết được liệu giao dịch mua có được tạo trong môi trường Sandbox hay không. Các giá trị có thể có là:
    0: Giao dịch mua được tạo trong môi trường Sandbox, do vậy, không nên đưa giao dịch vào dữ liệu phân tích.
    1: Giao dịch mua được thực hiện với mã khuyến mãi. 
  • autoResumeTimeMillis: Ngày gia hạn gói đăng ký. Tham số này chỉ có trong gói đăng ký đã bị tạm dừng trước đó. Nếu tham số này xuất hiện, bạn không cần trao quyền truy cập chức năng cao cấp cho người dùng trước ngày chỉ định.
  • cancelReason: Nguyên nhân tại sao gói đăng ký không được gia hạn. Các giá trị có thể có là:
    0: Người dùng huỷ tự động gia hạn gói đăng ký.
    1: Gói đăng ký do hệ thống huỷ. Nguyên nhân này thường do vấn đề thanh toán (billing issue) gây ra.
    2: Người dùng đổi sang gói đăng ký khác.
    3: Nhà phát triển huỷ gói đăng ký.
  • userCancellationTimeMillis: Dữ liệu huỷ gia hạn gói đăng ký. Thuộc tính này chỉ hiển thị khi cancelReason là 0. Gói đăng ký vẫn có thể hoạt động — để đảm bảo, xem giá trị tham số expiryTimeMillis.
  • cancelSurveyResult: Đối tượng lưu trữ nguyên nhân huỷ gói đăng ký, được hiển thị khi người dùng để lại phản hồi về vấn đề này.
  • introductoryPriceInfo: Đối tượng lưu trữ dữ liệu giá ưu đãi mặt hàng mới. Ví dụ, dữ liệu này có thể là ưu đãi đặc biệt về việc giảm giá 50% cho 1 tháng.
  • promotionType: Loại mã khuyến mãi được dùng để kích hoạt gói đăng ký. Các giá trị có thể có là:
    0: Mã khuyến mãi dùng một lần.
    1: Mã khuyến mãi tuỳ chỉnh có thể áp dụng bởi nhiều khách hàng. Mã này thường được dùng cho đối tác là blogger. 
  • promotionCode: Mã khuyến mãi tuỳ chỉnh được dùng để kích hoạt gói đăng ký. Tham số này sẽ không hiển thị cho mã khuyến mãi dùng một lần. 
  • priceChange: Đối tượng lưu trữ dữ liệu về thay đổi giá trong tương lai cũng như thông tin về việc người dùng có đồng ý với thay đổi không. 

Ghi nhận gói đăng ký

Như đã nhắc đến ở trên, trong trường hợp gói đăng ký không được ghi nhận trong 3 ngày sau khi giao dịch mua được tạo, giao dịch sẽ bị huỷ và được hoàn tiền tự động. Thực chất, tôi không thực sự hiểu logic phía sau và tôi chưa bao giờ gặp phải trường hợp này trong bất kỳ hệ thống xử lý thanh toán nào khác, bao gồm cả iOS. Tuy nhiên, nếu bạn nhận được giao dịch có acknowledgementState=0, bạn cần ghi nhận giao dịch.

Để ghi nhận, bạn sẽ cần gọi phương thức purchases.subscriptions.get. Phương thức này thực thi yêu cầu POST.

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}:acknowledge

Tham số giống với tham số cho xác thực yêu cầu. Nếu thành công thực thi yêu cầu, gói đăng ký sẽ được ghi nhận có nghĩa là bạn sẽ không bị mất khoản thu. 

Nếu gói đăng ký vẫn chưa được mua hoàn toàn thì bạn không cần phải ghi nhận. 

Hoãn, hoàn tiền, thu hồi và huỷ gia hạn gói đăng ký

Ngoài việc xác thực và ghi nhận gói đăng ký, Google Play Developer API cũng có thể dùng cho các hoạt động đăng ký khác. Vui lòng lưu ý là những hoạt động này rất hiếm và được Google Play Console hỗ trợ, ngoại trừ việc gia hạn. Tôi vẫn sẽ liệt kê các hoạt động này để giúp bạn có hiểu biết chung về phạm vi của các giải pháp của API. Tất cả những yêu cầu này có cùng tham số cần thiết như các phương thức đã nêu trước đó, cụ thể là packageName, subscriptionIdtoken.

  • Huỷ gia hạn. Phương thức purchases.subscriptions.cancel. Phương thức này huỷ gia hạn tự động cho gói đăng ký đã chọn. Tuy nhiên, gói đăng ký này vẫn sẽ khả dụng trong suốt kỳ thanh toán hiện tại.
  • Hoàn tiền gói đăng ký (subscription refund). Phương thức purchases.subscriptions.refund. Phương thức này hoàn tiền gói đăng ký. Tuy nhiên, người dùng vẫn sẽ có quyền truy cập gói đăng ký và gói đăng ký sẽ tự động gia hạn trong kỳ tiếp theo. Trong phần lớn các trường hợp, bạn nên thu hồi cả gói đăng ký khi hoàn tiền.
  • Thu hồi gói đăng ký (subscription revocation). Phương thức purchases.subscriptions.revoke. Phương thức này thu hồi gói đăng ký ngay lập tức, có nghĩa là người dùng sẽ không thể truy cập vào các chức năng cao cấp. Gói đăng ký sẽ không được gia hạn. Phương thức này thường được gọi khi hoàn tiền.
  • Hoãn giao dịch mua gói đăng ký. Phương thức purchases.subscriptions.defer. Phương thức này kéo dài gói đăng ký đến ngày chỉ định. Trong yêu cầu, nói rõ ngày gói đăng ký hết hạn cũng như ngày bạn muốn thay vào. Ngày thay vào phải cho ra thời hạn đăng ký dài hơn thời hạn trước đó.

Xác thực sản phẩm (không phải gói đăng ký)

Xác thực sản phẩm giống với xác thực gói đăng ký. Bạn cần phải gọi phương thức purchases.products.get để thực thi yêu cầu GET.

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}

Chúng ta đều đã quen với các tham số này qua việc xem xét các ví dụ nêu trên.

Dữ liệu giao dịch (cho sản phẩm):

{
  "purchaseTimeMillis": "1630529397125",
  "purchaseState": 0,
  "consumptionState": 0,
  "developerPayload": "",
  "orderId": "GPA.3374-2691-3583-90384",
  "acknowledgementState": 1,
  "kind": "androidpublisher#productPurchase",
  "regionCode": "RU"
}

Giao dịch mua sản phẩm có ít thuộc tính hơn so với giao dịch đăng ký. Hãy xem một số thuộc tính quan trọng:

  • kind: Loại giao dịch. Đối với gói đăng ký, thuộc tính này luôn có giá trị androidpublisher#productPurchase. Với tham số này, bạn có thể biết được mình đang xử lý gói đăng ký hay sản phẩm và chọn logic xử lý tương ứng.
  • purchaseState: Trạng thái thanh toán. Vui lòng lưu ý rằng các giá trị chính ở đây khác với tham số paymentState dành cho gói đăng ký. Các giá trị có thể có là:
    0: Giao dịch mua đã hoàn thành.
    1: Giao dịch mua đã bị huỷ. Có nghĩa là giao dịch mua đang chờ xử lý nhưng người dùng không bao giờ thanh toán giao dịch.
    2: Giao dịch mua đang chờ xử lý. Ở một số quốc gia, người dùng có thể trả tiền mua gói đăng ký bằng hình thức giao dịch trực tiếp. Nghĩa là, người dùng sẽ khởi tạo giao dịch mua gói đăng ký bằng thiết bị của họ và trả tiền tại điểm đầu cuối gần đó. Nhìn chung, trường hợp này rất hiếm nhưng vẫn nên ghi nhớ.
  • acknowledgementState: Trạng thái xác nhận giao dịch mua. Đây là tham số quan trọng để ghi nhận xem người dùng đã nhận được quyền truy cập cho những gì họ đã mua hay không. Giá trị ‘0’ nghĩa là người dùng vẫn chưa nhận được và ‘1’ nghĩa là người dùng đã nhận được. Nhà phát triển chịu trách nhiệm xác định trạng thái này. Việc này có thể được thực hiện trên cả ứng dụng di động và phía máy chủ. Nếu bạn không ghi nhận giao dịch mua trong 3 ngày sau khi giao dịch được tạo, giao dịch sẽ tự động toàn tiền. Tôi khuyến nghị triển khai logic này: sau khi bạn nhận được giao dịch có acknowledgementState=0, máy chủ sẽ đổi tham số. Về cách triển khai, tôi sẽ trình bày trong phần bên dưới. 
  • consumptionState: Trạng thái tiêu hao sản phẩm. Đây là cách iOS gọi ‘sản phẩm tiêu hao’. Trạng thái này được xác định ở phía ứng dụng di động. Trạng thái có giá trị ‘0’ nghĩa là sản phẩm chưa được tiêu hao; giá trị ‘1’ nghĩa là đã được tiêu hao. Sản phẩm, ví dụ như quyền truy cập trọn đời (lifetime) vào ứng dụng hoặc một số tính năng cao cấp cụ thể không thể tiêu hao, nghĩa là, sản phẩm phải có trạng thái 0. Sản phẩm, ví dụ như xu mà người dùng có thể mua lại, có tiêu hao, nghĩa là sản phẩm phải có trạng thái 1. consumptionState=0 nghĩa là chỉ có thể mua sản phẩm một lần, trong khi consumptionState=1 nghĩa là có thể mua sản phẩm nhiều lần.
  • orderId: Mã nhận dạng giao dịch duy nhất. Mỗi giao dịch mua hoặc gia hạn gói đăng ký sẽ có mã nhận dạng riêng có thể dùng để tìm hiểu xem giao dịch đã được xử lý trước đó hay chưa.
  • purchaseTimeMillis: Ngày mua.
  • regionCode: Quốc gia mua có định dạng 2 ký tự, ví dụ US. Vui lòng lưu ý rằng tên tham số này khác với tên tham số trong gói đăng ký, tức là countryCode
  • purchaseType: Loại giao dịch mua. Khoá này sẽ không hiển thị trong hầu hết các trường hợp. Đưa khoá này vào tính toán vẫn quan trọng vì khoá này giúp bạn biết được liệu giao dịch mua có được tạo trong môi trường Sandbox hay không. Các giá trị có thể có là:
    0: Giao dịch mua được tạo trong môi trường Sandbox, do vậy, không nên đưa giao dịch vào dữ liệu phân tích.
    1: Giao dịch mua được tạo với mã khuyến mãi.
    2: Giao dịch mua được trao cho một hành động đích, ví dụ như xem quảng cáo trong ứng dụng thay cho việc thanh toán.

Như bạn có thể thấy, xác thực sản phẩm khá giống với xác thực gói đăng ký. Tuy nhiên cần phải chú ý một vài điểm:

  • Giá không được trả về dù việc này sẽ rất hữu ích cho phân tích.
  • Giá trị tham số purchaseState có khác biệt lớn so với giá trị tham số paymentState trong gói đăng ký. Khác biệt này sẽ gây ra lỗi nếu không được tính đến. 
  • regionCode được trả về dù tham số này có tên countryCode trong gói đăng ký.

Tương tự như giao dịch mua gói đăng ký, ta cũng cần phải ghi nhận giao dịch mua sản phẩm Để ghi nhận, gọi phương thức purchases.products.acknowledge sẽ thực thi yêu cầu POST

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/products/{productId}/tokens/{token}:acknowledge

Không cần phải ghi nhận nếu giao dịch mua chưa hoàn tất.

Theo dõi hoàn tiền đối với gói đăng ký và sản phẩm

Không thể có phân tích chất lượng cao nếu không tính đến khoản hoàn tiền. Đáng tiếc, dữ liệu hoàn tiền không hiển thị trong giao dịch và cũng không được nhắc nhở dưới dạng một sự kiện (event) riêng biệt như trên iOS. Để nhận danh sách giao dịch đã hoàn tiền, bạn sẽ cần phải thường xuyên gọi purchases.voidedpurchases.list — ví dụ như một lần một ngày. Phương thức này sẽ thực thi yêu cầu GET:

https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/voidedpurchases

Để đáp lại yêu cầu, bạn sẽ nhận được danh sách tất cả giao dịch đã hoàn tiền. Tôi khuyến nghị tìm giao dịch trong cơ sở dữ liệu bằng tham số orderId thay vì tham số purchaseToken. Thứ nhất, dùng tham số này sẽ ít tốn thời gian hơn. Thứ hai, tất cả những lần gia hạn gói đăng ký sẽ dùng chung một token và bạn sẽ chỉ cần tìm nạp lần gần đây nhất.

Thông báo máy chủ (server notification) cho giao dịch

Thông báo máy chủ (thông báo của nhà phát triển theo thời gian thực) giúp bạn hiểu về các sự kiện xảy ra bên phía Google, trên máy chủ của bạn và gần như là trực tiếp. Sau khi cấu hình, bạn sẽ nhận được thông báo về giao dịch mua mới, gia hạn, vấn đề thanh toán, v.v. Việc này có thể giúp bạn thu thập các phân tích hay hơn cũng như giúp cho việc quản lý trạng thái người đăng ký dễ dàng hơn nhiều.

Để bắt đầu nhận thông báo máy chủ, bạn cần tạo một chủ đề Google Cloud Pub/Sub. Chủ đề này sẽ gửi thông báo đến địa chỉ bạn muốn. Sau đó chủ đề này sẽ được biểu thị trong phần Monetization setup của Google Play Console. Để xem hướng dẫn chi tiết kèm ảnh chụp màn hình, vui lòng xem Tài liệu Adapty.

Thông báo máy chủ:

{
  "message": {
    "data": "eyJ2ZXJzaW9uIjoiMS4wIiwicGFja2FnZU5hbWUiOiJjb20uYWRhcHR5LnNhbXBsZV9hcHAiLCJldmVudFRpbWVNaWxsaXMiOiIxNjMwNTI5Mzk3MTI1Iiwic3Vic2NyaXB0aW9uTm90aWZpY2F0aW9uIjp7InZlcnNpb24iOiIxLjAiLCJub3RpZmljYXRpb25UeXBlIjo2LCJwdXJjaGFzZVRva2VuIjoiY2o3anAuQU8tSjFPelIxMjMiLCJzdWJzY3JpcHRpb25JZCI6ImNvbS5hZGFwdHkuc2FtcGxlX2FwcC53ZWVrbHlfc3ViIn19",
    "messageId": "2829603729517390",
    "message_id": "2829603729517390",
    "publishTime": "2021-09-01T20:49:59.124Z",
    "publish_time": "2021-08-04T20:49:59.124Z"
  },
  "subscription": "projects/935083/subscriptions/adapty-rtdn"
}

Chúng ta đa phần lo ngại về khoá dữ liệu có chứa dữ liệu giao dịch được mã hoá với base64. Khoá messageId có thể dùng để khử trùng lặp thông báo, từ đó giúp bạn không cần phải xử lý các thông báo trùng lặp. 

Sau đây là một giao dịch trong thông báo máy chủ:

{
  "version": "1.0",
  "packageName": "com.adapty.sample_app",
  "eventTimeMillis": "1630529397125",
  "subscriptionNotification": {
    "version": "1.0",
    "notificationType": 6,
    "purchaseToken": "cj7jp.AO-J1OzR123",
    "subscriptionId": "com.adapty.sample_app.weekly_sub"
  }
}

Khoá packageName giúp bạn biết được ứng dụng mà sự kiện này thuộc về. Khoá subscriptionId cho biết gói đăng ký liên quan và purchaseToken giúp bạn tìm giao dịch cụ thể. Với gói đăng ký, bạn sẽ luôn tìm giao dịch mới nhất trong chuỗi gia hạn vì đó là giao dịch mà sự kiện này thuộc về. Khoá notificationType có chứa thông tin loại sự kiện. Theo ý kiến của tôi, các giá trị sau hữu ích nhất cho gói đăng ký:

  • (2) SUBSCRIPTION_RENEWED: Gói đăng ký đã được gia hạn thành công.
  • (3) SUBSCRIPTION_CANCELED: Người dùng đã vô hiệu tính năng tự động gia hạn gói đăng ký. Nếu tính năng tự động gia hạn bị vô hiệu, bạn sẽ cần phải cố gắng lấy lại người dùng dưới dạng người đăng ký đang hoạt động.
  • (5) SUBSCRIPTION_ON_HOLD, (6) SUBSCRIPTION_IN_GRACE_PERIOD: Không thể gia hạn gói đăng ký do lỗi thanh toán. Bạn cần thông báo lỗi cho người dùng để hệ thống không tự động huỷ gói đăng ký của họ.
  • (12) SUBSCRIPTION_REVOKED: gói đăng ký đã bị thu hồi. Điều này có nghĩa là người dùng sẽ mất quyền truy cập các chức năng cao cấp mà gói đăng ký trao cho họ trước đó. 

Trong sản phẩm (không phải gói đăng ký), bạn sẽ nhận được oneTimeProductNotification thay vì khoá subscriptionNotification. Nó cũng chứa khoá sku thay vì khoá subscriptionId. Thêm vào đó, bạn sẽ luôn chỉ nhận được 2 loại sự kiện đối với sản phẩm:

  • (1) ONE_TIME_PRODUCT_PURCHASED: Mua sản phẩm thành công.
  • (2) ONE_TIME_PRODUCT_CANCELED: Giao dịch mua sản phẩm đã bị huỷ vì người dùng chưa thanh toán.

Tổng kết

Xác thực giao dịch phía máy chủ giúp tăng tốc độ và chất lượng cho các phép phân tích mà bạn sẽ có thể nhận được đối với ứng dụng của mình. Xác thực làm những kẻ gian lận khó truy cập nội dung cao cấp và có thể được dùng để triển khai gói đăng ký đa nền tảng. Tuy nhiên, triển khai xác thực phía máy chủ có thể mất thời gian, đặc biệt là nếu bạn cần độ chính xác dữ liệu cao. Để cung cấp dữ liệu chất lượng cao, bạn sẽ cần tính đến vô số trường hợp bên lề như nâng cấp gói đăng ký, chuyển đổi gói đăng ký, thời gian dùng thử, ưu đãi khuyến mãi (promo offer) và ưu đãi ra mắt (introductory offer), thời gian gia hạn (grace period), hoàn tiền, v.v. Bạn cũng sẽ phải biết và tính đến tất cả các chi tiết trong chính sách ví dụ như Google chỉ tính 15% phí hoa hồng (thay vì 30%) cho gói đăng ký gia hạn một năm trở lên.

Further reading

Adapty July Update: New Segmentations, New Analytical Chart, Podcast!
Adapty July Update: New Segmentations, New Analytical Chart, Podcast!
August 2, 2021
2 min read
Branch & Adapty webinar: WWDC 2022 recap and in-app purchases updates in 2022
Branch & Adapty webinar: WWDC 2022 recap and in-app purchases updates in 2022
September 1, 2022
47 min listen
How to design paywall to pass review for the App Store
How to design paywall to pass review for the App Store
December 23, 2020
6 min read