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

Phát hiện hoàn tiền trong StoreKit 2 quy về một property duy nhất trên transaction, và đó là revocationDate

Khi Apple hoàn tiền cho một khách hàng của bạn, khoản hoàn tiền đó đã nằm sẵn bên trong app của bạn trên revocationDate của transaction, trước khi tác vụ máy chủ của bạn chạy. Đây là nơi phát hiện hoàn tiền StoreKit 2 hiện ra trên thiết bị, revocationDate và revocationReason cho bạn biết điều gì, và tại sao client là để nhanh còn server là để đúng.

Một chiếc điện thoại thông minh phát sáng trên bàn làm việc tối của lập trình viên bên cạnh một chiếc đồng hồ cơ, minh họa việc phát hiện hoàn tiền StoreKit 2 hiện ra bên trong một app

Điểm chính

  • Trong StoreKit 2 một purchase đã được hoàn tiền mang một revocationDate non-nil trên Transaction của nó, nên app của bạn có thể tự phát hiện khoản hoàn tiền, mà không cần gọi máy chủ của bạn.
  • revocationDate được đặt khi App Store hoàn tiền một transaction hoặc khi một khách hàng mất nó qua Family Sharing, nên một ngày non-nil không phải lúc nào cũng là hoàn tiền.
  • revocationReason cho bạn biết lý do: developerIssue nghĩa là khách hàng nêu ra một vấn đề trong app của bạn, và other bao gồm mọi lý do hoàn tiền còn lại.
  • Transaction.currentEntitlements vốn đã loại trừ các purchase đã hoàn tiền và bị thu hồi, nên cổng kiểm soát truy cập phía client sạch nhất chỉ đơn giản là liệu một product có còn xuất hiện ở đó hay không.
  • Transaction.updates chỉ chuyển giao một khoản hoàn tiền xảy ra khi app của bạn đang đóng nếu bạn bắt đầu lắng nghe lúc khởi chạy, nên một Task bị thiếu nghĩa là một khoản hoàn tiền bị bỏ lỡ.
  • Phát hiện phía client chỉ kích hoạt khi app đang mở, đó là lý do REFUND của App Store Server Notifications V2 vẫn là tín hiệu có thẩm quyền ngăn bạn trả tiền để phục vụ một người dùng đã được hoàn tiền.
  • Một khoản hoàn tiền có thể bị đảo ngược, và khi điều đó xảy ra, các trường revocation bị gỡ khỏi transaction và bạn được kỳ vọng khôi phục lại quyền truy cập mà bạn đã cắt.

Hầu hết app biết về một khoản hoàn tiền của Apple từ máy chủ của họ, qua một App Store Server Notification, và không bao giờ nhận ra rằng cùng khoản hoàn tiền đó đã nằm sẵn bên trong app. Nó nằm trên transaction, trong một property tên là revocationDate, và đọc nó cho phép app của bạn cắt quyền truy cập của một khách hàng đã được hoàn tiền vào lần tiếp theo họ mở app, thay vì chờ một tác vụ backend. Phát hiện hoàn tiền StoreKit 2 là một tín hiệu phía client mà hầu hết đội ngũ bỏ qua. Đây là chính xác nơi một khoản hoàn tiền hiện ra trên thiết bị, nó cho bạn biết điều gì, không cho biết điều gì, và tại sao nó thuộc về bên cạnh các thông báo máy chủ của bạn chứ không phải thay thế chúng.

Nơi một khoản hoàn tiền hiện ra bên trong StoreKit 2

StoreKit 2 trao cho bạn các transaction dưới dạng giá trị đã ký, và một khoản hoàn tiền không xóa transaction. Nó đánh dấu transaction. Hai property trên Transaction mang dấu đó, và cả hai đều ở nil suốt vòng đời của một purchase khỏe mạnh. Khi một trong hai trở thành non-nil, App Store đã lấy lại purchase đó.

revocationDate là trường lật giá trị

revocationDate là một Date tùy chọn. Mô tả của chính Apple rất chính xác: đó là ngày App Store hoàn tiền transaction hoặc thu hồi nó khỏi Family Sharing. Với một purchase vẫn còn tốt, nó là nil. Ngay khi một khoản hoàn tiền được xử lý, nó giữ dấu thời gian của khoản hoàn tiền đó. Chỉ một kiểm tra đó, liệu revocationDate có non-nil không, đó là toàn bộ việc phát hiện hoàn tiền phía client. Mọi thứ khác chỉ là các sắc thái chồng lên trên.

revocationReason cho bạn biết tại sao Apple thu hồi nó

revocationReason nằm ngay cạnh ngày và giải thích nguyên nhân. StoreKit cho nó hai giá trị quan trọng đối với hoàn tiền. developerIssue nghĩa là khách hàng nói với Apple rằng khoản hoàn tiền là do một vấn đề thực tế hoặc được cảm nhận trong app của bạn. other bao gồm mọi lý do còn lại. Một giá trị thứ ba, upgradedToBundle, hoàn toàn không phải hoàn tiền; nó đánh dấu một transaction mà App Store thu hồi vì khách hàng chuyển sang một subscription bundle. Hãy đọc lý do trước khi bạn hành động, vì developerIssue là cái đáng đếm: một cụm chúng chính là sản phẩm của bạn đang cho bạn biết nó hỏng ở đâu.

PropertyTypeMột giá trị non-nil nghĩa là gì
revocationDateDate?App Store đã hoàn tiền transaction này, hoặc thu hồi nó qua Family Sharing, vào ngày này
revocationReasondeveloperIssuereasonKhách hàng nêu một vấn đề thực tế hoặc được cảm nhận trong app của bạn
revocationReasonotherreasonKhoản hoàn tiền xảy ra vì một lý do khác mà Apple không liệt kê chi tiết
revocationReasonupgradedToBundlereasonKhông phải hoàn tiền; transaction bị thu hồi vì khách hàng chuyển sang một subscription bundle

currentEntitlements vốn đã loại bỏ một purchase đã hoàn tiền

Bạn không phải lúc nào cũng tự đọc các trường revocation. Transaction.currentEntitlements là chuỗi các purchase mà khách hàng vẫn còn quyền ngay lúc này, và Apple dựng nó để loại ra những cái bạn không nên tôn trọng. Một product mà App Store đã hoàn tiền hoặc thu hồi không xuất hiện trong đó. Các subscription hết hạn cũng vậy, hay các consumable, vốn biến mất ngay khi chúng được dùng xong.

Điều đó làm cho currentEntitlements trở thành cổng sạch nhất để kiểm soát truy cập. Hãy hỏi nó khách hàng sở hữu gì, cấp đúng thứ đó, và một khoản hoàn tiền gỡ bỏ entitlement giúp bạn mà không cần một kiểm tra revocationDate nào. Các trường revocation là dành cho khi bạn muốn chi tiết, ngày và lý do, để ghi lại sự kiện hoặc phản ứng với nó. Danh sách entitlement là dành cho câu hỏi đơn giản là có nên tiếp tục duy trì hay không.

Phát hiện hoàn tiền StoreKit 2 trong thực tế, từ lúc khởi chạy và khi app đang chạy

Có hai thời điểm app của bạn có thể bắt được một khoản hoàn tiền trên thiết bị, và chúng cần mã khác nhau. Một là khi app đang mở và một khoản hoàn tiền xảy ra trực tiếp hoặc trên một thiết bị khác. Cái kia là lúc khởi chạy, bắt kịp mọi thứ đã thay đổi khi bạn đang đóng. Bỏ lỡ cái thứ hai và việc phát hiện hoàn tiền StoreKit 2 của bạn có một lỗ hổng đúng ngay nơi hầu hết các khoản hoàn tiền rơi vào, vì khách hàng hiếm khi mở app của bạn khi họ yêu cầu hoàn tiền.

Bắt đầu lắng nghe lúc khởi chạy nếu không bạn bỏ lỡ các khoản hoàn tiền xảy ra khi đang đóng

Transaction.updates là chuỗi async phát ra một transaction bất cứ khi nào hệ thống tạo hoặc cập nhật một transaction bên ngoài app của bạn hoặc trên một thiết bị khác, bao gồm một khoản hoàn tiền. Chỉ dẫn của Apple thẳng thừng: hãy khởi động một Task lặp qua nó ngay khi app của bạn khởi chạy, nếu không bạn có thể bỏ lỡ các transaction mà nó chỉ chuyển giao một lần lúc khởi động. Một khoản hoàn tiền đến qua đêm sẽ đến qua updates vào lần tiếp theo app mở, nhưng chỉ khi một listener đã chạy sẵn để nhận nó. Không listener, không event, và khoản hoàn tiền vẫn vô hình cho đến khi một thứ khác đối soát nó.

Một purchase trên cùng thiết bị không đến qua updates

Một cái bẫy tóm được những người thử hoàn tiền bằng tay. Một purchase thông thường thực hiện trên cùng thiết bị không đến qua updates; StoreKit trả nó thẳng từ kết quả của lệnh gọi purchase. updates là dành cho các thay đổi ngoài luồng: các khoản hoàn tiền, các phê duyệt Ask to Buy, các lần đổi offer-code, và các purchase thực hiện ở nơi khác. Vậy nên hãy xây dựng việc xử lý hoàn tiền của bạn quanh updatescurrentEntitlements, chứ không phải quanh luồng purchase, vì khoản hoàn tiền sẽ không bao giờ đi ngược lại qua con đường mà việc bán hàng đã đi.

Một hóa đơn giấy nhàu nát trên một bề mặt tối với một con dấu đỏ mờ được ấn ngang qua nó, tượng trưng cho một transaction đã hoàn tiền mà StoreKit 2 đánh dấu bằng một revocation date

Điều mà phát hiện phía client không thể làm cho bạn

Đọc các khoản hoàn tiền trên thiết bị thì nhanh và miễn phí, nhưng nó có một trần, và giả vờ là không có chính là cách doanh thu rò rỉ. Thiết bị chỉ biết những gì StoreKit đã nói với nó, và StoreKit chỉ lên tiếng khi app của bạn đang chạy. Một khách hàng được hoàn tiền và không bao giờ mở lại app của bạn là một khách hàng mà kiểm tra phía client của bạn không bao giờ thấy.

Một revocationDate không phải lúc nào cũng là hoàn tiền

Cùng trường đó lật giá trị vì Family Sharing. Khi một khách hàng mất quyền truy cập vào một purchase được chia sẻ, vì người tổ chức gỡ họ ra hoặc việc chia sẻ kết thúc, transaction đó cũng nhận một revocationDate. Vậy nên một ngày non-nil nghĩa là khách hàng không còn purchase này, đúng là điều bạn cần cho việc kiểm soát truy cập, nhưng không phải lúc nào cũng nghĩa là tiền đã trở ra. Nếu bạn đang đếm các khoản hoàn tiền cho doanh thu, hãy tách các lần thu hồi Family Sharing khỏi các khoản thật sự trước khi bạn tin vào con số.

Một khoản hoàn tiền có thể bị đảo ngược

Một khoản hoàn tiền không phải lúc nào cũng là cuối cùng. Apple có thể đảo ngược một khoản, và khi làm vậy, các trường revocation bị gỡ khỏi transaction và purchase lại hợp lệ. Nếu bạn cắt quyền truy cập khi hoàn tiền, bạn được kỳ vọng khôi phục nó khi có sự đảo ngược. Trên thiết bị điều đó hiện ra như một event updates khác với một transaction sạch; trên máy chủ của bạn đó là một thông báo REFUND_REVERSED riêng biệt. Chỉ xử lý khoản hoàn tiền và bạn sẽ bỏ mặc một khách hàng đang trả tiền không có quyền truy cập mà lại có một hóa đơn còn hiệu lực.

Một lần thu hồi muộn thật sự tốn kém đến đâu

Một khoản hoàn tiền hiếm khi chỉ là giá bán rời khỏi tài khoản của bạn. Đến lúc nó được xử lý xong bạn thường đã tiêu tiền thật để phục vụ purchase đó rồi, và khoản chi đó không trở lại. Các hình ảnh được tạo ra tốn phút GPU. Các câu trả lời chat tốn các lệnh gọi model API mà bạn bị tính phí theo từng token. Các lần tải lên tốn dung lượng lưu trữ mà bạn vẫn đang trả tiền để giữ. Nếu purchase tài trợ một khoản chi trả cho một nhà sáng tạo, số tiền đó đã ra khỏi cửa. Không thứ nào trong số đó đảo ngược cùng với khoản hoàn tiền.

Phát hiện phía client thu hẹp cửa sổ trên một phần duy nhất bạn vẫn có thể kiểm soát, đó là chi tiêu tương lai. Bạn càng sớm biết một purchase đã được hoàn tiền, bạn càng sớm ngừng phục vụ nó. Nhưng thiết bị chỉ cho bạn biết khi app đang mở, nên một người dùng đã được hoàn tiền mà không bao giờ quay lại vẫn giữ bất kỳ quyền truy cập phía máy chủ nào bạn đã cấp, lặng lẽ khiến bạn tốn kém mỗi lần một tác vụ nền hoặc một thiết bị đã đồng bộ hành động thay cho họ. Client làm cho việc thu hồi nhanh. Nó không làm cho việc đó chắc chắn.

Dùng client để nhanh và server để đúng

Thiết kế sạch dùng cả hai tín hiệu cho việc mà mỗi cái giỏi. Trên thiết bị, Transaction.updatescurrentEntitlements cho bạn một phản ứng cục bộ tức thì ngay khi một khách hàng đã được hoàn tiền mở app, tốt cho UI và cho việc hoàn tất các thay đổi entitlement mà không cần một chuyến đi khứ hồi. Trên máy chủ, App Store Server Notifications V2 gửi một thông báo REFUND đến bất kể app có bao giờ được mở lại hay không, đó là tín hiệu duy nhất ngăn backend của bạn một cách đáng tin cậy khỏi chi tiêu trên một tài khoản đã được hoàn tiền.

SignalNó tồn tại ở đâuKích hoạt khiHãy tin nó để
revocationDate trên một transactionDevice, StoreKit 2App của bạn đọc transactionCho bạn biết một purchase cụ thể đã được hoàn tiền hoặc thu hồi
Transaction.updatesDevice, StoreKit 2Một khoản hoàn tiền đến khi app đang chạy, hoặc lúc khởi chạy nếu bạn lắng nghePhản ứng tức thì cho một khách hàng đang hiện diện
currentEntitlementsDevice, StoreKit 2Bạn kiểm tra khách hàng sở hữu gì lúc nàyKiểm soát truy cập mà không cần tự theo dõi các khoản hoàn tiền
REFUND notificationMáy chủ của bạn, App Store Server Notifications V2Apple xử lý khoản hoàn tiền, dù app mở hay khôngNgừng chi tiêu phía máy chủ trên một khách hàng không bao giờ quay lại

Hãy đấu nối các tín hiệu thiết bị cho khách hàng đang cầm điện thoại, và thông báo máy chủ cho người không cầm. Khoản hoàn tiền hiện ra ở cả hai nơi một cách có chủ đích. Chỉ đọc một trong hai chính là cách một tài khoản đã được hoàn tiền tiếp tục khiến bạn tốn kém sau khi việc bán hàng đã mất từ lâu.

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

Làm thế nào để tôi phát hiện một khoản hoàn tiền trong StoreKit 2?
Hãy kiểm tra revocationDate của transaction. Nó là nil đối với một purchase hợp lệ và giữ một ngày một khi App Store hoàn tiền transaction, nên một revocationDate non-nil là tín hiệu rằng một purchase đã được hoàn tiền hoặc thu hồi.
Sự khác biệt giữa revocationDate và revocationReason là gì?
revocationDate là khi App Store lấy lại purchase, và revocationReason là lý do tại sao. Lý do là developerIssue khi khách hàng nêu một vấn đề trong app của bạn và other cho bất cứ điều gì khác.
Một purchase đã được hoàn tiền có còn xuất hiện trong currentEntitlements không?
Không. Transaction.currentEntitlements loại trừ các purchase mà App Store đã hoàn tiền hoặc thu hồi, nên một product đã hoàn tiền tự rơi khỏi entitlements của khách hàng, điều này làm cho nó thành một cổng an toàn để kiểm soát truy cập.
StoreKit có báo cho app của tôi về một khoản hoàn tiền xảy ra khi app đang đóng không?
Chỉ khi bạn lắng nghe từ lúc khởi chạy. Transaction.updates chuyển giao những thay đổi đó một lần lúc khởi động, nên bạn phải khởi động một Task lặp qua nó ngay khi app của bạn khởi chạy nếu không khoản hoàn tiền bị bỏ lỡ cho đến khi một thứ khác đối soát nó.
Phát hiện hoàn tiền phía client có đủ khi đứng một mình không?
Không. Thiết bị chỉ biết về một khoản hoàn tiền khi app của bạn đang chạy, nên một khách hàng không bao giờ mở lại app là vô hình với nó. REFUND của App Store Server Notifications V2 là tín hiệu đến với bạn bất kể điều đó.
Một revocationDate có luôn nghĩa là khách hàng đã được hoàn tiền không?
Không. revocationDate cũng được đặt khi một khách hàng mất một purchase qua Family Sharing, nên một ngày non-nil nghĩa là họ không còn purchase đó nhưng không phải lúc nào cũng nghĩa là tiền đã được trả lại.

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.