Tất cả bài viết
Phân tích chuyên sâuĐọc trong 8 phút

Hãy xác minh thông báo máy chủ App Store trước khi xử lý hoàn tiền, vì bất kỳ ai cũng có thể gửi yêu cầu đến URL webhook của bạn

Một thông báo hoàn tiền chỉ là một yêu cầu HTTPS cho đến khi bạn kiểm tra chữ ký của nó. Bài viết này hướng dẫn cách xác minh thông báo máy chủ App Store và tin nhắn RTDN của Google Play, mỗi cửa hàng ký những gì, và một khoản hoàn tiền giả mạo hoặc bị từ chối nhầm khiến bạn tốn bao nhiêu.

Một con dấu sáp bằng đồng thau và một phong bì đã niêm phong bên cạnh máy tính xách tay trên bàn dưới ánh đèn ấm, hình ảnh minh họa lý do bạn cần xác minh thông báo máy chủ App Store trước khi tin tưởng chúng

Điểm chính

  • Apple gửi mọi thông báo máy chủ App Store phiên bản 2 dưới dạng signedPayload, một JSON Web Signature gồm ba phần Base64URL: header, payload và chữ ký.
  • Header JWS của thông báo máy chủ App Store chứa chuỗi chứng chỉ x5c gồm ba chứng chỉ: chứng chỉ lá dùng để ký, chứng chỉ trung gian của Apple mang phần mở rộng Apple Worldwide Developer Relations 1.2.840.113635.100.6.2.1, và chứng chỉ gốc của Apple.
  • App Store Server Library của Apple xác minh thông báo bằng SignedDataVerifier, công cụ kiểm tra chuỗi chứng chỉ, bundle ID, environment và, trong môi trường production, cả appAppleId, đồng thời có thể kiểm tra trực tuyến việc thu hồi chứng chỉ.
  • Real-time developer notifications của Google Play không được Google Play ký. Với push subscription, Pub/Sub có thể đính kèm một token OpenID Connect đã ký trong header Authorization, và máy chủ của bạn kiểm tra chữ ký, audience và các claim email của token đó.
  • Google cho biết RTDN chỉ báo cho bạn rằng trạng thái giao dịch mua đã thay đổi, vì vậy cách phản hồi an toàn với bất kỳ thông báo hoàn tiền nào là xác nhận lại bằng Google Play Developer API trước khi thu hồi quyền truy cập.
  • Apple gửi thông báo máy chủ App Store từ subnet 17.0.0.0/8, nhưng danh sách IP cho phép chỉ thu hẹp phạm vi những ai có thể truy cập endpoint của bạn. Chính chữ ký mới chứng minh tin nhắn đến từ Apple.
  • Một trình xác minh từ chối nhầm tin nhắn thật cũng gây tốn kém như một trình xác minh chấp nhận tin nhắn giả, vì một CONSUMPTION_REQUEST bị bỏ qua có thời hạn phản hồi 12 giờ và một đợt xem xét chargeback của Google Play có thời hạn 24 giờ.

Bất kỳ ai biết URL webhook của bạn đều có thể gửi đến đó một yêu cầu hoàn tiền. Đó là sự thật khó chịu đằng sau mọi thông báo máy chủ App Store và mọi Real-time developer notification của Google Play: cho đến khi bạn xác minh, tin nhắn đó chỉ là một HTTPS POST chứa một ít JSON. Nếu máy chủ của bạn hủy gói đăng ký, thu hồi tín dụng hoặc khóa tài khoản chỉ vì một yêu cầu ghi REFUND, bạn đã trao quyền đó cho bất kỳ ai biết viết một lệnh curl.

Cả hai cửa hàng đều cho bạn cách kiểm tra. Apple ký chính thông báo, nên bạn có thể xác minh thông báo máy chủ App Store chỉ với chứng chỉ gốc của Apple. Google Play đi theo hướng khác. Tin nhắn không được ký, nhưng việc phân phối qua Pub/Sub thì có thể được ký, và dù sao Google cũng yêu cầu bạn xác nhận mọi thay đổi bằng API của họ. Dưới đây là những gì mỗi bên chứng minh được, và cái giá phải trả khi bạn sai theo bất kỳ hướng nào.

Mỗi cửa hàng thực sự ký những gì

Câu hỏiApp Store Server Notifications V2Google Play RTDN
Bản thân tin nhắn có được ký không?Có, signedPayload là một JWSKhông, trường data là JSON mã hóa base64
Điều gì chứng minh người gửiChuỗi chứng chỉ x5c dẫn đến chứng chỉ gốc của AppleToken OIDC của Pub/Sub khi push, hoặc pull có xác thực của chính bạn
Đối tượng đã ký lồng bên trongsignedTransactionInfo và signedRenewalInfo, mỗi cái là một JWS riêngKhông có
Các trường cần khớp với ứng dụng của bạnbundleId, appAppleId, environmentpackageName
Mạng nguồn17.0.0.0/8, sandbox và productionGoogle Cloud Pub/Sub
Xác nhận trước khi hành độngKhông bắt buộc, dữ liệu đã ký chính là bản ghiBắt buộc, Google yêu cầu gọi Developer API
Giao thức truyền tảiHTTPS, TLS 1.2 trở lênHTTPS push, hoặc pull qua thư viện client

Cách xác minh thông báo máy chủ App Store

Tài liệu của Apple mô tả signedPayload là một chuỗi gồm ba thành phần mã hóa Base64URL, phân tách bằng dấu chấm: header, payload và chữ ký, được ký theo đặc tả JSON Web Signature. Giải mã payload thì dễ. Tin tưởng nó thì cần bốn bước kiểm tra.

  • Đọc mảng x5c từ header đã giải mã. Apple ghi rõ thứ tự: đầu tiên là chứng chỉ chứa khóa ký, tiếp theo là chứng chỉ trung gian của Apple, sau đó là chứng chỉ gốc của Apple.
  • Xác thực chuỗi đó với chứng chỉ gốc của Apple mà bạn tự tải về từ trang PKI của Apple, trên thực tế là Apple Root CA - G3. Đừng bao giờ tin chứng chỉ gốc đi kèm trong tin nhắn, vì kẻ giả mạo có thể đặt bất kỳ chứng chỉ nào vào đó.
  • Xác nhận chứng chỉ trung gian mang phần mở rộng Apple Worldwide Developer Relations, 1.2.840.113635.100.6.2.1, rồi xác minh chữ ký JWS bằng khóa công khai của chứng chỉ lá.
  • Đối chiếu dữ liệu đã giải mã với ứng dụng của bạn: bundleId, appAppleId trong production, và environment. Một chữ ký Apple hợp lệ trên ứng dụng của nhà phát triển khác, hoặc trên một giao dịch sandbox gửi đến endpoint production của bạn, vẫn không phải là khoản hoàn tiền của bạn.

Sau đó làm lại một lần nữa với phần bên trong. Đối tượng data chứa signedTransactionInfo và signedRenewalInfo, mỗi cái là một JWS riêng với chuỗi x5c riêng. Một handler REFUND xác minh lớp vỏ nhưng đọc giao dịch mà không xác minh nó đã để lại một lỗ hổng ở giữa.

Dùng thư viện của Apple thay vì tự viết

Apple phát hành App Store Server Library cho Swift, Java, Python và Node. SignedDataVerifier của thư viện nhận các chứng chỉ gốc của bạn, một cờ kiểm tra trực tuyến, environment, bundle ID của bạn và, với production, appAppleId của bạn. Mã nguồn Node cho thấy những gì nó bắt buộc: đúng ba chứng chỉ trong chuỗi, phần mở rộng chứng chỉ lá của Apple 1.2.840.113635.100.6.11.1 và phần mở rộng của chứng chỉ trung gian, ngày hiệu lực của chứng chỉ, cùng bundle ID, app ID và environment khớp nhau. Khi bật kiểm tra trực tuyến, nó còn hỏi máy chủ phản hồi OCSP xem chứng chỉ lá và chứng chỉ trung gian có bị thu hồi hay không.

Một chiếc kính lúp của thợ kim hoàn đặt trên một tờ chứng nhận giấy có con dấu nổi trên bàn gỗ, kiểu kiểm tra kỹ lưỡng mà bạn dành cho một tin nhắn hoàn tiền trước khi tin tưởng nó

Danh sách IP cho phép có đủ không?

Không. Apple cho biết nếu máy chủ của bạn cần danh sách cho phép, bạn nên thêm subnet 17.0.0.0/8, áp dụng cho cả sandbox và production. Việc đó giảm bớt nhiễu. Nhưng nó không chứng minh tin nhắn là thật, và nó sẽ hỏng ngay khi mạng của Apple thay đổi theo cách bạn không được báo trước. Hãy giữ bước kiểm tra chữ ký làm cửa chặn thực sự.

Cách xác minh thông báo hoàn tiền của Google Play

Google Play xuất bản tin nhắn RTDN lên một topic Pub/Sub do bạn sở hữu, và chỉ service account google-play-developer-notifications@system.gserviceaccount.com mới nên giữ vai trò Pub/Sub Publisher trên topic đó. Sau đó subscription của bạn phân phối tin nhắn theo một trong hai cách, và mỗi cách có bằng chứng riêng.

  • Pull subscription: máy chủ của bạn mở một kết nối có xác thực đến Pub/Sub và lấy tin nhắn về. Không có endpoint công khai, nên người lạ không có nơi nào để gửi đến.
  • Push subscription có bật xác thực: Pub/Sub ký một JSON Web Token và gửi nó trong header Authorization dưới dạng token Bearer. Hướng dẫn của Google yêu cầu xác thực chữ ký của token và kiểm tra rằng các claim email và audience khớp với giá trị bạn đã cấu hình, và mã mẫu của họ còn yêu cầu email_verified phải là true. Token có thể đã được tạo từ tối đa một giờ trước.
  • Push subscription không có xác thực: bất cứ thứ gì truy cập được URL đều có thể gửi một tin nhắn trông giống RTDN.

Vì sao Google muốn bạn xác nhận bằng API

Ngay cả một RTDN được xác thực hoàn hảo cũng không mang đầy đủ thông tin. Hướng dẫn thiết lập của Google nói rõ rằng các thông báo này chỉ cho bạn biết trạng thái giao dịch mua đã thay đổi, và bạn phải gọi Google Play Developer API để lấy trạng thái đầy đủ. Với voidedPurchaseNotification, điều đó có nghĩa là tìm giao dịch mua trong Voided Purchases API, hoặc lấy trạng thái hiện tại của nó, trước khi thu hồi bất cứ thứ gì. Lệnh gọi API được xác thực bằng service account của chính bạn, nên câu trả lời của nó là thứ kẻ giả mạo không thể làm giả.

Một khoản hoàn tiền giả mạo hoặc bị từ chối khiến bạn tốn bao nhiêu tiền

Một thông báo hoàn tiền giả mạo mà bạn tin tưởng sẽ khiến bạn mất một khách hàng trả phí. Bạn khóa quyền truy cập của một người mà bạn vẫn đang giữ tiền của họ, họ mở phiếu hỗ trợ, và một số người sẽ đến cửa hàng để yêu cầu khoản hoàn tiền mà hệ thống của bạn đã giả vờ chấp thuận. Một giao dịch mua hoặc gia hạn giả mạo thì gây tốn kém theo chiều ngược lại: bạn cung cấp tài nguyên tính toán GPU, các lệnh gọi API bên thứ ba trả phí, dung lượng lưu trữ và tiền chi trả cho nhà sáng tạo dựa trên doanh thu chưa từng tồn tại.

Kiểu thất bại ngược lại thì âm thầm hơn và thường lớn hơn. Một trình xác minh từ chối tin nhắn thật, do appAppleId sai, cấu hình sandbox trong production hoặc chứng chỉ gốc lỗi thời, sẽ đánh rơi đúng hai luồng hoàn tiền mà bằng chứng của bạn có giá trị. Apple yêu cầu dữ liệu tiêu thụ trong vòng 12 giờ kể từ CONSUMPTION_REQUEST. Google cho bạn 24 giờ để trả lời đợt xem xét chargeback qua orders.reviewrefund, và với các đơn hàng đặt sau ngày 3 tháng 8 năm 2026, một vụ chargeback Google Play bị thua sẽ khiến bạn mất giá mua trừ phí dịch vụ của Play, cộng thêm khoản phí mà ngân hàng của người mua tính.

Thất bạiĐiều gì xảy raCái giá phải trả
Chấp nhận REFUND giả mạoMột khách hàng trả phí mất quyền truy cậpThời gian hỗ trợ và một yêu cầu hoàn tiền thật do chính bạn gây ra
Chấp nhận giao dịch mua giả mạoTruy cập miễn phí vào tính năng trả phíTài nguyên tính toán, lệnh gọi API, lưu trữ và chi trả mà không có doanh thu
Từ chối CONSUMPTION_REQUEST thậtApple quyết định mà không có dữ liệu của bạnKhoản doanh thu đó, cộng mọi chi phí đã bỏ ra để cung cấp nó
Từ chối đợt xem xét chargeback thậtGoogle quyết định mà không có bằng chứng của bạnGiá mua và phí ngân hàng với đơn hàng sau ngày 3 tháng 8 năm 2026

Đừng để lỗi xác minh nuốt mất thời hạn

  • Lưu yêu cầu gốc trước khi xác minh, để một tin nhắn bị từ chối không bao giờ thực sự bị mất.
  • Cảnh báo với mọi lần xác minh thất bại. Một tin nhắn giả mạo chỉ là nhiễu. Mọi tin nhắn cùng thất bại một lúc là lỗi của chính bạn.
  • Với Apple, bạn có thể phát lại những gì đã từ chối. Get Notification History trả về signedPayload gốc trong 180 ngày gần nhất, vì vậy hãy sửa trình xác minh rồi chạy lại các payload đã lưu hoặc khôi phục qua nó.
  • Với Google, một đợt xem xét chargeback đang chờ chỉ tồn tại trong thông báo của nó, nên audience push bị cấu hình sai đồng nghĩa với một đợt xem xét bị bỏ lỡ. Hãy kiểm tra bằng Send Test Message trong Play Console sau mỗi lần thay đổi.

RefundHalt xác minh thông báo của cả hai cửa hàng, xác nhận hoàn tiền bằng API của cửa hàng trước khi bất cứ điều gì thay đổi, và trả lời CONSUMPTION_REQUEST cũng như các đợt xem xét chargeback trong thời hạn của chúng, để một tin nhắn giả không thể làm bạn mất khách hàng và một tin nhắn thật không thể lọt qua.

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

Làm thế nào để xác minh một thông báo máy chủ App Store?
Xác minh signedPayload như một JSON Web Signature. Đọc chuỗi chứng chỉ x5c từ header của nó, xác thực chuỗi đó với chứng chỉ gốc của Apple mà bạn tải về từ trang PKI của Apple, xác minh chữ ký bằng khóa của chứng chỉ lá, sau đó kiểm tra rằng bundleId, appAppleId và environment khớp với ứng dụng của bạn. App Store Server Library của Apple thực hiện tất cả những việc này trong SignedDataVerifier.
Tôi dùng khóa nào để xác thực thông báo máy chủ App Store?
Bạn không cần khóa nào từ App Store Connect. Khóa công khai nằm trong chứng chỉ đầu tiên của chuỗi x5c bên trong header JWS, và bạn chỉ tin nó sau khi đã xác thực chuỗi đến một chứng chỉ gốc của Apple mà bạn tự lấy về. Khóa In-App Purchase của bạn dùng để gọi API của Apple, không dùng để kiểm tra thông báo.
Real-time developer notifications của Google Play có được ký không?
Không, Google Play không ký chính tin nhắn RTDN. Với push subscription có xác thực, Pub/Sub gửi một token OpenID Connect đã ký trong header Authorization để bạn xác thực, và Google yêu cầu gọi Google Play Developer API sau mỗi thông báo để lấy trạng thái đầy đủ của giao dịch mua.
Máy chủ của tôi có nên trả về lỗi khi một thông báo xác minh thất bại không?
Hãy lưu yêu cầu gốc trước, rồi mới quyết định. Apple gửi lại thông báo phiên bản 2 sau phản hồi 40x hoặc 50x, vào các mốc 1, 12, 24, 48 và 72 giờ sau lần thử trước. Nếu lỗi nằm ở cấu hình của chính bạn, Get Notification History cho phép bạn khôi phục signedPayload gốc trong 180 ngày và xác minh lại sau khi đã sửa.
Tôi có còn cần xác minh thông báo nếu chỉ cho phép dải IP của Apple không?
Có. Apple yêu cầu các máy chủ dùng danh sách cho phép chấp nhận subnet 17.0.0.0/8, điều này giới hạn ai có thể kết nối nhưng không chứng minh tin nhắn là thật. Chữ ký JWS mới là bước kiểm tra gắn một thông báo với Apple và với ứng dụng của bạn.

Nguồn và tài liệu đọc thêm

RefundHalt

Chế độ tự động xử lý hoàn tiền cho App Store và Google Play

Đọc tiếp

Yêu cầu hoàn tiền tiếp theo đã đang trên đường đến.

Thiết lập RefundHalt trong khoảng thời gian bạn cần để đọc thêm một email hỗ trợ về khoản hoàn tiền mà mình chưa kịp phản biện.