การตรวจจับการคืนเงินใน StoreKit 2 สรุปลงที่ property เดียวบน transaction และนั่นคือ revocationDate
เมื่อ Apple คืนเงินให้ลูกค้าคนหนึ่งของคุณ การคืนเงินนั้นอยู่ภายในแอปของคุณแล้วบน revocationDate ของ transaction ก่อนที่งานฝั่งเซิร์ฟเวอร์ของคุณจะทำงาน นี่คือจุดที่การตรวจจับการคืนเงินของ StoreKit 2 ปรากฏบนอุปกรณ์ สิ่งที่ revocationDate และ revocationReason บอกคุณ และเหตุใดฝั่งไคลเอนต์ไว้เพื่อความเร็วและฝั่งเซิร์ฟเวอร์ไว้เพื่อความจริง

ประเด็นสำคัญ
- ใน StoreKit 2 purchase ที่ถูกคืนเงินจะมี revocationDate ที่ไม่ใช่ nil อยู่บน Transaction ของมัน ดังนั้นแอปของคุณจึงตรวจจับการคืนเงินได้เอง โดยไม่ต้องเรียกเซิร์ฟเวอร์ของคุณ
- revocationDate ถูกตั้งค่าเมื่อ App Store คืนเงิน transaction หรือเมื่อลูกค้าสูญเสียมันผ่าน Family Sharing ดังนั้นวันที่ที่ไม่ใช่ nil จึงไม่ได้หมายถึงการคืนเงินเสมอไป
- revocationReason บอกคุณว่าทำไม: developerIssue หมายถึงลูกค้าอ้างว่ามีปัญหาในแอปของคุณ และ other ครอบคลุมเหตุผลการคืนเงินอื่นทุกอย่างที่เหลือ
- Transaction.currentEntitlements ได้คัดออก purchase ที่ถูกคืนเงินและถูกเพิกถอนไว้อยู่แล้ว ดังนั้นประตูฝั่งไคลเอนต์ที่สะอาดที่สุดสำหรับการเข้าถึงคือเพียงว่า product ยังปรากฏอยู่ที่นั่นหรือไม่
- Transaction.updates จะส่งการคืนเงินที่เกิดขึ้นขณะแอปของคุณปิดอยู่ก็ต่อเมื่อคุณเริ่มฟังตอนเปิดแอป ดังนั้น Task ที่ขาดหายไปจึงหมายถึงการคืนเงินที่พลาดไป
- การตรวจจับฝั่งไคลเอนต์ทำงานเฉพาะขณะแอปเปิดอยู่ นั่นคือเหตุผลที่ REFUND ของ App Store Server Notifications V2 ยังคงเป็นสัญญาณที่เชื่อถือได้ซึ่งหยุดคุณจากการจ่ายเพื่อให้บริการผู้ใช้ที่ถูกคืนเงิน
- การคืนเงินสามารถถูกกลับรายการได้ และเมื่อเป็นเช่นนั้น ฟิลด์ revocation จะถูกลบออกจาก transaction และคุณถูกคาดหวังให้คืนสิทธิ์การเข้าถึงที่คุณตัดไป
แอปส่วนใหญ่รู้เรื่องการคืนเงินของ Apple จากเซิร์ฟเวอร์ของตน ผ่าน App Store Server Notification และไม่เคยสังเกตว่าการคืนเงินเดียวกันนั้นอยู่ภายในแอปแล้ว มันอยู่บน transaction ใน property ที่ชื่อ revocationDate และการอ่านมันช่วยให้แอปของคุณตัดสิทธิ์การเข้าถึงของลูกค้าที่ถูกคืนเงินในครั้งถัดไปที่พวกเขาเปิดแอป แทนที่จะรองานฝั่ง backend การตรวจจับการคืนเงินของ StoreKit 2 เป็นสัญญาณฝั่งไคลเอนต์ที่ทีมส่วนใหญ่มองข้าม นี่คือจุดที่การคืนเงินปรากฏบนอุปกรณ์อย่างแม่นยำ สิ่งที่มันบอกคุณ สิ่งที่มันไม่บอก และเหตุใดมันจึงควรอยู่ข้างการแจ้งเตือนฝั่งเซิร์ฟเวอร์ของคุณ ไม่ใช่แทนที่พวกมัน
จุดที่การคืนเงินปรากฏภายใน StoreKit 2
StoreKit 2 ส่งมอบ transaction ให้คุณเป็นค่าที่มีลายเซ็น และการคืนเงินไม่ได้ลบ transaction มันทำเครื่องหมายไว้ property สองตัวบน Transaction เป็นตัวถือเครื่องหมายนั้น และทั้งสองยังคงเป็น nil ตลอดอายุของ purchase ที่สมบูรณ์ เมื่อตัวใดตัวหนึ่งกลายเป็นไม่ใช่ nil แสดงว่า App Store ได้เอา purchase นั้นคืนไปแล้ว
revocationDate คือฟิลด์ที่พลิกค่า
revocationDate เป็น Date แบบ optional คำอธิบายของ Apple เองแม่นยำ: มันคือวันที่ที่ App Store คืนเงิน transaction หรือเพิกถอนมันจาก Family Sharing สำหรับ purchase ที่ยังใช้ได้ดี มันเป็น nil ในวินาทีที่การคืนเงินถูกดำเนินการ มันจะเก็บ timestamp ของการคืนเงินนั้น การตรวจสอบเพียงอย่างเดียวนั้น ว่า revocationDate ไม่ใช่ nil หรือไม่ คือทั้งหมดของการตรวจจับการคืนเงินฝั่งไคลเอนต์ ทุกอย่างที่เหลือคือรายละเอียดปลีกย่อยที่ซ้อนอยู่ด้านบน
revocationReason บอกคุณว่าทำไม Apple จึงดึงมันออก
revocationReason อยู่ข้างวันที่และอธิบายสาเหตุ StoreKit ให้ค่าสองค่าที่สำคัญต่อการคืนเงิน developerIssue หมายถึงลูกค้าบอก Apple ว่าการคืนเงินเกิดจากปัญหาจริงหรือที่รับรู้ได้ในแอปของคุณ other ครอบคลุมทุกเหตุผลที่เหลือ ค่าที่สาม upgradedToBundle ไม่ใช่การคืนเงินเลย มันบ่งชี้ transaction ที่ App Store เพิกถอนเพราะลูกค้าย้ายไปยัง subscription bundle อ่านเหตุผลก่อนที่คุณจะลงมือ เพราะ developerIssue คือค่าที่ควรค่าแก่การนับ กลุ่มก้อนของมันคือสินค้าของคุณเองกำลังบอกคุณว่ามันพังตรงไหน
| Property | Type | ค่าที่ไม่ใช่ nil หมายถึงอะไร |
|---|---|---|
revocationDate | Date? | App Store คืนเงิน transaction นี้ หรือเพิกถอนผ่าน Family Sharing ในวันที่นี้ |
revocationReason เป็น developerIssue | reason | ลูกค้าอ้างถึงปัญหาจริงหรือที่รับรู้ได้ในแอปของคุณ |
revocationReason เป็น other | reason | การคืนเงินเกิดขึ้นด้วยเหตุผลอื่นที่ Apple ไม่ได้แจกแจง |
revocationReason เป็น upgradedToBundle | reason | ไม่ใช่การคืนเงิน transaction ถูกเพิกถอนเพราะลูกค้าเปลี่ยนไปใช้ subscription bundle |
currentEntitlements ตัด purchase ที่ถูกคืนเงินออกไปแล้ว
คุณไม่จำเป็นต้องอ่านฟิลด์ revocation ด้วยตัวเองเสมอไป Transaction.currentEntitlements คือลำดับของ purchase ที่ลูกค้ายังมีสิทธิ์อยู่ในขณะนี้ และ Apple สร้างมันขึ้นมาเพื่อคัดสิ่งที่คุณไม่ควรให้เกียรติออกไป product ที่ App Store คืนเงินหรือเพิกถอนไปแล้วจะไม่ปรากฏในนั้น subscription ที่หมดอายุก็เช่นกัน หรือ consumable ซึ่งหายไปทันทีที่ใช้เสร็จ
นั่นทำให้ currentEntitlements เป็นประตูที่สะอาดที่สุดสำหรับการเข้าถึง ถามมันว่าลูกค้าเป็นเจ้าของอะไร มอบสิ่งนั้นให้พอดี และการคืนเงินจะลบ entitlement ให้คุณโดยไม่ต้องตรวจสอบ revocationDate แม้แต่ครั้งเดียว ฟิลด์ revocation มีไว้สำหรับตอนที่คุณต้องการรายละเอียด วันที่และเหตุผล เพื่อบันทึกเหตุการณ์หรือตอบสนองต่อมัน รายการ entitlement มีไว้สำหรับคำถามง่ายๆ ว่าจะเปิดไฟทิ้งไว้หรือไม่
การตรวจจับการคืนเงินของ StoreKit 2 ในทางปฏิบัติ ตั้งแต่ตอนเปิดแอปและขณะแอปทำงาน
มีสองช่วงเวลาที่แอปของคุณจับการคืนเงินบนอุปกรณ์ได้ และทั้งสองต้องใช้โค้ดต่างกัน ช่วงหนึ่งคือขณะแอปเปิดอยู่และการคืนเงินเกิดขึ้นแบบสดหรือบนอุปกรณ์อื่น อีกช่วงคือตอนเปิดแอป ตามเก็บทุกอย่างที่เปลี่ยนไปขณะคุณปิดอยู่ พลาดช่วงที่สองไป และการตรวจจับการคืนเงินของ StoreKit 2 ของคุณจะมีช่องโหว่ตรงจุดที่การคืนเงินส่วนใหญ่ตกลงไปพอดี เพราะลูกค้าแทบไม่เคยเปิดแอปของคุณค้างไว้ตอนที่ขอคืนเงิน
เริ่มฟังตอนเปิดแอป ไม่เช่นนั้นคุณจะพลาดการคืนเงินที่เกิดขึ้นขณะปิดอยู่
Transaction.updates คือลำดับ async ที่ปล่อย transaction ออกมาทุกครั้งที่ระบบสร้างหรืออัปเดตมันนอกแอปของคุณหรือบนอุปกรณ์อื่น รวมถึงการคืนเงินด้วย คำสั่งของ Apple ตรงไปตรงมา: เริ่ม Task ที่วนซ้ำมันทันทีที่แอปของคุณเปิดขึ้น ไม่เช่นนั้นคุณอาจพลาด transaction ที่มันส่งเพียงครั้งเดียวตอนเริ่มต้น การคืนเงินที่มาถึงข้ามคืนจะมาถึงผ่าน updates ในครั้งถัดไปที่แอปเปิด แต่ต่อเมื่อมี listener ทำงานอยู่แล้วเพื่อรับมัน ไม่มี listener ก็ไม่มี event และการคืนเงินยังคงมองไม่เห็นจนกว่าจะมีสิ่งอื่นมากระทบยอดมัน
purchase บนอุปกรณ์เดียวกันไม่ได้มาผ่าน updates
กับดักหนึ่งดักคนที่ทดสอบการคืนเงินด้วยมือ purchase ปกติที่ทำบนอุปกรณ์เดียวกันไม่ได้มาถึงผ่าน updates StoreKit ส่งมันกลับมาโดยตรงจากผลลัพธ์ของการเรียก purchase updates มีไว้สำหรับการเปลี่ยนแปลงนอกช่องทาง: การคืนเงิน การอนุมัติ Ask to Buy การแลก offer-code และ purchase ที่ทำที่อื่น ดังนั้นจงสร้างการจัดการการคืนเงินของคุณรอบ updates และ currentEntitlements ไม่ใช่รอบ flow การซื้อ เพราะการคืนเงินจะไม่มีทางเดินย้อนกลับมาตามเส้นทางที่การขายใช้

สิ่งที่การตรวจจับฝั่งไคลเอนต์ทำให้คุณไม่ได้
การอ่านการคืนเงินบนอุปกรณ์นั้นรวดเร็วและฟรี แต่มันมีเพดาน และการแสร้งทำเป็นว่าไม่มีคือวิธีที่รายได้รั่วไหล อุปกรณ์รู้เพียงสิ่งที่ StoreKit บอกมัน และ StoreKit พูดเฉพาะขณะแอปของคุณทำงาน ลูกค้าที่ได้รับการคืนเงินและไม่เคยเปิดแอปของคุณอีกเลยคือลูกค้าที่การตรวจสอบฝั่งไคลเอนต์ของคุณไม่มีวันเห็น
revocationDate ไม่ได้หมายถึงการคืนเงินเสมอไป
ฟิลด์เดียวกันพลิกค่าเพราะ Family Sharing ด้วย เมื่อลูกค้าสูญเสียการเข้าถึง purchase ที่แชร์กัน เพราะผู้จัดตั้งลบพวกเขาออกหรือการแชร์สิ้นสุดลง transaction นั้นก็ได้รับ revocationDate ด้วยเช่นกัน ดังนั้นวันที่ที่ไม่ใช่ nil หมายถึงลูกค้าไม่มี purchase นี้อีกต่อไป ซึ่งเป็นสิ่งที่คุณต้องการพอดีสำหรับการควบคุมการเข้าถึง แต่ไม่ได้หมายถึงเงินไหลออกกลับไปเสมอไป หากคุณกำลังนับการคืนเงินเพื่อดูรายได้ จงแยกการเพิกถอนของ Family Sharing ออกจากการคืนเงินจริงก่อนที่คุณจะเชื่อตัวเลขนั้น
การคืนเงินสามารถถูกกลับรายการได้
การคืนเงินไม่ได้เป็นที่สิ้นสุดเสมอไป Apple สามารถกลับรายการมันได้ และเมื่อทำเช่นนั้น ฟิลด์ revocation จะถูกลบออกจาก transaction และ purchase กลับมาใช้ได้อีกครั้ง หากคุณตัดการเข้าถึงตอนคืนเงิน คุณถูกคาดหวังให้คืนมันตอนกลับรายการ บนอุปกรณ์ มันปรากฏเป็น event updates อีกครั้งพร้อม transaction ที่สะอาด บนเซิร์ฟเวอร์ของคุณ มันคือการแจ้งเตือน REFUND_REVERSED ที่แยกออกมาต่างหาก จัดการเฉพาะการคืนเงินอย่างเดียว แล้วคุณจะทิ้งลูกค้าที่จ่ายเงินไว้โดยไม่มีการเข้าถึงและมีใบเสร็จที่ใช้งานได้
การเพิกถอนที่ล่าช้ามีต้นทุนจริงเท่าไร
การคืนเงินแทบไม่ใช่แค่ราคาขายที่ออกไปจากบัญชีของคุณ กว่ามันจะเคลียร์เสร็จ ปกติคุณได้ใช้เงินจริงไปแล้วเพื่อให้บริการ purchase นั้น และเงินที่ใช้ไปนั้นไม่ได้กลับมา รูปภาพที่สร้างขึ้นมีต้นทุนเป็นนาที GPU คำตอบแชตมีต้นทุนเป็นการเรียก model API ที่คุณถูกเรียกเก็บเงินต่อ token การอัปโหลดมีต้นทุนเป็นพื้นที่จัดเก็บที่คุณยังจ่ายเพื่อเก็บอยู่ หาก purchase เป็นทุนให้เงินจ่ายแก่ผู้สร้างเนื้อหา เงินนั้นก็ออกไปแล้ว ไม่มีสิ่งใดในนั้นย้อนกลับพร้อมการคืนเงิน
การตรวจจับฝั่งไคลเอนต์ย่อหน้าต่างให้แคบลงบนส่วนเดียวที่คุณยังควบคุมได้ ซึ่งก็คือค่าใช้จ่ายในอนาคต ยิ่งคุณรู้เร็วว่า purchase ถูกคืนเงิน คุณก็ยิ่งหยุดให้บริการมันได้เร็ว แต่อุปกรณ์บอกคุณเฉพาะขณะแอปเปิดอยู่ ดังนั้นผู้ใช้ที่ถูกคืนเงินซึ่งไม่เคยกลับมาอีกเลยจะยังคงเก็บการเข้าถึงฝั่งเซิร์ฟเวอร์ใดๆ ที่คุณมอบให้ไว้ ทำให้คุณเสียเงินอย่างเงียบๆ ทุกครั้งที่งานเบื้องหลังหรืออุปกรณ์ที่ซิงก์ทำงานแทนพวกเขา ฝั่งไคลเอนต์ทำให้การเพิกถอนรวดเร็ว แต่ไม่ได้ทำให้มันแน่นอน
ใช้ไคลเอนต์เพื่อความเร็วและเซิร์ฟเวอร์เพื่อความจริง
การออกแบบที่สะอาดใช้สัญญาณทั้งสองสำหรับสิ่งที่แต่ละอย่างทำได้ดี บนอุปกรณ์ Transaction.updates และ currentEntitlements ให้ปฏิกิริยาแบบทันทีในเครื่องแก่คุณในวินาทีที่ลูกค้าที่ถูกคืนเงินเปิดแอป ดีสำหรับ UI และสำหรับการทำการเปลี่ยนแปลง entitlement ให้เสร็จโดยไม่ต้องเดินทางไปกลับ บนเซิร์ฟเวอร์ App Store Server Notifications V2 ส่งข้อความ REFUND ที่มาถึงไม่ว่าแอปจะถูกเปิดอีกหรือไม่ ซึ่งเป็นสัญญาณเดียวที่หยุด backend ของคุณจากการใช้จ่ายบนบัญชีที่ถูกคืนเงินได้อย่างน่าเชื่อถือ
| Signal | มันอยู่ที่ไหน | ทำงานเมื่อ | เชื่อมันเพื่อ |
|---|---|---|---|
revocationDate บน transaction | Device, StoreKit 2 | แอปของคุณอ่าน transaction | บอกคุณว่า purchase หนึ่งๆ ถูกคืนเงินหรือเพิกถอน |
Transaction.updates | Device, StoreKit 2 | การคืนเงินมาถึงขณะแอปทำงาน หรือตอนเปิดแอปหากคุณฟังอยู่ | ตอบสนองทันทีสำหรับลูกค้าที่อยู่ตรงนั้น |
currentEntitlements | Device, StoreKit 2 | คุณตรวจสอบว่าลูกค้าเป็นเจ้าของอะไรตอนนี้ | คุมประตูการเข้าถึงโดยไม่ต้องติดตามการคืนเงินเอง |
REFUND notification | เซิร์ฟเวอร์ของคุณ, App Store Server Notifications V2 | Apple ดำเนินการคืนเงิน ไม่ว่าแอปจะเปิดหรือไม่ | หยุดค่าใช้จ่ายฝั่งเซิร์ฟเวอร์บนลูกค้าที่ไม่เคยกลับมา |
ต่อสัญญาณอุปกรณ์ไว้สำหรับลูกค้าที่ถือโทรศัพท์อยู่ และการแจ้งเตือนเซิร์ฟเวอร์สำหรับคนที่ไม่ได้ถือ การคืนเงินปรากฏในทั้งสองที่โดยตั้งใจ การอ่านเพียงอย่างใดอย่างหนึ่งคือวิธีที่บัญชีที่ถูกคืนเงินยังคงทำให้คุณเสียเงินหลังจากการขายได้จบลงไปแล้ว
คำถามที่พบบ่อย
- ฉันจะตรวจจับการคืนเงินใน StoreKit 2 ได้อย่างไร?
- ตรวจสอบ revocationDate ของ transaction มันเป็น nil สำหรับ purchase ที่ใช้ได้ และจะเก็บวันที่ไว้เมื่อ App Store คืนเงิน transaction ดังนั้น revocationDate ที่ไม่ใช่ nil คือสัญญาณว่า purchase ถูกคืนเงินหรือเพิกถอน
- revocationDate กับ revocationReason ต่างกันอย่างไร?
- revocationDate คือเวลาที่ App Store เอา purchase คืนไป และ revocationReason คือเหตุผลว่าทำไม เหตุผลเป็น developerIssue เมื่อลูกค้าอ้างถึงปัญหาในแอปของคุณ และ other สำหรับอย่างอื่น
- purchase ที่ถูกคืนเงินยังปรากฏใน currentEntitlements หรือไม่?
- ไม่ Transaction.currentEntitlements คัด purchase ที่ App Store คืนเงินหรือเพิกถอนไปแล้วออก ดังนั้น product ที่ถูกคืนเงินจึงหลุดออกจาก entitlements ของลูกค้าได้เอง ซึ่งทำให้มันเป็นประตูที่ปลอดภัยสำหรับการเข้าถึง
- StoreKit จะบอกแอปของฉันเกี่ยวกับการคืนเงินที่เกิดขึ้นขณะแอปปิดอยู่หรือไม่?
- เฉพาะเมื่อคุณฟังตั้งแต่ตอนเปิดแอป Transaction.updates ส่งการเปลี่ยนแปลงเหล่านั้นครั้งเดียวตอนเริ่มต้น ดังนั้นคุณต้องเริ่ม Task ที่วนซ้ำมันทันทีที่แอปของคุณเปิดขึ้น ไม่เช่นนั้นการคืนเงินจะพลาดไปจนกว่าจะมีสิ่งอื่นมากระทบยอดมัน
- การตรวจจับการคืนเงินฝั่งไคลเอนต์เพียงพอด้วยตัวมันเองหรือไม่?
- ไม่ อุปกรณ์รู้เรื่องการคืนเงินเฉพาะขณะแอปของคุณทำงาน ดังนั้นลูกค้าที่ไม่เคยเปิดแอปอีกเลยจึงมองไม่เห็นสำหรับมัน REFUND ของ App Store Server Notifications V2 คือสัญญาณที่มาถึงคุณไม่ว่าจะอย่างไร
- revocationDate หมายถึงลูกค้าได้รับการคืนเงินเสมอไปหรือไม่?
- ไม่ revocationDate ยังถูกตั้งค่าเมื่อลูกค้าสูญเสีย purchase ผ่าน Family Sharing ดังนั้นวันที่ที่ไม่ใช่ nil หมายถึงพวกเขาไม่มี purchase นั้นแล้ว แต่ไม่ได้หมายถึงมีการคืนเงินเสมอไป
แหล่งข้อมูลและเนื้อหาเพิ่มเติม
- Apple Developer: Transaction.revocationDate (StoreKit)
- Apple Developer: Transaction.RevocationReason (StoreKit)
- Apple Developer: Transaction.currentEntitlements (StoreKit)
- Apple Developer: Transaction.updates (StoreKit)
- Apple Developer: App Store Server Notifications V2 notificationType (REFUND, REFUND_REVERSED)
- Apple Developer Tech Talk: Support customers with StoreKit 2 and App Store Server API
RefundHalt
ระบบอัตโนมัติสำหรับการคืนเงินบน App Store และ Google Play
อ่านต่อ
ทุกหน้าต่างการคืนเงินของ App Store และ Google Play คือการนับถอยหลัง และนี่คือจำนวนชั่วโมงที่แต่ละหน้าต่างให้คุณ
การคืนเงินทุกครั้งใน App Store และ Google Play เริ่มจับเวลา และส่วนใหญ่เดินหน้าไปเองโดยไม่มีคุณ หน้าต่างคืนเงินที่สั้นที่สุดของ Apple คือ 12 ชั่วโมง หน้าต่างการโต้แย้งการเรียกเก็บเงินของ Google คือ 24 และตั้งแต่วันที่ 3 สิงหาคม 2026 หน้าต่างการโต้แย้งที่พลาดไปคือบิลค่าใช้จ่าย ไม่ใช่แค่ยอดขายที่หายไป นี่คือทุกกำหนดเวลาที่แตะบัญชีของคุณ
การแจ้งเตือนการซื้อที่ถูกยกเลิกช่วยให้เซิร์ฟเวอร์ Google Play ของคุณเพิกถอนสิทธิ์การเข้าถึงได้ทันทีที่การคืนเงินเกิดขึ้น
Google Play สามารถส่งการแจ้งเตือนการซื้อที่ถูกยกเลิกไปยังเซิร์ฟเวอร์ของคุณได้ทันทีที่การซื้อถูกคืนเงิน ถูกปฏิเสธการชำระเงิน หรือถูกทำให้เป็นโมฆะ มันพก purchaseToken, orderId, productType และ refundType มาด้วย และมันหมายถึงสิ่งเดียว คือเพิกถอนสิทธิ์การเข้าถึง นี่คือวิธีอ่านมันและเชื่อมต่อเข้ากับระบบ