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

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

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

Xác thực mua trên máy chủ là gì?

Xác thực máy chủ (xác thực biên nhận (receipt validation) phía máy chủ) là một cách để xác minh tính xác thực của giao dịch mua. Không giống như xác thực dựa trên thiết bị, xác thực máy chủ xảy ra - đợi xác thực - ở phía máy chủ. Xác thực biểu thị rằng thiết bị hoặc máy chủ đưa ra yêu cầu tới máy chủ của Apple để tìm hiểu xem giao dịch mua có thực sự xảy ra hay không và có hợp lệ hay không.

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

Tại sao phải xác thực mua?

Cần lưu ý rằng xác thực máy chủ không bắt buộc - mua trong ứng dụng sẽ vẫn hoạt động mà không cần xác thực. Tuy vậy, xác thực máy chủ vẫn đem lại lợi thế:

  1. Phân tích thanh toán nâng cao, đặc biệt quan trọng đối với gói đăng ký (subscription) vì mọi thứ xảy ra sau khi kích hoạt đều không được thiết bị xử lý. Nếu không có xử lý mua trên máy chủ thì bạn sẽ không thể truy xuất trạng thái gói đăng ký hiện tại và biết liệu người dùng đã gia hạn hay hủy gói đăng ký, hay liệu có bất kỳ vấn đề thanh toán nào, v.v.
  2. Khả năng xác minh tính xác thực của giao dịch mua. Bạn sẽ biết chắc rằng giao dịch không gian lận và người dùng đã thực sự thanh toá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 trong thời gian thực thì bạn có thể đồng bộ hóa trạng thái với các nền tảng khác.                                                                                      Ví dụ: người dùng đã mua gói đăng ký từ một thiết bị iOS có thể sử dụng gói đăng ký trên nền tảng Android, trên Web và các nền tảng khác.  
  4. Khả năng kiểm soát truy cập nội dung từ phía máy chủ, bảo vệ bạn khỏi những người dùng cố gắng truy cập dữ liệu mà không đăng ký chỉ bằng cách thực hiện các yêu cầu đến máy chủ. 

Từ kinh nghiệm của chúng tôi, chỉ lợi ích đầu tiên là đủ để thiết lập xử lý mua trên máy chủ.

Xác thực mua

Nhìn chung, quá trình xác thực biên nhận trên iOS như thế này:

Tạo khóa bí mật chia sẻ (shared secret)

Để gửi yêu cầu xác thực thanh toán, bạn phải thêm khóa bí mật chia sẻ để ủy quyền yêu cầu. Bạn có thể tạo một khóa trong App Store Connect. 

Khóa bí mật chia sẻ có thể được tạo cho một ứng dụng cụ thể (khóa bí mật dành riêng cho ứng dụng hoặc cho tất cả các ứng dụng trong tài khoản).

Để tạo khóa bí mật dành riêng cho ứng dụng, hãy mở trang của ứng dụng trong App Store Connect, truy cập In-App Purchases → Manage và nhấp vào App-Specific Shared Secret. Trong cửa sổ mở ra, bạn có thể tạo mã token mới hoặc sao chép mã hiện có.

Tạo một Khóa bí mật chia sẻ dành riêng cho ứng dụng

Để nhận khóa bí mật cho tất cả các ứng dụng trong tài khoản của bạn, hãy mở trang Users and Access và chọn tab Shared Secret.

Cách tìm Khóa bí mật chia sẻ cho tất cả ứng dụng

Yêu cầu xác thực thanh toán

Sau khi nhận khóa bí mật chia sẻ, bạn có thể gửi biên nhận để được xác thực trên các máy chủ của Apple. Điều này được thực hiện qua yêu cầu verifyReceipt. Bạn phải gửi một POST yêu cầu tới https://buy.itunes.apple.com/verifyReceipt. Trong phần nội dung JSON của yêu cầu, chuyển khóa bí mật chia sẻ vào trường password và biên nhận trong trường receipt-data. Ngoài ra còn có thông số exclude-old-transactions tùy chọn. Nếu nó có giá trị true thì đối với mỗi gói đăng ký tự động gia hạn, bạn sẽ chỉ nhận giao dịch cuối cùng thay vì toàn bộ lịch sử gia hạn.

Đây là phần dữ liệu truyền đi (payload) của yêu cầu xác thực mua:

{
  "password": "f4d35830e3...52aae",
  "receipt-data": "MIIUVQY...4rVpL8NlYh2/8l7rk0BcStXjQ==",
  "exclude-old-transactions": false
}

Nếu bạn đang làm việc trong môi trường Sandbox — nghĩa là bạn đang thử nghiệm mua — sau đó gửi yêu cầu xác thực tới https://sandbox.itunes.apple.com/verifyReceipt. Khóa bí mật chia sẻ cũng như dữ liệu truyền đi và định dạng phản hồi vẫn giữ nguyên. 

Điều quan trọng cần lưu ý là bạn sẽ không thể xác thực biên nhận được tạo trong môi trường Sandbox trên máy chủ Production (Production server) và ngược lại. Đó là lý do tại sao trong các hệ thống trong thế giới thực, cách tốt nhất là chuyển yêu cầu đầu tiên đến máy chủ Production và chuyển hướng yêu cầu đó đến máy chủ Sandbox trong trường hợp khóa trạng thái trả về mã lỗi 21007. Đây là hành vi bắt buộc trong quá trình đánh giá ứng dụng vì hành vi này cho phép nhân viên Apple thử nghiệm mua đồng thời cũng cho phép người dùng thực của ứng dụng thực hiện mua.

Trong số các lỗi khác cần lưu ý, mã lỗi 21004 nghĩa là chúng ta đang dùng sai khóa bí mật. Điều quan trọng là phải theo dõi vì dùng sai sẽ ảnh hưởng đến cả trải nghiệm người dùng và độ chính xác của số liệu phân tích. Trong trường hợp xấu nhất, ứng dụng có thể bị xóa khỏi App Store nếu người dùng không được cấp quyền truy cập vào các tính năng cao cấp sau khi họ trả tiền cho những tính năng này.

Nếu xác thực thành công (status=0) thì phản hồi sẽ chứa chi tiết giao dịch của người dùng.

Đây là phản hồi yêu cầu xác thực thanh toán:

{
  "environment": "Production",
  "receipt": {
    "receipt_type": "Production",
    "adam_id": 123,
    "app_item_id": 123,
    "bundle_id": "com.adapty.sample_app",
    "application_version": "1",
    "download_id": 123,
    "version_external_identifier": 123,
    "receipt_creation_date": "2021-04-28 19:42:01 Etc/GMT",
    "receipt_creation_date_ms": "1619638921000",
    "receipt_creation_date_pst": "2021-04-28 12:42:01 America/Los_Angeles",
    "request_date": "2021-08-09 18:26:02 Etc/GMT",
    "request_date_ms": "1628533562696",
    "request_date_pst": "2021-08-09 11:26:02 America/Los_Angeles",
    "original_purchase_date": "2017-04-09 21:18:41 Etc/GMT",
    "original_purchase_date_ms": "1491772721000",
    "original_purchase_date_pst": "2017-04-09 14:18:41 America/Los_Angeles",
    "original_application_version": "1",
    "in_app": [
      {
        "quantity": "1",
        "product_id": "basic_subscription_1_month",
        "transaction_id": "1000000831360853",
        "original_transaction_id": "1000000831360853",
        "purchase_date": "2021-04-28 19:41:58 Etc/GMT",
        "purchase_date_ms": "1619638918000",
        "purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
        "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
        "original_purchase_date_ms": "1619638918000",
        "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
        "expires_date": "2021-05-05 19:41:58 Etc/GMT",
        "expires_date_ms": "1620243718000",
        "expires_date_pst": "2021-05-05 12:41:58 America/Los_Angeles",
        "web_order_line_item_id": "230000397200750",
        "is_trial_period": "true",
        "is_in_intro_offer_period": "false",
        "in_app_ownership_type": "PURCHASED"
      }
    ]
  },
  "latest_receipt_info": [
    {
      "quantity": "1",
      "product_id": "basic_subscription_1_month",
      "transaction_id": "230001020690335",
      "original_transaction_id": "1000000831360853",
      "purchase_date": "2021-08-04 19:41:58 Etc/GMT",
      "purchase_date_ms": "1628106118000",
      "purchase_date_pst": "2021-08-04 12:41:58 America/Los_Angeles",
      "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
      "original_purchase_date_ms": "1619638918000",
      "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
      "expires_date": "2021-08-11 19:41:58 Etc/GMT",
      "expires_date_ms": "1628710918000",
      "expires_date_pst": "2021-08-11 12:41:58 America/Los_Angeles",
      "web_order_line_item_id": "230000438372383",
      "is_trial_period": "false",
      "is_in_intro_offer_period": "false",
      "in_app_ownership_type": "PURCHASED",
      "subscription_group_identifier": "272394410"
    },
    {
      "quantity": "1",
      "product_id": "basic_subscription_1_month",
      "transaction_id": "230001017218955",
      "original_transaction_id": "1000000831360853",
      "purchase_date": "2021-07-28 19:41:58 Etc/GMT",
      "purchase_date_ms": "1627501318000",
      "purchase_date_pst": "2021-07-28 12:41:58 America/Los_Angeles",
      "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
      "original_purchase_date_ms": "1619638918000",
      "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
      "expires_date": "2021-08-04 19:41:58 Etc/GMT",
      "expires_date_ms": "1628106118000",
      "expires_date_pst": "2021-08-04 12:41:58 America/Los_Angeles",
      "web_order_line_item_id": "230000849023623",
      "is_trial_period": "false",
      "is_in_intro_offer_period": "false",
      "in_app_ownership_type": "PURCHASED",
      "subscription_group_identifier": "272394410"
    }
  ],
  "latest_receipt": "MIIUVQY...4rVpL8NlYh2/8l7rk0BcStXjQ==",
  "pending_renewal_info": [
    {
      "auto_renew_product_id": "basic_subscription_1_month",
      "product_id": "basic_subscription_1_month",
      "original_transaction_id": "1000000831360853",
      "auto_renew_status": "1"
    }
  ],
  "status": 0
}

Phản hồi khá rườm rà và đã được đơn giản hóa trong phiên bản App Store Server API mới nhưng việc triển khai hiện tại không khó để nắm bắt.

Start for free

You don't need to write server code yourself,

because we did it for you. Try Adapty SDK!

Start for free

Truy xuất trạng thái gói đăng ký và lịch sử giao dịch

Để tìm hiểu xem liệu người dùng có quyền truy cập vào các tính năng cao cấp của ứng dụng hay không, bạn cần có cách xác định trạng thái gói đăng ký của họ. Vì không có yêu cầu riêng để truy xuất trạng thái gói đăng ký trong phiên bản API hiện tại nên bạn sẽ cần phải làm việc với lịch sử giao dịch trong mọi trường hợp.

Theo mặc định, dãy latest_receipt_info chứa tất cả các giao dịch mua trong ứng dụng của một người dùng cụ thể, ngoại trừ các sản phẩm tiêu thụ được hoàn thành ở phía ứng dụng. Theo cách này, bạn có thể truy xuất toàn bộ lịch sử mua của người dùng. Điều đó khá hữu ích cho cả việc phân tích và xác định trạng thái gói đăng ký hiện tại. 

Có vẻ như các giao dịch mới nhất luôn được xếp trước. Tuy nhiên, để chắc chắn, tôi vẫn khuyên bạn nên triển khai phân loại giao dịch theo ngày theo cách riêng của bạn. 

Dữ liệu truyền đi của giao dịch:

{
  "quantity": "1",
  "product_id": "basic_subscription_1_month",
  "transaction_id": "1000000831360853",
  "original_transaction_id": "1000000831360853",
  "purchase_date": "2021-04-28 19:41:58 Etc/GMT",
  "purchase_date_ms": "1619638918000",
  "purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
  "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
  "original_purchase_date_ms": "1619638918000",
  "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
  "expires_date": "2021-05-05 19:41:58 Etc/GMT",
  "expires_date_ms": "1620243718000",
  "expires_date_pst": "2021-05-05 12:41:58 America/Los_Angeles",
  "web_order_line_item_id": "230000397200750",
  "is_trial_period": "true",
  "is_in_intro_offer_period": "false",
  "in_app_ownership_type": "PURCHASED",
  "subscription_group_identifier": "272394410"
}

Để kiểm tra trạng thái đăng ký hiện tại, chỉ cần truy xuất giao dịch mới nhất của chuỗi và xem thuộc tính expires_date là đủ. Ngoại lệ sẽ là thời gian gia hạn (grace period) mà chúng ta sẽ nhắc đến sau.

Đối với mục đích phân tích, tôi khuyên bạn nên lưu các thuộc tính sau:

  • product_id: mã định danh văn bản của sản phẩm đã mua.
  • transaction_id: mã định danh số duy nhất của giao dịch. Mỗi giao dịch mua hoặc gia hạn sẽ có số nhận dạng riêng để nhận biết liệu giao dịch đã được xử lý trước đó hay chưa. 
  • original_transaction_id: mã định danh số duy nhất của chuỗi giao dịch. Mã sẽ khớp với transaction_id khi đăng ký hoặc kích hoạt dùng thử. Tuy nhiên, khi gia hạn gói đăng ký (subscription renewal) tiếp theo, transaction_id sẽ thay đổi, trong khi original_transaction_id sẽ giữ nguyên. Thuộc tính này rất hữu ích trong việc theo dõi số lần gói đăng ký đã được gia hạn.
  • purchase_date и original_purchase_date: ngày giao dịch và ngày giao dịch ban đầu. Thuộc tính này hoạt động theo cùng một logic như thuộc tính trước đó. 
  • expires_date: ngày hết hạn của gói đăng ký.
  • cancellation_date: ngày hoàn lại tiền, không phải ngày hủy gói đăng ký (subscription cancellation) như tên gợi ý. Nếu phản hồi có trường này thì bạn có thể chấm dứt quyền truy cập gói đăng ký của người dùng, cũng như giải thích cho việc hủy trong các phân tích của bạn — bạn sẽ không nhận được bất kỳ khoản thanh toán nào từ giao dịch này.
  • is_in_intro_offer_period: cờ cho biết liệu ưu đãi ra mắt có được sử dụng khi kích hoạt gói đăng ký hay không.
  • is_trial_period: cờ cho biết liệu có thời gian dùng thử khi kích hoạt gói đăng ký hay không.
  • offer_code_ref_name: mã ưu đãi được sử dụng khi kích hoạt gói đăng ký.
  • promotional_offer_id: mã nhận dạng văn bản của ưu đãi khuyến mại (promo offer) đã được sử dụng khi nhập kỳ thanh toán hiện tại.
  • in_app_ownership_type: loại quyền sở hữu gói đăng ký. Loại quyền này cho biết liệu người dùng tự mua sản phẩm hay nhận sản phẩm như một phần của gói đăng ký dành cho gia đình.  Một số giá trị có thể bao gồm:
  • PURCHASED: người dùng tự mua sản phẩm.
  • FAMILY_SHARED: người dùng đã nhận được sản phẩm như một phần của gói đăng ký dành cho gia đình.

Apple đã thông báo ở WWDC 2021 rằng họ đang có kế hoạch thêm trường appAccountToken vào thông tin giao dịch. Trường sẽ chứa mã nhận dạng của người dùng trong hệ thống của bạn. Mã nhận dạng này phải ở định dạng UUID và được xác định ở phía ứng dụng khi giao dịch mua được khởi tạo. Nếu được xác định thì mã trong tất cả các giao dịch của chuỗi này (gia hạn gói đăng ký, vấn đề thanh toán, v.v.) sẽ được trả lại, có nghĩa là bạn sẽ dễ dàng hiểu được người dùng nào đã mua.

Bạn cũng nên theo dõi thông số subscription_group_identifier.  Nếu người dùng trước đó đã có bất kỳ giao dịch nào với bản dùng thử đang hoạt động hoặc ưu đãi ra mắt thì họ sẽ không có quyền truy cập vào các giao dịch này cho cùng một nhóm đăng ký. Phía máy chủ cũng nên theo dõi thông số này.

Thông tin gia hạn gói đăng ký, thời gian gia hạn và vấn đề thanh toán

Dãy pending_renewal_info lưu trữ dữ liệu gia hạn gói đăng ký. Dãy cho bạn hiểu điều gì sẽ xảy ra với gói đăng ký trong kỳ thanh toán tiếp theo. Ví dụ: nếu bạn phát hiện ra người dùng từ chối tự động gia hạn thì bạn có thể đề xuất họ chuyển sang gói khác hoặc tặng họ một ưu đãi khuyến mại.  Những sự kiện này có thể được theo dõi thủ công bằng các thông báo của máy chủ mà tôi sẽ thảo luận ngay sau đây. 

Dữ liệu gia hạn gói đăng ký:

{
  "auto_renew_product_id": "basic_subscription_1_month",
  "product_id": "basic_subscription_1_month",
  "original_transaction_id": "1000000831360853",
  "auto_renew_status": "1"
}
  • product_id: mã định danh văn bản của sản phẩm đã mua.
  • auto_renew_product_id: mã nhận dạng văn bản của sản phẩm sẽ được kích hoạt trong kỳ thanh toán tiếp theo. Nếu mã này khác với mã hiện tại thì có nghĩa là người dùng đã thay đổi loại gói đăng ký của họ.
  • auto_renew_status: cờ cho biết liệu gói đăng ký có được tiếp tục vào kỳ thanh toán tiếp theo hay không.
  • expiration_intent: lý do đằng sau việc hết hạn gói đăng ký. Một số giá trị có thể bao gồm:
  • 1 — người dùng tự hủy gói đăng ký,
  • 2 — gói đăng ký đã bị hủy vì vấn đề thanh toán,
  • 3 — người dùng không đồng ý tăng giá,
  • 4 — sản phẩm đăng ký không có sẵn để gia hạn, ví dụ: nếu sản phẩm đã bị xóa khỏi App Store Connect.
  • 5 — một lý do không xác định.
  • grace_period_expires_date: ngày hết hạn của thời gian gia hạn nếu ứng dụng của bạn có.  Nếu đúng như vậy thì người dùng phải có quyền truy cập vào các tính năng cao cấp cho đến ngày được chỉ định ở đây chứ không phải tính năng trong chính giao dịch.  Nếu bạn có khóa này thì bạn có thể thông báo cho người dùng rằng họ cần cập nhật thông tin thẻ hoặc nạp tiền vào số dư của họ.
  • is_in_billing_retry_period: cờ cho biết gói đăng ký có trạng thái thử lại thanh toán (billing retry status) hay không.  Điều đó có nghĩa là gói đăng ký không bị hủy, nhưng Apple không thể tính phí thanh toán gia hạn và sẽ tiếp tục cố gắng thực hiện việc này trong vòng 60 ngày.
  • offer_code_ref_name: mã ưu đãi sẽ được sử dụng trong kỳ thanh toán tiếp theo.
  • promotion_offer_id: mã nhận dạng văn bản của ưu đãi khuyến mãi sẽ được sử dụng trong kỳ thanh toán tiếp theo.
  • price_consent_status: cờ hiển thị liệu người dùng có đồng ý với việc tăng giá gói đăng ký sắp tới hay không.  Nếu cờ có giá trị 0 thì bạn nên cung cấp cho người dùng một số sản phẩm khác nhau hoặc một ưu đãi khuyến mãi để họ không hủy gói đăng ký.

Gói đăng ký tiêu thụ được, không tiêu thụ được và không gia hạn được

Nếu người dùng không có sản phẩm tự động gia hạn (auto-renewable product) thì các khóa latest_receipt_infopending_renewal_info sẽ không được trả lại.  Trong trường hợp này, các giao dịch có thể được tìm thấy trong receiptin_app. Định dạng giao dịch tương tự như giao dịch tự động gia hạn nhưng không có trường dành cho thời hạn, gia hạn, ưu đãi và các thuộc tính khác dành riêng cho các giao dịch tự động gia hạn.

Cần lưu ý rằng receiptin_app cũng sẽ đến cho các giao dịch tự động gia hạn, nhưng cách tốt hơn là sử dụng latest_receipt_info, vì dữ liệu đăng ký trong định dạng sẽ mới nhất.

Thông báo giao dịch máy chủ

Thời gian trước đây, bạn phải thiết lập một hệ thống phức tạp để theo dõi các thay đổi trạng thái gói đăng ký. Ví dụ: để biết gói đăng ký có được gia hạn hay không, bạn phải gửi yêu cầu trạng thái gói đăng ký đến máy chủ của Apple mỗi giờ, bắt đầu từ 24 giờ trước khi đăng ký hết hạn.  Apple ngày càng thêm nhiều thông báo máy chủ (server notification) hơn theo thời gian và những thông báo này hiện bao gồm tất cả các sự kiện quan trọng liên quan đến gói đăng ký. Điều đó rất hữu ích:  sau khi có bất kỳ thay đổi nào từ phía Apple, bạn sẽ nhận được thông báo thay đổi trên máy chủ của mình.  Nghĩa là, bạn sẽ được thông báo về các giao dịch mua mới, gia hạn gói đăng ký, vấn đề thanh toán, v.v. Điều này cho phép bạn thu thập số liệu phân tích chính xác hơn, cũng như giúp quản lý trạng thái đăng ký dễ dàng hơn nhiều.

Bạn có thể bật thông báo máy chủ trong App Store Connect. Mở trang của ứng dụng và chuyển đến General -> App information.  Tiếp theo, đặt liên kết vào URL cho trường App Store Server Notifications và lưu các thay đổi của bạn.

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

{
  "notification_type": "DID_RENEW",
  "password": "f4d35830e3...52aae",
  "environment": "PROD",
  "auto_renew_product_id": "basic_subscription_1_month",
  "auto_renew_status": "true",
  "unified_receipt": {
    "status": 0,
    "environment": "Production",
    "latest_receipt_info": [
      {
        "quantity": "1",
        "product_id": "basic_subscription_1_month",
        "transaction_id": "230001020690335",
        "original_transaction_id": "1000000831360853",
        "purchase_date": "2021-08-04 19:41:58 Etc/GMT",
        "purchase_date_ms": "1628106118000",
        "purchase_date_pst": "2021-08-04 12:41:58 America/Los_Angeles",
        "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
        "original_purchase_date_ms": "1619638918000",
        "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
        "expires_date": "2021-08-11 19:41:58 Etc/GMT",
        "expires_date_ms": "1628710918000",
        "expires_date_pst": "2021-08-11 12:41:58 America/Los_Angeles",
        "web_order_line_item_id": "230000438372383",
        "is_trial_period": "false",
        "is_in_intro_offer_period": "false",
        "in_app_ownership_type": "PURCHASED",
        "subscription_group_identifier": "272394410"
      },
      {
        "quantity": "1",
        "product_id": "basic_subscription_1_month",
        "transaction_id": "230001017218955",
        "original_transaction_id": "1000000831360853",
        "purchase_date": "2021-07-28 19:41:58 Etc/GMT",
        "purchase_date_ms": "1627501318000",
        "purchase_date_pst": "2021-07-28 12:41:58 America/Los_Angeles",
        "original_purchase_date": "2021-04-28 19:41:58 Etc/GMT",
        "original_purchase_date_ms": "1619638918000",
        "original_purchase_date_pst": "2021-04-28 12:41:58 America/Los_Angeles",
        "expires_date": "2021-08-04 19:41:58 Etc/GMT",
        "expires_date_ms": "1628106118000",
        "expires_date_pst": "2021-08-04 12:41:58 America/Los_Angeles",
        "web_order_line_item_id": "230000849023623",
        "is_trial_period": "false",
        "is_in_intro_offer_period": "false",
        "in_app_ownership_type": "PURCHASED",
        "subscription_group_identifier": "272394410"
      }
    ],
    "latest_receipt": "MIIUVQY...4rVpL8NlYh2/8l7rk0BcStXjQ==",
    "pending_renewal_info": [
      {
        "auto_renew_status": "1",
        "auto_renew_product_id": "basic_subscription_1_month",
        "product_id": "basic_subscription_1_month",
        "original_transaction_id": "1000000831360853"
      }
    ]
  },
  "bid": "com.adapty.sample_app",
  "bvrs": "0"
}

Định dạng thông báo của máy chủ tương tự như phản hồi xác thực thanh toán. Chi tiết giao dịch được lưu trữ trong unified_receiptlatest_receipt_info. Khóa mật khẩu lưu trữ khóa bí mật chia sẻ cho ứng dụng của bạn, cho phép bạn xác minh tính xác thực của yêu cầu. Khóa notification_type lưu trữ loại sự kiện. Theo tôi, hữu ích nhất trong số này là:

  • DID_CHANGE_RENEWAL_STATUS: có nghĩa là người dùng đã tắt hoặc bật tính năng tự động gia hạn đăng ký (tùy chọn sau hiếm hơn nhiều).   Nếu người dùng chọn không chọn tự động gia hạn thì bạn có thể muốn khuyến khích họ đăng ký lại.
  • DID_FAIL_TO_RENEW: không thể gia hạn gói đăng ký vì vấn đề thanh toán.  Bạn nên thông báo cho người dùng về điều đó để gói đăng ký không bị hủy tự động.
  • DID_RENEW: gói đăng ký đã được gia hạn thành công. 
  • REFUND: gói đăng ký đã được hoàn lại tiền. Bạn nên hạn chế quyền truy cập của người dùng vào các tính năng cao cấp mà giao dịch mua này cấp và tính tiền hoàn lại (nghĩa là mất số tiền này) trong phân tích của bạn.

Kết luận

Xác thực máy chủ có thể tăng cường phân tích của bạn cho dữ liệu bạn thu thập từ ứng dụng của mình. Nó cũng khiến những kẻ gian lận khó tiếp cận nội dung cao cấp của bạn hơn cũng như cho phép bạn tạo những người dùng đăng ký trên nhiều nền tảng. Đồng thời, việc triển khai xác thực máy chủ có thể mất khá nhiều thời gian, đặc biệt nếu bạn cần dữ liệu có độ chính xác cao. Điều này sẽ yêu cầu tính đến nhiều trường hợp phụ: nâng cấp gói đăng ký, nâng cấp chéo gói đăng ký, thời gian dùng thử, ưu đãi khuyến mại/ra mắt, thời gian gia hạn, hoàn tiền, đăng ký gia đình, v.v. Bạn cũng phải biết và tính đến các sắc thái khác nhau, ví dụ: Apple có chính sách giảm hoa hồng từ 30% xuống 15% cho các gói đăng ký thường xuyên được gia hạn trong hơn một năm.

Further reading

Adapty February Updates: ARPPU Metric, improved attribution
Adapty February Updates: ARPPU Metric, improved attribution
March 9, 2022
3 min read
Marketing company with $1.5M MRR from mobile apps
Marketing company with $1.5M MRR from mobile apps
November 25, 2021
12 min read, 42 min listen
First steps to better monetization
First steps to better monetization
February 15, 2022
38 min listen