SubscriptionPurchase.Get 响应缺少 cancelSurveyResult
结论
cancelReason: 0 表示用户主动取消了订阅,但响应中不一定有 cancelSurveyResult。
只有当 Google Play 的取消流程收集并保存了问卷结果时,才会返回 cancelSurveyResult。如果用户没有填写、跳过了问卷,或者当前取消入口没有生成问卷数据,这个字段可能完全不存在。
userInputCancelReason 的返回条件更严格。通常,用户需要在取消问卷中选择“其他”,也就是 cancelSurveyReason: 0,并填写自定义文字,响应中才可能出现该字段。顶层的 cancelReason: 0 和问卷中的 cancelSurveyReason: 0 含义不同,不要混淆。
字段之间的关系
这些字段可以分为三个层级:
cancelReason == 0
└── 用户主动取消
└── cancelSurveyResult(仅在实际取得问卷结果时存在)
├── cancelSurveyReason
└── userInputCancelReason(选择“其他”并输入文字时才可能存在)
只有顶层字段时,响应可能是:
{
"cancelReason": 0,
"userCancellationTimeMillis": "1615196648401"
}
这已经可以说明订阅由用户主动取消,并记录了取消时间,但不表示用户提交过取消问卷。
如果用户选择了预定义原因,响应可能类似:
{
"cancelReason": 0,
"userCancellationTimeMillis": "1615196648401",
"cancelSurveyResult": {
"cancelSurveyReason": 3
}
}
这里没有 userInputCancelReason 很正常,因为用户选择的是预定义选项。
用户选择“其他”并填写说明后,才可能返回完整结构:
{
"cancelReason": 0,
"userCancellationTimeMillis": "1615196648401",
"cancelSurveyResult": {
"cancelSurveyReason": 0,
"userInputCancelReason": "暂时不再需要该服务"
}
}
因此,文档中的 “Only present if cancelReason is 0” 表达的是必要条件,而非充分条件。在 SubscriptionCancelSurveyResult 的上下文中,还需要确认这里的 cancelReason 是否指问卷原因值,而不是顶层的 SubscriptionPurchase.cancelReason。
建议的处理方式
收到 SUBSCRIPTION_CANCELED 后,应将 RTDN 视为订阅状态已发生变化的通知,再使用购买令牌查询最新状态。不要假定每种通知类型都会对应一组固定且完整的 JSON 字段。
处理取消数据时:
- 根据顶层的
cancelReason判断取消类别。 - 使用
userCancellationTimeMillis记录用户取消时间。 - 将
cancelSurveyResult和userInputCancelReason都按可选字段解析。 - 字段缺失时,将其保存为
null、unknown或“不详”,不要因此判定接口调用失败。 - 如果在 RTDN 到达后立即查询,可以延迟并重试有限次数,以排除短暂的数据同步延迟。重试后字段仍然缺失,也属于合法结果。
- 不要根据取消问卷控制用户权益。用户取消自动续订后,订阅通常仍可使用到
expiryTimeMillis。
代码示例
下面的 JavaScript 示例将问卷字段作为可选数据处理:
function parseCancellation(subscription) {
if (subscription.cancelReason !== 0) {
return {
cancelledByUser: false,
cancellationTimeMillis: null,
surveyReason: null,
userInputReason: null
};
}
const survey = subscription.cancelSurveyResult;
return {
cancelledByUser: true,
cancellationTimeMillis:
subscription.userCancellationTimeMillis ?? null,
surveyReason:
survey?.cancelSurveyReason ?? null,
userInputReason:
survey?.userInputCancelReason ?? null
};
}
业务逻辑还可以区分“没有问卷”和“选择了其他但未返回文字”:
const result = parseCancellation(subscription);
if (result.cancelledByUser) {
if (result.surveyReason === null) {
console.log("用户已取消,但 Google Play 未返回取消问卷");
} else if (result.surveyReason === 0) {
console.log(
"用户选择了其他原因:",
result.userInputReason ?? "未提供文字说明"
);
} else {
console.log("用户选择的取消原因代码:", result.surveyReason);
}
}
注意事项
SUBSCRIPTION_CANCELED表示自动续订已取消,不代表用户会立即失去订阅权益。autoRenewing: false表示订阅当前不会自动续订。实际有效期仍应以expiryTimeMillis等订阅状态字段为准。- RTDN 的作用主要是通知服务端重新查询订阅状态,不保证携带完整的业务信息。
- 取消问卷不是可靠的必填数据源,不应作为结算、权限控制或关键审计逻辑的依据。
- 如果需要稳定收集取消原因,可以在应用自己的取消或反馈流程中单独收集,但仍不能假定用户一定会经过应用内流程。
备注:内容仅供参考。
