Tất cả bài viết
Deep diveĐọc trong 7 phút

Có một endpoint trả về toàn bộ lịch sử hoàn tiền App Store của một khách hàng, và đây là những gì nó đưa lại

Endpoint Get Refund History của Apple trả về toàn bộ lịch sử hoàn tiền App Store của một khách hàng dưới dạng các giao dịch đã ký. Đây là từng trường, cách token revision phân trang, tại sao dữ liệu là theo khách hàng chứ không theo ứng dụng, và một lần hoàn tiền bạn bỏ lỡ khiến bạn tốn bao nhiêu.

Cảnh bàn làm việc nhìn từ trên xuống với một điện thoại thông minh, một sổ ghi giao dịch bằng giấy và một kính lúp, minh họa việc kéo lịch sử hoàn tiền App Store của một khách hàng từ endpoint Get Refund History của Apple

Điểm chính

  • Get Refund History là một endpoint của App Store Server API trả về các giao dịch mua trong ứng dụng đã được hoàn tiền của một khách hàng cho ứng dụng của bạn dưới dạng danh sách các giao dịch đã ký, để bạn có thể đối chiếu các lần hoàn tiền và thu hồi quyền truy cập ngay cả khi một thông báo chưa bao giờ đến được với bạn.
  • Bạn gọi GET /inApps/v2/refund/lookup/{transactionId} với bất kỳ id giao dịch nào của khách hàng đó, và Apple trả về các lần hoàn tiền của họ trên mọi loại giao dịch mua trong ứng dụng của bạn, không chỉ riêng cái bạn hỏi.
  • Phản hồi có ba trường: signedTransactions, tối đa 20 giao dịch JWS mỗi trang được sắp xếp với lần hoàn tiền cũ nhất trước, cùng một token revision và một boolean hasMore để phân trang.
  • Hãy lưu token revision cuối cùng. Gửi nó lại lần sau và Apple chỉ trả về các lần hoàn tiền mới hơn thời điểm đó, biến một bản kết xuất toàn bộ lịch sử thành một danh sách ngắn gồm các dòng mới trong mỗi lần chạy.
  • Mỗi giao dịch đã giải mã đều mang revocationDate và revocationReason. revocationReason bằng 1 nghĩa là khách hàng hoàn tiền vì một vấn đề thực tế hoặc được cảm nhận trong ứng dụng của bạn, còn 0 nghĩa là một lý do khác chẳng hạn như mua nhầm.
  • Endpoint này theo khách hàng, không theo ứng dụng. Không có lệnh gọi duy nhất nào liệt kê mọi lần hoàn tiền trên toàn bộ ứng dụng của bạn, nên bạn đối chiếu theo từng tài khoản từ một id giao dịch, hoặc đọc luồng thông báo REFUND của mình để có góc nhìn toàn ứng dụng.
  • Lý do để thiết lập nó là tiền. Một lần hoàn tiền bạn không bao giờ bắt được sẽ giữ tài khoản hoạt động, và bạn tiếp tục trả cho tính toán, các lệnh gọi model API, lưu trữ và chi trả cho một khách hàng mà App Store đã hoàn trả xong.

Apple lưu một bản ghi có thể truy vấn về mọi lần hoàn tiền mà họ đã cấp trên tài khoản của một khách hàng cho ứng dụng của bạn, và một lệnh gọi trả về nó. Endpoint là Get Refund History, một phần của App Store Server API, và nó đưa cho bạn toàn bộ lịch sử hoàn tiền App Store của khách hàng đó dưới dạng danh sách các giao dịch đã ký. Bạn gửi một id giao dịch, bạn nhận lại những gì Apple đã hoàn tiền, và bạn đối chiếu nó với những gì bạn vẫn đang bật.

Đây là lý do bạn nên bận tâm. Một lần hoàn tiền bạn không bao giờ thấy là một lần hoàn tiền bạn vẫn trả tiền. Tiền đã mất, nhưng tài khoản vẫn hoạt động, và mỗi giờ nó còn hoạt động bạn tiếp tục chi cho tính toán, các lệnh gọi model API, lưu trữ và bất kỳ khoản chi trả nào gắn với khách hàng đó. Các thông báo hoàn tiền của bạn nhằm bắt điều này ngay khoảnh khắc nó xảy ra. Get Refund History là chốt chặn cho khi chúng không làm được, sau một sự cố, một lần triển khai làm rớt webhook, hoặc một ca hỗ trợ nơi bạn cần toàn bộ bức tranh trong một lệnh gọi.

Endpoint lịch sử hoàn tiền App Store trả về những gì

Bạn gọi GET /inApps/v2/refund/lookup/{transactionId} tới App Store Server API, ký bằng cùng JWT bạn dùng cho mọi lệnh gọi khác tới nó. Id giao dịch trong đường dẫn có thể là bất kỳ giao dịch nào của khách hàng. Apple đọc nó như một danh tính, không phải một bộ lọc, và trả về các giao dịch mua đã hoàn tiền của khách hàng đó trên toàn bộ ứng dụng của bạn: hàng tiêu dùng, hàng không tiêu dùng, gói đăng ký tự động gia hạn và không gia hạn, tất cả như nhau. Phiên bản V1 cũ hơn của endpoint này trả về tối đa 50 lần hoàn tiền trong một phản hồi và đã bị ngừng dùng. Phiên bản hiện tại phân trang, nên bạn xử lý được các khách hàng có lịch sử dài mà không cần một payload khổng lồ.

Phản hồi gồm ba trường

FieldNội dung nó chứa
signedTransactionsTối đa 20 giao dịch đã hoàn tiền cho khách hàng này, mỗi cái là một JWS đã ký mà bạn xác minh và giải mã. Được sắp xếp với lần hoàn tiền cũ nhất trước, theo revocationDate. Một mảng rỗng nghĩa là khách hàng không có lần hoàn tiền nào trong ứng dụng của bạn
revisionMột token phân trang. Gửi nó lại để lấy trang tiếp theo, và giữ cái cuối cùng để lần sau chỉ lấy các lần hoàn tiền mới
hasMoreBằng true khi Apple còn giữ nhiều giao dịch đã hoàn tiền hơn số mà trang này trả về, nên bạn gọi lại với revision

Một giao dịch đã hoàn tiền cho bạn biết điều gì

Mỗi mục trong signedTransactions là một JWS. Xác minh nó dựa trên chuỗi chứng chỉ của Apple, giải mã nó, và bạn có một payload giao dịch thông thường với các trường hoàn tiền đã được điền. Đây là những trường quan trọng ở đây.

FieldNó cho bạn biết điều gì
transactionIdId của giao dịch đã hoàn tiền, khóa nối của bạn trở lại giao dịch mua bạn đã ghi nhận
originalTransactionIdId của giao dịch mua đầu tiên trong chuỗi, cách bạn liên kết các lần gia hạn của một gói đăng ký với nhau
productIdSản phẩm đã được hoàn tiền, để bạn thu hồi đúng quyền và không gì khác
revocationDateThời gian UNIX, tính bằng mili giây, khi Apple hoàn tiền giao dịch
revocationReasonLý do Apple hoàn tiền. 1 nghĩa là một vấn đề thực tế hoặc được cảm nhận với ứng dụng của bạn, 0 nghĩa là một lý do khác chẳng hạn như mua nhầm
price, currencySố tiền, tính bằng milliunits, và mã tiền tệ ISO 4217 của nó, để bạn có thể tính tổng số tiền được hoàn
appAccountTokenUUID bạn đính kèm lúc mua, cách sạch sẽ nhất để ánh xạ một lần hoàn tiền trở lại người dùng của chính bạn

Token revision là cách bạn ngừng đọc lại toàn bộ danh sách

Cách ngây thơ để dùng endpoint này là tra cứu một khách hàng và duyệt qua mọi trang mỗi lần. Cách đó có hiệu quả, và trên một khách hàng có năm mươi lần hoàn tiền thì đó là năm mươi dòng bạn đã biết cộng với một dòng mới. Token revision tồn tại để loại bỏ lãng phí đó. Mỗi phản hồi mang một revision. Khi hasMore bằng true, bạn gửi nó lại để lấy trang tiếp theo. Khi bạn đến cuối, bạn giữ revision cuối cùng bạn thấy.

Những gì endpoint này sẽ không làm

Có một kỳ vọng cần bỏ đi trước khi bạn xây dựng trên nó. Get Refund History theo khách hàng, không theo ứng dụng. Bạn không thể hỏi nó mọi lần hoàn tiền ứng dụng của bạn nhận tuần trước. Nó trả lời một câu hỏi, tài khoản này có những lần hoàn tiền nào, và bạn phải đến với một id giao dịch của tài khoản đó để hỏi. Các nhà phát triển liên tục đâm vào bức tường này và đi tìm một endpoint hoàn tiền toàn ứng dụng không hề tồn tại.

Góc nhìn toàn ứng dụng nằm ở nơi khác. Luồng App Store Server Notifications của bạn gửi một thông báo REFUND ngay khoảnh khắc Apple cấp mỗi lần, và Get Notification History cho phép bạn phát lại luồng đó được lọc theo các loại hoàn tiền trong một khoảng ngày. Vậy nên sự phân chia rất rõ ràng. Các thông báo và lịch sử của chúng cho bạn dòng chảy toàn ứng dụng. Get Refund History cho bạn danh sách có thẩm quyền của một tài khoản, theo yêu cầu, đó chính là thứ bạn muốn tại bàn hỗ trợ hoặc sau một sự cố.

Một kính lúp lơ lửng trên một dòng được tô sáng của một sổ ghi giao dịch bằng giấy bên cạnh một điện thoại thông minh, minh họa việc tra cứu các lần hoàn tiền của một khách hàng duy nhất trong lịch sử hoàn tiền App Store

Một lần hoàn tiền bị bỏ lỡ khiến bạn tốn bao nhiêu tiền

Endpoint là đường ống. Hóa đơn là lý do bạn lắp đường ống. Mỗi lần hoàn tiền trong danh sách đó là tiền đã được trả lại, và biến số duy nhất còn trong tầm kiểm soát của bạn là bạn tiếp tục chi tiêu bao lâu cho một tài khoản không còn trả tiền.

Bạn tiếp tục trả tiền để phục vụ một tài khoản đã hoàn tiền

Giá mua biến mất ngay khoảnh khắc Apple cấp lần hoàn tiền. Thứ vẫn chạy là chi phí cung cấp. Với một ứng dụng làm công việc thực tế trên mỗi người dùng, đó là tính toán, các lệnh gọi model API, lưu trữ, và bất kỳ khoản chi trả nào cho nhà sáng tạo hoặc đối tác gắn với việc sử dụng của họ. Một khách hàng đã hoàn tiền mà bạn không bao giờ cắt quyền truy cập là một gói đăng ký bạn tự bỏ tiền túi ra tài trợ. Đối chiếu với Get Refund History và thu hồi dựa trên những gì bạn tìm thấy là cách bạn tắt cái đồng hồ tính tiền đó khi một thông báo lọt qua.

Lý do hoàn tiền 1 là một báo cáo lỗi trá hình

revocationReason khiến bạn tốn gấp đôi nếu bạn phớt lờ nó. Chi phí đầu tiên là chính lần hoàn tiền đó. Chi phí thứ hai là mọi lần hoàn tiền tương lai từ cùng một nguyên nhân. Khi một sản phẩm cứ quay lại với revocationReason 1, một vấn đề thực tế hoặc được cảm nhận trong ứng dụng của bạn, Apple đang trao cho bạn một mẫu được gắn nhãn về điều khiến khách hàng đòi lại tiền. Hãy theo dõi xu hướng theo sản phẩm và bạn có thể vá chỗ rò rỉ thay vì chi trả nó từng lần hoàn tiền một.

Bắt được muộn vẫn hơn không bắt được

Một khoản bồi hoàn là dứt điểm với ngân hàng và, trên cửa hàng kia, giờ đây kèm theo một khoản phí mà nhà phát triển phải chịu. Một lần hoàn tiền App Store không như vậy. Nó đã xong, nhưng quyền là của bạn để thu hồi ngay khoảnh khắc bạn biết. Vậy nên ngay cả một lần hoàn tiền bạn tìm ra muộn vài ngày qua endpoint này cũng đáng để tìm. Bạn không thể đòi lại tiền, nhưng bạn có thể dừng khoản chi vẫn đang chạy phía sau nó.

Điều này ăn khớp với các thông báo, và với Google, ra sao

Hãy nghĩ về các mảnh ghép như một hệ thống duy nhất. Thông báo REFUND là tín hiệu trực tiếp, được đẩy tới máy chủ của bạn khi Apple quyết định. Get Refund History là nguồn sự thật theo kiểu kéo cho một khách hàng duy nhất, lệnh gọi bạn thực hiện khi lần đẩy thất bại hoặc khi một con người cần toàn bộ tài khoản ngay trước mặt. Về phía Google Play, hình thức là cùng một ý tưởng với những cái tên khác: một VoidedPurchaseNotification đẩy theo thời gian thực, và Voided Purchases API là danh sách bạn kéo về. Cả hai cửa hàng đều cho bạn một dòng chảy và một sổ cái. Sai lầm là chỉ tin vào dòng chảy, vì các dòng chảy có thể rớt.

Thiết lập nó theo cách RefundHalt

Vòng lặp nhỏ gọn một khi mọi mảnh ghép đã vào chỗ. Lấy một thông báo REFUND làm kích hoạt. Đối chiếu với Get Refund History để một webhook bị rớt không bao giờ để một tài khoản đã hoàn tiền còn hoạt động. Giải mã mỗi giao dịch, khóa nó theo appAccountToken hoặc transactionId trở lại người dùng của bạn, đọc revocationReason để một lần hoàn tiền do lỗi được đánh dấu chứ không chỉ được xếp hồ sơ, và thu hồi đúng quyền chứ không phải toàn bộ tài khoản. Phân trang bằng token revision để bạn đọc các lần hoàn tiền mới, không phải cái cũ.

Đây là phần RefundHalt chạy giúp bạn. Nó lắng nghe các thông báo hoàn tiền, lùi về Get Refund History khi cần danh sách có thẩm quyền, xác minh mọi giao dịch đã ký, thu hồi đúng giao dịch mua, và giữ revision để mỗi lượt chỉ đọc những gì đã thay đổi. Bạn có quyền truy cập bị cắt trong vài giây và một bản ghi sạch sẽ về ai đã được hoàn tiền, vì cái gì, và tại sao, mà không phải tự mình dựng lên việc polling và xác minh JWS.

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

Làm sao tôi xem mọi lần hoàn tiền trên toàn bộ ứng dụng, không chỉ một khách hàng?
Bạn không thể với Get Refund History, vì nó theo khách hàng và cần một id giao dịch cho tài khoản bạn đang hỏi. Để có góc nhìn toàn ứng dụng, dùng luồng App Store Server Notifications của bạn, nó gửi một thông báo REFUND cho mỗi lần hoàn tiền khi Apple cấp, và Get Notification History để phát lại luồng đó được lọc theo các loại hoàn tiền trong một khoảng ngày.
Endpoint Get Refund History trả về bao nhiêu lần hoàn tiền?
Phiên bản hiện tại trả về tối đa 20 giao dịch đã hoàn tiền mỗi trang, được sắp xếp với lần hoàn tiền cũ nhất trước, và phân trang phần còn lại bằng một token revision khi hasMore bằng true. Endpoint V1 đã ngừng dùng trả về tối đa 50 trong một phản hồi. Không có giới hạn về tổng số, nên một khách hàng có lịch sử dài đơn giản là trải qua nhiều trang hơn.
Token revision dùng để làm gì?
Đó là cách bạn phân trang và cách bạn tránh đọc lại toàn bộ lịch sử của một khách hàng mỗi lần. Mỗi phản hồi bao gồm một revision. Bạn gửi nó lại để lấy trang tiếp theo, và bạn lưu cái cuối cùng để lần tra cứu tới chỉ trả về các lần hoàn tiền mới hơn thời điểm đó. Điều đó giữ một lần đối chiếu theo lịch chỉ còn là một danh sách ngắn các dòng mới.
revocationReason trong một giao dịch đã hoàn tiền nghĩa là gì?
Đó là lý do Apple hoàn tiền giao dịch. Giá trị 1 nghĩa là khách hàng hoàn tiền vì một vấn đề thực tế hoặc được cảm nhận bên trong ứng dụng của bạn, còn 0 nghĩa là một lý do khác, chẳng hạn như mua nhầm. revocationDate cho bạn biết lần hoàn tiền xảy ra khi nào, tính bằng mili giây UNIX. Đọc revocationReason cho phép bạn tách một lỗi sản phẩm khỏi một lần hoàn tiền do hối tiếc chỉ xảy ra một lần.
Tôi có còn cần cái này nếu tôi đã xử lý các thông báo REFUND không?
Có, như một chốt chặn. Các thông báo là tín hiệu trực tiếp, nhưng một lần đẩy có thể không đến trong một sự cố, một lần triển khai lỗi, hoặc một thay đổi webhook, và một lần hoàn tiền bị bỏ lỡ để một tài khoản đã hoàn tiền còn hoạt động và tốn tiền của bạn. Get Refund History là nguồn sự thật theo kiểu kéo mà bạn đối chiếu để không có gì còn bật trong khi Apple đã hoàn tiề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.