App Store Refund Management

Cách gửi thông tin tiêu thụ cho yêu cầu hoàn tiền Apple

Tìm hiểu cách gửi thông tin tiêu thụ cho yêu cầu hoàn tiền Apple và hiểu cách apple refund automation giúp việc quản lý hoàn tiền nhanh hơn và dễ dàng hơn.

5 min read
Cách gửi thông tin tiêu thụ cho yêu cầu hoàn tiền Apple

Nếu bạn đã từng làm việc với luồng hoàn tiền của Apple, có lẽ bạn đã thấy tài liệu đề cập đến khung thời gian phản hồi 12 giờ. Điều mà tài liệu không phải lúc nào cũng nói rõ là cách thực sự phản hồi.

CONSUMPTION_REQUEST là một thông báo (notification); phản hồi lại là một lệnh gọi đến một API khác, với key riêng và một body đã thay đổi cấu trúc hai lần kể từ năm 2021. Chúng tôi đã đề cập đến nội dung mà thông báo này chứa trước đây. Đây là phần tiếp theo sau đó, khi bạn thực sự phải trả lời nó.

Những điểm chính

  • Đó là một request PUT duy nhất gửi đến App Store Server API. Đó là toàn bộ cơ chế.

  • Mỗi request được ký bằng một JWT từ in-app purchase key của bạn; các token cũ hơn 60 phút sẽ bị từ chối.

  • Body của phiên bản v2 có năm trường, trong đó ba trường là bắt buộc.

  • Apple chỉ phản hồi 202 và không gì khác. Quyết định hoàn tiền sẽ đến sau, dưới dạng một thông báo riêng.

  • Mười hai giờ tính từ thời điểm thông báo đến, và bạn có thể gửi lại trong khung thời gian đó.

Apple Consumption Request là gì?

Bỏ qua các thuật ngữ chuyên môn, một Apple consumption request thực chất là một câu hỏi. Một khách hàng đã yêu cầu Apple hoàn lại tiền, và trước khi trả lời họ, Apple quay sang hỏi server của bạn: người này có nhận được thứ họ đã trả tiền không, và họ đã sử dụng nó chưa?

Apple kết hợp bất cứ điều gì bạn nói với những thông tin mà hãng đã sẵn có, chẳng hạn như tần suất khách hàng này yêu cầu hoàn tiền, rồi tự mình đưa ra quyết định.

Phần cuối này là điều mọi người thường hiểu sai. Bạn không phê duyệt hay từ chối gì cả; bạn chỉ cung cấp dữ kiện.

CONSUMPTION_REQUEST mà Apple gửi sẽ xuất hiện tại URL server mà bạn đã nhập vào App Store Connect, dưới dạng một server notification phiên bản 2.

Năm 2021, nó chỉ được kích hoạt cho các sản phẩm consumable. Subscription được bổ sung vào năm 2024, và vào tháng 12 năm 2025 Apple đã công bố phiên bản 2 của endpoint này, với tài liệu cho biết nó áp dụng cho mọi loại sản phẩm, bao gồm cả non-consumable và non-renewing subscription. Trang tham chiếu về thông báo vẫn chỉ liệt kê hai loại ban đầu; tôi sẽ tin vào trang mới hơn và xây dựng cho đủ cả bốn loại.

Và nhân tiện nói về tên gọi: nhiều người tìm kiếm "Apple refund API" nhưng thực ra không có một API riêng như vậy. Lệnh gọi này là một phần của App Store Server API.

Những gì bạn cần trước khi gửi

Ba trong số bốn điều sau không thể làm vội, vì vậy hãy đọc phần này trước khi gửi request đầu tiên thay vì sau đó.

Điều đầu tiên là một key, cụ thể là in-app purchase key, được tạo trong App Store Connect ở mục Users and Access. Apple chỉ cấp cho bạn một file .p8 duy nhất một lần; bạn cũng sẽ cần key ID của file đó và issuer ID của mình.

Thứ hai, bạn cần một cách để đi từ transaction ID (thứ Apple cung cấp) đến một người cụ thể trong cơ sở dữ liệu của bạn (thứ bạn cần). Cách gọn gàng nhất là appAccountToken, được thiết lập tại thời điểm mua hàng, sau đó sẽ xuất hiện lại trong mọi thông báo liên quan đến giao dịch đó.

Nếu thuật ngữ này còn mới với bạn, hãy đọc tại sao appAccountToken quan trọng đối với việc chống hoàn tiền trước, vì nếu không có việc ánh xạ này thì phần còn lại của bài viết chỉ mang tính lý thuyết.

Thứ ba, và đây là điều không thể khắc phục vào lúc 2 giờ sáng: sự đồng ý (consent).

Tài liệu của Apple nói rõ rằng nếu khách hàng chưa đồng ý cho bạn chia sẻ dữ liệu này, bạn không được phản hồi gì cả. Họ cũng nói, theo cách diễn đạt mang tính pháp lý hơn, rằng điều gì được coi là sự đồng ý hợp lệ phụ thuộc vào nơi bạn hoạt động và họ không thể tư vấn cho bạn về việc đó.

Cách làm thông thường là có điều khoản opt-in trong terms của bạn, và điều khoản này cần tồn tại từ trước khi request xuất hiện.

Điều thứ tư chính là transaction ID, thứ bạn chỉ có được khi thông báo đã đến và bạn đã kiểm tra chữ ký của Apple.

Cách gửi thông tin tiêu thụ từng bước

Hầu hết những người tìm kiếm "send consumption information apple" đều kết thúc ở trang tham chiếu của Apple, trang này liệt kê các trường rồi dừng lại một cách khá đột ngột. Đây là trình tự thực tế.

  1. Bắt đầu bằng việc kiểm tra thông báo có thật hay không. signedPayload là một JWS; App Store Server Library của Apple xác minh nó và trả về transaction đã được giải mã, bao gồm cả transactionId và appAccountToken.

  2. Tìm giao dịch mua đó ở phía hệ thống của bạn. Những gì bạn cần xác định: chúng ta đã giao sản phẩm chưa, khoảng bao nhiêu phần đã được sử dụng, khách hàng đã xem bản mẫu hay dùng thử trước đó chưa, và chúng ta muốn Apple xử lý như thế nào.

  3. Tiếp theo là token. Header: alg là ES256, kid là key ID của bạn, typ là JWT. Payload: iss là issuer ID của bạn, iat là thời điểm hiện tại, exp tối đa là 60 phút sau đó, aud là chuỗi ký tự cố định appstoreconnect-v1, và bid là bundle ID của bạn. Hướng dẫn tạo JSON Web Token của Apple có cấu trúc chi tiết, mặc dù library sẽ tự xây dựng phần này cho bạn.

  4. Gửi PUT. URL production là https://api.storekit.apple.com/inApps/v2/transactions/consumption/{transactionId}, với JWT đóng vai trò Bearer token trong header Authorization, và một body có dạng như sau:

{

"customerConsented": true,

"deliveryStatus": "DELIVERED",

"consumptionPercentage": 94000,

"sampleContentProvided": false,

"refundPreference": "DECLINE"

}

  1. Apple phản hồi 202 Accepted, và đó là tất cả những gì bạn nhận được. Hãy ghi lại thời gian, body và transaction ID, vì một tháng sau sẽ có người hỏi chúng ta đã nói gì với Apple về trường hợp này.

  2. Sau đó bạn chờ. Quyết định sẽ đến dưới dạng thông báo REFUND hoặc REFUND_DECLINED. Nếu đó là REFUND, hãy thu hồi nội dung hoặc entitlement ngay trong ngày.

Vì vậy nếu bạn đang thắc mắc cách gửi thông tin tiêu thụ mà Apple sẽ thực sự sử dụng, đó chính là nó: một request, năm trường, trong đúng khung thời gian.

Apple cũng cho phép bạn gửi một payload cập nhật trong vòng 12 giờ đó, vì vậy nếu lệnh gọi đầu tiên chỉ có một nửa thông tin, hãy gửi thêm lần thứ hai.

Có vài điều mà tên các trường không nói rõ. consumptionPercentage được tính theo milliunit, điều này khiến nhiều người nhầm lẫn: 94000 nghĩa là 94%, 100000 nghĩa là toàn bộ, và giá trị này phải là 0 nếu deliveryStatus khác DELIVERED.

Các tùy chọn undelivered là UNDELIVERED_QUALITY_ISSUE, UNDELIVERED_WRONG_ITEM, UNDELIVERED_SERVER_OUTAGE và UNDELIVERED_OTHER.

refundPreference là tùy chọn và nhận các giá trị GRANT_FULL, GRANT_PRORATED hoặc DECLINE; đối với GRANT_PRORATED trên một auto-renewable subscription, hãy bỏ trống phần trăm và Apple sẽ tự tính dựa trên thời gian còn lại.

Trang tham chiếu Send Consumption Information có danh sách đầy đủ các lỗi.

Ý nghĩa của các mã phản hồi

Chỉ có một phản hồi tốt, đó là 202. Mọi phản hồi khác đều là Apple cho bạn biết bạn đã sai ở đâu, thường là chỉ rõ tên lỗi.

Các mã lỗi 400 là hữu ích nhất, vì body sẽ nêu rõ tên lỗi.

ConsumptionPercentageOutOfRangeError gần như luôn có nghĩa là ai đó đã gửi 94 thay vì 94000.

UndeliveredConsumptionPercentageNonZeroError có nghĩa là bạn đã nói sản phẩm chưa được giao nhưng lại khai rằng nó đã được sử dụng một phần.

InvalidCustomerConsentedError thì đã quá rõ nghĩa.

Lỗi 401 liên quan đến token: key sai, thời hạn hết hiệu lực vượt quá một giờ, bundle ID không khớp, hoặc clock của server bị lệch đủ xa khiến iat trông như thuộc về tương lai.

Lỗi 404 có nghĩa là không tìm thấy transaction ID, và câu hỏi đầu tiên tôi luôn đặt ra là liệu một giao dịch sandbox có bị gửi đến URL production (hoặc ngược lại) không.

429 là rate limit (50 request mỗi giây cho mỗi app trong môi trường production, bằng một phần mười con số đó trong sandbox); hãy đọc header Retry-After trước khi thử lại.

500 là lỗi từ phía Apple; hãy thử lại sau.

Cách kiểm tra trong sandbox

Việc kiểm tra điều này dễ hơn bạn nghĩ, nhưng có một điểm cần lưu ý.

Nhập một URL server sandbox vào App Store Connect, đăng nhập bằng một Apple Account sandbox, mua một thứ gì đó, sau đó mở refund sheet (beginRefundRequest) của app và gửi một request.

Hướng dẫn kiểm tra yêu cầu hoàn tiền của Apple giải thích cách môi trường thử nghiệm hoạt động: chọn bất kỳ lý do nào thì khoản hoàn tiền sẽ được tự động phê duyệt; chọn Other và nhập DECLINE thì nó sẽ bị từ chối; nhập GRANT_PRORATED để có một khoản hoàn tiền theo tỷ lệ.

Điểm cần lưu ý nằm trong chính các quy tắc đó: sandbox quyết định dựa trên những gì bạn nhập vào sheet, không phải những gì server của bạn gửi đi, vì vậy nó không thể cho bạn biết liệu dữ liệu của bạn có thuyết phục được Apple về điều gì hay không.

Điều nó có thể kiểm tra là phần hạ tầng kỹ thuật: thông báo được gửi đến, PUT của bạn tới api.storekit-sandbox.apple.com trả về 202, và REFUND hoặc REFUND_DECLINED được xử lý khi xuất hiện.

Ngoài ra, sandbox không gửi email hoàn tiền, vì vậy đừng chờ nhận một email như vậy.

Apple Refund Automation: khi nào nên dừng làm việc này bằng tay

Khi làm bằng tay, công việc này là một người canh chờ cảnh báo mọi giờ trong ngày, chạy một truy vấn và gửi đi năm trường trước khi đồng hồ 12 giờ chạy hết.

Điều này vẫn ổn khi chỉ có một vài Apple refund request mỗi tháng. Nhưng nó sẽ kém ổn hơn nhiều vào kỳ nghỉ cuối tuần dài đầu tiên không có ai theo dõi.

Để tự động hóa Apple refund request, bạn lấy tất cả những gì đã nói ở trên và loại bỏ con người ra khỏi quy trình: thông báo đến, các quy tắc của bạn chuyển dữ liệu giao hàng và sử dụng thành một payload, lệnh gọi được gửi đi trong vài phút, quyết định được ghi lại gắn với case đó, và một REFUND sẽ kích hoạt việc revoke.

Xây dựng hệ thống đó không phải là một dự án lớn; việc theo kịp những thay đổi của Apple sau đó (chẳng hạn như việc chuyển từ V1 sang v2) mới là phần âm thầm tốn kém nhất.

Nếu bạn không muốn tự làm, chúng tôi đã xem xét các dịch vụ được xây dựng cho việc này trong bài so sánh các công cụ quản lý hoàn tiền Apple của chúng tôi, bao gồm cả những trường hợp Refund Sensor phù hợp và những trường hợp không.

Có một điều tôi luôn phản biện mỗi khi chủ đề này xuất hiện: Apple refund automation thay đổi mức độ đáng tin cậy trong cách bạn phản hồi, không phải ai là người quyết định. Không có gì trong API này trao cho developer quyền quyết định cuối cùng.

Kết luận

Lệnh gọi này về bản chất rất nhỏ: một token đã ký, một PUT, năm trường, một 202 để ghi log.

Điều quyết định thành bại nằm ở những nơi khác: ở consent đã có sẵn trong terms của bạn, ở appAccountToken trên mọi giao dịch mua, ở các bản ghi sử dụng mà bạn có thể truy xuất trong vài giây, và ở thứ gì đó sẽ "thức dậy" vào 3 giờ sáng khi thông báo đến.

Giải quyết tốt những điều đó, và việc này sẽ không còn là một cuộc chạy đua vội vã nữa, mà trở thành một việc server của bạn chỉ đơn giản là thực hiện.

Câu hỏi thường gặp

Đây là một endpoint của App Store Server API được dùng để phản hồi một CONSUMPTION_REQUEST. Bạn gửi một request PUT kèm thông tin tiêu thụ, và Apple trả về 202 Accepted.

Bạn có 12 giờ tính từ khi CONSUMPTION_REQUEST đến. Apple cho phép gửi một payload cập nhật trong cùng khung thời gian 12 giờ này.

Có. Apple cho phép bạn gửi một payload cập nhật trong khung thời gian 12 giờ. Điều này hữu ích khi lần gửi đầu tiên chưa đầy đủ.

Không. 202 Accepted chỉ có nghĩa là Apple đã nhận được thông tin của bạn. Kết quả sẽ đến sau dưới dạng thông báo REFUND hoặc REFUND_DECLINED.

Có. Trường customerConsented là bắt buộc khi gửi thông tin tiêu thụ. Nếu khách hàng chưa đồng ý, tài liệu của Apple quy định rằng bạn không nên phản hồi.

#Apple Refund Automation#Apple Refund Request#Apple App Refund#Consumption Information#Apple Billing#Refund Process
Refund SensorRefund Sensor TeamRefund defense for App Store and Google Play developers