作为开发者,如何为 iOS App 的客户订单退款?
结论
开发者通常无法在 App Store Connect 中直接为某一笔 iOS App Store 订单手动退款。App、订阅和 App 内购买项目的交易及退款均由 Apple 管理。客户需要通过 Apple 的退款入口提交申请,退款结果由 Apple 审核决定。
收到客户的退款邮件后,应先确认具体诉求,再引导客户访问:
https://reportaproblem.apple.com/
不要要求客户通过邮件提供 Apple ID 密码、银行卡信息或完整付款凭证。
处理步骤
1. 确认订单类型
先确认客户购买的是哪类项目:
- App Store 付费 App
- In-App Purchase
- 自动续期订阅
- 通过开发者自有支付系统完成的订单
前三类通常由 Apple 收款,需要通过 Apple 的退款流程处理。由开发者自有支付系统收款的订单,则应按照相应支付渠道和退款政策处理,不能使用 App Store 的退款入口。
2. 引导客户向 Apple 申请退款
可以这样回复客户:
您的订单由 Apple 处理,我们无法直接操作退款。请使用购买时所用的 Apple Account 登录 https://reportaproblem.apple.com/,选择对应项目,点击“请求退款”并提交原因。退款结果和处理进度以 Apple 的审核通知为准。
页面显示的选项可能因地区、账户状态或交易类型而异。如果客户找不到对应订单,可以建议其联系 Apple 支持。
3. 不要承诺退款结果
开发者可以提供操作指引和必要的产品支持,但无法代替 Apple 批准退款,也不应承诺具体到账时间。退款申请是否符合条件、采用哪种退款方式,均由 Apple 根据交易情况和当地规则判断。
4. 在服务端处理退款后的权益
如果 App 包含订阅、虚拟商品或服务器端权益,开发者还需要识别退款事件并撤销相应权益。仅回复客户邮件,不会自动更新开发者自己的业务数据库。
可以使用 App Store Server Notifications 接收退款相关通知。以下代码只展示处理结构,实际字段和通知类型应以当前使用的 App Store Server API 及通知版本为准:
async function handleAppStoreNotification(signedPayload) {
const notification = await verifyAndDecodeJWS(signedPayload);
switch (notification.notificationType) {
case "REFUND":
await revokeEntitlement(notification.data);
break;
case "REFUND_REVERSED":
await restoreEntitlement(notification.data);
break;
default:
await handleOtherNotification(notification);
}
}
verifyAndDecodeJWS 不能只是普通的 Base64 解码。生产环境必须验证 JWS 签名、证书链和相关声明,同时保证通知处理具有幂等性,避免因通知重试而重复扣减或恢复权益。
使用 StoreKit 2 时,也可以检查已验证交易的撤销状态:
for await result in Transaction.currentEntitlements {
guard case .verified(let transaction) = result else {
continue
}
if transaction.revocationDate != nil {
// 该交易已被撤销,不再授予对应权益
continue
}
// 授予有效权益
}
具体实现取决于权益是在设备端维护,还是由开发者服务器维护。涉及跨设备同步、订阅或高价值虚拟商品时,通常应在服务端保存并校验权益状态。
注意事项
- 不要让客户向开发者发送 Apple ID 密码、验证码或完整银行卡信息。
- 不要混淆“取消订阅”和“申请退款”。取消订阅通常只会阻止下次续费,并不代表已经发生的扣款会自动退回。
- 不要仅凭客户提供的截图撤销权益。应根据经过验证的交易信息或 App Store Server Notification 更新状态。
- 如果客户因功能故障申请退款,可以同时协助排查问题,但不能以提供技术支持为由阻止其向 Apple 申请退款。
- 如果客户购买的是实体商品、线下服务或开发者自有支付订单,应按照对应商户和支付渠道的规则处理,不能套用 App Store 退款流程。
备注:内容仅供参考。
